summaryrefslogtreecommitdiffstats
path: root/src/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/application/LircClient.cpp100
-rw-r--r--src/gui/application/LircClient.h71
-rw-r--r--src/gui/application/LircCommander.cpp170
-rw-r--r--src/gui/application/LircCommander.h112
-rw-r--r--src/gui/application/RosegardenApplication.cpp145
-rw-r--r--src/gui/application/RosegardenApplication.h97
-rw-r--r--src/gui/application/RosegardenDCOP.h50
-rw-r--r--src/gui/application/RosegardenGUIApp.cpp8073
-rw-r--r--src/gui/application/RosegardenGUIApp.cpp.orig8043
-rw-r--r--src/gui/application/RosegardenGUIApp.h1691
-rw-r--r--src/gui/application/RosegardenGUIView.cpp2041
-rw-r--r--src/gui/application/RosegardenGUIView.h347
-rw-r--r--src/gui/application/RosegardenIface.cpp82
-rw-r--r--src/gui/application/RosegardenIface.h130
-rw-r--r--src/gui/application/SetWaitCursor.cpp95
-rw-r--r--src/gui/application/SetWaitCursor.h58
-rw-r--r--src/gui/application/StartupTester.cpp248
-rw-r--r--src/gui/application/StartupTester.h88
-rw-r--r--src/gui/application/main.cpp741
-rw-r--r--src/gui/configuration/AudioConfigurationPage.cpp323
-rw-r--r--src/gui/configuration/AudioConfigurationPage.h107
-rw-r--r--src/gui/configuration/AudioPropertiesPage.cpp184
-rw-r--r--src/gui/configuration/AudioPropertiesPage.h89
-rw-r--r--src/gui/configuration/ColourConfigurationPage.cpp165
-rw-r--r--src/gui/configuration/ColourConfigurationPage.h87
-rw-r--r--src/gui/configuration/ConfigurationPage.cpp37
-rw-r--r--src/gui/configuration/ConfigurationPage.h104
-rw-r--r--src/gui/configuration/DocumentMetaConfigurationPage.cpp366
-rw-r--r--src/gui/configuration/DocumentMetaConfigurationPage.h76
-rw-r--r--src/gui/configuration/GeneralConfigurationPage.cpp429
-rw-r--r--src/gui/configuration/GeneralConfigurationPage.h116
-rw-r--r--src/gui/configuration/HeadersConfigurationPage.cpp294
-rw-r--r--src/gui/configuration/HeadersConfigurationPage.h80
-rw-r--r--src/gui/configuration/LatencyConfigurationPage.cpp157
-rw-r--r--src/gui/configuration/LatencyConfigurationPage.h87
-rw-r--r--src/gui/configuration/MIDIConfigurationPage.cpp400
-rw-r--r--src/gui/configuration/MIDIConfigurationPage.h104
-rw-r--r--src/gui/configuration/MatrixConfigurationPage.cpp68
-rw-r--r--src/gui/configuration/MatrixConfigurationPage.h69
-rw-r--r--src/gui/configuration/NotationConfigurationPage.cpp741
-rw-r--r--src/gui/configuration/NotationConfigurationPage.h117
-rw-r--r--src/gui/configuration/TabbedConfigurationPage.cpp79
-rw-r--r--src/gui/configuration/TabbedConfigurationPage.h78
-rw-r--r--src/gui/dialogs/AddTracksDialog.cpp110
-rw-r--r--src/gui/dialogs/AddTracksDialog.h57
-rw-r--r--src/gui/dialogs/AudioManagerDialog.cpp1257
-rw-r--r--src/gui/dialogs/AudioManagerDialog.h206
-rw-r--r--src/gui/dialogs/AudioPlayingDialog.cpp55
-rw-r--r--src/gui/dialogs/AudioPlayingDialog.h56
-rw-r--r--src/gui/dialogs/AudioPluginDialog.cpp916
-rw-r--r--src/gui/dialogs/AudioPluginDialog.h167
-rw-r--r--src/gui/dialogs/AudioSplitDialog.cpp339
-rw-r--r--src/gui/dialogs/AudioSplitDialog.h88
-rw-r--r--src/gui/dialogs/BeatsBarsDialog.cpp66
-rw-r--r--src/gui/dialogs/BeatsBarsDialog.h63
-rw-r--r--src/gui/dialogs/ClefDialog.cpp273
-rw-r--r--src/gui/dialogs/ClefDialog.h93
-rw-r--r--src/gui/dialogs/CompositionLengthDialog.cpp84
-rw-r--r--src/gui/dialogs/CompositionLengthDialog.h64
-rw-r--r--src/gui/dialogs/ConfigureDialog.cpp118
-rw-r--r--src/gui/dialogs/ConfigureDialog.h58
-rw-r--r--src/gui/dialogs/ConfigureDialogBase.cpp76
-rw-r--r--src/gui/dialogs/ConfigureDialogBase.h69
-rw-r--r--src/gui/dialogs/CountdownBar.cpp68
-rw-r--r--src/gui/dialogs/CountdownBar.h59
-rw-r--r--src/gui/dialogs/CountdownDialog.cpp159
-rw-r--r--src/gui/dialogs/CountdownDialog.h87
-rw-r--r--src/gui/dialogs/DocumentConfigureDialog.cpp151
-rw-r--r--src/gui/dialogs/DocumentConfigureDialog.h60
-rw-r--r--src/gui/dialogs/EventEditDialog.cpp528
-rw-r--r--src/gui/dialogs/EventEditDialog.h113
-rw-r--r--src/gui/dialogs/EventFilterDialog.cpp476
-rw-r--r--src/gui/dialogs/EventFilterDialog.h170
-rw-r--r--src/gui/dialogs/EventParameterDialog.cpp185
-rw-r--r--src/gui/dialogs/EventParameterDialog.h80
-rw-r--r--src/gui/dialogs/ExportDeviceDialog.cpp66
-rw-r--r--src/gui/dialogs/ExportDeviceDialog.h60
-rw-r--r--src/gui/dialogs/FileLocateDialog.cpp104
-rw-r--r--src/gui/dialogs/FileLocateDialog.h66
-rw-r--r--src/gui/dialogs/FileMergeDialog.cpp84
-rw-r--r--src/gui/dialogs/FileMergeDialog.h63
-rw-r--r--src/gui/dialogs/FloatEdit.cpp72
-rw-r--r--src/gui/dialogs/FloatEdit.h68
-rw-r--r--src/gui/dialogs/IdentifyTextCodecDialog.cpp173
-rw-r--r--src/gui/dialogs/IdentifyTextCodecDialog.h71
-rw-r--r--src/gui/dialogs/ImportDeviceDialog.cpp389
-rw-r--r--src/gui/dialogs/ImportDeviceDialog.h110
-rw-r--r--src/gui/dialogs/InterpretDialog.cpp123
-rw-r--r--src/gui/dialogs/InterpretDialog.h65
-rw-r--r--src/gui/dialogs/IntervalDialog.cpp367
-rw-r--r--src/gui/dialogs/IntervalDialog.h94
-rw-r--r--src/gui/dialogs/KeySignatureDialog.cpp402
-rw-r--r--src/gui/dialogs/KeySignatureDialog.h118
-rw-r--r--src/gui/dialogs/LilyPondOptionsDialog.cpp363
-rw-r--r--src/gui/dialogs/LilyPondOptionsDialog.h86
-rw-r--r--src/gui/dialogs/LyricEditDialog.cpp253
-rw-r--r--src/gui/dialogs/LyricEditDialog.h78
-rw-r--r--src/gui/dialogs/MakeOrnamentDialog.cpp73
-rw-r--r--src/gui/dialogs/MakeOrnamentDialog.h62
-rw-r--r--src/gui/dialogs/ManageMetronomeDialog.cpp508
-rw-r--r--src/gui/dialogs/ManageMetronomeDialog.h94
-rw-r--r--src/gui/dialogs/MarkerModifyDialog.cpp113
-rw-r--r--src/gui/dialogs/MarkerModifyDialog.h84
-rw-r--r--src/gui/dialogs/PasteNotationDialog.cpp101
-rw-r--r--src/gui/dialogs/PasteNotationDialog.h72
-rw-r--r--src/gui/dialogs/PitchDialog.cpp57
-rw-r--r--src/gui/dialogs/PitchDialog.h58
-rw-r--r--src/gui/dialogs/PitchPickerDialog.cpp58
-rw-r--r--src/gui/dialogs/PitchPickerDialog.h57
-rw-r--r--src/gui/dialogs/QuantizeDialog.cpp68
-rw-r--r--src/gui/dialogs/QuantizeDialog.h60
-rw-r--r--src/gui/dialogs/RescaleDialog.cpp131
-rw-r--r--src/gui/dialogs/RescaleDialog.h68
-rw-r--r--src/gui/dialogs/ShowSequencerStatusDialog.cpp79
-rw-r--r--src/gui/dialogs/ShowSequencerStatusDialog.h54
-rw-r--r--src/gui/dialogs/SimpleEventEditDialog.cpp1061
-rw-r--r--src/gui/dialogs/SimpleEventEditDialog.h134
-rw-r--r--src/gui/dialogs/SplitByPitchDialog.cpp111
-rw-r--r--src/gui/dialogs/SplitByPitchDialog.h67
-rw-r--r--src/gui/dialogs/SplitByRecordingSrcDialog.cpp114
-rw-r--r--src/gui/dialogs/SplitByRecordingSrcDialog.h62
-rw-r--r--src/gui/dialogs/TempoDialog.cpp475
-rw-r--r--src/gui/dialogs/TempoDialog.h128
-rw-r--r--src/gui/dialogs/TextEventDialog.cpp593
-rw-r--r--src/gui/dialogs/TextEventDialog.h129
-rw-r--r--src/gui/dialogs/TimeDialog.cpp80
-rw-r--r--src/gui/dialogs/TimeDialog.h67
-rw-r--r--src/gui/dialogs/TimeSignatureDialog.cpp316
-rw-r--r--src/gui/dialogs/TimeSignatureDialog.h99
-rw-r--r--src/gui/dialogs/TransportDialog.cpp1164
-rw-r--r--src/gui/dialogs/TransportDialog.h231
-rw-r--r--src/gui/dialogs/TriggerSegmentDialog.cpp181
-rw-r--r--src/gui/dialogs/TriggerSegmentDialog.h71
-rw-r--r--src/gui/dialogs/TupletDialog.cpp365
-rw-r--r--src/gui/dialogs/TupletDialog.h99
-rw-r--r--src/gui/dialogs/UnusedAudioSelectionDialog.cpp92
-rw-r--r--src/gui/dialogs/UnusedAudioSelectionDialog.h62
-rw-r--r--src/gui/dialogs/UseOrnamentDialog.cpp264
-rw-r--r--src/gui/dialogs/UseOrnamentDialog.h82
-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
-rw-r--r--src/gui/general/ActiveItem.cpp32
-rw-r--r--src/gui/general/ActiveItem.h55
-rw-r--r--src/gui/general/BarLine.cpp165
-rw-r--r--src/gui/general/BarLine.h64
-rw-r--r--src/gui/general/BaseTool.cpp89
-rw-r--r--src/gui/general/BaseTool.h112
-rw-r--r--src/gui/general/BaseToolBox.cpp58
-rw-r--r--src/gui/general/BaseToolBox.h69
-rw-r--r--src/gui/general/CanvasCursor.cpp52
-rw-r--r--src/gui/general/CanvasCursor.h55
-rw-r--r--src/gui/general/CanvasItemGC.cpp64
-rw-r--r--src/gui/general/CanvasItemGC.h85
-rw-r--r--src/gui/general/CategoryElement.cpp61
-rw-r--r--src/gui/general/CategoryElement.h71
-rw-r--r--src/gui/general/ClefIndex.cpp100
-rw-r--r--src/gui/general/ClefIndex.h59
-rw-r--r--src/gui/general/EditTool.cpp143
-rw-r--r--src/gui/general/EditTool.h166
-rw-r--r--src/gui/general/EditToolBox.cpp56
-rw-r--r--src/gui/general/EditToolBox.h65
-rw-r--r--src/gui/general/EditView.cpp1717
-rw-r--r--src/gui/general/EditView.h405
-rw-r--r--src/gui/general/EditViewBase.cpp711
-rw-r--r--src/gui/general/EditViewBase.h396
-rw-r--r--src/gui/general/EditViewTimeSigNotifier.h56
-rw-r--r--src/gui/general/GUIPalette.cpp311
-rw-r--r--src/gui/general/GUIPalette.h185
-rw-r--r--src/gui/general/HZoomable.cpp32
-rw-r--r--src/gui/general/HZoomable.h53
-rw-r--r--src/gui/general/LinedStaff.cpp1217
-rw-r--r--src/gui/general/LinedStaff.h759
-rw-r--r--src/gui/general/LinedStaffManager.cpp33
-rw-r--r--src/gui/general/LinedStaffManager.h61
-rw-r--r--src/gui/general/MidiPitchLabel.cpp74
-rw-r--r--src/gui/general/MidiPitchLabel.h57
-rw-r--r--src/gui/general/PixmapFunctions.cpp271
-rw-r--r--src/gui/general/PixmapFunctions.h107
-rw-r--r--src/gui/general/PresetElement.cpp68
-rw-r--r--src/gui/general/PresetElement.h82
-rw-r--r--src/gui/general/PresetGroup.cpp269
-rw-r--r--src/gui/general/PresetGroup.h105
-rw-r--r--src/gui/general/PresetHandlerDialog.cpp281
-rw-r--r--src/gui/general/PresetHandlerDialog.h107
-rw-r--r--src/gui/general/ProgressReporter.cpp53
-rw-r--r--src/gui/general/ProgressReporter.h80
-rw-r--r--src/gui/general/RosegardenCanvasView.cpp485
-rw-r--r--src/gui/general/RosegardenCanvasView.h197
-rw-r--r--src/gui/general/RosegardenScrollView.cpp416
-rw-r--r--src/gui/general/RosegardenScrollView.h183
-rw-r--r--src/gui/general/Spline.cpp130
-rw-r--r--src/gui/general/Spline.h71
-rw-r--r--src/gui/general/StaffLine.cpp64
-rw-r--r--src/gui/general/StaffLine.h78
-rw-r--r--src/gui/kdeext/KLedButton.cpp60
-rw-r--r--src/gui/kdeext/KLedButton.h76
-rw-r--r--src/gui/kdeext/KStartupLogo.cpp159
-rw-r--r--src/gui/kdeext/KStartupLogo.h70
-rw-r--r--src/gui/kdeext/KTmpStatusMsg.cpp70
-rw-r--r--src/gui/kdeext/KTmpStatusMsg.h88
-rw-r--r--src/gui/kdeext/QCanvasGroupableItem.cpp279
-rw-r--r--src/gui/kdeext/QCanvasGroupableItem.h201
-rw-r--r--src/gui/kdeext/QCanvasSimpleSprite.cpp217
-rw-r--r--src/gui/kdeext/QCanvasSimpleSprite.h133
-rw-r--r--src/gui/kdeext/RGLed.cpp729
-rw-r--r--src/gui/kdeext/klearlook.cpp4095
-rw-r--r--src/gui/kdeext/klearlook.h344
-rw-r--r--src/gui/rulers/ChordNameRuler.cpp523
-rw-r--r--src/gui/rulers/ChordNameRuler.h146
-rw-r--r--src/gui/rulers/ControlChangeCommand.cpp50
-rw-r--r--src/gui/rulers/ControlChangeCommand.h55
-rw-r--r--src/gui/rulers/ControlItem.cpp195
-rw-r--r--src/gui/rulers/ControlItem.h79
-rw-r--r--src/gui/rulers/ControlRuler.cpp539
-rw-r--r--src/gui/rulers/ControlRuler.h182
-rw-r--r--src/gui/rulers/ControlRulerEventEraseCommand.cpp58
-rw-r--r--src/gui/rulers/ControlRulerEventEraseCommand.h54
-rw-r--r--src/gui/rulers/ControlRulerEventInsertCommand.cpp67
-rw-r--r--src/gui/rulers/ControlRulerEventInsertCommand.h56
-rw-r--r--src/gui/rulers/ControlSelector.cpp72
-rw-r--r--src/gui/rulers/ControlSelector.h60
-rw-r--r--src/gui/rulers/ControlTool.h39
-rw-r--r--src/gui/rulers/ControllerEventAdapter.cpp83
-rw-r--r--src/gui/rulers/ControllerEventAdapter.h53
-rw-r--r--src/gui/rulers/ControllerEventsRuler.cpp499
-rw-r--r--src/gui/rulers/ControllerEventsRuler.h118
-rw-r--r--src/gui/rulers/DefaultVelocityColour.cpp55
-rw-r--r--src/gui/rulers/DefaultVelocityColour.h54
-rw-r--r--src/gui/rulers/ElementAdapter.h46
-rw-r--r--src/gui/rulers/LoopRuler.cpp363
-rw-r--r--src/gui/rulers/LoopRuler.h148
-rw-r--r--src/gui/rulers/MarkerRuler.cpp490
-rw-r--r--src/gui/rulers/MarkerRuler.h121
-rw-r--r--src/gui/rulers/PercussionPitchRuler.cpp204
-rw-r--r--src/gui/rulers/PercussionPitchRuler.h91
-rw-r--r--src/gui/rulers/PitchRuler.cpp55
-rw-r--r--src/gui/rulers/PitchRuler.h78
-rw-r--r--src/gui/rulers/PropertyBox.cpp77
-rw-r--r--src/gui/rulers/PropertyBox.h74
-rw-r--r--src/gui/rulers/PropertyControlRuler.cpp441
-rw-r--r--src/gui/rulers/PropertyControlRuler.h120
-rw-r--r--src/gui/rulers/PropertyViewRuler.cpp175
-rw-r--r--src/gui/rulers/PropertyViewRuler.h102
-rw-r--r--src/gui/rulers/RawNoteRuler.cpp573
-rw-r--r--src/gui/rulers/RawNoteRuler.h128
-rw-r--r--src/gui/rulers/StandardRuler.cpp172
-rw-r--r--src/gui/rulers/StandardRuler.h108
-rw-r--r--src/gui/rulers/TempoColour.cpp55
-rw-r--r--src/gui/rulers/TempoColour.h60
-rw-r--r--src/gui/rulers/TempoRuler.cpp1091
-rw-r--r--src/gui/rulers/TempoRuler.h180
-rw-r--r--src/gui/rulers/TextRuler.cpp157
-rw-r--r--src/gui/rulers/TextRuler.h112
-rw-r--r--src/gui/rulers/VelocityColour.cpp120
-rw-r--r--src/gui/rulers/VelocityColour.h106
-rw-r--r--src/gui/rulers/ViewElementAdapter.cpp56
-rw-r--r--src/gui/rulers/ViewElementAdapter.h59
-rw-r--r--src/gui/seqmanager/AudioSegmentMmapper.cpp133
-rw-r--r--src/gui/seqmanager/AudioSegmentMmapper.h61
-rw-r--r--src/gui/seqmanager/CompositionMmapper.cpp174
-rw-r--r--src/gui/seqmanager/CompositionMmapper.h75
-rw-r--r--src/gui/seqmanager/ControlBlockMmapper.cpp226
-rw-r--r--src/gui/seqmanager/ControlBlockMmapper.h83
-rw-r--r--src/gui/seqmanager/MetronomeMmapper.cpp268
-rw-r--r--src/gui/seqmanager/MetronomeMmapper.h87
-rw-r--r--src/gui/seqmanager/MidiFilterDialog.cpp229
-rw-r--r--src/gui/seqmanager/MidiFilterDialog.h71
-rw-r--r--src/gui/seqmanager/SegmentMmapper.cpp562
-rw-r--r--src/gui/seqmanager/SegmentMmapper.h112
-rw-r--r--src/gui/seqmanager/SegmentMmapperFactory.cpp96
-rw-r--r--src/gui/seqmanager/SegmentMmapperFactory.h63
-rw-r--r--src/gui/seqmanager/SequenceManager.cpp2141
-rw-r--r--src/gui/seqmanager/SequenceManager.h322
-rw-r--r--src/gui/seqmanager/SequencerMapper.cpp105
-rw-r--r--src/gui/seqmanager/SequencerMapper.h113
-rw-r--r--src/gui/seqmanager/SpecialSegmentMmapper.cpp56
-rw-r--r--src/gui/seqmanager/SpecialSegmentMmapper.h59
-rw-r--r--src/gui/seqmanager/TempoSegmentMmapper.cpp77
-rw-r--r--src/gui/seqmanager/TempoSegmentMmapper.h60
-rw-r--r--src/gui/seqmanager/TimeSigSegmentMmapper.cpp72
-rw-r--r--src/gui/seqmanager/TimeSigSegmentMmapper.h62
-rw-r--r--src/gui/studio/AudioMixerWindow.cpp1734
-rw-r--r--src/gui/studio/AudioMixerWindow.h191
-rw-r--r--src/gui/studio/AudioPlugin.cpp78
-rw-r--r--src/gui/studio/AudioPlugin.h117
-rw-r--r--src/gui/studio/AudioPluginClipboard.cpp32
-rw-r--r--src/gui/studio/AudioPluginClipboard.h52
-rw-r--r--src/gui/studio/AudioPluginManager.cpp307
-rw-r--r--src/gui/studio/AudioPluginManager.h118
-rw-r--r--src/gui/studio/AudioPluginOSCGUI.cpp234
-rw-r--r--src/gui/studio/AudioPluginOSCGUI.h77
-rw-r--r--src/gui/studio/AudioPluginOSCGUIManager.cpp711
-rw-r--r--src/gui/studio/AudioPluginOSCGUIManager.h104
-rw-r--r--src/gui/studio/BankEditorDialog.cpp1713
-rw-r--r--src/gui/studio/BankEditorDialog.h211
-rw-r--r--src/gui/studio/ChangeRecordDeviceCommand.cpp66
-rw-r--r--src/gui/studio/ChangeRecordDeviceCommand.h54
-rw-r--r--src/gui/studio/DeviceEditorDialog.cpp406
-rw-r--r--src/gui/studio/DeviceEditorDialog.h87
-rw-r--r--src/gui/studio/DeviceManagerDialog.cpp833
-rw-r--r--src/gui/studio/DeviceManagerDialog.h121
-rw-r--r--src/gui/studio/MidiBankListViewItem.cpp98
-rw-r--r--src/gui/studio/MidiBankListViewItem.h70
-rw-r--r--src/gui/studio/MidiDeviceListViewItem.cpp88
-rw-r--r--src/gui/studio/MidiDeviceListViewItem.h69
-rw-r--r--src/gui/studio/MidiKeyMapListViewItem.cpp56
-rw-r--r--src/gui/studio/MidiKeyMapListViewItem.h59
-rw-r--r--src/gui/studio/MidiKeyMappingEditor.cpp197
-rw-r--r--src/gui/studio/MidiKeyMappingEditor.h78
-rw-r--r--src/gui/studio/MidiMixerVUMeter.cpp53
-rw-r--r--src/gui/studio/MidiMixerVUMeter.h61
-rw-r--r--src/gui/studio/MidiMixerWindow.cpp742
-rw-r--r--src/gui/studio/MidiMixerWindow.h125
-rw-r--r--src/gui/studio/MidiProgramsEditor.cpp631
-rw-r--r--src/gui/studio/MidiProgramsEditor.h119
-rw-r--r--src/gui/studio/MixerWindow.cpp75
-rw-r--r--src/gui/studio/MixerWindow.h77
-rw-r--r--src/gui/studio/NameSetEditor.cpp190
-rw-r--r--src/gui/studio/NameSetEditor.h90
-rw-r--r--src/gui/studio/OSCMessage.cpp87
-rw-r--r--src/gui/studio/OSCMessage.h75
-rw-r--r--src/gui/studio/RemapInstrumentDialog.cpp184
-rw-r--r--src/gui/studio/RemapInstrumentDialog.h84
-rw-r--r--src/gui/studio/StudioControl.cpp582
-rw-r--r--src/gui/studio/StudioControl.h152
-rw-r--r--src/gui/studio/SynthPluginManagerDialog.cpp360
-rw-r--r--src/gui/studio/SynthPluginManagerDialog.h98
-rw-r--r--src/gui/studio/TimerCallbackAssistant.cpp57
-rw-r--r--src/gui/studio/TimerCallbackAssistant.h61
-rw-r--r--src/gui/ui/RosegardenTransport.ui4361
-rw-r--r--src/gui/ui/audiomanager.rc67
-rw-r--r--src/gui/ui/bankeditor.rc22
-rw-r--r--src/gui/ui/clefinserter.rc11
-rw-r--r--src/gui/ui/controleditor.rc5
-rw-r--r--src/gui/ui/devicemanager.rc5
-rw-r--r--src/gui/ui/eventlist.rc105
-rw-r--r--src/gui/ui/markereditor.rc37
-rw-r--r--src/gui/ui/markerruler.rc14
-rw-r--r--src/gui/ui/matrix.rc301
-rw-r--r--src/gui/ui/matrixeraser.rc15
-rw-r--r--src/gui/ui/matrixmover.rc15
-rw-r--r--src/gui/ui/matrixpainter.rc22
-rw-r--r--src/gui/ui/matrixresizer.rc15
-rw-r--r--src/gui/ui/matrixselector.rc15
-rw-r--r--src/gui/ui/midimixer.rc34
-rw-r--r--src/gui/ui/mixer.rc65
-rw-r--r--src/gui/ui/notation.rc853
-rw-r--r--src/gui/ui/notationeraser.rc12
-rw-r--r--src/gui/ui/notationselector.rc26
-rw-r--r--src/gui/ui/noteinserter.rc23
-rw-r--r--src/gui/ui/restinserter.rc13
-rw-r--r--src/gui/ui/rosegardenui.rc440
-rw-r--r--src/gui/ui/temporuler.rc19
-rw-r--r--src/gui/ui/tempoview.rc96
-rw-r--r--src/gui/ui/textinserter.rc11
-rw-r--r--src/gui/ui/triggermanager.rc40
-rw-r--r--src/gui/widgets/AudioFaderBox.cpp294
-rw-r--r--src/gui/widgets/AudioFaderBox.h114
-rw-r--r--src/gui/widgets/AudioListItem.h97
-rw-r--r--src/gui/widgets/AudioListView.cpp67
-rw-r--r--src/gui/widgets/AudioListView.h44
-rw-r--r--src/gui/widgets/AudioRouteMenu.cpp381
-rw-r--r--src/gui/widgets/AudioRouteMenu.h94
-rw-r--r--src/gui/widgets/AudioVUMeter.cpp103
-rw-r--r--src/gui/widgets/AudioVUMeter.h96
-rw-r--r--src/gui/widgets/BigArrowButton.h47
-rw-r--r--src/gui/widgets/CollapsingFrame.cpp148
-rw-r--r--src/gui/widgets/CollapsingFrame.h75
-rw-r--r--src/gui/widgets/ColourTable.cpp131
-rw-r--r--src/gui/widgets/ColourTable.h72
-rw-r--r--src/gui/widgets/ColourTableItem.cpp52
-rw-r--r--src/gui/widgets/ColourTableItem.h60
-rw-r--r--src/gui/widgets/CurrentProgressDialog.cpp84
-rw-r--r--src/gui/widgets/CurrentProgressDialog.h81
-rw-r--r--src/gui/widgets/DiatonicPitchChooser.cpp244
-rw-r--r--src/gui/widgets/DiatonicPitchChooser.h103
-rw-r--r--src/gui/widgets/Fader.cpp567
-rw-r--r--src/gui/widgets/Fader.h137
-rw-r--r--src/gui/widgets/HSpinBox.cpp81
-rw-r--r--src/gui/widgets/HSpinBox.h67
-rw-r--r--src/gui/widgets/Label.cpp2
-rw-r--r--src/gui/widgets/Label.h63
-rw-r--r--src/gui/widgets/MidiFaderWidget.cpp41
-rw-r--r--src/gui/widgets/MidiFaderWidget.h72
-rw-r--r--src/gui/widgets/PitchChooser.cpp113
-rw-r--r--src/gui/widgets/PitchChooser.h73
-rw-r--r--src/gui/widgets/PitchDragLabel.cpp269
-rw-r--r--src/gui/widgets/PitchDragLabel.h99
-rw-r--r--src/gui/widgets/PluginControl.cpp228
-rw-r--r--src/gui/widgets/PluginControl.h104
-rw-r--r--src/gui/widgets/ProgressBar.cpp44
-rw-r--r--src/gui/widgets/ProgressBar.h56
-rw-r--r--src/gui/widgets/ProgressDialog.cpp209
-rw-r--r--src/gui/widgets/ProgressDialog.h98
-rw-r--r--src/gui/widgets/QDeferScrollView.cpp52
-rw-r--r--src/gui/widgets/QDeferScrollView.h75
-rw-r--r--src/gui/widgets/QuantizeParameters.cpp497
-rw-r--r--src/gui/widgets/QuantizeParameters.h117
-rw-r--r--src/gui/widgets/RosegardenPopupMenu.h43
-rw-r--r--src/gui/widgets/Rotary.cpp560
-rw-r--r--src/gui/widgets/Rotary.h167
-rw-r--r--src/gui/widgets/ScrollBox.cpp159
-rw-r--r--src/gui/widgets/ScrollBox.h89
-rw-r--r--src/gui/widgets/ScrollBoxDialog.cpp68
-rw-r--r--src/gui/widgets/ScrollBoxDialog.h71
-rw-r--r--src/gui/widgets/SpinBox.cpp73
-rw-r--r--src/gui/widgets/SpinBox.h65
-rw-r--r--src/gui/widgets/TextFloat.cpp112
-rw-r--r--src/gui/widgets/TextFloat.h64
-rw-r--r--src/gui/widgets/TimeWidget.cpp668
-rw-r--r--src/gui/widgets/TimeWidget.h125
-rw-r--r--src/gui/widgets/TristateCheckBox.cpp43
-rw-r--r--src/gui/widgets/TristateCheckBox.h69
-rw-r--r--src/gui/widgets/VUMeter.cpp694
-rw-r--r--src/gui/widgets/VUMeter.h154
-rw-r--r--src/gui/widgets/WheelyButton.cpp35
-rw-r--r--src/gui/widgets/WheelyButton.h68
-rw-r--r--src/gui/widgets/ZoomSlider.cpp34
-rw-r--r--src/gui/widgets/ZoomSlider.h175
650 files changed, 171320 insertions, 0 deletions
diff --git a/src/gui/application/LircClient.cpp b/src/gui/application/LircClient.cpp
new file mode 100644
index 0000000..ae731d7
--- /dev/null
+++ b/src/gui/application/LircClient.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ This file is Copyright 2005
+ Toni Arnold <toni__arnold@bluewin.ch>
+
+ 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 "LircClient.h"
+
+#ifdef HAVE_LIRC
+
+#include "misc/Debug.h"
+#include "base/Exception.h"
+#include <qobject.h>
+#include <qsocketnotifier.h>
+#include <fcntl.h>
+#include <cstdlib>
+
+namespace Rosegarden
+{
+
+LircClient::LircClient(void)
+ : QObject()
+{
+ int socketFlags;
+
+ // socket setup with nonblock
+ m_socket = lirc_init("rosegarden", 1);
+ if (m_socket == -1) {
+ throw Exception("Failed to connect to LIRC");
+ }
+
+ if (lirc_readconfig(NULL, &m_config, NULL) == -1) {
+ throw Exception("Failed reading LIRC config file");
+ }
+
+ fcntl(m_socket, F_GETOWN, getpid());
+ socketFlags = fcntl(m_socket, F_GETFL, 0);
+ if (socketFlags != -1) {
+ fcntl(socketFlags, F_SETFL, socketFlags | O_NONBLOCK);
+ }
+
+ m_socketNotifier = new QSocketNotifier(m_socket, QSocketNotifier::Read, 0);
+ connect(m_socketNotifier, SIGNAL(activated(int)), this, SLOT(readButton()) );
+
+ RG_DEBUG << "LircClient::LircClient: connected to socket: " << m_socket << endl;
+}
+
+LircClient::~LircClient()
+{
+ lirc_freeconfig(m_config);
+ delete m_socketNotifier;
+ lirc_deinit();
+
+ RG_DEBUG << "LircClient::~LircClient: cleaned up" << endl;
+}
+
+void LircClient::readButton()
+{
+ char *code;
+ int ret;
+
+ RG_DEBUG << "LircClient::readButton" << endl;
+
+ if (lirc_nextcode(&code) == 0 && code != NULL) { // no error && a string is available
+ // handle any command attached to that button
+ while ( (ret = lirc_code2char(m_config, code, &m_command)) == 0 && m_command != NULL )
+ {
+ emit buttonPressed(m_command);
+ }
+ free(code);
+ }
+}
+
+}
+
+#include "LircClient.moc"
+
+#endif
diff --git a/src/gui/application/LircClient.h b/src/gui/application/LircClient.h
new file mode 100644
index 0000000..b80d3d7
--- /dev/null
+++ b/src/gui/application/LircClient.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ This file is Copyright 2005
+ Toni Arnold <toni__arnold@bluewin.ch>
+
+ 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_LIRCCLIENT_H_
+#define _RG_LIRCCLIENT_H_
+
+#ifdef HAVE_LIRC
+
+#include <qobject.h>
+#include <lirc/lirc_client.h>
+
+class QSocketNotifier;
+
+
+namespace Rosegarden
+{
+
+
+
+class LircClient : public QObject
+{
+ Q_OBJECT
+public:
+ LircClient(void);
+ ~LircClient();
+
+public slots:
+ void readButton();
+
+signals:
+ void buttonPressed(char *);
+
+private:
+ int m_socket;
+ QSocketNotifier *m_socketNotifier;
+ struct lirc_config *m_config;
+ char *m_command;
+};
+
+
+
+}
+
+#endif
+
+#endif
diff --git a/src/gui/application/LircCommander.cpp b/src/gui/application/LircCommander.cpp
new file mode 100644
index 0000000..53562ca
--- /dev/null
+++ b/src/gui/application/LircCommander.cpp
@@ -0,0 +1,170 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ This file is Copyright 2005
+ Toni Arnold <toni__arnold@bluewin.ch>
+
+ 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 "LircCommander.h"
+#include "LircClient.h"
+
+#ifdef HAVE_LIRC
+
+#include "misc/Debug.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/editors/segment/TrackButtons.h"
+#include "RosegardenGUIApp.h"
+#include "RosegardenGUIView.h"
+
+#include <qobject.h>
+
+
+namespace Rosegarden
+{
+
+LircCommander::LircCommander(LircClient *lirc, RosegardenGUIApp *rgGUIApp)
+ : QObject()
+{
+ m_lirc = lirc;
+ m_rgGUIApp = rgGUIApp;
+ connect(m_lirc, SIGNAL(buttonPressed(char *)),
+ this, SLOT(slotExecute(char *)) );
+
+ connect(this, SIGNAL(play()),
+ m_rgGUIApp, SLOT(slotPlay()) );
+ connect(this, SIGNAL(stop()),
+ m_rgGUIApp, SLOT(slotStop()) );
+ connect(this, SIGNAL(record()),
+ m_rgGUIApp, SLOT(slotRecord()) );
+ connect(this, SIGNAL(rewind()),
+ m_rgGUIApp, SLOT(slotRewind()) );
+ connect(this, SIGNAL(rewindToBeginning()),
+ m_rgGUIApp, SLOT(slotRewindToBeginning()) );
+ connect(this, SIGNAL(fastForward()),
+ m_rgGUIApp, SLOT(slotFastforward()) );
+ connect(this, SIGNAL(fastForwardToEnd()),
+ m_rgGUIApp, SLOT(slotFastForwardToEnd()) );
+ connect(this, SIGNAL(toggleRecord()),
+ m_rgGUIApp, SLOT(slotToggleRecord()) );
+ connect(this, SIGNAL(trackDown()),
+ m_rgGUIApp, SLOT(slotTrackDown()) );
+ connect(this, SIGNAL(trackUp()),
+ m_rgGUIApp, SLOT(slotTrackUp()) );
+ connect(this, SIGNAL(trackMute()),
+ m_rgGUIApp, SLOT(slotToggleMutedCurrentTrack()) );
+ connect(this, SIGNAL(trackRecord()),
+ m_rgGUIApp, SLOT(slotToggleRecordCurrentTrack()) );
+}
+
+LircCommander::command LircCommander::commands[] =
+ {
+ { "FORWARD", cmd_fastForward },
+ { "FORWARDTOEND", cmd_fastForwardToEnd },
+ { "PLAY", cmd_play },
+ { "PUNCHINRECORD", cmd_toggleRecord },
+ { "RECORD", cmd_record },
+ { "REWIND", cmd_rewind },
+ { "REWINDTOBEGINNING", cmd_rewindToBeginning },
+ { "STOP", cmd_stop },
+ { "TRACK+", cmd_trackUp },
+ { "TRACK-", cmd_trackDown },
+ { "TRACK-MUTE", cmd_trackMute },
+ { "TRACK-RECORD", cmd_trackRecord },
+ };
+
+
+int LircCommander::compareCommandName(const void *c1, const void *c2)
+{
+ return (strcmp(((struct command *)c1)->name, ((struct command *)c2)->name));
+}
+
+void LircCommander::slotExecute(char *command)
+{
+ struct command tmp, *res;
+
+ RG_DEBUG << "LircCommander::slotExecute: invoking command: " << command << endl;
+
+ // find the function for the name
+ tmp.name = command;
+ res = (struct command *)bsearch(&tmp, commands,
+ sizeof(commands) / sizeof(struct command),
+ sizeof(struct command),
+ compareCommandName);
+ if (res != NULL)
+ {
+ switch (res->code)
+ {
+ case cmd_play:
+ emit play();
+ break;
+ case cmd_stop:
+ emit stop();
+ break;
+ case cmd_record:
+ emit record();
+ break;
+ case cmd_rewind:
+ emit rewind();
+ break;
+ case cmd_rewindToBeginning:
+ emit rewindToBeginning();
+ break;
+ case cmd_fastForward:
+ emit fastForward();
+ break;
+ case cmd_fastForwardToEnd:
+ emit fastForwardToEnd();
+ break;
+ case cmd_toggleRecord:
+ emit toggleRecord();
+ break;
+ case cmd_trackDown:
+ emit trackDown();
+ break;
+ case cmd_trackUp:
+ emit trackUp();
+ break;
+ case cmd_trackMute:
+ emit trackMute();
+ break;
+ case cmd_trackRecord:
+ emit trackRecord();
+ break;
+ default:
+ RG_DEBUG << "LircCommander::slotExecute: unhandled command " << command << endl;
+ return;
+ }
+ RG_DEBUG << "LircCommander::slotExecute: handled command: " << command << endl;
+ }
+ else
+ {
+ RG_DEBUG << "LircCommander::slotExecute: invoking command: " << command
+ << " failed (command not defined in LircCommander::commands[])" << endl;
+ };
+}
+
+}
+
+#include "LircCommander.moc"
+
+#endif
diff --git a/src/gui/application/LircCommander.h b/src/gui/application/LircCommander.h
new file mode 100644
index 0000000..84a857e
--- /dev/null
+++ b/src/gui/application/LircCommander.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ This file is Copyright 2005
+ Toni Arnold <toni__arnold@bluewin.ch>
+
+ 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_LIRCCOMMANDER_H_
+#define _RG_LIRCCOMMANDER_H_
+
+#ifdef HAVE_LIRC
+
+#include <qobject.h>
+#include "base/Track.h"
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIApp;
+class RosegardenGUIDoc;
+class TrackButtons;
+class LircClient;
+
+
+class LircCommander : public QObject
+{
+ Q_OBJECT
+public:
+ LircCommander(LircClient *lirc, RosegardenGUIApp *rgGUIApp);
+
+signals:
+ //for RosegardenGUIApp
+ void play();
+ void stop();
+ void record();
+ void rewind();
+ void rewindToBeginning();
+ void fastForward();
+ void fastForwardToEnd();
+ void toggleRecord();
+ void trackDown();
+ void trackUp();
+ void trackMute();
+ void trackRecord();
+
+private slots:
+ void slotExecute(char *);
+ //void slotDocumentChanged(RosegardenGUIDoc *);
+
+private:
+ LircClient *m_lirc;
+ RosegardenGUIApp *m_rgGUIApp;
+ //TrackButtons *m_trackButtons;
+
+ // commands invoked by lirc
+ enum commandCode {
+ cmd_play,
+ cmd_stop,
+ cmd_record,
+ cmd_rewind,
+ cmd_rewindToBeginning,
+ cmd_fastForward,
+ cmd_fastForwardToEnd,
+ cmd_toggleRecord,
+ cmd_trackDown,
+ cmd_trackUp,
+ cmd_trackMute,
+ cmd_trackRecord
+ };
+
+
+ // the command -> method mapping table
+ static struct command
+ {
+ char *name; /* command name */
+ commandCode code; /* function to process it */
+ }
+ commands[];
+
+ // utilities
+ static int compareCommandName(const void *c1, const void *c2);
+
+};
+
+
+
+}
+
+#endif
+
+#endif
diff --git a/src/gui/application/RosegardenApplication.cpp b/src/gui/application/RosegardenApplication.cpp
new file mode 100644
index 0000000..6e85aab
--- /dev/null
+++ b/src/gui/application/RosegardenApplication.cpp
@@ -0,0 +1,145 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "RosegardenApplication.h"
+
+#include "misc/Debug.h"
+#include "document/ConfigGroups.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/application/RosegardenDCOP.h"
+#include "gui/kdeext/KTmpStatusMsg.h"
+#include "RosegardenGUIApp.h"
+#include <kcmdlineargs.h>
+#include <klocale.h>
+#include <kmainwindow.h>
+#include <kmessagebox.h>
+#include <kprocess.h>
+#include <kuniqueapplication.h>
+#include <qcstring.h>
+#include <qeventloop.h>
+#include <qsessionmanager.h>
+#include <qstring.h>
+#include <dcopclient.h>
+#include <kconfig.h>
+#include <kstatusbar.h>
+
+
+namespace Rosegarden
+{
+
+int RosegardenApplication::newInstance()
+{
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ if (RosegardenGUIApp::self() && args->count() &&
+ RosegardenGUIApp::self()->getDocument() &&
+ RosegardenGUIApp::self()->getDocument()->saveIfModified()) {
+ // Check for modifications and save if necessary - if cancelled
+ // then don't load the new file.
+ //
+ RosegardenGUIApp::self()->openFile(args->arg(0));
+ }
+
+ return KUniqueApplication::newInstance();
+}
+
+bool RosegardenApplication::isSequencerRegistered()
+{
+ if (noSequencerMode())
+ return false;
+ return dcopClient()->isApplicationRegistered(ROSEGARDEN_SEQUENCER_APP_NAME);
+}
+
+bool RosegardenApplication::sequencerSend(QCString dcopCall, QByteArray params)
+{
+ if (noSequencerMode())
+ return false;
+
+ return dcopClient()->send(ROSEGARDEN_SEQUENCER_APP_NAME,
+ ROSEGARDEN_SEQUENCER_IFACE_NAME,
+ dcopCall, params);
+}
+
+bool RosegardenApplication::sequencerCall(QCString dcopCall, QCString& replyType, QByteArray& replyData,
+ QByteArray params, bool useEventLoop)
+{
+ if (noSequencerMode())
+ return false;
+ return dcopClient()->call(ROSEGARDEN_SEQUENCER_APP_NAME,
+ ROSEGARDEN_SEQUENCER_IFACE_NAME,
+ dcopCall, params, replyType, replyData, useEventLoop);
+}
+
+void RosegardenApplication::sfxLoadExited(KProcess *proc)
+{
+ if (!proc->normalExit()) {
+ QString configGroup = config()->group();
+ config()->setGroup(SequencerOptionsConfigGroup);
+ QString soundFontPath = config()->readEntry("soundfontpath", "");
+ config()->setGroup(configGroup);
+
+ KMessageBox::error(mainWidget(),
+ i18n("Failed to load soundfont %1").arg(soundFontPath));
+ } else {
+ RG_DEBUG << "RosegardenApplication::sfxLoadExited() : sfxload exited normally\n";
+ }
+
+}
+
+void RosegardenApplication::slotSetStatusMessage(QString msg)
+{
+ KMainWindow* mainWindow = dynamic_cast<KMainWindow*>(mainWidget());
+ if (mainWindow) {
+ if (msg.isEmpty())
+ msg = KTmpStatusMsg::getDefaultMsg();
+ mainWindow->statusBar()->changeItem(QString(" %1").arg(msg), KTmpStatusMsg::getDefaultId());
+ }
+
+}
+
+void
+RosegardenApplication::refreshGUI(int maxTime)
+{
+ eventLoop()->processEvents(QEventLoop::ExcludeUserInput |
+ QEventLoop::ExcludeSocketNotifiers,
+ maxTime);
+}
+
+void RosegardenApplication::saveState(QSessionManager& sm)
+{
+ emit aboutToSaveState();
+ KUniqueApplication::saveState(sm);
+}
+
+RosegardenApplication* RosegardenApplication::rgApp()
+{
+ return dynamic_cast<RosegardenApplication*>(kApplication());
+}
+
+QByteArray RosegardenApplication::Empty;
+
+}
+
+#include "RosegardenApplication.moc"
diff --git a/src/gui/application/RosegardenApplication.h b/src/gui/application/RosegardenApplication.h
new file mode 100644
index 0000000..4541308
--- /dev/null
+++ b/src/gui/application/RosegardenApplication.h
@@ -0,0 +1,97 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ROSEGARDENAPPLICATION_H_
+#define _RG_ROSEGARDENAPPLICATION_H_
+
+#include <kuniqueapplication.h>
+#include <qcstring.h>
+#include <qstring.h>
+
+
+class QSessionManager;
+class KProcess;
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * RosegardenApplication
+ *
+ * Handles RosegardenGUIApps perceived uniqueness for us.
+ *
+ */
+class RosegardenApplication : public KUniqueApplication
+{
+ Q_OBJECT
+public:
+ RosegardenApplication(): KUniqueApplication(), m_noSequencerMode(false) {}
+
+ /**
+ * Handle the attempt at creation of a new instance -
+ * only accept new file names which we attempt to load
+ * into the existing instance (if it exists)
+ */
+ virtual int newInstance();
+
+ void refreshGUI(int maxTime);
+
+ bool isSequencerRegistered();
+ bool sequencerSend(QCString dcopCall, QByteArray params = Empty);
+ bool sequencerCall(QCString dcopCall, QCString& replyType,
+ QByteArray& replyData, QByteArray params = Empty, bool useEventLoop = false);
+
+ static RosegardenApplication* rgApp();
+
+ static QByteArray Empty;
+
+ void setNoSequencerMode(bool m=true) { m_noSequencerMode = m; }
+ bool noSequencerMode() { return m_noSequencerMode; }
+
+ virtual void saveState(QSessionManager&);
+
+signals:
+ // connect this to RosegardenGUIApp
+ void aboutToSaveState();
+
+public slots:
+ void sfxLoadExited(KProcess *proc);
+ void slotSetStatusMessage(QString txt);
+
+protected:
+ //--------------- Data members ---------------------------------
+
+ bool m_noSequencerMode;
+};
+
+#define rgapp RosegardenApplication::rgApp()
+
+
+}
+
+#endif
diff --git a/src/gui/application/RosegardenDCOP.h b/src/gui/application/RosegardenDCOP.h
new file mode 100644
index 0000000..2689945
--- /dev/null
+++ b/src/gui/application/RosegardenDCOP.h
@@ -0,0 +1,50 @@
+// -*- c-basic-offset: 4 -*-
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <bownie@bownie.com>
+
+ 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 _ROSEGARDEN_DCOP_H_
+#define _ROSEGARDEN_DCOP_H_
+
+
+// The names of our applications and interfaces that we share
+//
+#define ROSEGARDEN_SEQUENCER_APP_NAME "rosegardensequencer"
+#define ROSEGARDEN_SEQUENCER_IFACE_NAME "RosegardenSequencerIface"
+
+#define ROSEGARDEN_GUI_APP_NAME "rosegarden"
+#define ROSEGARDEN_GUI_IFACE_NAME "RosegardenIface"
+
+
+// Sequencer communicates its state through this enum
+//
+typedef enum
+{
+ STOPPED,
+ PLAYING,
+ RECORDING,
+ STOPPING,
+ STARTING_TO_PLAY,
+ STARTING_TO_RECORD,
+ RECORDING_ARMED, // gui only state
+ QUIT
+} TransportStatus;
+
+
+#endif // _ROSEGARDEN_DCOP_H_
diff --git a/src/gui/application/RosegardenGUIApp.cpp b/src/gui/application/RosegardenGUIApp.cpp
new file mode 100644
index 0000000..608ad58
--- /dev/null
+++ b/src/gui/application/RosegardenGUIApp.cpp
@@ -0,0 +1,8073 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "RosegardenGUIApp.h"
+#include <kapplication.h>
+
+#include "gui/editors/segment/TrackEditor.h"
+#include "gui/editors/segment/TrackButtons.h"
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "gui/application/RosegardenDCOP.h"
+#include "base/AnalysisTypes.h"
+#include "base/AudioPluginInstance.h"
+#include "base/Clipboard.h"
+#include "base/Composition.h"
+#include "base/CompositionTimeSliceAdapter.h"
+#include "base/Configuration.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/Profiler.h"
+#include "base/RealTime.h"
+#include "base/Segment.h"
+#include "base/SegmentNotationHelper.h"
+#include "base/Selection.h"
+#include "base/Studio.h"
+#include "base/Track.h"
+#include "commands/edit/CopyCommand.h"
+#include "commands/edit/CutCommand.h"
+#include "commands/edit/EventQuantizeCommand.h"
+#include "commands/edit/PasteSegmentsCommand.h"
+#include "commands/edit/TransposeCommand.h"
+#include "commands/edit/AddMarkerCommand.h"
+#include "commands/edit/ModifyMarkerCommand.h"
+#include "commands/edit/RemoveMarkerCommand.h"
+#include "commands/notation/KeyInsertionCommand.h"
+#include "commands/segment/AddTempoChangeCommand.h"
+#include "commands/segment/AddTimeSignatureAndNormalizeCommand.h"
+#include "commands/segment/AddTimeSignatureCommand.h"
+#include "commands/segment/AudioSegmentAutoSplitCommand.h"
+#include "commands/segment/AudioSegmentRescaleCommand.h"
+#include "commands/segment/AudioSegmentSplitCommand.h"
+#include "commands/segment/ChangeCompositionLengthCommand.h"
+#include "commands/segment/CreateTempoMapFromSegmentCommand.h"
+#include "commands/segment/CutRangeCommand.h"
+#include "commands/segment/DeleteRangeCommand.h"
+#include "commands/segment/InsertRangeCommand.h"
+#include "commands/segment/ModifyDefaultTempoCommand.h"
+#include "commands/segment/MoveTracksCommand.h"
+#include "commands/segment/PasteRangeCommand.h"
+#include "commands/segment/RemoveTempoChangeCommand.h"
+#include "commands/segment/SegmentAutoSplitCommand.h"
+#include "commands/segment/SegmentChangeTransposeCommand.h"
+#include "commands/segment/SegmentJoinCommand.h"
+#include "commands/segment/SegmentLabelCommand.h"
+#include "commands/segment/SegmentReconfigureCommand.h"
+#include "commands/segment/SegmentRescaleCommand.h"
+#include "commands/segment/SegmentSplitByPitchCommand.h"
+#include "commands/segment/SegmentSplitByRecordingSrcCommand.h"
+#include "commands/segment/SegmentSplitCommand.h"
+#include "commands/segment/SegmentTransposeCommand.h"
+#include "commands/studio/CreateOrDeleteDeviceCommand.h"
+#include "commands/studio/ModifyDeviceCommand.h"
+#include "document/io/CsoundExporter.h"
+#include "document/io/HydrogenLoader.h"
+#include "document/io/LilyPondExporter.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/io/RG21Loader.h"
+#include "document/io/MupExporter.h"
+#include "document/io/MusicXmlExporter.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "gui/application/RosegardenApplication.h"
+#include "gui/dialogs/AddTracksDialog.h"
+#include "gui/dialogs/AudioManagerDialog.h"
+#include "gui/dialogs/AudioPluginDialog.h"
+#include "gui/dialogs/AudioSplitDialog.h"
+#include "gui/dialogs/BeatsBarsDialog.h"
+#include "gui/dialogs/CompositionLengthDialog.h"
+#include "gui/dialogs/ConfigureDialog.h"
+#include "gui/dialogs/CountdownDialog.h"
+#include "gui/dialogs/DocumentConfigureDialog.h"
+#include "gui/dialogs/FileMergeDialog.h"
+#include "gui/dialogs/IdentifyTextCodecDialog.h"
+#include "gui/dialogs/IntervalDialog.h"
+#include "gui/dialogs/LilyPondOptionsDialog.h"
+#include "gui/dialogs/ManageMetronomeDialog.h"
+#include "gui/dialogs/QuantizeDialog.h"
+#include "gui/dialogs/RescaleDialog.h"
+#include "gui/dialogs/SplitByPitchDialog.h"
+#include "gui/dialogs/SplitByRecordingSrcDialog.h"
+#include "gui/dialogs/TempoDialog.h"
+#include "gui/dialogs/TimeDialog.h"
+#include "gui/dialogs/TimeSignatureDialog.h"
+#include "gui/dialogs/TransportDialog.h"
+#include "gui/editors/parameters/InstrumentParameterBox.h"
+#include "gui/editors/parameters/RosegardenParameterArea.h"
+#include "gui/editors/parameters/SegmentParameterBox.h"
+#include "gui/editors/parameters/TrackParameterBox.h"
+#include "gui/editors/segment/segmentcanvas/CompositionView.h"
+#include "gui/editors/segment/ControlEditorDialog.h"
+#include "gui/editors/segment/MarkerEditor.h"
+#include "gui/editors/segment/PlayListDialog.h"
+#include "gui/editors/segment/PlayList.h"
+#include "gui/editors/segment/segmentcanvas/SegmentEraser.h"
+#include "gui/editors/segment/segmentcanvas/SegmentJoiner.h"
+#include "gui/editors/segment/segmentcanvas/SegmentMover.h"
+#include "gui/editors/segment/segmentcanvas/SegmentPencil.h"
+#include "gui/editors/segment/segmentcanvas/SegmentResizer.h"
+#include "gui/editors/segment/segmentcanvas/SegmentSelector.h"
+#include "gui/editors/segment/segmentcanvas/SegmentSplitter.h"
+#include "gui/editors/segment/segmentcanvas/SegmentToolBox.h"
+#include "gui/editors/segment/TrackLabel.h"
+#include "gui/editors/segment/TriggerSegmentManager.h"
+#include "gui/editors/tempo/TempoView.h"
+#include "gui/general/EditViewBase.h"
+#include "gui/kdeext/KStartupLogo.h"
+#include "gui/kdeext/KTmpStatusMsg.h"
+#include "gui/seqmanager/MidiFilterDialog.h"
+#include "gui/seqmanager/SequenceManager.h"
+#include "gui/seqmanager/SequencerMapper.h"
+#include "gui/studio/AudioMixerWindow.h"
+#include "gui/studio/AudioPlugin.h"
+#include "gui/studio/AudioPluginManager.h"
+#include "gui/studio/AudioPluginOSCGUIManager.h"
+#include "gui/studio/BankEditorDialog.h"
+#include "gui/studio/DeviceManagerDialog.h"
+#include "gui/studio/MidiMixerWindow.h"
+#include "gui/studio/RemapInstrumentDialog.h"
+#include "gui/studio/StudioControl.h"
+#include "gui/studio/SynthPluginManagerDialog.h"
+#include "gui/widgets/CurrentProgressDialog.h"
+#include "gui/widgets/ProgressBar.h"
+#include "gui/widgets/ProgressDialog.h"
+#include "LircClient.h"
+#include "LircCommander.h"
+#include "RosegardenGUIView.h"
+#include "RosegardenIface.h"
+#include "SetWaitCursor.h"
+#include "sound/AudioFile.h"
+#include "sound/AudioFileManager.h"
+#include "sound/MappedCommon.h"
+#include "sound/MappedComposition.h"
+#include "sound/MappedEvent.h"
+#include "sound/MappedStudio.h"
+#include "sound/MidiFile.h"
+#include "sound/PluginIdentifier.h"
+#include "sound/SoundDriver.h"
+#include "StartupTester.h"
+#include <dcopclient.h>
+#include <dcopobject.h>
+#include <dcopref.h>
+#include <kaction.h>
+#include <kconfig.h>
+#include <kdcopactionproxy.h>
+#include <kdockwidget.h>
+#include <kedittoolbar.h>
+#include <kfiledialog.h>
+#include <kglobal.h>
+#include <kinputdialog.h>
+#include <kio/netaccess.h>
+#include <kkeydialog.h>
+#include <klocale.h>
+#include <kmainwindow.h>
+#include <kmessagebox.h>
+#include <kmimetype.h>
+#include <kprocess.h>
+#include <kstatusbar.h>
+#include <kstdaccel.h>
+#include <kstdaction.h>
+#include <kstddirs.h>
+#include <ktempfile.h>
+#include <ktip.h>
+#include <ktoolbar.h>
+#include <kurl.h>
+#include <kxmlguiclient.h>
+#include <qaccel.h>
+#include <qcanvas.h>
+#include <qcstring.h>
+#include <qcursor.h>
+#include <qdatastream.h>
+#include <qdialog.h>
+#include <qdir.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qiconset.h>
+#include <qinputdialog.h>
+#include <qlabel.h>
+#include <qobject.h>
+#include <qobjectlist.h>
+#include <qpixmap.h>
+#include <qpopupmenu.h>
+#include <qpushbutton.h>
+#include <qregexp.h>
+#include <qslider.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qtextcodec.h>
+#include <qtimer.h>
+#include <qvaluevector.h>
+#include <qwidget.h>
+
+#ifdef HAVE_LIBJACK
+#include <jack/jack.h>
+#endif
+
+
+namespace Rosegarden
+{
+
+RosegardenGUIApp::RosegardenGUIApp(bool useSequencer,
+ bool useExistingSequencer,
+ QObject *startupStatusMessageReceiver)
+ : DCOPObject("RosegardenIface"), RosegardenIface(this), KDockMainWindow(0),
+ m_actionsSetup(false),
+ m_fileRecent(0),
+ m_view(0),
+ m_swapView(0),
+ m_mainDockWidget(0),
+ m_dockLeft(0),
+ m_doc(0),
+ m_sequencerProcess(0),
+ m_sequencerCheckedIn(false),
+#ifdef HAVE_LIBJACK
+ m_jackProcess(0),
+#endif
+ m_zoomSlider(0),
+ m_seqManager(0),
+ m_transport(0),
+ m_audioManagerDialog(0),
+ m_originatingJump(false),
+ m_storedLoopStart(0),
+ m_storedLoopEnd(0),
+ m_useSequencer(useSequencer),
+ m_dockVisible(true),
+ m_autoSaveTimer(new QTimer(this)),
+ m_clipboard(new Clipboard),
+ m_playList(0),
+ m_deviceManager(0),
+ m_synthManager(0),
+ m_audioMixer(0),
+ m_midiMixer(0),
+ m_bankEditor(0),
+ m_markerEditor(0),
+ m_tempoView(0),
+ m_triggerSegmentManager(0),
+#ifdef HAVE_LIBLO
+ m_pluginGUIManager(new AudioPluginOSCGUIManager(this)),
+#endif
+ m_playTimer(new QTimer(this)),
+ m_stopTimer(new QTimer(this)),
+ m_startupTester(0),
+#ifdef HAVE_LIRC
+ m_lircClient(0),
+ m_lircCommander(0),
+#endif
+ m_haveAudioImporter(false),
+ m_firstRun(false),
+ m_parameterArea(0)
+{
+ m_myself = this;
+
+
+ if (startupStatusMessageReceiver) {
+ QObject::connect(this, SIGNAL(startupStatusMessage(QString)),
+ startupStatusMessageReceiver,
+ SLOT(slotShowStatusMessage(QString)));
+ }
+
+ // Try to start the sequencer
+ //
+ if (m_useSequencer) {
+
+#ifdef HAVE_LIBJACK
+#define OFFER_JACK_START_OPTION 1
+#ifdef OFFER_JACK_START_OPTION
+ // First we check if jackd is running allready
+
+ std::string jackClientName = "rosegarden";
+
+ // attempt connection to JACK server
+ //
+ jack_client_t* testJackClient;
+ testJackClient = jack_client_new(jackClientName.c_str());
+ if (testJackClient == 0 ) {
+
+ // If no connection to JACK
+ // try to launch JACK - if the configuration wants us to.
+ if (!launchJack()) {
+ KStartupLogo::hideIfStillThere();
+ KMessageBox::error(this, i18n("Attempted to launch JACK audio daemon failed. Audio will be disabled.\nPlease check configuration (Settings -> Configure Rosegarden -> Audio -> Startup)\n and restart."));
+ }
+ } else {
+ //this client was just for testing
+ jack_client_close(testJackClient);
+ }
+#endif // OFFER_JACK_START_OPTION
+#endif // HAVE_LIBJACK
+
+ emit startupStatusMessage(i18n("Starting sequencer..."));
+ launchSequencer(useExistingSequencer);
+
+ } else
+ RG_DEBUG << "RosegardenGUIApp : don't use sequencer\n";
+
+ // Plugin manager
+ //
+ emit startupStatusMessage(i18n("Initializing plugin manager..."));
+ m_pluginManager = new AudioPluginManager();
+
+ // call inits to invoke all other construction parts
+ //
+ emit startupStatusMessage(i18n("Initializing view..."));
+ initStatusBar();
+ setupActions();
+ iFaceDelayedInit(this);
+ initZoomToolbar();
+
+ QPixmap dummyPixmap; // any icon will do
+ m_mainDockWidget = createDockWidget("Rosegarden MainDockWidget", dummyPixmap, 0L, "main_dock_widget");
+ // allow others to dock to the left and right sides only
+ m_mainDockWidget->setDockSite(KDockWidget::DockLeft | KDockWidget::DockRight);
+ // forbit docking abilities of m_mainDockWidget itself
+ m_mainDockWidget->setEnableDocking(KDockWidget::DockNone);
+ setView(m_mainDockWidget); // central widget in a KDE mainwindow
+ setMainDockWidget(m_mainDockWidget); // master dockwidget
+
+ m_dockLeft = createDockWidget("params dock", dummyPixmap, 0L,
+ i18n("Special 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)));
+
+ stateChanged("parametersbox_closed", KXMLGUIClient::StateReverse);
+
+ RosegardenGUIDoc* doc = new RosegardenGUIDoc(this, m_pluginManager);
+
+ m_parameterArea = new RosegardenParameterArea(m_dockLeft);
+ m_dockLeft->setWidget(m_parameterArea);
+
+ // Populate the parameter-box area with the respective
+ // parameter box widgets.
+
+ m_segmentParameterBox = new SegmentParameterBox(doc, m_parameterArea);
+ m_parameterArea->addRosegardenParameterBox(m_segmentParameterBox);
+ m_trackParameterBox = new TrackParameterBox(doc, m_parameterArea);
+ m_parameterArea->addRosegardenParameterBox(m_trackParameterBox);
+ m_instrumentParameterBox = new InstrumentParameterBox(doc, m_parameterArea);
+ m_parameterArea->addRosegardenParameterBox(m_instrumentParameterBox);
+
+ // Lookup the configuration parameter that specifies the default
+ // arrangement, and instantiate it.
+
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+ m_parameterArea->setArrangement((RosegardenParameterArea::Arrangement)
+ kapp->config()->readUnsignedNumEntry("sidebarstyle",
+ RosegardenParameterArea::CLASSIC_STYLE));
+
+ m_dockLeft->update();
+
+ connect(m_instrumentParameterBox,
+ SIGNAL(selectPlugin(QWidget *, InstrumentId, int)),
+ this,
+ SLOT(slotShowPluginDialog(QWidget *, InstrumentId, int)));
+
+ connect(m_instrumentParameterBox,
+ SIGNAL(showPluginGUI(InstrumentId, int)),
+ this,
+ SLOT(slotShowPluginGUI(InstrumentId, int)));
+
+ // relay this through our own signal so that others can use it too
+ connect(m_instrumentParameterBox,
+ SIGNAL(instrumentParametersChanged(InstrumentId)),
+ this,
+ SIGNAL(instrumentParametersChanged(InstrumentId)));
+
+ connect(this,
+ SIGNAL(instrumentParametersChanged(InstrumentId)),
+ m_instrumentParameterBox,
+ SLOT(slotInstrumentParametersChanged(InstrumentId)));
+
+ connect(this,
+ SIGNAL(pluginSelected(InstrumentId, int, int)),
+ m_instrumentParameterBox,
+ SLOT(slotPluginSelected(InstrumentId, int, int)));
+
+ connect(this,
+ SIGNAL(pluginBypassed(InstrumentId, int, bool)),
+ m_instrumentParameterBox,
+ SLOT(slotPluginBypassed(InstrumentId, int, bool)));
+
+ // Load the initial document (this includes doc's own autoload)
+ //
+ setDocument(doc);
+
+ emit startupStatusMessage(i18n("Starting sequence manager..."));
+
+ // transport is created by setupActions()
+ m_seqManager = new SequenceManager(m_doc, getTransport());
+
+ if (m_useSequencer) {
+ // Check the sound driver status and warn the user of any
+ // problems. This warning has to happen early, in case it
+ // affects the ability to load plugins etc from a file on the
+ // command line.
+ m_seqManager->checkSoundDriverStatus(true);
+ }
+
+ if (m_view) {
+ connect(m_seqManager, SIGNAL(controllerDeviceEventReceived(MappedEvent *)),
+ m_view, SLOT(slotControllerDeviceEventReceived(MappedEvent *)));
+ }
+
+ if (m_seqManager->getSoundDriverStatus() & AUDIO_OK) {
+ slotStateChanged("got_audio", true);
+ } else {
+ slotStateChanged("got_audio", false);
+ }
+
+ // If we're restarting the gui then make sure any transient
+ // studio objects are cleared away.
+ emit startupStatusMessage(i18n("Clearing studio data..."));
+ m_seqManager->reinitialiseSequencerStudio();
+
+ // Send the transport control statuses for MMC and JACK
+ //
+ m_seqManager->sendTransportControlStatuses();
+
+ // Now autoload
+ //
+ stateChanged("new_file");
+ stateChanged("have_segments", KXMLGUIClient::StateReverse);
+ stateChanged("have_selection", KXMLGUIClient::StateReverse);
+ slotTestClipboard();
+
+ // Check for lack of MIDI devices and disable Studio options accordingly
+ //
+ if (!m_doc->getStudio().haveMidiDevices())
+ stateChanged("got_midi_devices", KXMLGUIClient::StateReverse);
+
+ emit startupStatusMessage(i18n("Starting..."));
+
+ setupFileDialogSpeedbar();
+ readOptions();
+
+ // All toolbars should be created before this is called
+ setAutoSaveSettings(MainWindowConfigGroup, true);
+
+#ifdef HAVE_LIRC
+
+ try {
+ m_lircClient = new LircClient();
+ } catch (Exception e) {
+ RG_DEBUG << e.getMessage().c_str() << endl;
+ // continue without
+ m_lircClient = 0;
+ }
+ if (m_lircClient) {
+ m_lircCommander = new LircCommander(m_lircClient, this);
+ }
+#endif
+
+ stateChanged("have_project_packager", KXMLGUIClient::StateReverse);
+ stateChanged("have_lilypondview", KXMLGUIClient::StateReverse);
+ QTimer::singleShot(1000, this, SLOT(slotTestStartupTester()));
+}
+
+RosegardenGUIApp::~RosegardenGUIApp()
+{
+ RG_DEBUG << "~RosegardenGUIApp()\n";
+
+ if (getView() &&
+ getView()->getTrackEditor() &&
+ getView()->getTrackEditor()->getSegmentCanvas()) {
+ getView()->getTrackEditor()->getSegmentCanvas()->endAudioPreviewGeneration();
+ }
+
+#ifdef HAVE_LIBLO
+ delete m_pluginGUIManager;
+#endif
+
+ if (isSequencerRunning() && !isSequencerExternal()) {
+ m_sequencerProcess->blockSignals(true);
+ rgapp->sequencerSend("quit()");
+ usleep(300000);
+ delete m_sequencerProcess;
+ }
+
+ delete m_jumpToQuickMarkerAction;
+ delete m_setQuickMarkerAction;
+
+ delete m_transport;
+
+ delete m_seqManager;
+
+#ifdef HAVE_LIRC
+
+ delete m_lircCommander;
+ delete m_lircClient;
+#endif
+
+ delete m_doc;
+ Profiles::getInstance()->dump();
+}
+
+void RosegardenGUIApp::setupActions()
+{
+ // setup File menu
+ // New Window ?
+ KStdAction::openNew (this, SLOT(slotFileNew()), actionCollection());
+ KStdAction::open (this, SLOT(slotFileOpen()), actionCollection());
+ m_fileRecent = KStdAction::openRecent(this,
+ SLOT(slotFileOpenRecent(const KURL&)),
+ actionCollection());
+ KStdAction::save (this, SLOT(slotFileSave()), actionCollection());
+ KStdAction::saveAs(this, SLOT(slotFileSaveAs()), actionCollection());
+ KStdAction::revert(this, SLOT(slotRevertToSaved()), actionCollection());
+ KStdAction::close (this, SLOT(slotFileClose()), actionCollection());
+ KStdAction::print (this, SLOT(slotFilePrint()), actionCollection());
+ KStdAction::printPreview (this, SLOT(slotFilePrintPreview()), actionCollection());
+
+ new KAction(i18n("Import Rosegarden &Project file..."), 0, 0, this,
+ SLOT(slotImportProject()), actionCollection(),
+ "file_import_project");
+
+ new KAction(i18n("Import &MIDI file..."), 0, 0, this,
+ SLOT(slotImportMIDI()), actionCollection(),
+ "file_import_midi");
+
+ new KAction(i18n("Import &Rosegarden 2.1 file..."), 0, 0, this,
+ SLOT(slotImportRG21()), actionCollection(),
+ "file_import_rg21");
+
+ new KAction(i18n("Import &Hydrogen file..."), 0, 0, this,
+ SLOT(slotImportHydrogen()), actionCollection(),
+ "file_import_hydrogen");
+
+ new KAction(i18n("Merge &File..."), 0, 0, this,
+ SLOT(slotMerge()), actionCollection(),
+ "file_merge");
+
+ new KAction(i18n("Merge &MIDI file..."), 0, 0, this,
+ SLOT(slotMergeMIDI()), actionCollection(),
+ "file_merge_midi");
+
+ new KAction(i18n("Merge &Rosegarden 2.1 file..."), 0, 0, this,
+ SLOT(slotMergeRG21()), actionCollection(),
+ "file_merge_rg21");
+
+ new KAction(i18n("Merge &Hydrogen file..."), 0, 0, this,
+ SLOT(slotMergeHydrogen()), actionCollection(),
+ "file_merge_hydrogen");
+
+ new KAction(i18n("Export Rosegarden &Project file..."), 0, 0, this,
+ SLOT(slotExportProject()), actionCollection(),
+ "file_export_project");
+
+ new KAction(i18n("Export &MIDI file..."), 0, 0, this,
+ SLOT(slotExportMIDI()), actionCollection(),
+ "file_export_midi");
+
+ new KAction(i18n("Export &LilyPond file..."), 0, 0, this,
+ SLOT(slotExportLilyPond()), actionCollection(),
+ "file_export_lilypond");
+
+ new KAction(i18n("Export Music&XML file..."), 0, 0, this,
+ SLOT(slotExportMusicXml()), actionCollection(),
+ "file_export_musicxml");
+
+ new KAction(i18n("Export &Csound score file..."), 0, 0, this,
+ SLOT(slotExportCsound()), actionCollection(),
+ "file_export_csound");
+
+ new KAction(i18n("Export M&up file..."), 0, 0, this,
+ SLOT(slotExportMup()), actionCollection(),
+ "file_export_mup");
+
+ 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");
+
+ new KAction(i18n("Play&list"), 0, 0, this,
+ SLOT(slotPlayList()), actionCollection(),
+ "file_show_playlist");
+
+ KStdAction::quit (this, SLOT(slotQuit()), actionCollection());
+
+ // help menu
+ new KAction(i18n("Rosegarden &Tutorial"), 0, 0, this,
+ SLOT(slotTutorial()), actionCollection(),
+ "tutorial");
+
+ new KAction(i18n("&Bug Reporting Guidelines"), 0, 0, this,
+ SLOT(slotBugGuidelines()), actionCollection(),
+ "guidelines");
+
+ // setup edit menu
+ KStdAction::cut (this, SLOT(slotEditCut()), actionCollection());
+ KStdAction::copy (this, SLOT(slotEditCopy()), actionCollection());
+ KStdAction::paste (this, SLOT(slotEditPaste()), actionCollection());
+
+ //
+ // undo/redo actions are special in that they are connected to
+ // slots later on, when the current document is set up - see
+ // MultiViewCommandHistory::attachView
+ //
+ new KToolBarPopupAction(i18n("Und&o"),
+ "undo",
+ KStdAccel::shortcut(KStdAccel::Undo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Undo));
+
+ new KToolBarPopupAction(i18n("Re&do"),
+ "redo",
+ KStdAccel::shortcut(KStdAccel::Redo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Redo));
+ /////
+
+
+
+ // setup Settings menu
+ //
+ m_viewToolBar = KStdAction::showToolbar (this, SLOT(slotToggleToolBar()), actionCollection(),
+ "show_stock_toolbar");
+
+ m_viewToolsToolBar = new KToggleAction(i18n("Show T&ools Toolbar"), 0, this,
+ SLOT(slotToggleToolsToolBar()), actionCollection(),
+ "show_tools_toolbar");
+
+ m_viewTracksToolBar = new KToggleAction(i18n("Show Trac&ks Toolbar"), 0, this,
+ SLOT(slotToggleTracksToolBar()), actionCollection(),
+ "show_tracks_toolbar");
+
+ m_viewEditorsToolBar = new KToggleAction(i18n("Show &Editors Toolbar"), 0, this,
+ SLOT(slotToggleEditorsToolBar()), actionCollection(),
+ "show_editors_toolbar");
+
+ m_viewTransportToolBar = new KToggleAction(i18n("Show Trans&port Toolbar"), 0, this,
+ SLOT(slotToggleTransportToolBar()), actionCollection(),
+ "show_transport_toolbar");
+
+ m_viewZoomToolBar = new KToggleAction(i18n("Show &Zoom Toolbar"), 0, this,
+ SLOT(slotToggleZoomToolBar()), actionCollection(),
+ "show_zoom_toolbar");
+
+ m_viewStatusBar = KStdAction::showStatusbar(this, SLOT(slotToggleStatusBar()),
+ actionCollection(), "show_status_bar");
+
+ m_viewTransport = new KToggleAction(i18n("Show Tra&nsport"), Key_T, this,
+ SLOT(slotToggleTransport()),
+ actionCollection(),
+ "show_transport");
+
+ m_viewTrackLabels = new KToggleAction(i18n("Show Track &Labels"), 0, this,
+ SLOT(slotToggleTrackLabels()),
+ actionCollection(),
+ "show_tracklabels");
+
+ m_viewRulers = new KToggleAction(i18n("Show Playback Position R&uler"), 0, this,
+ SLOT(slotToggleRulers()),
+ actionCollection(),
+ "show_rulers");
+
+ m_viewTempoRuler = new KToggleAction(i18n("Show Te&mpo Ruler"), 0, this,
+ SLOT(slotToggleTempoRuler()),
+ actionCollection(),
+ "show_tempo_ruler");
+
+ m_viewChordNameRuler = new KToggleAction(i18n("Show Cho&rd Name Ruler"), 0, this,
+ SLOT(slotToggleChordNameRuler()),
+ actionCollection(),
+ "show_chord_name_ruler");
+
+
+ m_viewPreviews = new KToggleAction(i18n("Show Segment Pre&views"), 0, this,
+ SLOT(slotTogglePreviews()),
+ actionCollection(),
+ "show_previews");
+
+ new KAction(i18n("Show Special &Parameters"), Key_P, this,
+ SLOT(slotDockParametersBack()),
+ actionCollection(),
+ "show_inst_segment_parameters");
+
+ KStdAction::tipOfDay( this, SLOT( slotShowTip() ), actionCollection() );
+
+ // Standard Actions
+ //
+ KStdAction::saveOptions(this,
+ SLOT(slotSaveOptions()),
+ actionCollection());
+
+ KStdAction::preferences(this,
+ SLOT(slotConfigure()),
+ actionCollection());
+
+ KStdAction::keyBindings(this,
+ SLOT(slotEditKeys()),
+ actionCollection());
+
+ KStdAction::configureToolbars(this,
+ SLOT(slotEditToolbars()),
+ actionCollection());
+
+ KRadioAction *action = 0;
+
+ // Create the select icon
+ //
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QCanvasPixmap pixmap(pixmapDir + "/toolbar/select.xpm");
+ QIconSet icon = QIconSet(pixmap);
+
+ // TODO : add some shortcuts here
+ action = new KRadioAction(i18n("&Select and Edit"), icon, Key_F2,
+ this, SLOT(slotPointerSelected()),
+ actionCollection(), "select");
+ action->setExclusiveGroup("segmenttools");
+
+ action = new KRadioAction(i18n("&Draw"), "pencil", Key_F3,
+ this, SLOT(slotDrawSelected()),
+ actionCollection(), "draw");
+ action->setExclusiveGroup("segmenttools");
+
+ action = new KRadioAction(i18n("&Erase"), "eraser", Key_F4,
+ this, SLOT(slotEraseSelected()),
+ actionCollection(), "erase");
+ action->setExclusiveGroup("segmenttools");
+
+ action = new KRadioAction(i18n("&Move"), "move", Key_F5,
+ this, SLOT(slotMoveSelected()),
+ actionCollection(), "move");
+ action->setExclusiveGroup("segmenttools");
+
+ pixmap.load(pixmapDir + "/toolbar/resize.xpm");
+ icon = QIconSet(pixmap);
+ action = new KRadioAction(i18n("&Resize"), icon, Key_F6,
+ this, SLOT(slotResizeSelected()),
+ actionCollection(), "resize");
+ action->setExclusiveGroup("segmenttools");
+
+ pixmap.load(pixmapDir + "/toolbar/split.xpm");
+ icon = QIconSet(pixmap);
+ action = new KRadioAction(i18n("&Split"), icon, Key_F7,
+ this, SLOT(slotSplitSelected()),
+ actionCollection(), "split");
+ action->setExclusiveGroup("segmenttools");
+
+ pixmap.load(pixmapDir + "/toolbar/join.xpm");
+ icon = QIconSet(pixmap);
+ action = new KRadioAction(i18n("&Join"), icon, 0,
+ this, SLOT(slotJoinSelected()),
+ actionCollection(), "join");
+ action->setExclusiveGroup("segmenttools");
+
+
+ new KAction(i18n("&Harmonize"), 0, this,
+ SLOT(slotHarmonizeSelection()), actionCollection(),
+ "harmonize_selection");
+
+ pixmap.load(pixmapDir + "/toolbar/event-insert-timesig.png");
+ icon = QIconSet(pixmap);
+ new KAction(AddTimeSignatureCommand::getGlobalName(),
+ icon, 0,
+ this, SLOT(slotEditTimeSignature()),
+ actionCollection(), "add_time_signature");
+
+ new KAction(i18n("Open Tempo and Time Signature Editor"), 0, this,
+ SLOT(slotEditTempos()), actionCollection(), "edit_tempos");
+
+ //
+ // Edit menu
+ //
+ new KAction(i18n("Cut Range"), Key_X + CTRL + SHIFT, this,
+ SLOT(slotCutRange()), actionCollection(),
+ "cut_range");
+
+ new KAction(i18n("Copy Range"), Key_C + CTRL + SHIFT, this,
+ SLOT(slotCopyRange()), actionCollection(),
+ "copy_range");
+
+ new KAction(i18n("Paste Range"), Key_V + CTRL + SHIFT, this,
+ SLOT(slotPasteRange()), actionCollection(),
+ "paste_range");
+/*
+ new KAction(i18n("Delete Range"), Key_Delete + SHIFT, this,
+ SLOT(slotDeleteRange()), actionCollection(),
+ "delete_range");
+*/
+ new KAction(i18n("Insert Range..."), Key_Insert + SHIFT, this,
+ SLOT(slotInsertRange()), actionCollection(),
+ "insert_range");
+
+ new KAction(i18n("De&lete"), Key_Delete, this,
+ SLOT(slotDeleteSelectedSegments()), actionCollection(),
+ "delete");
+
+ new KAction(i18n("Select &All Segments"), Key_A + CTRL, this,
+ SLOT(slotSelectAll()), actionCollection(),
+ "select_all");
+
+ pixmap.load(pixmapDir + "/toolbar/event-insert-tempo.png");
+ icon = QIconSet(pixmap);
+ new KAction(AddTempoChangeCommand::getGlobalName(),
+ icon, 0,
+ this, SLOT(slotEditTempo()),
+ actionCollection(), "add_tempo");
+
+ new KAction(ChangeCompositionLengthCommand::getGlobalName(),
+ 0,
+ this, SLOT(slotChangeCompositionLength()),
+ actionCollection(), "change_composition_length");
+
+ new KAction(i18n("Edit Mar&kers..."), Key_K + CTRL, this,
+ SLOT(slotEditMarkers()),
+ actionCollection(), "edit_markers");
+
+ new KAction(i18n("Edit Document P&roperties..."), 0, this,
+ SLOT(slotEditDocumentProperties()),
+ actionCollection(), "edit_doc_properties");
+
+
+ //
+ // Segments menu
+ //
+ new KAction(i18n("Open in &Default Editor"), Key_Return, this,
+ SLOT(slotEdit()), actionCollection(),
+ "edit_default");
+
+ pixmap.load(pixmapDir + "/toolbar/matrix.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Open in Matri&x Editor"), icon, Key_M, this,
+ SLOT(slotEditInMatrix()), actionCollection(),
+ "edit_matrix");
+
+ pixmap.load(pixmapDir + "/toolbar/matrix-percussion.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Open in &Percussion Matrix Editor"), icon, Key_D, this,
+ SLOT(slotEditInPercussionMatrix()), actionCollection(),
+ "edit_percussion_matrix");
+
+ pixmap.load(pixmapDir + "/toolbar/notation.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Open in &Notation Editor"), icon, Key_N, this,
+ SLOT(slotEditAsNotation()), actionCollection(),
+ "edit_notation");
+
+ pixmap.load(pixmapDir + "/toolbar/eventlist.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Open in &Event List Editor"), icon, Key_E, this,
+ SLOT(slotEditInEventList()), actionCollection(),
+ "edit_event_list");
+
+ pixmap.load(pixmapDir + "/toolbar/quantize.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("&Quantize..."), icon, Key_Equal, this,
+ SLOT(slotQuantizeSelection()), actionCollection(),
+ "quantize_selection");
+
+ new KAction(SegmentLabelCommand::getGlobalName(),
+ 0,
+ this, SLOT(slotRelabelSegments()),
+ actionCollection(), "relabel_segment");
+
+ new KAction(SegmentTransposeCommand::getGlobalName(),
+ 0,
+ this, SLOT(slotTransposeSegments()),
+ actionCollection(), "transpose");
+
+ new KAction(i18n("Repeat Last Quantize"), Key_Plus, this,
+ SLOT(slotRepeatQuantizeSelection()), actionCollection(),
+ "repeat_quantize");
+
+ new KAction(SegmentRescaleCommand::getGlobalName(), 0, this,
+ SLOT(slotRescaleSelection()), actionCollection(),
+ "rescale");
+
+ new KAction(SegmentAutoSplitCommand::getGlobalName(), 0, this,
+ SLOT(slotAutoSplitSelection()), actionCollection(),
+ "auto_split");
+
+ new KAction(SegmentSplitByPitchCommand::getGlobalName(), 0, this,
+ SLOT(slotSplitSelectionByPitch()), actionCollection(),
+ "split_by_pitch");
+
+ new KAction(SegmentSplitByRecordingSrcCommand::getGlobalName(), 0, this,
+ SLOT(slotSplitSelectionByRecordedSrc()), actionCollection(),
+ "split_by_recording");
+
+ new KAction(i18n("Split at Time..."), 0, this,
+ SLOT(slotSplitSelectionAtTime()), actionCollection(),
+ "split_at_time");
+
+ new KAction(i18n("Jog &Left"), Key_Left + ALT, this,
+ SLOT(slotJogLeft()), actionCollection(),
+ "jog_left");
+
+ new KAction(i18n("Jog &Right"), Key_Right + ALT, this,
+ SLOT(slotJogRight()), actionCollection(),
+ "jog_right");
+
+ new KAction(i18n("Set Start Time..."), 0, this,
+ SLOT(slotSetSegmentStartTimes()), actionCollection(),
+ "set_segment_start");
+
+ new KAction(i18n("Set Duration..."), 0, this,
+ SLOT(slotSetSegmentDurations()), actionCollection(),
+ "set_segment_duration");
+
+ new KAction(SegmentJoinCommand::getGlobalName(),
+ Key_J + CTRL,
+ this, SLOT(slotJoinSegments()),
+ actionCollection(), "join_segments");
+
+ new KAction(i18n("Turn Re&peats into Copies"),
+ 0,
+ this, SLOT(slotRepeatingSegments()),
+ actionCollection(), "repeats_to_real_copies");
+
+ new KAction(i18n("Manage Tri&ggered Segments"), 0,
+ this, SLOT(slotManageTriggerSegments()),
+ actionCollection(), "manage_trigger_segments");
+
+ new KAction(i18n("Set Tempos from &Beat Segment"), 0, this,
+ SLOT(slotGrooveQuantize()), actionCollection(),
+ "groove_quantize");
+
+ new KAction(i18n("Set &Tempo to Audio Segment Duration"), 0, this,
+ SLOT(slotTempoToSegmentLength()), actionCollection(),
+ "set_tempo_to_segment_length");
+
+ pixmap.load(pixmapDir + "/toolbar/manage-audio-segments.xpm");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Manage A&udio Files"), icon,
+ Key_U + CTRL,
+ this, SLOT(slotAudioManager()),
+ actionCollection(), "audio_manager");
+
+ m_viewSegmentLabels = new KToggleAction(i18n("Show Segment Labels"), 0, this,
+ SLOT(slotToggleSegmentLabels()), actionCollection(),
+ "show_segment_labels");
+
+ //
+ // Tracks menu
+ //
+ pixmap.load(pixmapDir + "/toolbar/add_tracks.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Add &Track"), icon, CTRL + Key_T,
+ this, SLOT(slotAddTrack()),
+ actionCollection(), "add_track");
+
+ new KAction(i18n("&Add Tracks..."), 0,
+ this, SLOT(slotAddTracks()),
+ actionCollection(), "add_tracks");
+
+ pixmap.load(pixmapDir + "/toolbar/delete_track.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("D&elete Track"), icon, CTRL + Key_D,
+ this, SLOT(slotDeleteTrack()),
+ actionCollection(), "delete_track");
+
+ pixmap.load(pixmapDir + "/toolbar/move_track_down.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Move Track &Down"), icon, SHIFT + Key_Down,
+ this, SLOT(slotMoveTrackDown()),
+ actionCollection(), "move_track_down");
+
+ pixmap.load(pixmapDir + "/toolbar/move_track_up.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Move Track &Up"), icon, SHIFT + Key_Up,
+ this, SLOT(slotMoveTrackUp()),
+ actionCollection(), "move_track_up");
+
+ new KAction(i18n("Select &Next Track"),
+ Key_Down,
+ this, SLOT(slotTrackDown()),
+ actionCollection(), "select_next_track");
+
+ new KAction(i18n("Select &Previous Track"),
+ Key_Up,
+ this, SLOT(slotTrackUp()),
+ actionCollection(), "select_previous_track");
+
+ new KAction(i18n("Mute or Unmute Track"),
+ Key_U,
+ this, SLOT(slotToggleMutedCurrentTrack()),
+ actionCollection(), "toggle_mute_track");
+
+ new KAction(i18n("Arm or Un-arm Track for Record"),
+ Key_R,
+ this, SLOT(slotToggleRecordCurrentTrack()),
+ actionCollection(), "toggle_arm_track");
+
+ pixmap.load(pixmapDir + "/toolbar/mute-all.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("&Mute all Tracks"), icon, 0,
+ this, SLOT(slotMuteAllTracks()),
+ actionCollection(), "mute_all_tracks");
+
+ pixmap.load(pixmapDir + "/toolbar/un-mute-all.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("&Unmute all Tracks"), icon, 0,
+ this, SLOT(slotUnmuteAllTracks()),
+ actionCollection(), "unmute_all_tracks");
+
+ new KAction(i18n("&Remap Instruments..."), 0, this,
+ SLOT(slotRemapInstruments()),
+ actionCollection(), "remap_instruments");
+
+ //
+ // Studio menu
+ //
+ pixmap.load(pixmapDir + "/toolbar/mixer.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("&Audio Mixer"), icon, 0, this,
+ SLOT(slotOpenAudioMixer()),
+ actionCollection(), "audio_mixer");
+
+ pixmap.load(pixmapDir + "/toolbar/midimixer.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Midi Mi&xer"), icon, 0, this,
+ SLOT(slotOpenMidiMixer()),
+ actionCollection(), "midi_mixer");
+
+ pixmap.load(pixmapDir + "/toolbar/manage-midi-devices.xpm");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Manage MIDI &Devices"), icon, 0, this,
+ SLOT(slotManageMIDIDevices()),
+ actionCollection(), "manage_devices");
+
+ pixmap.load(pixmapDir + "/toolbar/manage-synth-plugins.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Manage S&ynth Plugins"), icon, 0, this,
+ SLOT(slotManageSynths()),
+ actionCollection(), "manage_synths");
+
+ new KAction(i18n("Modify MIDI &Filters"), "filter", 0, this,
+ SLOT(slotModifyMIDIFilters()),
+ actionCollection(), "modify_midi_filters");
+
+ m_enableMIDIrouting = new KToggleAction(i18n("MIDI Thru Routing"), 0, this,
+ SLOT(slotEnableMIDIThruRouting()),
+ actionCollection(), "enable_midi_routing");
+
+ pixmap.load(pixmapDir + "/toolbar/time-musical.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Manage &Metronome"), 0, this,
+ SLOT(slotManageMetronome()),
+ actionCollection(), "manage_metronome");
+
+ new KAction(i18n("&Save Current Document as Default Studio"), 0, this,
+ SLOT(slotSaveDefaultStudio()),
+ actionCollection(), "save_default_studio");
+
+ new KAction(i18n("&Import Default Studio"), 0, this,
+ SLOT(slotImportDefaultStudio()),
+ actionCollection(), "load_default_studio");
+
+ new KAction(i18n("Im&port Studio from File..."), 0, this,
+ SLOT(slotImportStudio()),
+ actionCollection(), "load_studio");
+
+ new KAction(i18n("&Reset MIDI Network"), 0, this,
+ SLOT(slotResetMidiNetwork()),
+ actionCollection(), "reset_midi_network");
+
+ m_setQuickMarkerAction = new KAction(i18n("Set Quick Marker at Playback Position"), 0, CTRL + Key_1, this,
+ SLOT(slotSetQuickMarker()), actionCollection(),
+ "set_quick_marker");
+
+ m_jumpToQuickMarkerAction = new KAction(i18n("Jump to Quick Marker"), 0, Key_1, this,
+ SLOT(slotJumpToQuickMarker()), actionCollection(),
+ "jump_to_quick_marker");
+
+ //
+ // Marker Ruler popup menu
+ //
+// new KAction(i18n("Insert Marker"), 0, 0, this,
+// SLOT(slotInsertMarkerHere()), actionCollection(),
+// "insert_marker_here");
+//
+// new KAction(i18n("Insert Marker at Playback Position"), 0, 0, this,
+// SLOT(slotInsertMarkerAtPointer()), actionCollection(),
+// "insert_marker_at_pointer");
+//
+// new KAction(i18n("Delete Marker"), 0, 0, this,
+// SLOT(slotDeleteMarker()), actionCollection(),
+// "delete_marker");
+
+
+
+ //
+ // Transport menu
+ //
+
+ // Transport controls [rwb]
+ //
+ // We set some default key bindings - with numlock off
+ // use 1 (End) and 3 (Page Down) for Rwd and Ffwd and
+ // 0 (insert) and keypad Enter for Play and Stop
+ //
+ pixmap.load(pixmapDir + "/toolbar/transport-play.png");
+ icon = QIconSet(pixmap);
+ m_playTransport = new KAction(i18n("&Play"), icon, Key_Enter, this,
+ SLOT(slotPlay()), actionCollection(),
+ "play");
+ // Alternative shortcut for Play
+ KShortcut playShortcut = m_playTransport->shortcut();
+ playShortcut.append( KKey(Key_Return + CTRL) );
+ m_playTransport->setShortcut(playShortcut);
+ m_playTransport->setGroup(TransportDialogConfigGroup);
+
+ pixmap.load(pixmapDir + "/toolbar/transport-stop.png");
+ icon = QIconSet(pixmap);
+ m_stopTransport = new KAction(i18n("&Stop"), icon, Key_Insert, this,
+ SLOT(slotStop()), actionCollection(),
+ "stop");
+ m_stopTransport->setGroup(TransportDialogConfigGroup);
+
+ pixmap.load(pixmapDir + "/toolbar/transport-ffwd.png");
+ icon = QIconSet(pixmap);
+ m_ffwdTransport = new KAction(i18n("&Fast Forward"), icon, Key_PageDown,
+ this,
+ SLOT(slotFastforward()), actionCollection(),
+ "fast_forward");
+ m_ffwdTransport->setGroup(TransportDialogConfigGroup);
+
+ pixmap.load(pixmapDir + "/toolbar/transport-rewind.png");
+ icon = QIconSet(pixmap);
+ m_rewindTransport = new KAction(i18n("Re&wind"), icon, Key_End, this,
+ SLOT(slotRewind()), actionCollection(),
+ "rewind");
+ m_rewindTransport->setGroup(TransportDialogConfigGroup);
+
+ pixmap.load(pixmapDir + "/toolbar/transport-record.png");
+ icon = QIconSet(pixmap);
+ m_recordTransport = new KAction(i18n("P&unch in Record"), icon, Key_Space, this,
+ SLOT(slotToggleRecord()), actionCollection(),
+ "recordtoggle");
+ m_recordTransport->setGroup(TransportDialogConfigGroup);
+
+ pixmap.load(pixmapDir + "/toolbar/transport-record.png");
+ icon = QIconSet(pixmap);
+ m_recordTransport = new KAction(i18n("&Record"), icon, 0, this,
+ SLOT(slotRecord()), actionCollection(),
+ "record");
+ m_recordTransport->setGroup(TransportDialogConfigGroup);
+
+ pixmap.load(pixmapDir + "/toolbar/transport-rewind-end.png");
+ icon = QIconSet(pixmap);
+ m_rewindEndTransport = new KAction(i18n("Rewind to &Beginning"), icon, 0, this,
+ SLOT(slotRewindToBeginning()), actionCollection(),
+ "rewindtobeginning");
+ m_rewindEndTransport->setGroup(TransportDialogConfigGroup);
+
+ pixmap.load(pixmapDir + "/toolbar/transport-ffwd-end.png");
+ icon = QIconSet(pixmap);
+ m_ffwdEndTransport = new KAction(i18n("Fast Forward to &End"), icon, 0, this,
+ SLOT(slotFastForwardToEnd()), actionCollection(),
+ "fastforwardtoend");
+ m_ffwdEndTransport->setGroup(TransportDialogConfigGroup);
+
+ pixmap.load(pixmapDir + "/toolbar/transport-tracking.png");
+ icon = QIconSet(pixmap);
+ (new KToggleAction(i18n("Scro&ll to Follow Playback"), icon, Key_Pause, this,
+ SLOT(slotToggleTracking()), actionCollection(),
+ "toggle_tracking"))->setChecked(true);
+
+ pixmap.load(pixmapDir + "/toolbar/transport-panic.png");
+ icon = QIconSet(pixmap);
+ new KAction( i18n("Panic"), icon, Key_P + CTRL + ALT, this, SLOT(slotPanic()),
+ actionCollection(), "panic");
+
+ // DEBUG FACILITY
+ new KAction(i18n("Segment Debug Dump "), 0, this,
+ SLOT(slotDebugDump()), actionCollection(),
+ "debug_dump_segments");
+
+ // create main gui
+ //
+ createGUI("rosegardenui.rc", false);
+
+ createAndSetupTransport();
+
+ // transport toolbar is hidden by default - TODO : this should be in options
+ //
+ //toolBar("Transport Toolbar")->hide();
+
+ QPopupMenu* setTrackInstrumentMenu = dynamic_cast<QPopupMenu*>(factory()->container("set_track_instrument", this));
+
+ if (setTrackInstrumentMenu) {
+ connect(setTrackInstrumentMenu, SIGNAL(aboutToShow()),
+ this, SLOT(slotPopulateTrackInstrumentPopup()));
+ } else {
+ RG_DEBUG << "RosegardenGUIApp::setupActions() : couldn't find set_track_instrument menu - check rosegardenui.rcn\n";
+ }
+
+ setRewFFwdToAutoRepeat();
+}
+
+void RosegardenGUIApp::setRewFFwdToAutoRepeat()
+{
+ QWidget* transportToolbar = factory()->container("Transport Toolbar", this);
+
+ if (transportToolbar) {
+ QObjectList *l = transportToolbar->queryList();
+ QObjectListIt it(*l); // iterate over the buttons
+ QObject *obj;
+
+ while ( (obj = it.current()) != 0 ) {
+ // for each found object...
+ ++it;
+ // RG_DEBUG << "obj name : " << obj->name() << endl;
+ QString objName = obj->name();
+
+ if (objName.endsWith("rewind") || objName.endsWith("fast_forward")) {
+ QButton* btn = dynamic_cast<QButton*>(obj);
+ if (!btn) {
+ RG_DEBUG << "Very strange - found widgets in transport_toolbar which aren't buttons\n";
+
+ continue;
+ }
+ btn->setAutoRepeat(true);
+ }
+
+
+ }
+ delete l;
+
+ } else {
+ RG_DEBUG << "transportToolbar == 0\n";
+ }
+
+}
+
+void RosegardenGUIApp::initZoomToolbar()
+{
+ KToolBar *zoomToolbar = toolBar("Zoom Toolbar");
+ if (!zoomToolbar) {
+ RG_DEBUG << "RosegardenGUIApp::initZoomToolbar() : "
+ << "zoom toolbar not found" << endl;
+ return ;
+ }
+
+ new QLabel(i18n(" Zoom: "), zoomToolbar, "kde toolbar widget");
+
+ 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 };
+
+ for (unsigned int i = 0; i < sizeof(factors) / sizeof(factors[0]); ++i) {
+ zoomSizes.push_back(duration44 / (defaultBarWidth44 * factors[i]));
+ }
+
+ // zoom labels
+ QString minZoom = QString("%1%").arg(factors[0] * 100.0);
+ QString maxZoom = QString("%1%").arg(factors[(sizeof(factors) / sizeof(factors[0])) - 1] * 100.0);
+
+ m_zoomSlider = new ZoomSlider<double>
+ (zoomSizes, -1, QSlider::Horizontal, zoomToolbar, "kde toolbar widget");
+ m_zoomSlider->setTracking(true);
+ m_zoomSlider->setFocusPolicy(QWidget::NoFocus);
+ m_zoomLabel = new QLabel(minZoom, zoomToolbar, "kde toolbar widget");
+ m_zoomLabel->setIndent(10);
+
+ connect(m_zoomSlider, SIGNAL(valueChanged(int)),
+ this, SLOT(slotChangeZoom(int)));
+
+ // set initial zoom - we might want to make this a config option
+ // m_zoomSlider->setToDefault();
+
+}
+
+void RosegardenGUIApp::initStatusBar()
+{
+ KTmpStatusMsg::setDefaultMsg("");
+ statusBar()->insertItem(KTmpStatusMsg::getDefaultMsg(),
+ KTmpStatusMsg::getDefaultId(), 1);
+ statusBar()->setItemAlignment(KTmpStatusMsg::getDefaultId(),
+ AlignLeft | AlignVCenter);
+
+ m_progressBar = new ProgressBar(100, true, statusBar());
+ // m_progressBar->setMinimumWidth(100);
+ m_progressBar->setFixedWidth(60);
+ m_progressBar->setFixedHeight(18);
+ m_progressBar->setTextEnabled(false);
+ statusBar()->addWidget(m_progressBar);
+}
+
+void RosegardenGUIApp::initView()
+{
+ ////////////////////////////////////////////////////////////////////
+ // create the main widget here that is managed by KTMainWindow's view-region and
+ // connect the widget to your document to display document contents.
+
+ RG_DEBUG << "RosegardenGUIApp::initView()" << endl;
+
+ Composition &comp = m_doc->getComposition();
+
+ // Ensure that the start and end markers for the piece are set
+ // to something reasonable
+ //
+ if (comp.getStartMarker() == 0 &&
+ comp.getEndMarker() == 0) {
+ int endMarker = comp.getBarRange(100 + comp.getNbBars()).second;
+ comp.setEndMarker(endMarker);
+ }
+
+ m_swapView = new RosegardenGUIView(m_viewTrackLabels->isChecked(),
+ m_segmentParameterBox,
+ m_instrumentParameterBox,
+ m_trackParameterBox, this);
+
+ // Connect up this signal so that we can force tool mode
+ // changes from the view
+ connect(m_swapView, SIGNAL(activateTool(QString)),
+ this, SLOT(slotActivateTool(QString)));
+
+ connect(m_swapView,
+ SIGNAL(segmentsSelected(const SegmentSelection &)),
+ SIGNAL(segmentsSelected(const SegmentSelection &)));
+
+ connect(m_swapView,
+ SIGNAL(addAudioFile(AudioFileId)),
+ SLOT(slotAddAudioFile(AudioFileId)));
+
+ connect(m_swapView, SIGNAL(toggleSolo(bool)), SLOT(slotToggleSolo(bool)));
+
+ m_doc->attachView(m_swapView);
+
+ m_mainDockWidget->setWidget(m_swapView);
+
+ // setCentralWidget(m_swapView);
+ setCaption(m_doc->getTitle());
+
+
+ // Transport setup
+ //
+ std::string transportMode = m_doc->getConfiguration().
+ get
+ <String>
+ (DocumentConfiguration::TransportMode);
+
+
+ slotEnableTransport(true);
+
+ // and the time signature
+ //
+ getTransport()->setTimeSignature(comp.getTimeSignatureAt(comp.getPosition()));
+
+ // set the tempo in the transport
+ //
+ getTransport()->setTempo(comp.getCurrentTempo());
+
+ // bring the transport to the front
+ //
+ getTransport()->raise();
+
+ // set the play metronome button
+ getTransport()->MetronomeButton()->setOn(comp.usePlayMetronome());
+
+ // Set the solo button
+ getTransport()->SoloButton()->setOn(comp.isSolo());
+
+ // set the transport mode found in the configuration
+ getTransport()->setNewMode(transportMode);
+
+ // set the pointer position
+ //
+ slotSetPointerPosition(m_doc->getComposition().getPosition());
+
+ // make sure we show
+ //
+ RosegardenGUIView *oldView = m_view;
+ m_view = m_swapView;
+
+ connect(m_view, SIGNAL(stateChange(QString, bool)),
+ this, SLOT (slotStateChanged(QString, bool)));
+
+ connect(m_view, SIGNAL(instrumentParametersChanged(InstrumentId)),
+ this, SIGNAL(instrumentParametersChanged(InstrumentId)));
+
+ // We only check for the SequenceManager to make sure
+ // we're not on the first pass though - we don't want
+ // to send these toggles twice on initialisation.
+ //
+ // Clunky but we just about get away with it for the
+ // moment.
+ //
+ if (m_seqManager != 0) {
+ slotToggleChordNameRuler();
+ slotToggleRulers();
+ slotToggleTempoRuler();
+ slotTogglePreviews();
+ slotToggleSegmentLabels();
+
+ // Reset any loop on the sequencer
+ //
+ try {
+ if (isUsingSequencer())
+ m_seqManager->setLoop(0, 0);
+ stateChanged("have_range", KXMLGUIClient::StateReverse);
+ } catch (QString s) {
+ KStartupLogo::hideIfStillThere();
+ CurrentProgressDialog::freeze();
+ KMessageBox::error(this, s);
+ CurrentProgressDialog::thaw();
+ }
+
+ connect(m_seqManager, SIGNAL(controllerDeviceEventReceived(MappedEvent *)),
+ m_view, SLOT(slotControllerDeviceEventReceived(MappedEvent *)));
+ }
+
+ // delete m_playList;
+ // m_playList = 0;
+
+ delete m_deviceManager;
+ m_deviceManager = 0;
+
+ delete m_synthManager;
+ m_synthManager = 0;
+
+ delete m_audioMixer;
+ m_audioMixer = 0;
+
+ delete m_bankEditor;
+ m_bankEditor = 0;
+
+ delete m_markerEditor;
+ m_markerEditor = 0;
+
+ delete m_tempoView;
+ m_tempoView = 0;
+
+ delete m_triggerSegmentManager;
+ m_triggerSegmentManager = 0;
+
+ delete oldView;
+
+ // set the highlighted track
+ m_view->slotSelectTrackSegments(comp.getSelectedTrack());
+
+ // play tracking on in the editor by default: turn off if need be
+ KToggleAction *trackingAction = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("toggle_tracking"));
+ if (trackingAction && !trackingAction->isChecked()) {
+ m_view->getTrackEditor()->slotToggleTracking();
+ }
+
+ m_view->show();
+
+ connect(m_view->getTrackEditor()->getSegmentCanvas(),
+ SIGNAL(showContextHelp(const QString &)),
+ this,
+ SLOT(slotShowToolHelp(const QString &)));
+
+ // We have to do this to make sure that the 2nd call ("select")
+ // actually has any effect. Activating the same radio action
+ // doesn't work the 2nd time (like pressing down the same radio
+ // button twice - it doesn't have any effect), so if you load two
+ // files in a row, on the 2nd file a new SegmentCanvas will be
+ // created but its tool won't be set, even though it will appear
+ // to be selected.
+ //
+ actionCollection()->action("move")->activate();
+ if (m_doc->getComposition().getNbSegments() > 0)
+ actionCollection()->action("select")->activate();
+ else
+ actionCollection()->action("draw")->activate();
+
+ int zoomLevel = m_doc->getConfiguration().
+ get
+ <Int>
+ (DocumentConfiguration::ZoomLevel);
+
+ m_zoomSlider->setSize(double(zoomLevel) / 1000.0);
+ slotChangeZoom(zoomLevel);
+
+ //slotChangeZoom(int(m_zoomSlider->getCurrentSize()));
+
+ stateChanged("new_file");
+
+ ProgressDialog::processEvents();
+
+ if (m_viewChordNameRuler->isChecked()) {
+ SetWaitCursor swc;
+ m_view->initChordNameRuler();
+ } else {
+ m_view->initChordNameRuler();
+ }
+}
+
+void RosegardenGUIApp::setDocument(RosegardenGUIDoc* newDocument)
+{
+ if (m_doc == newDocument)
+ return ;
+
+ emit documentAboutToChange();
+ kapp->processEvents(); // to make sure all opened dialogs (mixer, midi devices...) are closed
+
+ // Take care of all subparts which depend on the document
+
+ // Caption
+ //
+ QString caption = kapp->caption();
+ setCaption(caption + ": " + newDocument->getTitle());
+
+ // // reset AudioManagerDialog
+ // //
+ // delete m_audioManagerDialog; // TODO : replace this with a connection to documentAboutToChange() sig.
+ // m_audioManagerDialog = 0;
+
+ RosegardenGUIDoc* oldDoc = m_doc;
+
+ m_doc = newDocument;
+
+ if (m_seqManager) // when we're called at startup, the seq. man. isn't created yet
+ m_seqManager->setDocument(m_doc);
+
+ if (m_markerEditor)
+ m_markerEditor->setDocument(m_doc);
+ if (m_tempoView) {
+ delete m_tempoView;
+ m_tempoView = 0;
+ }
+ if (m_triggerSegmentManager)
+ m_triggerSegmentManager->setDocument(m_doc);
+
+ m_trackParameterBox->setDocument(m_doc);
+ m_segmentParameterBox->setDocument(m_doc);
+ m_instrumentParameterBox->setDocument(m_doc);
+
+#ifdef HAVE_LIBLO
+
+ if (m_pluginGUIManager) {
+ m_pluginGUIManager->stopAllGUIs();
+ m_pluginGUIManager->setStudio(&m_doc->getStudio());
+ }
+#endif
+
+ if (getView() &&
+ getView()->getTrackEditor() &&
+ getView()->getTrackEditor()->getSegmentCanvas()) {
+ getView()->getTrackEditor()->getSegmentCanvas()->endAudioPreviewGeneration();
+ }
+
+ // this will delete all edit views
+ //
+ delete oldDoc;
+
+ // connect needed signals
+ //
+ connect(m_segmentParameterBox, SIGNAL(documentModified()),
+ m_doc, SLOT(slotDocumentModified()));
+
+ connect(m_doc, SIGNAL(pointerPositionChanged(timeT)),
+ this, SLOT(slotSetPointerPosition(timeT)));
+
+ connect(m_doc, SIGNAL(documentModified(bool)),
+ this, SLOT(slotDocumentModified(bool)));
+
+ connect(m_doc, SIGNAL(loopChanged(timeT, timeT)),
+ this, SLOT(slotSetLoop(timeT, timeT)));
+
+ m_doc->getCommandHistory()->attachView(actionCollection());
+
+ connect(m_doc->getCommandHistory(), SIGNAL(commandExecuted()),
+ SLOT(update()));
+ connect(m_doc->getCommandHistory(), SIGNAL(commandExecuted()),
+ SLOT(slotTestClipboard()));
+
+ // connect and start the autosave timer
+ connect(m_autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave()));
+ m_autoSaveTimer->start(m_doc->getAutoSavePeriod() * 1000);
+
+ // Connect the playback timer
+ //
+ connect(m_playTimer, SIGNAL(timeout()), this, SLOT(slotUpdatePlaybackPosition()));
+ connect(m_stopTimer, SIGNAL(timeout()), this, SLOT(slotUpdateMonitoring()));
+
+ // finally recreate the main view
+ //
+ initView();
+
+ if (getView() && getView()->getTrackEditor()) {
+ connect(m_doc, SIGNAL(makeTrackVisible(int)),
+ getView()->getTrackEditor(), SLOT(slotScrollToTrack(int)));
+ }
+
+ connect(m_doc, SIGNAL(devicesResyncd()),
+ this, SLOT(slotDocumentDevicesResyncd()));
+
+ m_doc->syncDevices();
+ m_doc->clearModifiedStatus();
+
+ if (newDocument->getStudio().haveMidiDevices()) {
+ stateChanged("got_midi_devices");
+ } else {
+ stateChanged("got_midi_devices", KXMLGUIClient::StateReverse);
+ }
+
+ // Ensure the sequencer knows about any audio files
+ // we've loaded as part of the new Composition
+ //
+ m_doc->prepareAudio();
+
+ // Do not reset instrument prog. changes after all.
+ // if (m_seqManager)
+ // m_seqManager->preparePlayback(true);
+
+ Composition &comp = m_doc->getComposition();
+
+ // Set any loaded loop at the Composition and
+ // on the marker on SegmentCanvas and clients
+ //
+ if (m_seqManager)
+ m_doc->setLoop(comp.getLoopStart(), comp.getLoopEnd());
+
+ emit documentChanged(m_doc);
+
+ m_doc->clearModifiedStatus(); // because it's set as modified by the various
+ // init operations
+ // TODO: this sucks, have to sort it out somehow.
+
+ // Readjust canvas size
+ //
+ m_view->getTrackEditor()->slotReadjustCanvasSize();
+
+ m_stopTimer->start(100);
+}
+
+void
+RosegardenGUIApp::openFile(QString filePath, ImportType type)
+{
+ RG_DEBUG << "RosegardenGUIApp::openFile " << filePath << endl;
+
+ if (type == ImportCheckType && filePath.endsWith(".rgp")) {
+ importProject(filePath);
+ return ;
+ }
+
+ RosegardenGUIDoc *doc = createDocument(filePath, type);
+ if (doc) {
+ setDocument(doc);
+
+ // fix # 1235755, "SPB combo not updating after document swap"
+ RG_DEBUG << "RosegardenGUIApp::openFile(): calling slotDocColoursChanged() in doc" << endl;
+ doc->slotDocColoursChanged();
+
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+ if (kapp->config()->readBoolEntry("alwaysusedefaultstudio", false)) {
+
+ QString autoloadFile =
+ KGlobal::dirs()->findResource("appdata", "autoload.rg");
+
+ QFileInfo autoloadFileInfo(autoloadFile);
+ if (autoloadFileInfo.isReadable()) {
+
+ RG_DEBUG << "Importing default studio from " << autoloadFile << endl;
+
+ slotImportStudioFromFile(autoloadFile);
+ }
+ }
+
+ QFileInfo fInfo(filePath);
+ m_fileRecent->addURL(fInfo.absFilePath());
+ }
+}
+
+RosegardenGUIDoc*
+RosegardenGUIApp::createDocument(QString filePath, ImportType importType)
+{
+ QFileInfo info(filePath);
+ RosegardenGUIDoc *doc = 0;
+
+ if (!info.exists()) {
+ // can happen with command-line arg, so...
+ KStartupLogo::hideIfStillThere();
+ KMessageBox::sorry(this, i18n("File \"%1\" does not exist").arg(filePath));
+ return 0;
+ }
+
+ if (info.isDir()) {
+ KStartupLogo::hideIfStillThere();
+ KMessageBox::sorry(this, i18n("File \"%1\" is actually a directory"));
+ return 0;
+ }
+
+ QFile file(filePath);
+
+ if (!file.open(IO_ReadOnly)) {
+ KStartupLogo::hideIfStillThere();
+ QString errStr =
+ i18n("You do not have read permission for \"%1\"").arg(filePath);
+
+ KMessageBox::sorry(this, errStr);
+ return 0;
+ }
+
+ // Stop if playing
+ //
+ if (m_seqManager && m_seqManager->getTransportStatus() == PLAYING)
+ slotStop();
+
+ slotEnableTransport(false);
+
+ if (importType == ImportCheckType) {
+ KMimeType::Ptr fileMimeType = KMimeType::findByPath(filePath);
+ if (fileMimeType->name() == "audio/x-midi")
+ importType = ImportMIDI;
+ else if (fileMimeType->name() == "audio/x-rosegarden")
+ importType = ImportRG4;
+ else if (filePath.endsWith(".rose"))
+ importType = ImportRG21;
+ else if (filePath.endsWith(".h2song"))
+ importType = ImportHydrogen;
+ }
+
+
+ switch (importType) {
+ case ImportMIDI:
+ doc = createDocumentFromMIDIFile(filePath);
+ break;
+ case ImportRG21:
+ doc = createDocumentFromRG21File(filePath);
+ break;
+ case ImportHydrogen:
+ doc = createDocumentFromHydrogenFile(filePath);
+ break;
+ default:
+ doc = createDocumentFromRGFile(filePath);
+ }
+
+ slotEnableTransport(true);
+
+ return doc;
+}
+
+RosegardenGUIDoc*
+RosegardenGUIApp::createDocumentFromRGFile(QString filePath)
+{
+ // Check for an autosaved file to recover
+ QString effectiveFilePath = filePath;
+ bool canRecover = false;
+ QString autoSaveFileName = kapp->checkRecoverFile(filePath, canRecover);
+
+ if (canRecover) {
+ // First check if the auto-save file is more recent than the doc
+ QFileInfo docFileInfo(filePath), autoSaveFileInfo(autoSaveFileName);
+
+ if (docFileInfo.lastModified() < autoSaveFileInfo.lastModified()) {
+
+ RG_DEBUG << "RosegardenGUIApp::openFile : "
+ << "found a more recent autosave file\n";
+
+ // At this point the splash screen may still be there, hide it if
+ // it's the case
+ KStartupLogo::hideIfStillThere();
+
+ // It is, so ask the user if he wants to use the autosave file
+ int reply = KMessageBox::questionYesNo(this,
+ i18n("An auto-save file for this document has been found\nDo you want to open it instead ?"));
+
+ if (reply == KMessageBox::Yes)
+ // open the autosave file instead
+ effectiveFilePath = autoSaveFileName;
+ else {
+ // user doesn't want the autosave, so delete it
+ // so it won't bother us again if we reload
+ canRecover = false;
+ QFile::remove
+ (autoSaveFileName);
+ }
+
+ } else
+ canRecover = false;
+ }
+
+ // Create a new blank document
+ //
+ RosegardenGUIDoc *newDoc = new RosegardenGUIDoc(this, m_pluginManager,
+ true); // skipAutoload
+
+ // ignore return thingy
+ //
+ if (newDoc->openDocument(effectiveFilePath)) {
+ if (canRecover) {
+ // Mark the document as modified,
+ // set the "regular" filepath and name (not those of
+ // the autosaved doc)
+ //
+ newDoc->slotDocumentModified();
+ QFileInfo info(filePath);
+ newDoc->setAbsFilePath(info.absFilePath());
+ newDoc->setTitle(info.fileName());
+ } else {
+ newDoc->clearModifiedStatus();
+ }
+ } else {
+ delete newDoc;
+ return 0;
+ }
+
+ return newDoc;
+}
+
+void RosegardenGUIApp::slotSaveOptions()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotSaveOptions()\n";
+
+#ifdef SETTING_LOG_DEBUG
+
+ _settingLog(QString("SETTING 2 : transport flap extended = %1").arg(getTransport()->isExpanded()));
+ _settingLog(QString("SETTING 2 : show track labels = %1").arg(m_viewTrackLabels->isChecked()));
+#endif
+
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+ kapp->config()->writeEntry("Show Transport", m_viewTransport->isChecked());
+ kapp->config()->writeEntry("Expanded Transport", m_transport ? getTransport()->isExpanded() : true);
+ kapp->config()->writeEntry("Show Track labels", m_viewTrackLabels->isChecked());
+ kapp->config()->writeEntry("Show Rulers", m_viewRulers->isChecked());
+ kapp->config()->writeEntry("Show Tempo Ruler", m_viewTempoRuler->isChecked());
+ kapp->config()->writeEntry("Show Chord Name Ruler", m_viewChordNameRuler->isChecked());
+ kapp->config()->writeEntry("Show Previews", m_viewPreviews->isChecked());
+ kapp->config()->writeEntry("Show Segment Labels", m_viewSegmentLabels->isChecked());
+ kapp->config()->writeEntry("Show Parameters", m_dockVisible);
+ kapp->config()->writeEntry("MIDI Thru Routing", m_enableMIDIrouting->isChecked());
+
+#ifdef SETTING_LOG_DEBUG
+
+ RG_DEBUG << "SHOW PARAMETERS = " << m_dockVisible << endl;
+#endif
+
+ m_fileRecent->saveEntries(kapp->config());
+
+ // saveMainWindowSettings(kapp->config(), RosegardenGUIApp::MainWindowConfigGroup); - no need to, done by KMainWindow
+ kapp->config()->sync();
+}
+
+void RosegardenGUIApp::setupFileDialogSpeedbar()
+{
+ KConfig *config = kapp->config();
+
+ config->setGroup("KFileDialog Speedbar");
+
+ RG_DEBUG << "RosegardenGUIApp::setupFileDialogSpeedbar" << endl;
+
+ bool hasSetExamplesItem = config->readBoolEntry("Examples Set", false);
+
+ RG_DEBUG << "RosegardenGUIApp::setupFileDialogSpeedbar: examples set " << hasSetExamplesItem << endl;
+
+ if (!hasSetExamplesItem) {
+
+ unsigned int n = config->readUnsignedNumEntry("Number of Entries", 0);
+
+ config->writeEntry(QString("Description_%1").arg(n), i18n("Example Files"));
+ config->writeEntry(QString("IconGroup_%1").arg(n), 4);
+ config->writeEntry(QString("Icon_%1").arg(n), "folder");
+ config->writeEntry(QString("URL_%1").arg(n),
+ KGlobal::dirs()->findResource("appdata", "examples/"));
+
+ RG_DEBUG << "wrote url " << config->readEntry(QString("URL_%1").arg(n)) << endl;
+
+ config->writeEntry("Examples Set", true);
+ config->writeEntry("Number of Entries", n + 1);
+ config->sync();
+ }
+
+}
+
+void RosegardenGUIApp::readOptions()
+{
+ applyMainWindowSettings(kapp->config(), MainWindowConfigGroup);
+
+ kapp->config()->reparseConfiguration();
+
+ // Statusbar and toolbars toggling action status
+ //
+ m_viewStatusBar ->setChecked(!statusBar() ->isHidden());
+ m_viewToolBar ->setChecked(!toolBar() ->isHidden());
+ m_viewToolsToolBar ->setChecked(!toolBar("Tools Toolbar") ->isHidden());
+ m_viewTracksToolBar ->setChecked(!toolBar("Tracks Toolbar") ->isHidden());
+ m_viewEditorsToolBar ->setChecked(!toolBar("Editors Toolbar") ->isHidden());
+ m_viewTransportToolBar->setChecked(!toolBar("Transport Toolbar")->isHidden());
+ m_viewZoomToolBar ->setChecked(!toolBar("Zoom Toolbar") ->isHidden());
+
+ bool opt;
+
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+
+ opt = kapp->config()->readBoolEntry("Show Transport", true);
+ m_viewTransport->setChecked(opt);
+ slotToggleTransport();
+
+ opt = kapp->config()->readBoolEntry("Expanded Transport", true);
+
+#ifdef SETTING_LOG_DEBUG
+
+ _settingLog(QString("SETTING 3 : transport flap extended = %1").arg(opt));
+#endif
+
+ if (opt)
+ getTransport()->slotPanelOpenButtonClicked();
+ else
+ getTransport()->slotPanelCloseButtonClicked();
+
+ opt = kapp->config()->readBoolEntry("Show Track labels", true);
+
+#ifdef SETTING_LOG_DEBUG
+
+ _settingLog(QString("SETTING 3 : show track labels = %1").arg(opt));
+#endif
+
+ m_viewTrackLabels->setChecked(opt);
+ slotToggleTrackLabels();
+
+ opt = kapp->config()->readBoolEntry("Show Rulers", true);
+ m_viewRulers->setChecked(opt);
+ slotToggleRulers();
+
+ opt = kapp->config()->readBoolEntry("Show Tempo Ruler", true);
+ m_viewTempoRuler->setChecked(opt);
+ slotToggleTempoRuler();
+
+ opt = kapp->config()->readBoolEntry("Show Chord Name Ruler", false);
+ m_viewChordNameRuler->setChecked(opt);
+ slotToggleChordNameRuler();
+
+ opt = kapp->config()->readBoolEntry("Show Previews", true);
+ m_viewPreviews->setChecked(opt);
+ slotTogglePreviews();
+
+ opt = kapp->config()->readBoolEntry("Show Segment Labels", true);
+ m_viewSegmentLabels->setChecked(opt);
+ slotToggleSegmentLabels();
+
+ opt = kapp->config()->readBoolEntry("Show Parameters", true);
+ if (!opt) {
+ m_dockLeft->undock();
+ m_dockLeft->hide();
+ stateChanged("parametersbox_closed", KXMLGUIClient::StateNoReverse);
+ m_dockVisible = false;
+ }
+
+ // MIDI Thru routing
+ opt = kapp->config()->readBoolEntry("MIDI Thru Routing", true);
+ m_enableMIDIrouting->setChecked(opt);
+ slotEnableMIDIThruRouting();
+
+ // initialise the recent file list
+ //
+ m_fileRecent->loadEntries(kapp->config());
+
+ m_actionsSetup = true;
+
+}
+
+void RosegardenGUIApp::saveGlobalProperties(KConfig *cfg)
+{
+ if (m_doc->getTitle() != i18n("Untitled") && !m_doc->isModified()) {
+ // saving to tempfile not necessary
+ } else {
+ QString filename = m_doc->getAbsFilePath();
+ cfg->writeEntry("filename", filename);
+ cfg->writeEntry("modified", m_doc->isModified());
+
+ QString tempname = kapp->tempSaveName(filename);
+ QString errMsg;
+ bool res = m_doc->saveDocument(tempname, errMsg);
+ if (!res) {
+ if (errMsg)
+ KMessageBox::error(this, i18n(QString("Could not save document at %1\nError was : %2")
+ .arg(tempname).arg(errMsg)));
+ else
+ KMessageBox::error(this, i18n(QString("Could not save document at %1")
+ .arg(tempname)));
+ }
+ }
+}
+
+void RosegardenGUIApp::readGlobalProperties(KConfig* _cfg)
+{
+ QString filename = _cfg->readEntry("filename", "");
+ bool modified = _cfg->readBoolEntry("modified", false);
+
+ if (modified) {
+ bool canRecover;
+ QString tempname = kapp->checkRecoverFile(filename, canRecover);
+
+ if (canRecover) {
+ slotEnableTransport(false);
+ m_doc->openDocument(tempname);
+ slotEnableTransport(true);
+ m_doc->slotDocumentModified();
+ QFileInfo info(filename);
+ m_doc->setAbsFilePath(info.absFilePath());
+ m_doc->setTitle(info.fileName());
+ }
+ } else {
+ if (!filename.isEmpty()) {
+ slotEnableTransport(false);
+ m_doc->openDocument(filename);
+ slotEnableTransport(true);
+ }
+ }
+
+ QString caption = kapp->caption();
+ setCaption(caption + ": " + m_doc->getTitle());
+}
+
+void RosegardenGUIApp::showEvent(QShowEvent* e)
+{
+ RG_DEBUG << "RosegardenGUIApp::showEvent()\n";
+
+ getTransport()->raise();
+ KMainWindow::showEvent(e);
+}
+
+bool RosegardenGUIApp::queryClose()
+{
+ RG_DEBUG << "RosegardenGUIApp::queryClose" << endl;
+#ifdef SETTING_LOG_DEBUG
+
+ _settingLog(QString("SETTING 1 : transport flap extended = %1").arg(getTransport()->isExpanded()));
+ _settingLog(QString("SETTING 1 : show track labels = %1").arg(m_viewTrackLabels->isChecked()));
+#endif
+
+ QString errMsg;
+
+ bool canClose = m_doc->saveIfModified();
+
+ /*
+ if (canClose && m_transport) {
+
+ // or else the closing of the transport will toggle off the
+ // 'view transport' action, and its state will be saved as
+ // 'off'
+ //
+
+ disconnect(m_transport, SIGNAL(closed()),
+ this, SLOT(slotCloseTransport()));
+ }
+ */
+
+ return canClose;
+
+}
+
+bool RosegardenGUIApp::queryExit()
+{
+ RG_DEBUG << "RosegardenGUIApp::queryExit" << endl;
+ if (m_actionsSetup)
+ slotSaveOptions();
+
+ return true;
+}
+
+void RosegardenGUIApp::slotFileNewWindow()
+{
+ KTmpStatusMsg msg(i18n("Opening a new application window..."), this);
+
+ RosegardenGUIApp *new_window = new RosegardenGUIApp();
+ new_window->show();
+}
+
+void RosegardenGUIApp::slotFileNew()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotFileNew()\n";
+
+ KTmpStatusMsg msg(i18n("Creating new document..."), this);
+
+ bool makeNew = false;
+
+ if (!m_doc->isModified()) {
+ makeNew = true;
+ // m_doc->closeDocument();
+ } else if (m_doc->saveIfModified()) {
+ makeNew = true;
+ }
+
+ if (makeNew) {
+
+ setDocument(new RosegardenGUIDoc(this, m_pluginManager));
+ }
+}
+
+void RosegardenGUIApp::slotOpenDroppedURL(QString url)
+{
+ ProgressDialog::processEvents(); // or else we get a crash because the
+ // track editor is erased too soon - it is the originator of the signal
+ // this slot is connected to.
+
+ if (!m_doc->saveIfModified())
+ return ;
+
+ openURL(KURL(url));
+}
+
+void RosegardenGUIApp::openURL(QString url)
+{
+ RG_DEBUG << "RosegardenGUIApp::openURL: QString " << url << endl;
+ openURL(KURL(url));
+}
+
+void RosegardenGUIApp::openURL(const KURL& url)
+{
+ SetWaitCursor waitCursor;
+
+ QString netFile = url.prettyURL();
+ RG_DEBUG << "RosegardenGUIApp::openURL: KURL " << netFile << endl;
+
+ if (!url.isValid()) {
+ QString string;
+ string = i18n( "Malformed URL\n%1").arg(netFile);
+
+ KMessageBox::sorry(this, string);
+ return ;
+ }
+
+ QString target, caption(url.path());
+
+ if (KIO::NetAccess::download(url, target, this) == false) {
+ KMessageBox::error(this, i18n("Cannot download file %1").arg(url.prettyURL()));
+ return ;
+ }
+
+ RG_DEBUG << "RosegardenGUIApp::openURL: target : " << target << endl;
+
+ if (!m_doc->saveIfModified())
+ return ;
+
+ openFile(target);
+
+ setCaption(caption);
+}
+
+void RosegardenGUIApp::slotFileOpen()
+{
+ slotStatusHelpMsg(i18n("Opening file..."));
+
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+
+ QString lastOpenedVersion =
+ kapp->config()->readEntry("Last File Opened Version", "none");
+
+ if (lastOpenedVersion != VERSION) {
+
+ // We haven't opened any files with this version of the
+ // program before. Default to the examples directory.
+
+ QString examplesDir = KGlobal::dirs()->findResource("appdata", "examples/");
+ kapp->config()->setGroup("Recent Dirs");
+ QString recentString = kapp->config()->readEntry("ROSEGARDEN", "");
+ kapp->config()->writeEntry
+ ("ROSEGARDEN", QString("file:%1,%2").arg(examplesDir).arg(recentString));
+ }
+
+ KURL url = KFileDialog::getOpenURL
+ (":ROSEGARDEN",
+ "audio/x-rosegarden audio/x-midi audio/x-rosegarden21", this,
+ i18n("Open File"));
+ if ( url.isEmpty() ) {
+ return ;
+ }
+
+ if (m_doc && !m_doc->saveIfModified())
+ return ;
+
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+ kapp->config()->writeEntry("Last File Opened Version", VERSION);
+
+ openURL(url);
+}
+
+void RosegardenGUIApp::slotMerge()
+{
+ KURL url = KFileDialog::getOpenURL
+ (":ROSEGARDEN",
+ "audio/x-rosegarden audio/x-midi audio/x-rosegarden21", this,
+ i18n("Open File"));
+ if ( url.isEmpty() ) {
+ return ;
+ }
+
+
+ QString target;
+
+ if (KIO::NetAccess::download(url, target, this) == false) {
+ KMessageBox::error(this, i18n("Cannot download file %1").arg(url.prettyURL()));
+ return ;
+ }
+
+ mergeFile(target);
+
+ KIO::NetAccess::removeTempFile( target );
+}
+
+void RosegardenGUIApp::slotFileOpenRecent(const KURL &url)
+{
+ KTmpStatusMsg msg(i18n("Opening file..."), this);
+
+ if (m_doc) {
+
+ if (!m_doc->saveIfModified()) {
+ return ;
+
+ }
+ }
+
+ openURL(url);
+}
+
+void RosegardenGUIApp::slotFileSave()
+{
+ if (!m_doc /*|| !m_doc->isModified()*/)
+ return ; // ALWAYS save, even if doc is not modified.
+
+ KTmpStatusMsg msg(i18n("Saving file..."), this);
+
+ // if it's a new file (no file path), or an imported file
+ // (file path doesn't end with .rg), call saveAs
+ //
+ if (!m_doc->isRegularDotRGFile()) {
+
+ slotFileSaveAs();
+
+ } else {
+
+ SetWaitCursor waitCursor;
+ QString errMsg, docFilePath = m_doc->getAbsFilePath();
+
+ bool res = m_doc->saveDocument(docFilePath, errMsg);
+ if (!res) {
+ if (errMsg)
+ KMessageBox::error(this, i18n(QString("Could not save document at %1\nError was : %2")
+ .arg(docFilePath).arg(errMsg)));
+ else
+ KMessageBox::error(this, i18n(QString("Could not save document at %1")
+ .arg(docFilePath)));
+ }
+ }
+}
+
+QString
+RosegardenGUIApp::getValidWriteFile(QString descriptiveExtension,
+ QString label)
+{
+ // extract first extension listed in descriptiveExtension, for instance,
+ // ".rg" from "*.rg|Rosegarden files", or ".mid" from "*.mid *.midi|MIDI Files"
+ //
+ QString extension = descriptiveExtension.left(descriptiveExtension.find('|')).mid(1).section(' ', 0, 0);
+
+ RG_DEBUG << "RosegardenGUIApp::getValidWriteFile() : extension = " << extension << endl;
+
+ // It's too bad there isn't this functionality within
+ // KFileDialog::getSaveFileName
+ KFileDialog saveFileDialog(":ROSEGARDEN", descriptiveExtension, this, label, true);
+ saveFileDialog.setOperationMode(KFileDialog::Saving);
+ if (m_doc) {
+ QString saveFileName = m_doc->getAbsFilePath();
+ // Show filename without the old extension
+ int dotLoc = saveFileName.findRev('.');
+ if (dotLoc >= int(saveFileName.length() - 4)) {
+ saveFileName = saveFileName.left(dotLoc);
+ }
+ saveFileDialog.setSelection(saveFileName);
+ }
+ saveFileDialog.exec();
+ QString name = saveFileDialog.selectedFile();
+
+ // RG_DEBUG << "RosegardenGUIApp::getValidWriteFile() : KFileDialog::getSaveFileName returned "
+ // << name << endl;
+
+
+ if (name.isEmpty())
+ return name;
+
+ // Append extension if we don't have one
+ //
+ if (!extension.isEmpty()) {
+ static QRegExp rgFile("\\..{1,4}$");
+ if (rgFile.match(name) == -1) {
+ name += extension;
+ }
+ }
+
+ KURL *u = new KURL(name);
+
+ if (!u->isValid()) {
+ KMessageBox::sorry(this, i18n("This is not a valid filename.\n"));
+ return "";
+ }
+
+ if (!u->isLocalFile()) {
+ KMessageBox::sorry(this, i18n("This is not a local file.\n"));
+ return "";
+ }
+
+ QFileInfo info(name);
+
+ if (info.isDir()) {
+ KMessageBox::sorry(this, i18n("You have specified a directory"));
+ return "";
+ }
+
+ if (info.exists()) {
+ int overwrite = KMessageBox::questionYesNo
+ (this, i18n("The specified file exists. Overwrite?"));
+
+ if (overwrite != KMessageBox::Yes)
+ return "";
+ }
+
+ return name;
+}
+
+bool RosegardenGUIApp::slotFileSaveAs()
+{
+ if (!m_doc)
+ return false;
+
+ KTmpStatusMsg msg(i18n("Saving file with a new filename..."), this);
+
+ QString newName = getValidWriteFile("*.rg|" + i18n("Rosegarden files") +
+ "\n*|" + i18n("All files"),
+ i18n("Save as..."));
+ if (newName.isEmpty())
+ return false;
+
+ SetWaitCursor waitCursor;
+ QFileInfo saveAsInfo(newName);
+ m_doc->setTitle(saveAsInfo.fileName());
+ m_doc->setAbsFilePath(saveAsInfo.absFilePath());
+ QString errMsg;
+ bool res = m_doc->saveDocument(newName, errMsg);
+ if (!res) {
+ if (errMsg)
+ KMessageBox::error(this, i18n(QString("Could not save document at %1\nError was : %2")
+ .arg(newName).arg(errMsg)));
+ else
+ KMessageBox::error(this, i18n(QString("Could not save document at %1")
+ .arg(newName)));
+
+ } else {
+
+ m_fileRecent->addURL(newName);
+
+ QString caption = kapp->caption();
+ setCaption(caption + ": " + m_doc->getTitle());
+ // update the edit view's captions too
+ emit compositionStateUpdate();
+ }
+
+ return res;
+}
+
+void RosegardenGUIApp::slotFileClose()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotFileClose()" << endl;
+
+ if (!m_doc)
+ return ;
+
+ KTmpStatusMsg msg(i18n("Closing file..."), this);
+
+ if (m_doc->saveIfModified()) {
+ setDocument(new RosegardenGUIDoc(this, m_pluginManager));
+ }
+
+ // Don't close the whole view (i.e. Quit), just close the doc.
+ // close();
+}
+
+void RosegardenGUIApp::slotFilePrint()
+{
+ if (m_doc->getComposition().getNbSegments() == 0) {
+ KMessageBox::sorry(0, "Please create some tracks first (until we implement menu state management)");
+ return ;
+ }
+
+ KTmpStatusMsg msg(i18n("Printing..."), this);
+
+ m_view->print(&m_doc->getComposition());
+}
+
+void RosegardenGUIApp::slotFilePrintPreview()
+{
+ if (m_doc->getComposition().getNbSegments() == 0) {
+ KMessageBox::sorry(0, "Please create some tracks first (until we implement menu state management)");
+ return ;
+ }
+
+ KTmpStatusMsg msg(i18n("Previewing..."), this);
+
+ m_view->print(&m_doc->getComposition(), true);
+}
+
+void RosegardenGUIApp::slotQuit()
+{
+ slotStatusMsg(i18n("Exiting..."));
+
+ Profiles::getInstance()->dump();
+
+ // close the first window, the list makes the next one the first again.
+ // This ensures that queryClose() is called on each window to ask for closing
+ KMainWindow* w;
+ if (memberList) {
+
+ for (w = memberList->first(); w != 0; w = memberList->next()) {
+ // only close the window if the closeEvent is accepted. If
+ // the user presses Cancel on the saveIfModified() dialog,
+ // the window and the application stay open.
+ if (!w->close())
+ break;
+ }
+ }
+}
+
+void RosegardenGUIApp::slotEditCut()
+{
+ if (!m_view->haveSelection())
+ return ;
+ KTmpStatusMsg msg(i18n("Cutting selection..."), this);
+
+ SegmentSelection selection(m_view->getSelection());
+ m_doc->getCommandHistory()->addCommand
+ (new CutCommand(selection, m_clipboard));
+}
+
+void RosegardenGUIApp::slotEditCopy()
+{
+ if (!m_view->haveSelection())
+ return ;
+ KTmpStatusMsg msg(i18n("Copying selection to clipboard..."), this);
+
+ SegmentSelection selection(m_view->getSelection());
+ m_doc->getCommandHistory()->addCommand
+ (new CopyCommand(selection, m_clipboard));
+}
+
+void RosegardenGUIApp::slotEditPaste()
+{
+ if (m_clipboard->isEmpty()) {
+ KTmpStatusMsg msg(i18n("Clipboard is empty"), this);
+ return ;
+ }
+ KTmpStatusMsg msg(i18n("Inserting clipboard contents..."), this);
+
+ // for now, but we could paste at the time of the first copied
+ // segment and then do ghosting drag or something
+ timeT insertionTime = m_doc->getComposition().getPosition();
+ m_doc->getCommandHistory()->addCommand
+ (new PasteSegmentsCommand(&m_doc->getComposition(),
+ m_clipboard, insertionTime,
+ m_doc->getComposition().getSelectedTrack(),
+ false));
+
+ // User preference? Update song pointer position on paste
+ m_doc->slotSetPointerPosition(m_doc->getComposition().getPosition());
+}
+
+void RosegardenGUIApp::slotCutRange()
+{
+ timeT t0 = m_doc->getComposition().getLoopStart();
+ timeT t1 = m_doc->getComposition().getLoopEnd();
+
+ if (t0 == t1)
+ return ;
+
+ m_doc->getCommandHistory()->addCommand
+ (new CutRangeCommand(&m_doc->getComposition(), t0, t1, m_clipboard));
+}
+
+void RosegardenGUIApp::slotCopyRange()
+{
+ timeT t0 = m_doc->getComposition().getLoopStart();
+ timeT t1 = m_doc->getComposition().getLoopEnd();
+
+ if (t0 == t1)
+ return ;
+
+ m_doc->getCommandHistory()->addCommand
+ (new CopyCommand(&m_doc->getComposition(), t0, t1, m_clipboard));
+}
+
+void RosegardenGUIApp::slotPasteRange()
+{
+ if (m_clipboard->isEmpty())
+ return ;
+
+ m_doc->getCommandHistory()->addCommand
+ (new PasteRangeCommand(&m_doc->getComposition(), m_clipboard,
+ m_doc->getComposition().getPosition()));
+
+ m_doc->setLoop(0, 0);
+}
+
+void RosegardenGUIApp::slotDeleteRange()
+{
+ timeT t0 = m_doc->getComposition().getLoopStart();
+ timeT t1 = m_doc->getComposition().getLoopEnd();
+
+ if (t0 == t1)
+ return ;
+
+ m_doc->getCommandHistory()->addCommand
+ (new DeleteRangeCommand(&m_doc->getComposition(), t0, t1));
+
+ m_doc->setLoop(0, 0);
+}
+
+void RosegardenGUIApp::slotInsertRange()
+{
+ timeT t0 = m_doc->getComposition().getPosition();
+ std::pair<timeT, timeT> r = m_doc->getComposition().getBarRangeForTime(t0);
+ TimeDialog dialog(m_view, i18n("Duration of empty range to insert"),
+ &m_doc->getComposition(), t0, r.second - r.first, false);
+ if (dialog.exec() == QDialog::Accepted) {
+ m_doc->getCommandHistory()->addCommand
+ (new InsertRangeCommand(&m_doc->getComposition(), t0, dialog.getTime()));
+ m_doc->setLoop(0, 0);
+ }
+}
+
+void RosegardenGUIApp::slotSelectAll()
+{
+ m_view->slotSelectAllSegments();
+}
+
+void RosegardenGUIApp::slotDeleteSelectedSegments()
+{
+ m_view->getTrackEditor()->slotDeleteSelectedSegments();
+}
+
+void RosegardenGUIApp::slotQuantizeSelection()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ //!!! this should all be in rosegardenguiview
+
+ QuantizeDialog dialog(m_view);
+ if (dialog.exec() != QDialog::Accepted)
+ return ;
+
+ SegmentSelection selection = m_view->getSelection();
+
+ KMacroCommand *command = new KMacroCommand
+ (EventQuantizeCommand::getGlobalName());
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+ command->addCommand(new EventQuantizeCommand
+ (**i, (*i)->getStartTime(), (*i)->getEndTime(),
+ dialog.getQuantizer()));
+ }
+
+ m_view->slotAddCommandToHistory(command);
+}
+
+void RosegardenGUIApp::slotRepeatQuantizeSelection()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ //!!! this should all be in rosegardenguiview
+
+ SegmentSelection selection = m_view->getSelection();
+
+ KMacroCommand *command = new KMacroCommand
+ (EventQuantizeCommand::getGlobalName());
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+ command->addCommand(new EventQuantizeCommand
+ (**i, (*i)->getStartTime(), (*i)->getEndTime(),
+ "Quantize Dialog Grid", false)); // no i18n (config group name)
+ }
+
+ m_view->slotAddCommandToHistory(command);
+}
+
+void RosegardenGUIApp::slotGrooveQuantize()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ SegmentSelection selection = m_view->getSelection();
+
+ if (selection.size() != 1) {
+ KMessageBox::sorry(this, i18n("This function needs no more than one segment to be selected."));
+ return ;
+ }
+
+ Segment *s = *selection.begin();
+ m_view->slotAddCommandToHistory(new CreateTempoMapFromSegmentCommand(s));
+}
+
+void RosegardenGUIApp::slotJoinSegments()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ //!!! this should all be in rosegardenguiview
+ //!!! should it?
+
+ SegmentSelection selection = m_view->getSelection();
+ if (selection.size() == 0)
+ return ;
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+ if ((*i)->getType() != Segment::Internal) {
+ KMessageBox::sorry(this, i18n("Can't join Audio segments"));
+ return ;
+ }
+ }
+
+ m_view->slotAddCommandToHistory(new SegmentJoinCommand(selection));
+ m_view->updateSelectionContents();
+}
+
+void RosegardenGUIApp::slotRescaleSelection()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ //!!! this should all be in rosegardenguiview
+ //!!! should it?
+
+ SegmentSelection selection = m_view->getSelection();
+
+ timeT startTime = 0, endTime = 0;
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+ if ((i == selection.begin()) || ((*i)->getStartTime() < startTime)) {
+ startTime = (*i)->getStartTime();
+ }
+ if ((i == selection.begin()) || ((*i)->getEndMarkerTime() > endTime)) {
+ endTime = (*i)->getEndMarkerTime();
+ }
+ }
+
+ RescaleDialog dialog(m_view, &m_doc->getComposition(),
+ startTime, endTime - startTime,
+ false, false);
+ if (dialog.exec() != QDialog::Accepted)
+ return ;
+
+ std::vector<AudioSegmentRescaleCommand *> asrcs;
+
+ int mult = dialog.getNewDuration();
+ int div = endTime - startTime;
+ float ratio = float(mult) / float(div);
+
+ std::cerr << "slotRescaleSelection: mult = " << mult << ", div = " << div << ", ratio = " << ratio << std::endl;
+
+ KMacroCommand *command = new KMacroCommand
+ (SegmentRescaleCommand::getGlobalName());
+
+ bool pathTested = false;
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+ if ((*i)->getType() == Segment::Audio) {
+ if (!pathTested) {
+ testAudioPath(i18n("rescaling an audio file"));
+ pathTested = true;
+ }
+ AudioSegmentRescaleCommand *asrc = new AudioSegmentRescaleCommand
+ (m_doc, *i, ratio);
+ command->addCommand(asrc);
+ asrcs.push_back(asrc);
+ } else {
+ command->addCommand(new SegmentRescaleCommand(*i, mult, div));
+ }
+ }
+
+ ProgressDialog *progressDlg = 0;
+
+ if (!asrcs.empty()) {
+ progressDlg = new ProgressDialog
+ (i18n("Rescaling audio file..."), 100, this);
+ progressDlg->setAutoClose(false);
+ progressDlg->setAutoReset(false);
+ progressDlg->show();
+ for (size_t i = 0; i < asrcs.size(); ++i) {
+ asrcs[i]->connectProgressDialog(progressDlg);
+ }
+ }
+
+ m_view->slotAddCommandToHistory(command);
+
+ if (!asrcs.empty()) {
+
+ progressDlg->setLabel(i18n("Generating audio preview..."));
+
+ for (size_t i = 0; i < asrcs.size(); ++i) {
+ asrcs[i]->disconnectProgressDialog(progressDlg);
+ }
+
+ connect(&m_doc->getAudioFileManager(), SIGNAL(setProgress(int)),
+ progressDlg->progressBar(), SLOT(setValue(int)));
+ connect(progressDlg, SIGNAL(cancelClicked()),
+ &m_doc->getAudioFileManager(), SLOT(slotStopPreview()));
+
+ for (size_t i = 0; i < asrcs.size(); ++i) {
+ int fid = asrcs[i]->getNewAudioFileId();
+ if (fid >= 0) {
+ slotAddAudioFile(fid);
+ m_doc->getAudioFileManager().generatePreview(fid);
+ }
+ }
+ }
+
+ if (progressDlg) delete progressDlg;
+}
+
+bool
+RosegardenGUIApp::testAudioPath(QString op)
+{
+ try {
+ m_doc->getAudioFileManager().testAudioPath();
+ } catch (AudioFileManager::BadAudioPathException) {
+ if (KMessageBox::warningContinueCancel
+ (this,
+ 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 %1.\nWould you like to set it now?").arg(op),
+ i18n("Warning"),
+ i18n("Set audio file path")) == KMessageBox::Continue) {
+ slotOpenAudioPathSettings();
+ }
+ return false;
+ }
+ return true;
+}
+
+void RosegardenGUIApp::slotAutoSplitSelection()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ //!!! this should all be in rosegardenguiview
+ //!!! or should it?
+
+ SegmentSelection selection = m_view->getSelection();
+
+ KMacroCommand *command = new KMacroCommand
+ (SegmentAutoSplitCommand::getGlobalName());
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+
+ if ((*i)->getType() == Segment::Audio) {
+ AudioSplitDialog aSD(this, (*i), m_doc);
+
+ if (aSD.exec() == QDialog::Accepted) {
+ // split to threshold
+ //
+ command->addCommand(
+ new AudioSegmentAutoSplitCommand(m_doc,
+ *i,
+ aSD.getThreshold()));
+ // dmm - verifying that widget->value() accessors *can* work without crashing
+ // std::cout << "SILVAN: getThreshold() = " << aSD.getThreshold() << std::endl;
+ }
+ } else {
+ command->addCommand(new SegmentAutoSplitCommand(*i));
+ }
+ }
+
+ m_view->slotAddCommandToHistory(command);
+}
+
+void RosegardenGUIApp::slotJogLeft()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotJogLeft" << endl;
+ jogSelection( -Note(Note::Demisemiquaver).getDuration());
+}
+
+void RosegardenGUIApp::slotJogRight()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotJogRight" << endl;
+ jogSelection(Note(Note::Demisemiquaver).getDuration());
+}
+
+void RosegardenGUIApp::jogSelection(timeT amount)
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ SegmentSelection selection = m_view->getSelection();
+
+ SegmentReconfigureCommand *command = new SegmentReconfigureCommand(i18n("Jog Selection"));
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+
+ command->addSegment((*i),
+ (*i)->getStartTime() + amount,
+ (*i)->getEndMarkerTime() + amount,
+ (*i)->getTrack());
+ }
+
+ m_view->slotAddCommandToHistory(command);
+}
+
+void RosegardenGUIApp::createAndSetupTransport()
+{
+ // create the Transport GUI and add the callbacks to the
+ // buttons and keyboard accelerators
+ //
+ m_transport =
+ new TransportDialog(this);
+ plugAccelerators(m_transport, m_transport->getAccelerators());
+
+ m_transport->getAccelerators()->connectItem
+ (m_transport->getAccelerators()->insertItem(Key_T),
+ this,
+ SLOT(slotHideTransport()));
+
+ // Ensure that the checkbox is unchecked if the dialog
+ // is closed
+ connect(m_transport, SIGNAL(closed()),
+ SLOT(slotCloseTransport()));
+
+ // Handle loop setting and unsetting from the transport loop button
+ //
+
+ connect(m_transport, SIGNAL(setLoop()), SLOT(slotSetLoop()));
+ connect(m_transport, SIGNAL(unsetLoop()), SLOT(slotUnsetLoop()));
+ connect(m_transport, SIGNAL(panic()), SLOT(slotPanic()));
+
+ connect(m_transport, SIGNAL(editTempo(QWidget*)),
+ SLOT(slotEditTempo(QWidget*)));
+
+ connect(m_transport, SIGNAL(editTimeSignature(QWidget*)),
+ SLOT(slotEditTimeSignature(QWidget*)));
+
+ connect(m_transport, SIGNAL(editTransportTime(QWidget*)),
+ SLOT(slotEditTransportTime(QWidget*)));
+
+ // Handle set loop start/stop time buttons.
+ //
+ connect(m_transport, SIGNAL(setLoopStartTime()), SLOT(slotSetLoopStart()));
+ connect(m_transport, SIGNAL(setLoopStopTime()), SLOT(slotSetLoopStop()));
+
+ if (m_seqManager != 0)
+ m_seqManager->setTransport(m_transport);
+
+}
+
+void RosegardenGUIApp::slotSplitSelectionByPitch()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ SplitByPitchDialog dialog(m_view);
+ if (dialog.exec() != QDialog::Accepted)
+ return ;
+
+ SegmentSelection selection = m_view->getSelection();
+
+ KMacroCommand *command = new KMacroCommand
+ (SegmentSplitByPitchCommand::getGlobalName());
+
+ bool haveSomething = false;
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+
+ if ((*i)->getType() == Segment::Audio) {
+ // nothing
+ } else {
+ command->addCommand
+ (new SegmentSplitByPitchCommand
+ (*i,
+ dialog.getPitch(),
+ dialog.getShouldRange(),
+ dialog.getShouldDuplicateNonNoteEvents(),
+ (SegmentSplitByPitchCommand::ClefHandling)
+ dialog.getClefHandling()));
+ haveSomething = true;
+ }
+ }
+
+ if (haveSomething)
+ m_view->slotAddCommandToHistory(command);
+ //!!! else complain
+}
+
+void
+RosegardenGUIApp::slotSplitSelectionByRecordedSrc()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ SplitByRecordingSrcDialog dialog(m_view, m_doc);
+ if (dialog.exec() != QDialog::Accepted)
+ return ;
+
+ SegmentSelection selection = m_view->getSelection();
+
+ KMacroCommand *command = new KMacroCommand
+ (SegmentSplitByRecordingSrcCommand::getGlobalName());
+
+ bool haveSomething = false;
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+
+ if ((*i)->getType() == Segment::Audio) {
+ // nothing
+ } else {
+ command->addCommand
+ (new SegmentSplitByRecordingSrcCommand(*i,
+ dialog.getChannel(),
+ dialog.getDevice()));
+ haveSomething = true;
+ }
+ }
+ if (haveSomething)
+ m_view->slotAddCommandToHistory(command);
+}
+
+void
+RosegardenGUIApp::slotSplitSelectionAtTime()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ SegmentSelection selection = m_view->getSelection();
+ if (selection.empty())
+ return ;
+
+ timeT now = m_doc->getComposition().getPosition();
+
+ QString title = i18n("Split Segment at Time",
+ "Split %n Segments at Time",
+ selection.size());
+
+ TimeDialog dialog(m_view, title,
+ &m_doc->getComposition(),
+ now, true);
+
+ KMacroCommand *command = new KMacroCommand( title );
+
+ if (dialog.exec() == QDialog::Accepted) {
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+
+ if ((*i)->getType() == Segment::Audio) {
+ command->addCommand(new AudioSegmentSplitCommand(*i, dialog.getTime()));
+ } else {
+ command->addCommand(new SegmentSplitCommand(*i, dialog.getTime()));
+ }
+ }
+ m_view->slotAddCommandToHistory(command);
+ }
+}
+
+void
+RosegardenGUIApp::slotSetSegmentStartTimes()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ SegmentSelection selection = m_view->getSelection();
+ if (selection.empty())
+ return ;
+
+ timeT someTime = (*selection.begin())->getStartTime();
+
+ TimeDialog dialog(m_view, i18n("Segment Start Time"),
+ &m_doc->getComposition(),
+ someTime, false);
+
+ if (dialog.exec() == QDialog::Accepted) {
+
+ bool plural = (selection.size() > 1);
+
+ SegmentReconfigureCommand *command =
+ new SegmentReconfigureCommand(plural ?
+ i18n("Set Segment Start Times") :
+ i18n("Set Segment Start Time"));
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+
+ command->addSegment
+ (*i, dialog.getTime(),
+ (*i)->getEndMarkerTime() - (*i)->getStartTime() + dialog.getTime(),
+ (*i)->getTrack());
+ }
+
+ m_view->slotAddCommandToHistory(command);
+ }
+}
+
+void
+RosegardenGUIApp::slotSetSegmentDurations()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ SegmentSelection selection = m_view->getSelection();
+ if (selection.empty())
+ return ;
+
+ timeT someTime =
+ (*selection.begin())->getStartTime();
+
+ timeT someDuration =
+ (*selection.begin())->getEndMarkerTime() -
+ (*selection.begin())->getStartTime();
+
+ TimeDialog dialog(m_view, i18n("Segment Duration"),
+ &m_doc->getComposition(),
+ someTime,
+ someDuration,
+ false);
+
+ if (dialog.exec() == QDialog::Accepted) {
+
+ bool plural = (selection.size() > 1);
+
+ SegmentReconfigureCommand *command =
+ new SegmentReconfigureCommand(plural ?
+ i18n("Set Segment Durations") :
+ i18n("Set Segment Duration"));
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+
+ command->addSegment
+ (*i, (*i)->getStartTime(),
+ (*i)->getStartTime() + dialog.getTime(),
+ (*i)->getTrack());
+ }
+
+ m_view->slotAddCommandToHistory(command);
+ }
+}
+
+void RosegardenGUIApp::slotHarmonizeSelection()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ SegmentSelection selection = m_view->getSelection();
+ //!!! This should be somewhere else too
+
+ CompositionTimeSliceAdapter adapter(&m_doc->getComposition(),
+ &selection);
+
+ AnalysisHelper helper;
+ Segment *segment = new Segment;
+ helper.guessHarmonies(adapter, *segment);
+
+ //!!! do nothing with the results yet
+ delete segment;
+}
+
+void RosegardenGUIApp::slotTempoToSegmentLength()
+{
+ slotTempoToSegmentLength(this);
+}
+
+void RosegardenGUIApp::slotTempoToSegmentLength(QWidget* parent)
+{
+ RG_DEBUG << "RosegardenGUIApp::slotTempoToSegmentLength" << endl;
+
+ if (!m_view->haveSelection())
+ return ;
+
+ SegmentSelection selection = m_view->getSelection();
+
+ // Only set for a single selection
+ //
+ if (selection.size() == 1 &&
+ (*selection.begin())->getType() == Segment::Audio) {
+ Composition &comp = m_doc->getComposition();
+ Segment *seg = *selection.begin();
+
+ TimeSignature timeSig =
+ comp.getTimeSignatureAt( seg->getStartTime());
+
+ timeT endTime = seg->getEndTime();
+
+ if (seg->getRawEndMarkerTime())
+ endTime = seg->getEndMarkerTime();
+
+ RealTime segDuration =
+ seg->getAudioEndTime() - seg->getAudioStartTime();
+
+ int beats = 0;
+
+ // Get user to tell us how many beats or bars the segment contains
+ BeatsBarsDialog dialog(parent);
+ if (dialog.exec() == QDialog::Accepted) {
+ beats = dialog.getQuantity(); // beats (or bars)
+ if (dialog.getMode() == 1) // bars (multiply by time sig)
+ beats *= timeSig.getBeatsPerBar();
+#ifdef DEBUG_TEMPO_FROM_AUDIO
+
+ RG_DEBUG << "RosegardenGUIApp::slotTempoToSegmentLength - beats = " << beats
+ << " mode = " << ((dialog.getMode() == 0) ? "bars" : "beats") << endl
+ << " beats per bar = " << timeSig.getBeatsPerBar()
+ << " user quantity = " << dialog.getQuantity()
+ << " user mode = " << dialog.getMode() << endl;
+#endif
+
+ } else {
+ RG_DEBUG << "RosegardenGUIApp::slotTempoToSegmentLength - BeatsBarsDialog aborted"
+ << endl;
+ return ;
+ }
+
+ double beatLengthUsec =
+ double(segDuration.sec * 1000000 + segDuration.usec()) /
+ double(beats);
+
+ // New tempo is a minute divided by time of beat
+ // converted up (#1414252) to a sane value via getTempoFoQpm()
+ //
+ tempoT newTempo =
+ comp.getTempoForQpm(60.0 * 1000000.0 / beatLengthUsec);
+
+#ifdef DEBUG_TEMPO_FROM_AUDIO
+
+ RG_DEBUG << "RosegardenGUIApp::slotTempoToSegmentLength info: " << endl
+ << " beatLengthUsec = " << beatLengthUsec << endl
+ << " segDuration.usec = " << segDuration.usec() << endl
+ << " newTempo = " << newTempo << endl;
+#endif
+
+ KMacroCommand *macro = new KMacroCommand(i18n("Set Global Tempo"));
+
+ // Remove all tempo changes in reverse order so as the index numbers
+ // don't becoming meaningless as the command gets unwound.
+ //
+ for (int i = 0; i < comp.getTempoChangeCount(); i++)
+ macro->addCommand(new RemoveTempoChangeCommand(&comp,
+ (comp.getTempoChangeCount() - 1 - i)));
+
+ // add tempo change at time zero
+ //
+ macro->addCommand(new AddTempoChangeCommand(&comp, 0, newTempo));
+
+ // execute
+ m_doc->getCommandHistory()->addCommand(macro);
+ }
+}
+
+void RosegardenGUIApp::slotToggleSegmentLabels()
+{
+ KToggleAction* act = dynamic_cast<KToggleAction*>(actionCollection()->action("show_segment_labels"));
+ if (act) {
+ m_view->slotShowSegmentLabels(act->isChecked());
+ }
+}
+
+void RosegardenGUIApp::slotEdit()
+{
+ m_view->slotEditSegment(0);
+}
+
+void RosegardenGUIApp::slotEditAsNotation()
+{
+ m_view->slotEditSegmentNotation(0);
+}
+
+void RosegardenGUIApp::slotEditInMatrix()
+{
+ m_view->slotEditSegmentMatrix(0);
+}
+
+void RosegardenGUIApp::slotEditInPercussionMatrix()
+{
+ m_view->slotEditSegmentPercussionMatrix(0);
+}
+
+void RosegardenGUIApp::slotEditInEventList()
+{
+ m_view->slotEditSegmentEventList(0);
+}
+
+void RosegardenGUIApp::slotEditTempos()
+{
+ slotEditTempos(m_doc->getComposition().getPosition());
+}
+
+void RosegardenGUIApp::slotToggleToolBar()
+{
+ KTmpStatusMsg msg(i18n("Toggle the toolbar..."), this);
+
+ if (m_viewToolBar->isChecked())
+ toolBar("mainToolBar")->show();
+ else
+ toolBar("mainToolBar")->hide();
+}
+
+void RosegardenGUIApp::slotToggleToolsToolBar()
+{
+ KTmpStatusMsg msg(i18n("Toggle the tools toolbar..."), this);
+
+ if (m_viewToolsToolBar->isChecked())
+ toolBar("Tools Toolbar")->show();
+ else
+ toolBar("Tools Toolbar")->hide();
+}
+
+void RosegardenGUIApp::slotToggleTracksToolBar()
+{
+ KTmpStatusMsg msg(i18n("Toggle the tracks toolbar..."), this);
+
+ if (m_viewTracksToolBar->isChecked())
+ toolBar("Tracks Toolbar")->show();
+ else
+ toolBar("Tracks Toolbar")->hide();
+}
+
+void RosegardenGUIApp::slotToggleEditorsToolBar()
+{
+ KTmpStatusMsg msg(i18n("Toggle the editor toolbar..."), this);
+
+ if (m_viewEditorsToolBar->isChecked())
+ toolBar("Editors Toolbar")->show();
+ else
+ toolBar("Editors Toolbar")->hide();
+}
+
+void RosegardenGUIApp::slotToggleTransportToolBar()
+{
+ KTmpStatusMsg msg(i18n("Toggle the transport toolbar..."), this);
+
+ if (m_viewTransportToolBar->isChecked())
+ toolBar("Transport Toolbar")->show();
+ else
+ toolBar("Transport Toolbar")->hide();
+}
+
+void RosegardenGUIApp::slotToggleZoomToolBar()
+{
+ KTmpStatusMsg msg(i18n("Toggle the zoom toolbar..."), this);
+
+ if (m_viewZoomToolBar->isChecked())
+ toolBar("Zoom Toolbar")->show();
+ else
+ toolBar("Zoom Toolbar")->hide();
+}
+
+void RosegardenGUIApp::slotToggleTransport()
+{
+ KTmpStatusMsg msg(i18n("Toggle the Transport"), this);
+
+ if (m_viewTransport->isChecked()) {
+ getTransport()->show();
+ getTransport()->raise();
+ getTransport()->blockSignals(false);
+ } else {
+ getTransport()->hide();
+ getTransport()->blockSignals(true);
+ }
+}
+
+void RosegardenGUIApp::slotHideTransport()
+{
+ if (m_viewTransport->isChecked()) {
+ m_viewTransport->blockSignals(true);
+ m_viewTransport->setChecked(false);
+ m_viewTransport->blockSignals(false);
+ }
+ getTransport()->hide();
+ getTransport()->blockSignals(true);
+}
+
+void RosegardenGUIApp::slotToggleTrackLabels()
+{
+ if (m_viewTrackLabels->isChecked()) {
+#ifdef SETTING_LOG_DEBUG
+ _settingLog("toggle track labels on");
+#endif
+
+ m_view->getTrackEditor()->getTrackButtons()->
+ changeTrackInstrumentLabels(TrackLabel::ShowTrack);
+ } else {
+#ifdef SETTING_LOG_DEBUG
+ _settingLog("toggle track labels off");
+#endif
+
+ m_view->getTrackEditor()->getTrackButtons()->
+ changeTrackInstrumentLabels(TrackLabel::ShowInstrument);
+ }
+}
+
+void RosegardenGUIApp::slotToggleRulers()
+{
+ m_view->slotShowRulers(m_viewRulers->isChecked());
+}
+
+void RosegardenGUIApp::slotToggleTempoRuler()
+{
+ m_view->slotShowTempoRuler(m_viewTempoRuler->isChecked());
+}
+
+void RosegardenGUIApp::slotToggleChordNameRuler()
+{
+ m_view->slotShowChordNameRuler(m_viewChordNameRuler->isChecked());
+}
+
+void RosegardenGUIApp::slotTogglePreviews()
+{
+ m_view->slotShowPreviews(m_viewPreviews->isChecked());
+}
+
+void RosegardenGUIApp::slotDockParametersBack()
+{
+ m_dockLeft->dockBack();
+}
+
+void RosegardenGUIApp::slotParametersClosed()
+{
+ stateChanged("parametersbox_closed");
+ m_dockVisible = false;
+}
+
+void RosegardenGUIApp::slotParametersDockedBack(KDockWidget* dw, KDockWidget::DockPosition)
+{
+ if (dw == m_dockLeft) {
+ stateChanged("parametersbox_closed", KXMLGUIClient::StateReverse);
+ m_dockVisible = true;
+ }
+}
+
+void RosegardenGUIApp::slotToggleStatusBar()
+{
+ KTmpStatusMsg msg(i18n("Toggle the statusbar..."), this);
+
+ if (!m_viewStatusBar->isChecked())
+ statusBar()->hide();
+ else
+ statusBar()->show();
+}
+
+void RosegardenGUIApp::slotStatusMsg(QString text)
+{
+ ///////////////////////////////////////////////////////////////////
+ // change status message permanently
+ statusBar()->clear();
+ statusBar()->changeItem(text, EditViewBase::ID_STATUS_MSG);
+}
+
+void RosegardenGUIApp::slotStatusHelpMsg(QString text)
+{
+ ///////////////////////////////////////////////////////////////////
+ // change status message of whole statusbar temporary (text, msec)
+ statusBar()->message(text, 2000);
+}
+
+void RosegardenGUIApp::slotEnableTransport(bool enable)
+{
+ if (m_transport)
+ getTransport()->setEnabled(enable);
+}
+
+void RosegardenGUIApp::slotPointerSelected()
+{
+ m_view->selectTool(SegmentSelector::ToolName);
+}
+
+void RosegardenGUIApp::slotEraseSelected()
+{
+ m_view->selectTool(SegmentEraser::ToolName);
+}
+
+void RosegardenGUIApp::slotDrawSelected()
+{
+ m_view->selectTool(SegmentPencil::ToolName);
+}
+
+void RosegardenGUIApp::slotMoveSelected()
+{
+ m_view->selectTool(SegmentMover::ToolName);
+}
+
+void RosegardenGUIApp::slotResizeSelected()
+{
+ m_view->selectTool(SegmentResizer::ToolName);
+}
+
+void RosegardenGUIApp::slotJoinSelected()
+{
+ KMessageBox::information(this,
+ i18n("The join tool isn't implemented yet. Instead please highlight "
+ "the segments you want to join and then use the menu option:\n\n"
+ " Segments->Collapse Segments.\n"),
+ i18n("Join tool not yet implemented"));
+
+ m_view->selectTool(SegmentJoiner::ToolName);
+}
+
+void RosegardenGUIApp::slotSplitSelected()
+{
+ m_view->selectTool(SegmentSplitter::ToolName);
+}
+
+void RosegardenGUIApp::slotAddTrack()
+{
+ if (!m_view)
+ return ;
+
+ // default to the base number - might not actually exist though
+ //
+ InstrumentId id = MidiInstrumentBase;
+
+ // Get the first Internal/MIDI instrument
+ //
+ DeviceList *devices = m_doc->getStudio().getDevices();
+ bool have = false;
+
+ for (DeviceList::iterator it = devices->begin();
+ it != devices->end() && !have; it++) {
+
+ if ((*it)->getType() != Device::Midi)
+ continue;
+
+ InstrumentList instruments = (*it)->getAllInstruments();
+ for (InstrumentList::iterator iit = instruments.begin();
+ iit != instruments.end(); iit++) {
+
+ if ((*iit)->getId() >= MidiInstrumentBase) {
+ id = (*iit)->getId();
+ have = true;
+ break;
+ }
+ }
+ }
+
+ Composition &comp = m_doc->getComposition();
+ TrackId trackId = comp.getSelectedTrack();
+ Track *track = comp.getTrackById(trackId);
+
+ int pos = -1;
+ if (track) pos = track->getPosition() + 1;
+
+ m_view->slotAddTracks(1, id, pos);
+}
+
+void RosegardenGUIApp::slotAddTracks()
+{
+ if (!m_view)
+ return ;
+
+ // default to the base number - might not actually exist though
+ //
+ InstrumentId id = MidiInstrumentBase;
+
+ // Get the first Internal/MIDI instrument
+ //
+ DeviceList *devices = m_doc->getStudio().getDevices();
+ bool have = false;
+
+ for (DeviceList::iterator it = devices->begin();
+ it != devices->end() && !have; it++) {
+
+ if ((*it)->getType() != Device::Midi)
+ continue;
+
+ InstrumentList instruments = (*it)->getAllInstruments();
+ for (InstrumentList::iterator iit = instruments.begin();
+ iit != instruments.end(); iit++) {
+
+ if ((*iit)->getId() >= MidiInstrumentBase) {
+ id = (*iit)->getId();
+ have = true;
+ break;
+ }
+ }
+ }
+
+ Composition &comp = m_doc->getComposition();
+ TrackId trackId = comp.getSelectedTrack();
+ Track *track = comp.getTrackById(trackId);
+
+ int pos = 0;
+ if (track) pos = track->getPosition();
+
+ bool ok = false;
+
+ AddTracksDialog dialog(this, pos);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ m_view->slotAddTracks(dialog.getTracks(), id,
+ dialog.getInsertPosition());
+ }
+}
+
+void RosegardenGUIApp::slotDeleteTrack()
+{
+ if (!m_view)
+ return ;
+
+ Composition &comp = m_doc->getComposition();
+ TrackId trackId = comp.getSelectedTrack();
+ Track *track = comp.getTrackById(trackId);
+
+ RG_DEBUG << "RosegardenGUIApp::slotDeleteTrack() : about to delete track id "
+ << trackId << endl;
+
+ if (track == 0)
+ return ;
+
+ // Always have at least one track in a composition
+ //
+ if (comp.getNbTracks() == 1)
+ return ;
+
+ // VLADA
+ if (m_view->haveSelection()) {
+
+ SegmentSelection selection = m_view->getSelection();
+ m_view->slotSelectTrackSegments(trackId);
+ m_view->getTrackEditor()->slotDeleteSelectedSegments();
+ m_view->slotPropagateSegmentSelection(selection);
+
+ } else {
+
+ m_view->slotSelectTrackSegments(trackId);
+ m_view->getTrackEditor()->slotDeleteSelectedSegments();
+ }
+ //VLADA
+
+ int position = track->getPosition();
+
+ // Delete the track
+ //
+ std::vector<TrackId> tracks;
+ tracks.push_back(trackId);
+
+ m_view->slotDeleteTracks(tracks);
+
+ // Select a new valid track
+ //
+ if (comp.getTrackByPosition(position))
+ trackId = comp.getTrackByPosition(position)->getId();
+ else if (comp.getTrackByPosition(position - 1))
+ trackId = comp.getTrackByPosition(position - 1)->getId();
+ else {
+ RG_DEBUG << "RosegardenGUIApp::slotDeleteTrack - "
+ << "can't select a highlighted track after delete"
+ << endl;
+ }
+
+ comp.setSelectedTrack(trackId);
+
+ Instrument *inst = m_doc->getStudio().
+ getInstrumentById(comp.getTrackById(trackId)->getInstrument());
+
+ //VLADA
+ // m_view->slotSelectTrackSegments(trackId);
+ //VLADA
+}
+
+void RosegardenGUIApp::slotMoveTrackDown()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotMoveTrackDown" << endl;
+
+ Composition &comp = m_doc->getComposition();
+ Track *srcTrack = comp.getTrackById(comp.getSelectedTrack());
+
+ // Check for track object
+ //
+ if (srcTrack == 0)
+ return ;
+
+ // Check destination track exists
+ //
+ Track *destTrack =
+ comp.getTrackByPosition(srcTrack->getPosition() + 1);
+
+ if (destTrack == 0)
+ return ;
+
+ MoveTracksCommand *command =
+ new MoveTracksCommand(&comp, srcTrack->getId(), destTrack->getId());
+
+ m_doc->getCommandHistory()->addCommand(command);
+
+ // make sure we're showing the right selection
+ m_view->slotSelectTrackSegments(comp.getSelectedTrack());
+
+}
+
+void RosegardenGUIApp::slotMoveTrackUp()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotMoveTrackUp" << endl;
+
+ Composition &comp = m_doc->getComposition();
+ Track *srcTrack = comp.getTrackById(comp.getSelectedTrack());
+
+ // Check for track object
+ //
+ if (srcTrack == 0)
+ return ;
+
+ // Check we're not at the top already
+ //
+ if (srcTrack->getPosition() == 0)
+ return ;
+
+ // Check destination track exists
+ //
+ Track *destTrack =
+ comp.getTrackByPosition(srcTrack->getPosition() - 1);
+
+ if (destTrack == 0)
+ return ;
+
+ MoveTracksCommand *command =
+ new MoveTracksCommand(&comp, srcTrack->getId(), destTrack->getId());
+
+ m_doc->getCommandHistory()->addCommand(command);
+
+ // make sure we're showing the right selection
+ m_view->slotSelectTrackSegments(comp.getSelectedTrack());
+}
+
+void RosegardenGUIApp::slotRevertToSaved()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotRevertToSaved" << endl;
+
+ if (m_doc->isModified()) {
+ int revert =
+ KMessageBox::questionYesNo(this,
+ i18n("Revert modified document to previous saved version?"));
+
+ if (revert == KMessageBox::No)
+ return ;
+
+ openFile(m_doc->getAbsFilePath());
+ }
+}
+
+void RosegardenGUIApp::slotImportProject()
+{
+ if (m_doc && !m_doc->saveIfModified())
+ return ;
+
+ KURL url = KFileDialog::getOpenURL
+ (":RGPROJECT",
+ i18n("*.rgp|Rosegarden Project files\n*|All files"), this,
+ i18n("Import Rosegarden Project File"));
+ if (url.isEmpty()) {
+ return ;
+ }
+
+ QString tmpfile;
+ KIO::NetAccess::download(url, tmpfile, this);
+
+ importProject(tmpfile);
+
+ KIO::NetAccess::removeTempFile(tmpfile);
+}
+
+void RosegardenGUIApp::importProject(QString filePath)
+{
+ KProcess *proc = new KProcess;
+ *proc << "rosegarden-project-package";
+ *proc << "--unpack";
+ *proc << filePath;
+
+ KStartupLogo::hideIfStillThere();
+ proc->start(KProcess::Block, KProcess::All);
+
+ if (!proc->normalExit() || proc->exitStatus()) {
+ CurrentProgressDialog::freeze();
+ KMessageBox::sorry(this, i18n("Failed to import project file \"%1\"").arg(filePath));
+ CurrentProgressDialog::thaw();
+ delete proc;
+ return ;
+ }
+
+ delete proc;
+
+ QString rgFile = filePath;
+ rgFile.replace(QRegExp(".rg.rgp$"), ".rg");
+ rgFile.replace(QRegExp(".rgp$"), ".rg");
+ openURL(rgFile);
+}
+
+void RosegardenGUIApp::slotImportMIDI()
+{
+ if (m_doc && !m_doc->saveIfModified())
+ return ;
+
+ KURL url = KFileDialog::getOpenURL
+ (":MIDI",
+ "audio/x-midi", this,
+ i18n("Open MIDI File"));
+ if (url.isEmpty()) {
+ return ;
+ }
+
+ QString tmpfile;
+ KIO::NetAccess::download(url, tmpfile, this);
+ openFile(tmpfile, ImportMIDI); // does everything including setting the document
+
+ KIO::NetAccess::removeTempFile( tmpfile );
+}
+
+void RosegardenGUIApp::slotMergeMIDI()
+{
+ KURL url = KFileDialog::getOpenURL
+ (":MIDI",
+ "audio/x-midi", this,
+ i18n("Merge MIDI File"));
+ if (url.isEmpty()) {
+ return ;
+ }
+
+ QString tmpfile;
+ KIO::NetAccess::download(url, tmpfile, this);
+ mergeFile(tmpfile, ImportMIDI);
+
+ KIO::NetAccess::removeTempFile( tmpfile );
+}
+
+QTextCodec *
+RosegardenGUIApp::guessTextCodec(std::string text)
+{
+ QTextCodec *codec = 0;
+
+ for (int c = 0; c < text.length(); ++c) {
+ if (text[c] & 0x80) {
+
+ CurrentProgressDialog::freeze();
+ KStartupLogo::hideIfStillThere();
+
+ IdentifyTextCodecDialog dialog(0, text);
+ dialog.exec();
+
+ std::string codecName = dialog.getCodec();
+
+ CurrentProgressDialog::thaw();
+
+ if (codecName != "") {
+ codec = QTextCodec::codecForName(codecName.c_str());
+ }
+ break;
+ }
+ }
+
+ return codec;
+}
+
+void
+RosegardenGUIApp::fixTextEncodings(Composition *c)
+
+{
+ QTextCodec *codec = 0;
+
+ for (Composition::iterator i = c->begin();
+ i != c->end(); ++i) {
+
+ for (Segment::iterator j = (*i)->begin();
+ j != (*i)->end(); ++j) {
+
+ if ((*j)->isa(Text::EventType)) {
+
+ std::string text;
+
+ if ((*j)->get
+ <String>
+ (Text::TextPropertyName, text)) {
+
+ if (!codec)
+ codec = guessTextCodec(text);
+
+ if (codec) {
+ (*j)->set
+ <String>
+ (Text::TextPropertyName,
+ convertFromCodec(text, codec));
+ }
+ }
+ }
+ }
+ }
+
+ if (!codec)
+ codec = guessTextCodec(c->getCopyrightNote());
+ if (codec)
+ c->setCopyrightNote(convertFromCodec(c->getCopyrightNote(), codec));
+
+ for (Composition::trackcontainer::iterator i =
+ c->getTracks().begin(); i != c->getTracks().end(); ++i) {
+ if (!codec)
+ codec = guessTextCodec(i->second->getLabel());
+ if (codec)
+ i->second->setLabel(convertFromCodec(i->second->getLabel(), codec));
+ }
+
+ for (Composition::iterator i = c->begin(); i != c->end(); ++i) {
+ if (!codec)
+ codec = guessTextCodec((*i)->getLabel());
+ if (codec)
+ (*i)->setLabel(convertFromCodec((*i)->getLabel(), codec));
+ }
+}
+
+RosegardenGUIDoc*
+RosegardenGUIApp::createDocumentFromMIDIFile(QString file)
+{
+ //if (!merge && !m_doc->saveIfModified()) return;
+
+ // Create new document (autoload is inherent)
+ //
+ RosegardenGUIDoc *newDoc = new RosegardenGUIDoc(this, m_pluginManager);
+
+ std::string fname(QFile::encodeName(file));
+
+ MidiFile midiFile(fname,
+ &newDoc->getStudio());
+
+ KStartupLogo::hideIfStillThere();
+ ProgressDialog progressDlg(i18n("Importing MIDI file..."),
+ 200,
+ this);
+
+ CurrentProgressDialog::set
+ (&progressDlg);
+
+ connect(&midiFile, SIGNAL(setProgress(int)),
+ progressDlg.progressBar(), SLOT(setValue(int)));
+
+ connect(&midiFile, SIGNAL(incrementProgress(int)),
+ progressDlg.progressBar(), SLOT(advance(int)));
+
+ if (!midiFile.open()) {
+ CurrentProgressDialog::freeze();
+ KMessageBox::error(this, strtoqstr(midiFile.getError())); //!!! i18n
+ delete newDoc;
+ return 0;
+ }
+
+ midiFile.convertToRosegarden(newDoc->getComposition(),
+ MidiFile::CONVERT_REPLACE);
+
+ fixTextEncodings(&newDoc->getComposition());
+
+ // Set modification flag
+ //
+ newDoc->slotDocumentModified();
+
+ // Set the caption
+ //
+ newDoc->setTitle(QFileInfo(file).fileName());
+ newDoc->setAbsFilePath(QFileInfo(file).absFilePath());
+
+ // Clean up for notation purposes (after reinitialise, because that
+ // sets the composition's end marker time which is needed here)
+
+ progressDlg.slotSetOperationName(i18n("Calculating notation..."));
+ ProgressDialog::processEvents();
+
+ Composition *comp = &newDoc->getComposition();
+
+ for (Composition::iterator i = comp->begin();
+ i != comp->end(); ++i) {
+
+ Segment &segment = **i;
+ SegmentNotationHelper helper(segment);
+ segment.insert(helper.guessClef(segment.begin(),
+ segment.getEndMarker()).getAsEvent
+ (segment.getStartTime()));
+ }
+
+ progressDlg.progressBar()->setProgress(100);
+
+ for (Composition::iterator i = comp->begin();
+ i != comp->end(); ++i) {
+
+ // find first key event in each segment (we'd have done the
+ // same for clefs, except there is no MIDI clef event)
+
+ Segment &segment = **i;
+ timeT firstKeyTime = segment.getEndMarkerTime();
+
+ for (Segment::iterator si = segment.begin();
+ segment.isBeforeEndMarker(si); ++si) {
+ if ((*si)->isa(Rosegarden::Key::EventType)) {
+ firstKeyTime = (*si)->getAbsoluteTime();
+ break;
+ }
+ }
+
+ if (firstKeyTime > segment.getStartTime()) {
+ CompositionTimeSliceAdapter adapter
+ (comp, timeT(0), firstKeyTime);
+ AnalysisHelper helper;
+ segment.insert(helper.guessKey(adapter).getAsEvent
+ (segment.getStartTime()));
+ }
+ }
+
+ int progressPer = 100;
+ if (comp->getNbSegments() > 0)
+ progressPer = (int)(100.0 / double(comp->getNbSegments()));
+
+ KMacroCommand *command = new KMacroCommand(i18n("Calculate Notation"));
+
+ for (Composition::iterator i = comp->begin();
+ i != comp->end(); ++i) {
+
+ Segment &segment = **i;
+ timeT startTime(segment.getStartTime());
+ timeT endTime(segment.getEndMarkerTime());
+
+// std::cerr << "segment: start time " << segment.getStartTime() << ", end time " << segment.getEndTime() << ", end marker time " << segment.getEndMarkerTime() << ", events " << segment.size() << std::endl;
+
+ EventQuantizeCommand *subCommand = new EventQuantizeCommand
+ (segment, startTime, endTime, "Notation Options", true);
+
+ subCommand->setProgressTotal(progressPer + 1);
+ QObject::connect(subCommand, SIGNAL(incrementProgress(int)),
+ progressDlg.progressBar(), SLOT(advance(int)));
+
+ command->addCommand(subCommand);
+ }
+
+ newDoc->getCommandHistory()->addCommand(command);
+
+ if (comp->getTimeSignatureCount() == 0) {
+ CompositionTimeSliceAdapter adapter(comp);
+ AnalysisHelper analysisHelper;
+ TimeSignature timeSig =
+ analysisHelper.guessTimeSignature(adapter);
+ comp->addTimeSignature(0, timeSig);
+ }
+
+ return newDoc;
+}
+
+void RosegardenGUIApp::slotImportRG21()
+{
+ if (m_doc && !m_doc->saveIfModified())
+ return ;
+
+ KURL url = KFileDialog::getOpenURL
+ (":ROSEGARDEN21",
+ i18n("*.rose|Rosegarden-2 files\n*|All files"), this,
+ i18n("Open Rosegarden 2.1 File"));
+ if (url.isEmpty()) {
+ return ;
+ }
+
+ QString tmpfile;
+ KIO::NetAccess::download(url, tmpfile, this);
+ openFile(tmpfile, ImportRG21);
+
+ KIO::NetAccess::removeTempFile(tmpfile);
+}
+
+void RosegardenGUIApp::slotMergeRG21()
+{
+ KURL url = KFileDialog::getOpenURL
+ (":ROSEGARDEN21",
+ i18n("*.rose|Rosegarden-2 files\n*|All files"), this,
+ i18n("Open Rosegarden 2.1 File"));
+ if (url.isEmpty()) {
+ return ;
+ }
+
+ QString tmpfile;
+ KIO::NetAccess::download(url, tmpfile, this);
+ mergeFile(tmpfile, ImportRG21);
+
+ KIO::NetAccess::removeTempFile( tmpfile );
+}
+
+RosegardenGUIDoc*
+RosegardenGUIApp::createDocumentFromRG21File(QString file)
+{
+ KStartupLogo::hideIfStillThere();
+ ProgressDialog progressDlg(
+ i18n("Importing Rosegarden 2.1 file..."), 100, this);
+
+ CurrentProgressDialog::set
+ (&progressDlg);
+
+ // Inherent autoload
+ //
+ RosegardenGUIDoc *newDoc = new RosegardenGUIDoc(this, m_pluginManager);
+
+ RG21Loader rg21Loader(&newDoc->getStudio());
+
+ // TODO: make RG21Loader to actually emit these signals
+ //
+ connect(&rg21Loader, SIGNAL(setProgress(int)),
+ progressDlg.progressBar(), SLOT(setValue(int)));
+
+ connect(&rg21Loader, SIGNAL(incrementProgress(int)),
+ progressDlg.progressBar(), SLOT(advance(int)));
+
+ // "your starter for 40%" - helps the "freeze" work
+ //
+ progressDlg.progressBar()->advance(40);
+
+ if (!rg21Loader.load(file, newDoc->getComposition())) {
+ CurrentProgressDialog::freeze();
+ KMessageBox::error(this,
+ i18n("Can't load Rosegarden 2.1 file. It appears to be corrupted."));
+ delete newDoc;
+ return 0;
+ }
+
+ // Set modification flag
+ //
+ newDoc->slotDocumentModified();
+
+ // Set the caption and add recent
+ //
+ newDoc->setTitle(QFileInfo(file).fileName());
+ newDoc->setAbsFilePath(QFileInfo(file).absFilePath());
+
+ return newDoc;
+
+}
+
+void
+RosegardenGUIApp::slotImportHydrogen()
+{
+ if (m_doc && !m_doc->saveIfModified())
+ return ;
+
+ KURL url = KFileDialog::getOpenURL
+ (":HYDROGEN",
+ i18n("*.h2song|Hydrogen files\n*|All files"), this,
+ i18n("Open Hydrogen File"));
+ if (url.isEmpty()) {
+ return ;
+ }
+
+ QString tmpfile;
+ KIO::NetAccess::download(url, tmpfile, this);
+ openFile(tmpfile, ImportHydrogen);
+
+ KIO::NetAccess::removeTempFile(tmpfile);
+}
+
+void RosegardenGUIApp::slotMergeHydrogen()
+{
+ KURL url = KFileDialog::getOpenURL
+ (":HYDROGEN",
+ i18n("*.h2song|Hydrogen files\n*|All files"), this,
+ i18n("Open Hydrogen File"));
+ if (url.isEmpty()) {
+ return ;
+ }
+
+ QString tmpfile;
+ KIO::NetAccess::download(url, tmpfile, this);
+ mergeFile(tmpfile, ImportHydrogen);
+
+ KIO::NetAccess::removeTempFile( tmpfile );
+}
+
+RosegardenGUIDoc*
+RosegardenGUIApp::createDocumentFromHydrogenFile(QString file)
+{
+ KStartupLogo::hideIfStillThere();
+ ProgressDialog progressDlg(
+ i18n("Importing Hydrogen file..."), 100, this);
+
+ CurrentProgressDialog::set
+ (&progressDlg);
+
+ // Inherent autoload
+ //
+ RosegardenGUIDoc *newDoc = new RosegardenGUIDoc(this, m_pluginManager);
+
+ HydrogenLoader hydrogenLoader(&newDoc->getStudio());
+
+ // TODO: make RG21Loader to actually emit these signals
+ //
+ connect(&hydrogenLoader, SIGNAL(setProgress(int)),
+ progressDlg.progressBar(), SLOT(setValue(int)));
+
+ connect(&hydrogenLoader, SIGNAL(incrementProgress(int)),
+ progressDlg.progressBar(), SLOT(advance(int)));
+
+ // "your starter for 40%" - helps the "freeze" work
+ //
+ progressDlg.progressBar()->advance(40);
+
+ if (!hydrogenLoader.load(file, newDoc->getComposition())) {
+ CurrentProgressDialog::freeze();
+ KMessageBox::error(this,
+ i18n("Can't load Hydrogen file. It appears to be corrupted."));
+ delete newDoc;
+ return 0;
+ }
+
+ // Set modification flag
+ //
+ newDoc->slotDocumentModified();
+
+ // Set the caption and add recent
+ //
+ newDoc->setTitle(QFileInfo(file).fileName());
+ newDoc->setAbsFilePath(QFileInfo(file).absFilePath());
+
+ return newDoc;
+
+}
+
+void
+RosegardenGUIApp::mergeFile(QString filePath, ImportType type)
+{
+ RosegardenGUIDoc *doc = createDocument(filePath, type);
+
+ if (doc) {
+ if (m_doc) {
+
+ bool timingsDiffer = false;
+ Composition &c1 = m_doc->getComposition();
+ Composition &c2 = doc->getComposition();
+
+ // compare tempos and time sigs in the two -- rather laborious
+
+ if (c1.getTimeSignatureCount() != c2.getTimeSignatureCount()) {
+ timingsDiffer = true;
+ } else {
+ for (int i = 0; i < c1.getTimeSignatureCount(); ++i) {
+ std::pair<timeT, TimeSignature> t1 =
+ c1.getTimeSignatureChange(i);
+ std::pair<timeT, TimeSignature> t2 =
+ c2.getTimeSignatureChange(i);
+ if (t1.first != t2.first || t1.second != t2.second) {
+ timingsDiffer = true;
+ break;
+ }
+ }
+ }
+
+ if (c1.getTempoChangeCount() != c2.getTempoChangeCount()) {
+ timingsDiffer = true;
+ } else {
+ for (int i = 0; i < c1.getTempoChangeCount(); ++i) {
+ std::pair<timeT, tempoT> t1 = c1.getTempoChange(i);
+ std::pair<timeT, tempoT> t2 = c2.getTempoChange(i);
+ if (t1.first != t2.first || t1.second != t2.second) {
+ timingsDiffer = true;
+ break;
+ }
+ }
+ }
+
+ FileMergeDialog dialog(this, filePath, timingsDiffer);
+ if (dialog.exec() == QDialog::Accepted) {
+ m_doc->mergeDocument(doc, dialog.getMergeOptions());
+ }
+
+ delete doc;
+
+ } else {
+ setDocument(doc);
+ }
+ }
+}
+
+void
+RosegardenGUIApp::slotUpdatePlaybackPosition()
+{
+ static int callbackCount = 0;
+
+ // Either sequencer mappper or the sequence manager could be missing at
+ // this point.
+ //
+ if (!m_seqManager || !m_seqManager->getSequencerMapper())
+ return ;
+
+ SequencerMapper *mapper = m_seqManager->getSequencerMapper();
+
+ MappedEvent ev;
+ bool haveEvent = mapper->getVisual(ev);
+ if (haveEvent)
+ getTransport()->setMidiOutLabel(&ev);
+
+ RealTime position = mapper->getPositionPointer();
+
+ // std::cerr << "RosegardenGUIApp::slotUpdatePlaybackPosition: mapper pos = " << position << std::endl;
+
+ Composition &comp = m_doc->getComposition();
+ timeT elapsedTime = comp.getElapsedTimeForRealTime(position);
+
+ // std::cerr << "RosegardenGUIApp::slotUpdatePlaybackPosition: mapper timeT = " << elapsedTime << std::endl;
+
+ if (m_seqManager->getTransportStatus() == RECORDING) {
+
+ MappedComposition mC;
+ if (mapper->getRecordedEvents(mC) > 0) {
+ m_seqManager->processAsynchronousMidi(mC, 0);
+ m_doc->insertRecordedMidi(mC);
+ }
+
+ m_doc->updateRecordingMIDISegment();
+ m_doc->updateRecordingAudioSegments();
+ }
+
+ m_originatingJump = true;
+ m_doc->slotSetPointerPosition(elapsedTime);
+ m_originatingJump = false;
+
+ if (m_audioMixer && m_audioMixer->isVisible())
+ m_audioMixer->updateMeters(mapper);
+
+ if (m_midiMixer && m_midiMixer->isVisible())
+ m_midiMixer->updateMeters(mapper);
+
+ m_view->updateMeters(mapper);
+
+ if (++callbackCount == 60) {
+ slotUpdateCPUMeter(true);
+ callbackCount = 0;
+ }
+
+ // if (elapsedTime >= comp.getEndMarker())
+ // slotStop();
+}
+
+void
+RosegardenGUIApp::slotUpdateCPUMeter(bool playing)
+{
+ static std::ifstream *statstream = 0;
+ static bool modified = false;
+ static unsigned long lastBusy = 0, lastIdle = 0;
+
+ if (playing) {
+
+ if (!statstream) {
+ statstream = new std::ifstream("/proc/stat", std::ios::in);
+ }
+
+ if (!statstream || !*statstream)
+ return ;
+ statstream->seekg(0, std::ios::beg);
+
+ std::string cpu;
+ unsigned long user, nice, sys, idle;
+ *statstream >> cpu;
+ *statstream >> user;
+ *statstream >> nice;
+ *statstream >> sys;
+ *statstream >> idle;
+
+ unsigned long busy = user + nice + sys;
+ unsigned long count = 0;
+
+ if (lastBusy > 0) {
+ unsigned long bd = busy - lastBusy;
+ unsigned long id = idle - lastIdle;
+ if (bd + id > 0)
+ count = bd * 100 / (bd + id);
+ if (count > 100)
+ count = 100;
+ }
+
+ lastBusy = busy;
+ lastIdle = idle;
+
+ if (m_progressBar) {
+ if (!modified) {
+ m_progressBar->setTextEnabled(true);
+ m_progressBar->setFormat("CPU");
+ }
+ m_progressBar->setProgress(count);
+ }
+
+ modified = true;
+
+ } else if (modified) {
+ if (m_progressBar) {
+ m_progressBar->setTextEnabled(false);
+ m_progressBar->setFormat("%p%");
+ m_progressBar->setProgress(0);
+ }
+ modified = false;
+ }
+}
+
+void
+RosegardenGUIApp::slotUpdateMonitoring()
+{
+ // Either sequencer mappper or the sequence manager could be missing at
+ // this point.
+ //
+ if (!m_seqManager || !m_seqManager->getSequencerMapper())
+ return ;
+
+ SequencerMapper *mapper = m_seqManager->getSequencerMapper();
+
+ if (m_audioMixer && m_audioMixer->isVisible())
+ m_audioMixer->updateMonitorMeters(mapper);
+
+ if (m_midiMixer && m_midiMixer->isVisible())
+ m_midiMixer->updateMonitorMeter(mapper);
+
+ m_view->updateMonitorMeters(mapper);
+
+ slotUpdateCPUMeter(false);
+}
+
+void RosegardenGUIApp::slotSetPointerPosition(timeT t)
+{
+ Composition &comp = m_doc->getComposition();
+
+ // std::cerr << "RosegardenGUIApp::slotSetPointerPosition: t = " << t << std::endl;
+
+ if (m_seqManager) {
+ if ( m_seqManager->getTransportStatus() == PLAYING ||
+ m_seqManager->getTransportStatus() == RECORDING ) {
+ if (t > comp.getEndMarker()) {
+ if (m_seqManager->getTransportStatus() == PLAYING) {
+
+ slotStop();
+ t = comp.getEndMarker();
+ m_doc->slotSetPointerPosition(t); //causes this method to be re-invoked
+ return ;
+
+ } else { // if recording, increase composition duration
+ std::pair<timeT, timeT> timeRange = comp.getBarRangeForTime(t);
+ timeT barDuration = timeRange.second - timeRange.first;
+ timeT newEndMarker = t + 10 * barDuration;
+ comp.setEndMarker(newEndMarker);
+ getView()->getTrackEditor()->slotReadjustCanvasSize();
+ getView()->getTrackEditor()->updateRulers();
+ }
+ }
+ }
+
+ // cc 20050520 - jump at the sequencer even if we're not playing,
+ // because we might be a transport master of some kind
+ try {
+ if (!m_originatingJump) {
+ m_seqManager->sendSequencerJump(comp.getElapsedRealTime(t));
+ }
+ } catch (QString s) {
+ KMessageBox::error(this, s);
+ }
+ }
+
+ // set the time sig
+ getTransport()->setTimeSignature(comp.getTimeSignatureAt(t));
+
+ // and the tempo
+ getTransport()->setTempo(comp.getTempoAtTime(t));
+
+ // and the time
+ //
+ TransportDialog::TimeDisplayMode mode =
+ getTransport()->getCurrentMode();
+
+ if (mode == TransportDialog::BarMode ||
+ mode == TransportDialog::BarMetronomeMode) {
+
+ slotDisplayBarTime(t);
+
+ } else {
+
+ RealTime rT(comp.getElapsedRealTime(t));
+
+ if (getTransport()->isShowingTimeToEnd()) {
+ rT = rT - comp.getElapsedRealTime(comp.getDuration());
+ }
+
+ if (mode == TransportDialog::RealMode) {
+
+ getTransport()->displayRealTime(rT);
+
+ } else if (mode == TransportDialog::SMPTEMode) {
+
+ getTransport()->displaySMPTETime(rT);
+
+ } else {
+
+ getTransport()->displayFrameTime(rT);
+ }
+ }
+
+ // handle transport mode configuration changes
+ std::string modeAsString = getTransport()->getCurrentModeAsString();
+
+ if (m_doc->getConfiguration().get<String>
+ (DocumentConfiguration::TransportMode) != modeAsString) {
+
+ m_doc->getConfiguration().set<String>
+ (DocumentConfiguration::TransportMode, modeAsString);
+
+ //m_doc->slotDocumentModified(); to avoid being prompted for a file change when merely changing the transport display
+ }
+
+ // Update position on the marker editor if it's available
+ //
+ if (m_markerEditor)
+ m_markerEditor->updatePosition();
+}
+
+void RosegardenGUIApp::slotDisplayBarTime(timeT t)
+{
+ Composition &comp = m_doc->getComposition();
+
+ int barNo = comp.getBarNumber(t);
+ timeT barStart = comp.getBarStart(barNo);
+
+ TimeSignature timeSig = comp.getTimeSignatureAt(t);
+ timeT beatDuration = timeSig.getBeatDuration();
+
+ int beatNo = (t - barStart) / beatDuration;
+ int unitNo = (t - barStart) - (beatNo * beatDuration);
+
+ if (getTransport()->isShowingTimeToEnd()) {
+ barNo = barNo + 1 - comp.getNbBars();
+ beatNo = timeSig.getBeatsPerBar() - 1 - beatNo;
+ unitNo = timeSig.getBeatDuration() - 1 - unitNo;
+ } else {
+ // convert to 1-based display bar numbers
+ barNo += 1;
+ beatNo += 1;
+ }
+
+ // show units in hemidemis (or whatever), not in raw time ticks
+ unitNo /= Note(Note::Shortest).getDuration();
+
+ getTransport()->displayBarTime(barNo, beatNo, unitNo);
+}
+
+void RosegardenGUIApp::slotRefreshTimeDisplay()
+{
+ if ( m_seqManager->getTransportStatus() == PLAYING ||
+ m_seqManager->getTransportStatus() == RECORDING ) {
+ return ; // it'll be refreshed in a moment anyway
+ }
+ slotSetPointerPosition(m_doc->getComposition().getPosition());
+}
+
+bool
+RosegardenGUIApp::isTrackEditorPlayTracking() const
+{
+ return m_view->getTrackEditor()->isTracking();
+}
+
+void RosegardenGUIApp::slotToggleTracking()
+{
+ m_view->getTrackEditor()->slotToggleTracking();
+}
+
+void RosegardenGUIApp::slotTestStartupTester()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotTestStartupTester" << endl;
+
+ if (!m_startupTester) {
+ m_startupTester = new StartupTester();
+ connect(m_startupTester, SIGNAL(newerVersionAvailable(QString)),
+ this, SLOT(slotNewerVersionAvailable(QString)));
+ m_startupTester->start();
+ QTimer::singleShot(100, this, SLOT(slotTestStartupTester()));
+ return ;
+ }
+
+ if (!m_startupTester->isReady()) {
+ QTimer::singleShot(100, this, SLOT(slotTestStartupTester()));
+ return ;
+ }
+
+ QStringList missingFeatures;
+ QStringList allMissing;
+
+ QStringList missing;
+ bool have = m_startupTester->haveProjectPackager(&missing);
+
+ stateChanged("have_project_packager",
+ have ?
+ KXMLGUIClient::StateNoReverse : KXMLGUIClient::StateReverse);
+
+ if (!have) {
+ missingFeatures.push_back(i18n("Export and import of Rosegarden Project files"));
+ if (missing.count() == 0) {
+ allMissing.push_back(i18n("The Rosegarden Project Packager helper script"));
+ } else {
+ for (int i = 0; i < missing.count(); ++i) {
+// if (missingFeatures.count() > 1) {
+ allMissing.push_back(i18n("%1 - for project file support").arg(missing[i]));
+// } else {
+// allMissing.push_back(missing[i]);
+// }
+ }
+ }
+ }
+
+ have = m_startupTester->haveLilyPondView(&missing);
+
+ stateChanged("have_lilypondview",
+ have ?
+ KXMLGUIClient::StateNoReverse : KXMLGUIClient::StateReverse);
+
+ if (!have) {
+ missingFeatures.push_back("Notation previews through LilyPond");
+ if (missing.count() == 0) {
+ allMissing.push_back(i18n("The Rosegarden LilyPondView helper script"));
+ } else {
+ for (int i = 0; i < missing.count(); ++i) {
+ if (missingFeatures.count() > 1) {
+ allMissing.push_back(i18n("%1 - for LilyPond preview support").arg(missing[i]));
+ } else {
+ allMissing.push_back(missing[i]);
+ }
+ }
+ }
+ }
+
+#ifdef HAVE_LIBJACK
+ if (m_seqManager && (m_seqManager->getSoundDriverStatus() & AUDIO_OK)) {
+
+ m_haveAudioImporter = m_startupTester->haveAudioFileImporter(&missing);
+
+ if (!m_haveAudioImporter) {
+ missingFeatures.push_back("General audio file import and conversion");
+ if (missing.count() == 0) {
+ allMissing.push_back(i18n("The Rosegarden Audio File Importer helper script"));
+ } else {
+ for (int i = 0; i < missing.count(); ++i) {
+ if (missingFeatures.count() > 1) {
+ allMissing.push_back(i18n("%1 - for audio file import").arg(missing[i]));
+ } else {
+ allMissing.push_back(missing[i]);
+ }
+ }
+ }
+ }
+ }
+#endif
+
+ if (missingFeatures.count() > 0) {
+ QString message = i18n("<h3>Helper programs not found</h3><p>Rosegarden could not find one or more helper programs which it needs to provide some features. The following features will not be available:</p>");
+ message += i18n("<ul>");
+ for (int i = 0; i < missingFeatures.count(); ++i) {
+ message += i18n("<li>%1</li>").arg(missingFeatures[i]);
+ }
+ message += i18n("</ul>");
+ message += i18n("<p>To fix this, you should install the following additional programs:</p>");
+ message += i18n("<ul>");
+ for (int i = 0; i < allMissing.count(); ++i) {
+ message += i18n("<li>%1</li>").arg(allMissing[i]);
+ }
+ message += i18n("</ul>");
+
+ awaitDialogClearance();
+
+ KMessageBox::information
+ (m_view,
+ message,
+ i18n("Helper programs not found"),
+ "startup-helpers-missing");
+ }
+
+ delete m_startupTester;
+ m_startupTester = 0;
+}
+
+void RosegardenGUIApp::slotDebugDump()
+{
+ Composition &comp = m_doc->getComposition();
+ comp.dump(std::cerr);
+}
+
+bool RosegardenGUIApp::launchSequencer(bool useExisting)
+{
+ if (!isUsingSequencer()) {
+ RG_DEBUG << "RosegardenGUIApp::launchSequencer() - not using seq. - returning\n";
+ return false; // no need to launch anything
+ }
+
+ if (isSequencerRunning()) {
+ RG_DEBUG << "RosegardenGUIApp::launchSequencer() - sequencer already running - returning\n";
+ if (m_seqManager) m_seqManager->checkSoundDriverStatus(false);
+ return true;
+ }
+
+ // Check to see if we're clearing down sequencer processes -
+ // if we're not we check DCOP for an existing sequencer and
+ // try to talk to use that (that's the "developer" mode).
+ //
+ // User mode should clear down sequencer processes.
+ //
+ if (kapp->dcopClient()->isApplicationRegistered(
+ QCString(ROSEGARDEN_SEQUENCER_APP_NAME))) {
+ RG_DEBUG << "RosegardenGUIApp::launchSequencer() - "
+ << "existing DCOP registered sequencer found\n";
+
+ if (useExisting) {
+ if (m_seqManager) m_seqManager->checkSoundDriverStatus(false);
+ m_sequencerProcess = (KProcess*)SequencerExternal;
+ return true;
+ }
+
+ KProcess *proc = new KProcess;
+ *proc << "/usr/bin/killall";
+ *proc << "rosegardensequencer";
+ *proc << "lt-rosegardensequencer";
+
+ proc->start(KProcess::Block, KProcess::All);
+
+ if (!proc->normalExit() || proc->exitStatus()) {
+ RG_DEBUG << "couldn't kill any sequencer processes" << endl;
+ }
+ delete proc;
+
+ sleep(1);
+
+ if (kapp->dcopClient()->isApplicationRegistered(
+ QCString(ROSEGARDEN_SEQUENCER_APP_NAME))) {
+ RG_DEBUG << "RosegardenGUIApp::launchSequencer() - "
+ << "failed to kill existing sequencer\n";
+
+ KProcess *proc = new KProcess;
+ *proc << "/usr/bin/killall";
+ *proc << "-9";
+ *proc << "rosegardensequencer";
+ *proc << "lt-rosegardensequencer";
+
+ proc->start(KProcess::Block, KProcess::All);
+
+ if (proc->exitStatus()) {
+ RG_DEBUG << "couldn't kill any sequencer processes" << endl;
+ }
+ delete proc;
+
+ sleep(1);
+ }
+ }
+
+ //
+ // No sequencer is running, so start one
+ //
+ KTmpStatusMsg msg(i18n("Starting the sequencer..."), this);
+
+ if (!m_sequencerProcess) {
+ m_sequencerProcess = new KProcess;
+
+ (*m_sequencerProcess) << "rosegardensequencer";
+
+ // Command line arguments
+ //
+ KConfig *config = kapp->config();
+ config->setGroup(SequencerOptionsConfigGroup);
+ QString options = config->readEntry("commandlineoptions");
+ if (!options.isEmpty()) {
+ (*m_sequencerProcess) << options;
+ RG_DEBUG << "sequencer options \"" << options << "\"" << endl;
+ }
+
+ } else {
+ RG_DEBUG << "RosegardenGUIApp::launchSequencer() - sequencer KProcess already created\n";
+ m_sequencerProcess->disconnect(); // disconnect processExit signal
+ // it will be reconnected later on
+ }
+
+ bool res = m_sequencerProcess->start();
+
+ if (!res) {
+ KMessageBox::error(0, i18n("Couldn't start the sequencer"));
+ RG_DEBUG << "Couldn't start the sequencer\n";
+ m_sequencerProcess = 0;
+ // If starting it didn't even work, fall back to no sequencer mode
+ m_useSequencer = false;
+ } else {
+ // connect processExited only after start, otherwise
+ // a failed startup will call slotSequencerExited()
+ // right away and we don't get to check the result
+ // of m_sequencerProcess->start() and thus make the distinction
+ // between the case where the sequencer was successfully launched
+ // but crashed right away, or the case where the process couldn't
+ // be launched at all (missing executable, etc...)
+ //
+ // We also re-check that the process is still running at this
+ // point in case it crashed between the moment we check res above
+ // and now.
+ //
+ //usleep(1000 * 1000); // even wait half a sec. to make sure it's actually running
+ if (m_sequencerProcess->isRunning()) {
+
+ try {
+ // if (m_seqManager) {
+ // RG_DEBUG << "RosegardenGUIApp::launchSequencer : checking sound driver status\n";
+ // m_seqManager->checkSoundDriverStatus();
+ // }
+
+ stateChanged("sequencer_running");
+ slotEnableTransport(true);
+
+ connect(m_sequencerProcess, SIGNAL(processExited(KProcess*)),
+ this, SLOT(slotSequencerExited(KProcess*)));
+
+ } catch (Exception e) {
+ m_sequencerProcess = 0;
+ m_useSequencer = false;
+ stateChanged("sequencer_running", KXMLGUIClient::StateReverse);
+ slotEnableTransport(false);
+ }
+
+ } else { // if it crashed so fast, it's probably pointless
+ // to try restarting it later, so also fall back to no
+ // sequencer mode
+ m_sequencerProcess = 0;
+ m_useSequencer = false;
+ stateChanged("sequencer_running", KXMLGUIClient::StateReverse);
+ slotEnableTransport(false);
+ }
+
+ }
+
+ // Sync current devices with the sequencer
+ //
+ if (m_doc)
+ m_doc->syncDevices();
+
+ if (m_doc && m_doc->getStudio().haveMidiDevices()) {
+ stateChanged("got_midi_devices");
+ } else {
+ stateChanged("got_midi_devices", KXMLGUIClient::StateReverse);
+ }
+
+ return res;
+}
+
+#ifdef HAVE_LIBJACK
+bool RosegardenGUIApp::launchJack()
+{
+ KConfig* config = kapp->config();
+ config->setGroup(SequencerOptionsConfigGroup);
+
+ bool startJack = config->readBoolEntry("jackstart", false);
+ if (!startJack)
+ return true; // we don't do anything
+
+ QString jackPath = config->readEntry("jackcommand", "");
+
+ emit startupStatusMessage(i18n("Clearing down jackd..."));
+
+ KProcess *proc = new KProcess; // TODO: do it in a less clumsy way
+ *proc << "/usr/bin/killall";
+ *proc << "-9";
+ *proc << "jackd";
+
+ proc->start(KProcess::Block, KProcess::All);
+
+ if (proc->exitStatus())
+ RG_DEBUG << "couldn't kill any jackd processes" << endl;
+ else
+ RG_DEBUG << "killed old jackd processes" << endl;
+
+ emit startupStatusMessage(i18n("Starting jackd..."));
+
+ if (jackPath != "") {
+
+ RG_DEBUG << "starting jack \"" << jackPath << "\"" << endl;
+
+ QStringList splitCommand;
+ splitCommand = QStringList::split(" ", jackPath);
+
+ RG_DEBUG << "RosegardenGUIApp::launchJack() : splitCommand length : "
+ << splitCommand.size() << endl;
+
+ // start jack process
+ m_jackProcess = new KProcess;
+
+ *m_jackProcess << splitCommand;
+
+ m_jackProcess->start();
+ }
+
+
+ return m_jackProcess != 0 ? m_jackProcess->isRunning() : true;
+}
+#endif
+
+void RosegardenGUIApp::slotDocumentDevicesResyncd()
+{
+ m_sequencerCheckedIn = true;
+ m_trackParameterBox->populateDeviceLists();
+}
+
+void RosegardenGUIApp::slotSequencerExited(KProcess*)
+{
+ RG_DEBUG << "RosegardenGUIApp::slotSequencerExited Sequencer exited\n";
+
+ KStartupLogo::hideIfStillThere();
+
+ if (m_sequencerCheckedIn) {
+
+ KMessageBox::error(0, i18n("The Rosegarden sequencer process has exited unexpectedly. Sound and recording will no longer be available for this session.\nPlease exit and restart Rosegarden to restore sound capability."));
+
+ } else {
+
+ KMessageBox::error(0, i18n("The Rosegarden sequencer could not be started, so sound and recording will be unavailable for this session.\nFor assistance with correct audio and MIDI configuration, go to http://rosegardenmusic.com."));
+ }
+
+ m_sequencerProcess = 0; // isSequencerRunning() will return false
+ // but isUsingSequencer() will keep returning true
+ // so pressing the play button may attempt to restart the sequencer
+}
+
+void RosegardenGUIApp::slotExportProject()
+{
+ KTmpStatusMsg msg(i18n("Exporting Rosegarden Project file..."), this);
+
+ QString fileName = getValidWriteFile
+ ("*.rgp|" + i18n("Rosegarden Project files\n") +
+ "\n*|" + i18n("All files"),
+ i18n("Export as..."));
+
+ if (fileName.isEmpty())
+ return ;
+
+ QString rgFile = fileName;
+ rgFile.replace(QRegExp(".rg.rgp$"), ".rg");
+ rgFile.replace(QRegExp(".rgp$"), ".rg");
+
+ CurrentProgressDialog::freeze();
+
+ QString errMsg;
+ if (!m_doc->saveDocument(rgFile, errMsg,
+ true)) { // pretend it's autosave
+ KMessageBox::sorry(this, i18n("Saving Rosegarden file to package failed: %1").arg(errMsg));
+ CurrentProgressDialog::thaw();
+ return ;
+ }
+
+ KProcess *proc = new KProcess;
+ *proc << "rosegarden-project-package";
+ *proc << "--pack";
+ *proc << rgFile;
+ *proc << fileName;
+
+ proc->start(KProcess::Block, KProcess::All);
+
+ if (!proc->normalExit() || proc->exitStatus()) {
+ KMessageBox::sorry(this, i18n("Failed to export to project file \"%1\"").arg(fileName));
+ CurrentProgressDialog::thaw();
+ delete proc;
+ return ;
+ }
+
+ delete proc;
+}
+
+void RosegardenGUIApp::slotExportMIDI()
+{
+ KTmpStatusMsg msg(i18n("Exporting MIDI file..."), this);
+
+ QString fileName = getValidWriteFile
+ ("*.mid *.midi|" + i18n("Standard MIDI files\n") +
+ "\n*|" + i18n("All files"),
+ i18n("Export as..."));
+
+ if (fileName.isEmpty())
+ return ;
+
+ exportMIDIFile(fileName);
+}
+
+void RosegardenGUIApp::exportMIDIFile(QString file)
+{
+ ProgressDialog progressDlg(i18n("Exporting MIDI file..."),
+ 100,
+ this);
+
+ std::string fname(QFile::encodeName(file));
+
+ MidiFile midiFile(fname,
+ &m_doc->getStudio());
+
+ connect(&midiFile, SIGNAL(setProgress(int)),
+ progressDlg.progressBar(), SLOT(setValue(int)));
+
+ connect(&midiFile, SIGNAL(incrementProgress(int)),
+ progressDlg.progressBar(), SLOT(advance(int)));
+
+ midiFile.convertToMidi(m_doc->getComposition());
+
+ if (!midiFile.write()) {
+ CurrentProgressDialog::freeze();
+ KMessageBox::sorry(this, i18n("Export failed. The file could not be opened for writing."));
+ }
+}
+
+void RosegardenGUIApp::slotExportCsound()
+{
+ KTmpStatusMsg msg(i18n("Exporting Csound score file..."), this);
+
+ QString fileName = getValidWriteFile(QString("*|") + i18n("All files"),
+ i18n("Export as..."));
+ if (fileName.isEmpty())
+ return ;
+
+ exportCsoundFile(fileName);
+}
+
+void RosegardenGUIApp::exportCsoundFile(QString file)
+{
+ ProgressDialog progressDlg(i18n("Exporting Csound score file..."),
+ 100,
+ this);
+
+ CsoundExporter e(this, &m_doc->getComposition(), 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."));
+ }
+}
+
+void RosegardenGUIApp::slotExportMup()
+{
+ KTmpStatusMsg msg(i18n("Exporting Mup file..."), this);
+
+ QString fileName = getValidWriteFile
+ ("*.mup|" + i18n("Mup files\n") + "\n*|" + i18n("All files"),
+ i18n("Export as..."));
+ if (fileName.isEmpty())
+ return ;
+
+ exportMupFile(fileName);
+}
+
+void RosegardenGUIApp::exportMupFile(QString file)
+{
+ ProgressDialog progressDlg(i18n("Exporting Mup file..."),
+ 100,
+ this);
+
+ MupExporter e(this, &m_doc->getComposition(), 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."));
+ }
+}
+
+void RosegardenGUIApp::slotExportLilyPond()
+{
+ KTmpStatusMsg msg(i18n("Exporting LilyPond file..."), this);
+
+ QString fileName = getValidWriteFile
+ (QString("*.ly|") + i18n("LilyPond files") +
+ "\n*|" + i18n("All files"),
+ i18n("Export as..."));
+
+ if (fileName.isEmpty())
+ return ;
+
+ exportLilyPondFile(fileName);
+}
+
+std::map<KProcess *, KTempFile *> RosegardenGUIApp::m_lilyTempFileMap;
+
+
+void RosegardenGUIApp::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 RosegardenGUIApp::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 RosegardenGUIApp::slotLilyPondViewProcessExited(KProcess *p)
+{
+ delete m_lilyTempFileMap[p];
+ m_lilyTempFileMap.erase(p);
+ delete p;
+}
+
+bool RosegardenGUIApp::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 RosegardenGUIApp::slotExportMusicXml()
+{
+ KTmpStatusMsg msg(i18n("Exporting MusicXML file..."), this);
+
+ QString fileName = getValidWriteFile
+ (QString("*.xml|") + i18n("XML files") +
+ "\n*|" + i18n("All files"), i18n("Export as..."));
+
+ if (fileName.isEmpty())
+ return ;
+
+ exportMusicXmlFile(fileName);
+}
+
+void RosegardenGUIApp::exportMusicXmlFile(QString file)
+{
+ ProgressDialog progressDlg(i18n("Exporting MusicXML file..."),
+ 100,
+ this);
+
+ MusicXmlExporter 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."));
+ }
+}
+
+void
+RosegardenGUIApp::slotCloseTransport()
+{
+ m_viewTransport->setChecked(false);
+ slotToggleTransport(); // hides the transport
+}
+
+void
+RosegardenGUIApp::slotDeleteTransport()
+{
+ delete m_transport;
+ m_transport = 0;
+}
+
+void
+RosegardenGUIApp::slotActivateTool(QString toolName)
+{
+ if (toolName == SegmentSelector::ToolName) {
+ actionCollection()->action("select")->activate();
+ }
+}
+
+void
+RosegardenGUIApp::slotToggleMetronome()
+{
+ Composition &comp = m_doc->getComposition();
+
+ if (m_seqManager->getTransportStatus() == STARTING_TO_RECORD ||
+ m_seqManager->getTransportStatus() == RECORDING ||
+ m_seqManager->getTransportStatus() == RECORDING_ARMED) {
+ if (comp.useRecordMetronome())
+ comp.setRecordMetronome(false);
+ else
+ comp.setRecordMetronome(true);
+
+ getTransport()->MetronomeButton()->setOn(comp.useRecordMetronome());
+ } else {
+ if (comp.usePlayMetronome())
+ comp.setPlayMetronome(false);
+ else
+ comp.setPlayMetronome(true);
+
+ getTransport()->MetronomeButton()->setOn(comp.usePlayMetronome());
+ }
+}
+
+void
+RosegardenGUIApp::slotRewindToBeginning()
+{
+ // ignore requests if recording
+ //
+ if (m_seqManager->getTransportStatus() == RECORDING)
+ return ;
+
+ m_seqManager->rewindToBeginning();
+}
+
+void
+RosegardenGUIApp::slotFastForwardToEnd()
+{
+ // ignore requests if recording
+ //
+ if (m_seqManager->getTransportStatus() == RECORDING)
+ return ;
+
+ m_seqManager->fastForwardToEnd();
+}
+
+void
+RosegardenGUIApp::slotSetPlayPosition(timeT time)
+{
+ RG_DEBUG << "RosegardenGUIApp::slotSetPlayPosition(" << time << ")" << endl;
+ if (m_seqManager->getTransportStatus() == RECORDING)
+ return ;
+
+ m_doc->slotSetPointerPosition(time);
+
+ if (m_seqManager->getTransportStatus() == PLAYING)
+ return ;
+
+ slotPlay();
+}
+
+void RosegardenGUIApp::notifySequencerStatus(int status)
+{
+ stateChanged("not_playing",
+ (status == PLAYING ||
+ status == RECORDING) ?
+ KXMLGUIClient::StateReverse : KXMLGUIClient::StateNoReverse);
+
+ if (m_seqManager)
+ m_seqManager->setTransportStatus((TransportStatus) status);
+}
+
+void RosegardenGUIApp::processAsynchronousMidi(const MappedComposition &mC)
+{
+ if (!m_seqManager) {
+ return ; // probably getting this from a not-yet-killed runaway sequencer
+ }
+
+ m_seqManager->processAsynchronousMidi(mC, 0);
+ SequencerMapper *mapper = m_seqManager->getSequencerMapper();
+ if (mapper)
+ m_view->updateMeters(mapper);
+}
+
+void
+RosegardenGUIApp::slotRecord()
+{
+ if (!isUsingSequencer())
+ return ;
+
+ if (!isSequencerRunning()) {
+
+ // Try to launch sequencer and return if we fail
+ //
+ if (!launchSequencer(false))
+ return ;
+ }
+
+ if (m_seqManager->getTransportStatus() == RECORDING) {
+ slotStop();
+ return ;
+ } else if (m_seqManager->getTransportStatus() == PLAYING) {
+ slotToggleRecord();
+ return ;
+ }
+
+ // Attempt to start recording
+ //
+ try {
+ m_seqManager->record(false);
+ } catch (QString s) {
+ // We should already be stopped by this point so just unset
+ // the buttons after clicking the dialog.
+ //
+ KMessageBox::error(this, s);
+
+ getTransport()->MetronomeButton()->setOn(false);
+ getTransport()->RecordButton()->setOn(false);
+ getTransport()->PlayButton()->setOn(false);
+ return ;
+ } 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) {
+ slotOpenAudioPathSettings();
+ }
+ getTransport()->MetronomeButton()->setOn(false);
+ getTransport()->RecordButton()->setOn(false);
+ getTransport()->PlayButton()->setOn(false);
+ return ;
+ } catch (Exception e) {
+ KMessageBox::error(this, strtoqstr(e.getMessage()));
+
+ getTransport()->MetronomeButton()->setOn(false);
+ getTransport()->RecordButton()->setOn(false);
+ getTransport()->PlayButton()->setOn(false);
+ return ;
+ }
+
+ // plugin the keyboard accelerators for focus on this dialog
+ plugAccelerators(m_seqManager->getCountdownDialog(),
+ m_seqManager->getCountdownDialog()->getAccelerators());
+
+ connect(m_seqManager->getCountdownDialog(), SIGNAL(stopped()),
+ this, SLOT(slotStop()));
+
+ // Start the playback timer - this fetches the current sequencer position &c
+ //
+ m_stopTimer->stop();
+ m_playTimer->start(23); // avoid multiples of 10 just so as
+ // to avoid always having the same digit
+ // in one place on the transport. How
+ // shallow.)
+}
+
+void
+RosegardenGUIApp::slotToggleRecord()
+{
+ if (!isUsingSequencer() ||
+ (!isSequencerRunning() && !launchSequencer(false)))
+ return ;
+
+ try {
+ m_seqManager->record(true);
+ } catch (QString s) {
+ KMessageBox::error(this, s);
+ } 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 you start to record audio.\nWould you like to set it now?"),
+ i18n("Error"),
+ i18n("Set audio file path")) == KMessageBox::Continue) {
+ slotOpenAudioPathSettings();
+ }
+ } catch (Exception e) {
+ KMessageBox::error(this, strtoqstr(e.getMessage()));
+ }
+
+}
+
+void
+RosegardenGUIApp::slotSetLoop(timeT lhs, timeT rhs)
+{
+ try {
+ m_doc->slotDocumentModified();
+
+ m_seqManager->setLoop(lhs, rhs);
+
+ // toggle the loop button
+ if (lhs != rhs) {
+ getTransport()->LoopButton()->setOn(true);
+ stateChanged("have_range", KXMLGUIClient::StateNoReverse);
+ } else {
+ getTransport()->LoopButton()->setOn(false);
+ stateChanged("have_range", KXMLGUIClient::StateReverse);
+ }
+ } catch (QString s) {
+ KMessageBox::error(this, s);
+ }
+}
+
+void RosegardenGUIApp::alive()
+{
+ if (m_doc)
+ m_doc->syncDevices();
+
+ if (m_doc && m_doc->getStudio().haveMidiDevices()) {
+ stateChanged("got_midi_devices");
+ } else {
+ stateChanged("got_midi_devices", KXMLGUIClient::StateReverse);
+ }
+}
+
+void RosegardenGUIApp::slotPlay()
+{
+ if (!isUsingSequencer())
+ return ;
+
+ if (!isSequencerRunning()) {
+
+ // Try to launch sequencer and return if it fails
+ //
+ if (!launchSequencer(false))
+ return ;
+ }
+
+ if (!m_seqManager)
+ return ;
+
+ // If we're armed and ready to record then do this instead (calling
+ // slotRecord ensures we don't toggle the recording state in
+ // SequenceManager)
+ //
+ if (m_seqManager->getTransportStatus() == RECORDING_ARMED) {
+ slotRecord();
+ return ;
+ }
+
+ // Send the controllers at start of playback if required
+ //
+ KConfig *config = kapp->config();
+ config->setGroup(SequencerOptionsConfigGroup);
+ bool sendControllers = config->readBoolEntry("alwayssendcontrollers", false);
+
+ if (sendControllers)
+ m_doc->initialiseControllers();
+
+ bool pausedPlayback = false;
+
+ try {
+ pausedPlayback = m_seqManager->play(); // this will stop playback (pause) if it's already running
+ // Check the new state of the transport and start or stop timer
+ // accordingly
+ //
+ if (!pausedPlayback) {
+
+ // Start the playback timer - this fetches the current sequencer position &c
+ //
+ m_stopTimer->stop();
+ m_playTimer->start(23);
+ } else {
+ m_playTimer->stop();
+ m_stopTimer->start(100);
+ }
+ } catch (QString s) {
+ KMessageBox::error(this, s);
+ m_playTimer->stop();
+ m_stopTimer->start(100);
+ } catch (Exception e) {
+ KMessageBox::error(this, e.getMessage());
+ m_playTimer->stop();
+ m_stopTimer->start(100);
+ }
+
+}
+
+void RosegardenGUIApp::slotJumpToTime(int sec, int usec)
+{
+ Composition *comp = &m_doc->getComposition();
+ timeT t = comp->getElapsedTimeForRealTime
+ (RealTime(sec, usec * 1000));
+ m_doc->slotSetPointerPosition(t);
+}
+
+void RosegardenGUIApp::slotStartAtTime(int sec, int usec)
+{
+ slotJumpToTime(sec, usec);
+ slotPlay();
+}
+
+void RosegardenGUIApp::slotStop()
+{
+ if (m_seqManager &&
+ m_seqManager->getCountdownDialog()) {
+ disconnect(m_seqManager->getCountdownDialog(), SIGNAL(stopped()),
+ this, SLOT(slotStop()));
+ disconnect(m_seqManager->getCountdownDialog(), SIGNAL(completed()),
+ this, SLOT(slotStop()));
+ }
+
+ try {
+ if (m_seqManager)
+ m_seqManager->stopping();
+ } catch (Exception e) {
+ KMessageBox::error(this, strtoqstr(e.getMessage()));
+ }
+
+ // stop the playback timer
+ m_playTimer->stop();
+ m_stopTimer->start(100);
+}
+
+void RosegardenGUIApp::slotRewind()
+{
+ // ignore requests if recording
+ //
+ if (m_seqManager->getTransportStatus() == RECORDING)
+ return ;
+ if (m_seqManager)
+ m_seqManager->rewind();
+}
+
+void RosegardenGUIApp::slotFastforward()
+{
+ // ignore requests if recording
+ //
+ if (m_seqManager->getTransportStatus() == RECORDING)
+ return ;
+
+ if (m_seqManager)
+ m_seqManager->fastforward();
+}
+
+void
+RosegardenGUIApp::slotSetLoop()
+{
+ // restore loop
+ m_doc->setLoop(m_storedLoopStart, m_storedLoopEnd);
+}
+
+void
+RosegardenGUIApp::slotUnsetLoop()
+{
+ Composition &comp = m_doc->getComposition();
+
+ // store the loop
+ m_storedLoopStart = comp.getLoopStart();
+ m_storedLoopEnd = comp.getLoopEnd();
+
+ // clear the loop at the composition and propagate to the rest
+ // of the display items
+ m_doc->setLoop(0, 0);
+}
+
+void
+RosegardenGUIApp::slotSetLoopStart()
+{
+ // Check so that start time is before endtime, othervise move upp the
+ // endtime to that same pos.
+ if ( m_doc->getComposition().getPosition() < m_doc->getComposition().getLoopEnd() ) {
+ m_doc->setLoop(m_doc->getComposition().getPosition(), m_doc->getComposition().getLoopEnd());
+ } else {
+ m_doc->setLoop(m_doc->getComposition().getPosition(), m_doc->getComposition().getPosition());
+ }
+}
+
+void
+RosegardenGUIApp::slotSetLoopStop()
+{
+ // Check so that end time is after start time, othervise move upp the
+ // start time to that same pos.
+ if ( m_doc->getComposition().getLoopStart() < m_doc->getComposition().getPosition() ) {
+ m_doc->setLoop(m_doc->getComposition().getLoopStart(), m_doc->getComposition().getPosition());
+ } else {
+ m_doc->setLoop(m_doc->getComposition().getPosition(), m_doc->getComposition().getPosition());
+ }
+}
+
+void RosegardenGUIApp::slotToggleSolo(bool value)
+{
+ RG_DEBUG << "RosegardenGUIApp::slotToggleSolo value = " << value << endl;
+
+ m_doc->getComposition().setSolo(value);
+ getTransport()->SoloButton()->setOn(value);
+
+ m_doc->slotDocumentModified();
+
+ emit compositionStateUpdate();
+}
+
+void RosegardenGUIApp::slotTrackUp()
+{
+ Composition &comp = m_doc->getComposition();
+
+ TrackId tid = comp.getSelectedTrack();
+ TrackId pos = comp.getTrackById(tid)->getPosition();
+
+ // If at top already
+ if (pos == 0)
+ return ;
+
+ Track *track = comp.getTrackByPosition(pos - 1);
+
+ // If the track exists
+ if (track) {
+ comp.setSelectedTrack(track->getId());
+ m_view->slotSelectTrackSegments(comp.getSelectedTrack());
+ }
+
+}
+
+void RosegardenGUIApp::slotTrackDown()
+{
+ Composition &comp = m_doc->getComposition();
+
+ TrackId tid = comp.getSelectedTrack();
+ TrackId pos = comp.getTrackById(tid)->getPosition();
+
+ Track *track = comp.getTrackByPosition(pos + 1);
+
+ // If the track exists
+ if (track) {
+ comp.setSelectedTrack(track->getId());
+ m_view->slotSelectTrackSegments(comp.getSelectedTrack());
+ }
+
+}
+
+void RosegardenGUIApp::slotMuteAllTracks()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotMuteAllTracks" << endl;
+
+ Composition &comp = m_doc->getComposition();
+
+ Composition::trackcontainer tracks = comp.getTracks();
+ Composition::trackiterator tit;
+ for (tit = tracks.begin(); tit != tracks.end(); ++tit)
+ m_view->slotSetMute((*tit).second->getInstrument(), true);
+}
+
+void RosegardenGUIApp::slotUnmuteAllTracks()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotUnmuteAllTracks" << endl;
+
+ Composition &comp = m_doc->getComposition();
+
+ Composition::trackcontainer tracks = comp.getTracks();
+ Composition::trackiterator tit;
+ for (tit = tracks.begin(); tit != tracks.end(); ++tit)
+ m_view->slotSetMute((*tit).second->getInstrument(), false);
+}
+
+void RosegardenGUIApp::slotToggleMutedCurrentTrack()
+{
+ Composition &comp = m_doc->getComposition();
+ TrackId tid = comp.getSelectedTrack();
+ Track *track = comp.getTrackById(tid);
+ // If the track exists
+ if (track) {
+ bool isMuted = track->isMuted();
+ m_view->slotSetMuteButton(tid, !isMuted);
+ }
+}
+
+void RosegardenGUIApp::slotToggleRecordCurrentTrack()
+{
+ Composition &comp = m_doc->getComposition();
+ TrackId tid = comp.getSelectedTrack();
+ int pos = comp.getTrackPositionById(tid);
+ m_view->getTrackEditor()->getTrackButtons()->slotToggleRecordTrack(pos);
+}
+
+
+void RosegardenGUIApp::slotConfigure()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotConfigure\n";
+
+ ConfigureDialog *configDlg =
+ new ConfigureDialog(m_doc, kapp->config(), this);
+
+ connect(configDlg, SIGNAL(updateAutoSaveInterval(unsigned int)),
+ this, SLOT(slotUpdateAutoSaveInterval(unsigned int)));
+ connect(configDlg, SIGNAL(updateSidebarStyle(unsigned int)),
+ this, SLOT(slotUpdateSidebarStyle(unsigned int)));
+
+ configDlg->show();
+}
+
+void RosegardenGUIApp::slotEditDocumentProperties()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotEditDocumentProperties\n";
+
+ DocumentConfigureDialog *configDlg =
+ new DocumentConfigureDialog(m_doc, this);
+
+ configDlg->show();
+}
+
+void RosegardenGUIApp::slotOpenAudioPathSettings()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotOpenAudioPathSettings\n";
+
+ DocumentConfigureDialog *configDlg =
+ new DocumentConfigureDialog(m_doc, this);
+
+ configDlg->showAudioPage();
+ configDlg->show();
+}
+
+void RosegardenGUIApp::slotEditKeys()
+{
+ KKeyDialog::configure(actionCollection());
+}
+
+void RosegardenGUIApp::slotEditToolbars()
+{
+ KEditToolbar dlg(actionCollection(), "rosegardenui.rc");
+
+ connect(&dlg, SIGNAL(newToolbarConfig()),
+ SLOT(slotUpdateToolbars()));
+
+ dlg.exec();
+}
+
+void RosegardenGUIApp::slotUpdateToolbars()
+{
+ createGUI("rosegardenui.rc");
+ m_viewToolBar->setChecked(!toolBar()->isHidden());
+}
+
+void RosegardenGUIApp::slotEditTempo()
+{
+ slotEditTempo(this);
+}
+
+void RosegardenGUIApp::slotEditTempo(timeT atTime)
+{
+ slotEditTempo(this, atTime);
+}
+
+void RosegardenGUIApp::slotEditTempo(QWidget *parent)
+{
+ slotEditTempo(parent, m_doc->getComposition().getPosition());
+}
+
+void RosegardenGUIApp::slotEditTempo(QWidget *parent, timeT atTime)
+{
+ RG_DEBUG << "RosegardenGUIApp::slotEditTempo\n";
+
+ TempoDialog tempoDialog(parent, m_doc);
+
+ connect(&tempoDialog,
+ SIGNAL(changeTempo(timeT,
+ tempoT,
+ tempoT,
+ TempoDialog::TempoDialogAction)),
+ SLOT(slotChangeTempo(timeT,
+ tempoT,
+ tempoT,
+ TempoDialog::TempoDialogAction)));
+
+ tempoDialog.setTempoPosition(atTime);
+ tempoDialog.exec();
+}
+
+void RosegardenGUIApp::slotEditTimeSignature()
+{
+ slotEditTimeSignature(this);
+}
+
+void RosegardenGUIApp::slotEditTimeSignature(timeT atTime)
+{
+ slotEditTimeSignature(this, atTime);
+}
+
+void RosegardenGUIApp::slotEditTimeSignature(QWidget *parent)
+{
+ slotEditTimeSignature(parent, m_doc->getComposition().getPosition());
+}
+
+void RosegardenGUIApp::slotEditTimeSignature(QWidget *parent,
+ timeT time)
+{
+ Composition &composition(m_doc->getComposition());
+
+ TimeSignature sig = composition.getTimeSignatureAt(time);
+
+ TimeSignatureDialog dialog(parent, &composition, time, sig);
+
+ if (dialog.exec() == QDialog::Accepted) {
+
+ time = dialog.getTime();
+
+ if (dialog.shouldNormalizeRests()) {
+ m_doc->getCommandHistory()->addCommand
+ (new AddTimeSignatureAndNormalizeCommand
+ (&composition, time, dialog.getTimeSignature()));
+ } else {
+ m_doc->getCommandHistory()->addCommand
+ (new AddTimeSignatureCommand
+ (&composition, time, dialog.getTimeSignature()));
+ }
+ }
+}
+
+void RosegardenGUIApp::slotEditTransportTime()
+{
+ slotEditTransportTime(this);
+}
+
+void RosegardenGUIApp::slotEditTransportTime(QWidget *parent)
+{
+ TimeDialog dialog(parent, i18n("Move playback pointer to time"),
+ &m_doc->getComposition(),
+ m_doc->getComposition().getPosition(),
+ true);
+ if (dialog.exec() == QDialog::Accepted) {
+ m_doc->slotSetPointerPosition(dialog.getTime());
+ }
+}
+
+void RosegardenGUIApp::slotChangeZoom(int)
+{
+ double duration44 = TimeSignature(4, 4).getBarDuration();
+ double value = double(m_zoomSlider->getCurrentSize());
+ m_zoomLabel->setText(i18n("%1%").arg(duration44 / value));
+
+ RG_DEBUG << "RosegardenGUIApp::slotChangeZoom : zoom size = "
+ << m_zoomSlider->getCurrentSize() << endl;
+
+ // initZoomToolbar sets the zoom value. With some old versions of
+ // Qt3.0, this can cause slotChangeZoom() to be called while the
+ // view hasn't been initialized yet, so we need to check it's not
+ // null
+ //
+ if (m_view)
+ m_view->setZoomSize(m_zoomSlider->getCurrentSize());
+
+ long newZoom = int(m_zoomSlider->getCurrentSize() * 1000.0);
+
+ if (m_doc->getConfiguration().get<Int>
+ (DocumentConfiguration::ZoomLevel) != newZoom) {
+
+ m_doc->getConfiguration().set<Int>
+ (DocumentConfiguration::ZoomLevel, newZoom);
+
+ m_doc->slotDocumentModified();
+ }
+}
+
+void
+RosegardenGUIApp::slotZoomIn()
+{
+ m_zoomSlider->increment();
+}
+
+void
+RosegardenGUIApp::slotZoomOut()
+{
+ m_zoomSlider->decrement();
+}
+
+void
+RosegardenGUIApp::slotChangeTempo(timeT time,
+ tempoT value,
+ tempoT target,
+ TempoDialog::TempoDialogAction action)
+{
+ //!!! handle target
+
+ Composition &comp = m_doc->getComposition();
+
+ // We define a macro command here and build up the command
+ // label as we add commands on.
+ //
+ if (action == TempoDialog::AddTempo) {
+ m_doc->getCommandHistory()->addCommand
+ (new AddTempoChangeCommand(&comp, time, value, target));
+ } else if (action == TempoDialog::ReplaceTempo) {
+ int index = comp.getTempoChangeNumberAt(time);
+
+ // if there's no previous tempo change then just set globally
+ //
+ if (index == -1) {
+ m_doc->getCommandHistory()->addCommand
+ (new AddTempoChangeCommand(&comp, 0, value, target));
+ return ;
+ }
+
+ // get time of previous tempo change
+ timeT prevTime = comp.getTempoChange(index).first;
+
+ KMacroCommand *macro =
+ new KMacroCommand(i18n("Replace Tempo Change at %1").arg(time));
+
+ macro->addCommand(new RemoveTempoChangeCommand(&comp, index));
+ macro->addCommand(new AddTempoChangeCommand(&comp, prevTime, value,
+ target));
+
+ m_doc->getCommandHistory()->addCommand(macro);
+
+ } else if (action == TempoDialog::AddTempoAtBarStart) {
+ m_doc->getCommandHistory()->addCommand(new
+ AddTempoChangeCommand(&comp, comp.getBarStartForTime(time),
+ value, target));
+ } else if (action == TempoDialog::GlobalTempo ||
+ action == TempoDialog::GlobalTempoWithDefault) {
+ KMacroCommand *macro = new KMacroCommand(i18n("Set Global Tempo"));
+
+ // Remove all tempo changes in reverse order so as the index numbers
+ // don't becoming meaningless as the command gets unwound.
+ //
+ for (int i = 0; i < comp.getTempoChangeCount(); i++)
+ macro->addCommand(new RemoveTempoChangeCommand(&comp,
+ (comp.getTempoChangeCount() - 1 - i)));
+
+ // add tempo change at time zero
+ //
+ macro->addCommand(new AddTempoChangeCommand(&comp, 0, value, target));
+
+ // are we setting default too?
+ //
+ if (action == TempoDialog::GlobalTempoWithDefault) {
+ macro->setName(i18n("Set Global and Default Tempo"));
+ macro->addCommand(new ModifyDefaultTempoCommand(&comp, value));
+ }
+
+ m_doc->getCommandHistory()->addCommand(macro);
+
+ } else {
+ RG_DEBUG << "RosegardenGUIApp::slotChangeTempo() - "
+ << "unrecognised tempo command" << endl;
+ }
+}
+
+void
+RosegardenGUIApp::slotMoveTempo(timeT oldTime,
+ timeT newTime)
+{
+ Composition &comp = m_doc->getComposition();
+ int index = comp.getTempoChangeNumberAt(oldTime);
+
+ if (index < 0)
+ return ;
+
+ KMacroCommand *macro =
+ new KMacroCommand(i18n("Move Tempo Change"));
+
+ std::pair<timeT, tempoT> tc =
+ comp.getTempoChange(index);
+ std::pair<bool, tempoT> tr =
+ comp.getTempoRamping(index, false);
+
+ macro->addCommand(new RemoveTempoChangeCommand(&comp, index));
+ macro->addCommand(new AddTempoChangeCommand(&comp,
+ newTime,
+ tc.second,
+ tr.first ? tr.second : -1));
+
+ m_doc->getCommandHistory()->addCommand(macro);
+}
+
+void
+RosegardenGUIApp::slotDeleteTempo(timeT t)
+{
+ Composition &comp = m_doc->getComposition();
+ int index = comp.getTempoChangeNumberAt(t);
+
+ if (index < 0)
+ return ;
+
+ m_doc->getCommandHistory()->addCommand(new RemoveTempoChangeCommand
+ (&comp, index));
+}
+
+void
+RosegardenGUIApp::slotAddMarker(timeT time)
+{
+ AddMarkerCommand *command =
+ new AddMarkerCommand(&m_doc->getComposition(),
+ time,
+ i18n("new marker"),
+ i18n("no description"));
+
+ m_doc->getCommandHistory()->addCommand(command);
+}
+
+void
+RosegardenGUIApp::slotDeleteMarker(int id, timeT time, QString name, QString description)
+{
+ RemoveMarkerCommand *command =
+ new RemoveMarkerCommand(&m_doc->getComposition(),
+ id,
+ time,
+ qstrtostr(name),
+ qstrtostr(description));
+
+ m_doc->getCommandHistory()->addCommand(command);
+}
+
+void
+RosegardenGUIApp::slotDocumentModified(bool m)
+{
+ RG_DEBUG << "RosegardenGUIApp::slotDocumentModified(" << m << ") - doc path = "
+ << m_doc->getAbsFilePath() << endl;
+
+ if (!m_doc->getAbsFilePath().isEmpty()) {
+ slotStateChanged("saved_file_modified", m);
+ } else {
+ slotStateChanged("new_file_modified", m);
+ }
+
+}
+
+void
+RosegardenGUIApp::slotStateChanged(QString s,
+ bool noReverse)
+{
+ // RG_DEBUG << "RosegardenGUIApp::slotStateChanged " << s << "," << noReverse << endl;
+
+ stateChanged(s, noReverse ? KXMLGUIClient::StateNoReverse : KXMLGUIClient::StateReverse);
+}
+
+void
+RosegardenGUIApp::slotTestClipboard()
+{
+ if (m_clipboard->isEmpty()) {
+ stateChanged("have_clipboard", KXMLGUIClient::StateReverse);
+ stateChanged("have_clipboard_single_segment",
+ KXMLGUIClient::StateReverse);
+ } else {
+ stateChanged("have_clipboard", KXMLGUIClient::StateNoReverse);
+ stateChanged("have_clipboard_single_segment",
+ (m_clipboard->isSingleSegment() ?
+ KXMLGUIClient::StateNoReverse :
+ KXMLGUIClient::StateReverse));
+ }
+}
+
+void
+RosegardenGUIApp::plugAccelerators(QWidget *widget, QAccel *acc)
+{
+
+ acc->connectItem(acc->insertItem(Key_Enter),
+ this,
+ SLOT(slotPlay()));
+ // Alternative shortcut for Play
+ acc->connectItem(acc->insertItem(Key_Return + CTRL),
+ this,
+ SLOT(slotPlay()));
+
+ acc->connectItem(acc->insertItem(Key_Insert),
+ this,
+ SLOT(slotStop()));
+
+ acc->connectItem(acc->insertItem(Key_PageDown),
+ this,
+ SLOT(slotFastforward()));
+
+ acc->connectItem(acc->insertItem(Key_End),
+ this,
+ SLOT(slotRewind()));
+
+ acc->connectItem(acc->insertItem(Key_Space),
+ this,
+ SLOT(slotToggleRecord()));
+
+ TransportDialog *transport =
+ dynamic_cast<TransportDialog*>(widget);
+
+ if (transport) {
+ acc->connectItem(acc->insertItem(m_jumpToQuickMarkerAction->shortcut()),
+ this,
+ SLOT(slotJumpToQuickMarker()));
+
+ acc->connectItem(acc->insertItem(m_setQuickMarkerAction->shortcut()),
+ this,
+ SLOT(slotSetQuickMarker()));
+
+ connect(transport->PlayButton(),
+ SIGNAL(clicked()),
+ this,
+ SLOT(slotPlay()));
+
+ connect(transport->StopButton(),
+ SIGNAL(clicked()),
+ this,
+ SLOT(slotStop()));
+
+ connect(transport->FfwdButton(),
+ SIGNAL(clicked()),
+ SLOT(slotFastforward()));
+
+ connect(transport->RewindButton(),
+ SIGNAL(clicked()),
+ this,
+ SLOT(slotRewind()));
+
+ connect(transport->RecordButton(),
+ SIGNAL(clicked()),
+ this,
+ SLOT(slotRecord()));
+
+ connect(transport->RewindEndButton(),
+ SIGNAL(clicked()),
+ this,
+ SLOT(slotRewindToBeginning()));
+
+ connect(transport->FfwdEndButton(),
+ SIGNAL(clicked()),
+ this,
+ SLOT(slotFastForwardToEnd()));
+
+ connect(transport->MetronomeButton(),
+ SIGNAL(clicked()),
+ this,
+ SLOT(slotToggleMetronome()));
+
+ connect(transport->SoloButton(),
+ SIGNAL(toggled(bool)),
+ this,
+ SLOT(slotToggleSolo(bool)));
+
+ connect(transport->TimeDisplayButton(),
+ SIGNAL(clicked()),
+ this,
+ SLOT(slotRefreshTimeDisplay()));
+
+ connect(transport->ToEndButton(),
+ SIGNAL(clicked()),
+ SLOT(slotRefreshTimeDisplay()));
+ }
+}
+
+void
+RosegardenGUIApp::setCursor(const QCursor& cursor)
+{
+ KDockMainWindow::setCursor(cursor);
+
+ // play it safe, so we can use this class at anytime even very early in the app init
+ if ((getView() &&
+ getView()->getTrackEditor() &&
+ getView()->getTrackEditor()->getSegmentCanvas() &&
+ getView()->getTrackEditor()->getSegmentCanvas()->viewport())) {
+
+ getView()->getTrackEditor()->getSegmentCanvas()->viewport()->setCursor(cursor);
+ }
+
+ // view, main window...
+ //
+ getView()->setCursor(cursor);
+
+ // toolbars...
+ //
+ QPtrListIterator<KToolBar> tbIter = toolBarIterator();
+ KToolBar* tb = 0;
+ while ((tb = tbIter.current()) != 0) {
+ tb->setCursor(cursor);
+ ++tbIter;
+ }
+
+ m_dockLeft->setCursor(cursor);
+}
+
+QString
+RosegardenGUIApp::createNewAudioFile()
+{
+ AudioFile *aF = 0;
+ try {
+ aF = m_doc->getAudioFileManager().createRecordingAudioFile();
+ if (!aF) {
+ // createRecordingAudioFile doesn't actually write to the disk,
+ // and in principle it shouldn't fail
+ std::cerr << "ERROR: RosegardenGUIApp::createNewAudioFile: Failed to create recording audio file" << std::endl;
+ return "";
+ } else {
+ return aF->getFilename().c_str();
+ }
+ } catch (AudioFileManager::BadAudioPathException e) {
+ delete aF;
+ std::cerr << "ERROR: RosegardenGUIApp::createNewAudioFile: Failed to create recording audio file: " << e.getMessage() << std::endl;
+ return "";
+ }
+}
+
+QValueVector<QString>
+RosegardenGUIApp::createRecordAudioFiles(const QValueVector<InstrumentId> &recordInstruments)
+{
+ QValueVector<QString> qv;
+ for (unsigned int i = 0; i < recordInstruments.size(); ++i) {
+ AudioFile *aF = 0;
+ try {
+ aF = m_doc->getAudioFileManager().createRecordingAudioFile();
+ if (aF) {
+ // createRecordingAudioFile doesn't actually write to the disk,
+ // and in principle it shouldn't fail
+ qv.push_back(aF->getFilename().c_str());
+ m_doc->addRecordAudioSegment(recordInstruments[i],
+ aF->getId());
+ } else {
+ std::cerr << "ERROR: RosegardenGUIApp::createRecordAudioFiles: Failed to create recording audio file" << std::endl;
+ return qv;
+ }
+ } catch (AudioFileManager::BadAudioPathException e) {
+ delete aF;
+ std::cerr << "ERROR: RosegardenGUIApp::createRecordAudioFiles: Failed to create recording audio file: " << e.getMessage() << std::endl;
+ return qv;
+ }
+ }
+ return qv;
+}
+
+QString
+RosegardenGUIApp::getAudioFilePath()
+{
+ return QString(m_doc->getAudioFileManager().getAudioPath().c_str());
+}
+
+QValueVector<InstrumentId>
+RosegardenGUIApp::getArmedInstruments()
+{
+ std::set
+ <InstrumentId> iid;
+
+ const Composition::recordtrackcontainer &tr =
+ m_doc->getComposition().getRecordTracks();
+
+ for (Composition::recordtrackcontainer::const_iterator i =
+ tr.begin(); i != tr.end(); ++i) {
+ TrackId tid = (*i);
+ Track *track = m_doc->getComposition().getTrackById(tid);
+ if (track) {
+ iid.insert(track->getInstrument());
+ } else {
+ std::cerr << "Warning: RosegardenGUIApp::getArmedInstruments: Armed track " << tid << " not found in Composition" << std::endl;
+ }
+ }
+
+ QValueVector<InstrumentId> iv;
+ for (std::set
+ <InstrumentId>::iterator ii = iid.begin();
+ ii != iid.end(); ++ii) {
+ iv.push_back(*ii);
+ }
+ return iv;
+}
+
+void
+RosegardenGUIApp::showError(QString error)
+{
+ KStartupLogo::hideIfStillThere();
+ CurrentProgressDialog::freeze();
+
+ // This is principally used for return values from DSSI plugin
+ // configure() calls. It seems some plugins return a string
+ // telling you when everything's OK, as well as error strings, but
+ // dssi.h does make it reasonably clear that configure() should
+ // only return a string when there is actually a problem, so we're
+ // going to stick with a sorry dialog here rather than an
+ // information one
+
+ KMessageBox::sorry(0, error);
+
+ CurrentProgressDialog::thaw();
+}
+
+void
+RosegardenGUIApp::slotAudioManager()
+{
+ if (m_audioManagerDialog) {
+ m_audioManagerDialog->show();
+ m_audioManagerDialog->raise();
+ m_audioManagerDialog->setActiveWindow();
+ return ;
+ }
+
+ m_audioManagerDialog =
+ new AudioManagerDialog(this, m_doc);
+
+ connect(m_audioManagerDialog,
+ SIGNAL(playAudioFile(AudioFileId,
+ const RealTime &,
+ const RealTime&)),
+ SLOT(slotPlayAudioFile(AudioFileId,
+ const RealTime &,
+ const RealTime &)));
+
+ connect(m_audioManagerDialog,
+ SIGNAL(addAudioFile(AudioFileId)),
+ SLOT(slotAddAudioFile(AudioFileId)));
+
+ connect(m_audioManagerDialog,
+ SIGNAL(deleteAudioFile(AudioFileId)),
+ SLOT(slotDeleteAudioFile(AudioFileId)));
+
+ //
+ // Sync segment selection between audio man. dialog and main window
+ //
+
+ // from dialog to us...
+ connect(m_audioManagerDialog,
+ SIGNAL(segmentsSelected(const SegmentSelection&)),
+ m_view,
+ SLOT(slotPropagateSegmentSelection(const SegmentSelection&)));
+
+ // and from us to dialog
+ connect(this, SIGNAL(segmentsSelected(const SegmentSelection&)),
+ m_audioManagerDialog,
+ SLOT(slotSegmentSelection(const SegmentSelection&)));
+
+
+ connect(m_audioManagerDialog,
+ SIGNAL(deleteSegments(const SegmentSelection&)),
+ SLOT(slotDeleteSegments(const SegmentSelection&)));
+
+ connect(m_audioManagerDialog,
+ SIGNAL(insertAudioSegment(AudioFileId,
+ const RealTime&,
+ const RealTime&)),
+ m_view,
+ SLOT(slotAddAudioSegmentDefaultPosition(AudioFileId,
+ const RealTime&,
+ const RealTime&)));
+ connect(m_audioManagerDialog,
+ SIGNAL(cancelPlayingAudioFile(AudioFileId)),
+ SLOT(slotCancelAudioPlayingFile(AudioFileId)));
+
+ connect(m_audioManagerDialog,
+ SIGNAL(deleteAllAudioFiles()),
+ SLOT(slotDeleteAllAudioFiles()));
+
+ // Make sure we know when the audio man. dialog is closing
+ //
+ connect(m_audioManagerDialog,
+ SIGNAL(closing()),
+ SLOT(slotAudioManagerClosed()));
+
+ // And that it goes away when the current document is changing
+ //
+ connect(this, SIGNAL(documentAboutToChange()),
+ m_audioManagerDialog, SLOT(close()));
+
+ m_audioManagerDialog->setAudioSubsystemStatus(
+ m_seqManager->getSoundDriverStatus() & AUDIO_OK);
+
+ plugAccelerators(m_audioManagerDialog,
+ m_audioManagerDialog->getAccelerators());
+
+ m_audioManagerDialog->show();
+}
+
+void
+RosegardenGUIApp::slotPlayAudioFile(unsigned int id,
+ const RealTime &startTime,
+ const RealTime &duration)
+{
+ AudioFile *aF = m_doc->getAudioFileManager().getAudioFile(id);
+
+ if (aF == 0)
+ return ;
+
+ MappedEvent mE(m_doc->getStudio().
+ getAudioPreviewInstrument(),
+ id,
+ RealTime( -120, 0),
+ duration, // duration
+ startTime); // start index
+
+ StudioControl::sendMappedEvent(mE);
+
+}
+
+void
+RosegardenGUIApp::slotAddAudioFile(unsigned int id)
+{
+ AudioFile *aF = m_doc->getAudioFileManager().getAudioFile(id);
+
+ if (aF == 0)
+ return ;
+
+ QCString replyType;
+ QByteArray replyData;
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ // We have to pass the filename as a QString
+ //
+ streamOut << QString(strtoqstr(aF->getFilename()));
+ streamOut << (int)aF->getId();
+
+ if (rgapp->sequencerCall("addAudioFile(QString, int)", replyType, replyData, data)) {
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ int result;
+ streamIn >> result;
+ if (!result) {
+ KMessageBox::error(this, i18n("Sequencer failed to add audio file %1").arg(aF->getFilename().c_str()));
+ }
+ }
+}
+
+void
+RosegardenGUIApp::slotDeleteAudioFile(unsigned int id)
+{
+ if (m_doc->getAudioFileManager().removeFile(id) == false)
+ return ;
+
+ QCString replyType;
+ QByteArray replyData;
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ // file id
+ //
+ streamOut << (int)id;
+
+ if (rgapp->sequencerCall("removeAudioFile(int)", replyType, replyData, data)) {
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ int result;
+ streamIn >> result;
+ if (!result) {
+ KMessageBox::error(this, i18n("Sequencer failed to remove audio file id %1").arg(id));
+ }
+ }
+}
+
+void
+RosegardenGUIApp::slotDeleteSegments(const SegmentSelection &selection)
+{
+ m_view->slotPropagateSegmentSelection(selection);
+ slotDeleteSelectedSegments();
+}
+
+void
+RosegardenGUIApp::slotCancelAudioPlayingFile(AudioFileId id)
+{
+ AudioFile *aF = m_doc->getAudioFileManager().getAudioFile(id);
+
+ if (aF == 0)
+ return ;
+
+ MappedEvent mE(m_doc->getStudio().
+ getAudioPreviewInstrument(),
+ MappedEvent::AudioCancel,
+ id);
+
+ StudioControl::sendMappedEvent(mE);
+}
+
+void
+RosegardenGUIApp::slotDeleteAllAudioFiles()
+{
+ m_doc->getAudioFileManager().clear();
+
+ // Clear at the sequencer
+ //
+ QCString replyType;
+ QByteArray replyData;
+ QByteArray data;
+
+ rgapp->sequencerCall("clearAllAudioFiles()", replyType, replyData, data);
+}
+
+void
+RosegardenGUIApp::slotRepeatingSegments()
+{
+ m_view->getTrackEditor()->slotTurnRepeatingSegmentToRealCopies();
+}
+
+void
+RosegardenGUIApp::slotRelabelSegments()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ SegmentSelection selection(m_view->getSelection());
+ QString editLabel;
+
+ if (selection.size() == 0)
+ return ;
+ else if (selection.size() == 1)
+ editLabel = i18n("Modify Segment label");
+ else
+ editLabel = i18n("Modify Segments label");
+
+ KTmpStatusMsg msg(i18n("Relabelling selection..."), this);
+
+ // Generate label
+ QString label = strtoqstr((*selection.begin())->getLabel());
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+ if (strtoqstr((*i)->getLabel()) != label)
+ label = "";
+ }
+
+ bool ok = false;
+
+ QString newLabel = KInputDialog::getText(editLabel,
+ i18n("Enter new label"),
+ label,
+ &ok,
+ this);
+
+ if (ok) {
+ m_doc->getCommandHistory()->addCommand
+ (new SegmentLabelCommand(selection, newLabel));
+ m_view->getTrackEditor()->getSegmentCanvas()->slotUpdateSegmentsDrawBuffer();
+ }
+}
+
+void
+RosegardenGUIApp::slotTransposeSegments()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ IntervalDialog intervalDialog(this, true, true);
+ int ok = intervalDialog.exec();
+
+ int semitones = intervalDialog.getChromaticDistance();
+ int steps = intervalDialog.getDiatonicDistance();
+
+ if (!ok || (semitones == 0 && steps == 0)) return;
+
+ m_doc->getCommandHistory()->addCommand
+ (new SegmentTransposeCommand(m_view->getSelection(), intervalDialog.getChangeKey(), steps, semitones, intervalDialog.getTransposeSegmentBack()));
+}
+
+void
+RosegardenGUIApp::slotChangeCompositionLength()
+{
+ CompositionLengthDialog dialog(this, &m_doc->getComposition());
+
+ if (dialog.exec() == QDialog::Accepted) {
+ ChangeCompositionLengthCommand *command
+ = new ChangeCompositionLengthCommand(
+ &m_doc->getComposition(),
+ dialog.getStartMarker(),
+ dialog.getEndMarker());
+
+ m_view->getTrackEditor()->getSegmentCanvas()->clearSegmentRectsCache(true);
+ m_doc->getCommandHistory()->addCommand(command);
+ }
+}
+
+void
+RosegardenGUIApp::slotManageMIDIDevices()
+{
+ if (m_deviceManager) {
+ m_deviceManager->show();
+ m_deviceManager->raise();
+ m_deviceManager->setActiveWindow();
+ return ;
+ }
+
+ m_deviceManager = new DeviceManagerDialog(this, m_doc);
+
+ connect(m_deviceManager, SIGNAL(closing()),
+ this, SLOT(slotDeviceManagerClosed()));
+
+ connect(this, SIGNAL(documentAboutToChange()),
+ m_deviceManager, SLOT(close()));
+
+ // Cheating way of updating the track/instrument list
+ //
+ connect(m_deviceManager, SIGNAL(deviceNamesChanged()),
+ m_view, SLOT(slotSynchroniseWithComposition()));
+
+ connect(m_deviceManager, SIGNAL(editBanks(DeviceId)),
+ this, SLOT(slotEditBanks(DeviceId)));
+
+ connect(m_deviceManager, SIGNAL(editControllers(DeviceId)),
+ this, SLOT(slotEditControlParameters(DeviceId)));
+
+ if (m_midiMixer) {
+ connect(m_deviceManager, SIGNAL(deviceNamesChanged()),
+ m_midiMixer, SLOT(slotSynchronise()));
+
+ }
+
+
+ m_deviceManager->show();
+}
+
+void
+RosegardenGUIApp::slotManageSynths()
+{
+ if (m_synthManager) {
+ m_synthManager->show();
+ m_synthManager->raise();
+ m_synthManager->setActiveWindow();
+ return ;
+ }
+
+ m_synthManager = new SynthPluginManagerDialog(this, m_doc
+#ifdef HAVE_LIBLO
+ , m_pluginGUIManager
+#endif
+ );
+
+ connect(m_synthManager, SIGNAL(closing()),
+ this, SLOT(slotSynthPluginManagerClosed()));
+
+ connect(this, SIGNAL(documentAboutToChange()),
+ m_synthManager, SLOT(close()));
+
+ connect(m_synthManager,
+ SIGNAL(pluginSelected(InstrumentId, int, int)),
+ this,
+ SLOT(slotPluginSelected(InstrumentId, int, int)));
+
+ connect(m_synthManager,
+ SIGNAL(showPluginDialog(QWidget *, InstrumentId, int)),
+ this,
+ SLOT(slotShowPluginDialog(QWidget *, InstrumentId, int)));
+
+ connect(m_synthManager,
+ SIGNAL(showPluginGUI(InstrumentId, int)),
+ this,
+ SLOT(slotShowPluginGUI(InstrumentId, int)));
+
+ m_synthManager->show();
+}
+
+void
+RosegardenGUIApp::slotOpenAudioMixer()
+{
+ if (m_audioMixer) {
+ m_audioMixer->show();
+ m_audioMixer->raise();
+ m_audioMixer->setActiveWindow();
+ return ;
+ }
+
+ m_audioMixer = new AudioMixerWindow(this, m_doc);
+
+ connect(m_audioMixer, SIGNAL(windowActivated()),
+ m_view, SLOT(slotActiveMainWindowChanged()));
+
+ connect(m_view, SIGNAL(controllerDeviceEventReceived(MappedEvent *, const void *)),
+ m_audioMixer, SLOT(slotControllerDeviceEventReceived(MappedEvent *, const void *)));
+
+ connect(m_audioMixer, SIGNAL(closing()),
+ this, SLOT(slotAudioMixerClosed()));
+
+ connect(m_audioMixer, SIGNAL(selectPlugin(QWidget *, InstrumentId, int)),
+ this, SLOT(slotShowPluginDialog(QWidget *, InstrumentId, int)));
+
+ connect(this,
+ SIGNAL(pluginSelected(InstrumentId, int, int)),
+ m_audioMixer,
+ SLOT(slotPluginSelected(InstrumentId, int, int)));
+
+ connect(this,
+ SIGNAL(pluginBypassed(InstrumentId, int, bool)),
+ m_audioMixer,
+ SLOT(slotPluginBypassed(InstrumentId, int, bool)));
+
+ connect(this, SIGNAL(documentAboutToChange()),
+ m_audioMixer, SLOT(close()));
+
+ connect(m_view, SIGNAL(checkTrackAssignments()),
+ m_audioMixer, SLOT(slotTrackAssignmentsChanged()));
+
+ connect(m_audioMixer, SIGNAL(play()),
+ this, SLOT(slotPlay()));
+ connect(m_audioMixer, SIGNAL(stop()),
+ this, SLOT(slotStop()));
+ connect(m_audioMixer, SIGNAL(fastForwardPlayback()),
+ this, SLOT(slotFastforward()));
+ connect(m_audioMixer, SIGNAL(rewindPlayback()),
+ this, SLOT(slotRewind()));
+ connect(m_audioMixer, SIGNAL(fastForwardPlaybackToEnd()),
+ this, SLOT(slotFastForwardToEnd()));
+ connect(m_audioMixer, SIGNAL(rewindPlaybackToBeginning()),
+ this, SLOT(slotRewindToBeginning()));
+ connect(m_audioMixer, SIGNAL(record()),
+ this, SLOT(slotRecord()));
+ connect(m_audioMixer, SIGNAL(panic()),
+ this, SLOT(slotPanic()));
+
+ connect(m_audioMixer,
+ SIGNAL(instrumentParametersChanged(InstrumentId)),
+ this,
+ SIGNAL(instrumentParametersChanged(InstrumentId)));
+
+ connect(this,
+ SIGNAL(instrumentParametersChanged(InstrumentId)),
+ m_audioMixer,
+ SLOT(slotUpdateInstrument(InstrumentId)));
+
+ if (m_synthManager) {
+ connect(m_synthManager,
+ SIGNAL(pluginSelected(InstrumentId, int, int)),
+ m_audioMixer,
+ SLOT(slotPluginSelected(InstrumentId, int, int)));
+ }
+
+ plugAccelerators(m_audioMixer, m_audioMixer->getAccelerators());
+
+ m_audioMixer->show();
+}
+
+void
+RosegardenGUIApp::slotOpenMidiMixer()
+{
+ if (m_midiMixer) {
+ m_midiMixer->show();
+ m_midiMixer->raise();
+ m_midiMixer->setActiveWindow();
+ return ;
+ }
+
+ m_midiMixer = new MidiMixerWindow(this, m_doc);
+
+ connect(m_midiMixer, SIGNAL(windowActivated()),
+ m_view, SLOT(slotActiveMainWindowChanged()));
+
+ connect(m_view, SIGNAL(controllerDeviceEventReceived(MappedEvent *, const void *)),
+ m_midiMixer, SLOT(slotControllerDeviceEventReceived(MappedEvent *, const void *)));
+
+ connect(m_midiMixer, SIGNAL(closing()),
+ this, SLOT(slotMidiMixerClosed()));
+
+ connect(this, SIGNAL(documentAboutToChange()),
+ m_midiMixer, SLOT(close()));
+
+ connect(m_midiMixer, SIGNAL(play()),
+ this, SLOT(slotPlay()));
+ connect(m_midiMixer, SIGNAL(stop()),
+ this, SLOT(slotStop()));
+ connect(m_midiMixer, SIGNAL(fastForwardPlayback()),
+ this, SLOT(slotFastforward()));
+ connect(m_midiMixer, SIGNAL(rewindPlayback()),
+ this, SLOT(slotRewind()));
+ connect(m_midiMixer, SIGNAL(fastForwardPlaybackToEnd()),
+ this, SLOT(slotFastForwardToEnd()));
+ connect(m_midiMixer, SIGNAL(rewindPlaybackToBeginning()),
+ this, SLOT(slotRewindToBeginning()));
+ connect(m_midiMixer, SIGNAL(record()),
+ this, SLOT(slotRecord()));
+ connect(m_midiMixer, SIGNAL(panic()),
+ this, SLOT(slotPanic()));
+
+ connect(m_midiMixer,
+ SIGNAL(instrumentParametersChanged(InstrumentId)),
+ this,
+ SIGNAL(instrumentParametersChanged(InstrumentId)));
+
+ connect(this,
+ SIGNAL(instrumentParametersChanged(InstrumentId)),
+ m_midiMixer,
+ SLOT(slotUpdateInstrument(InstrumentId)));
+
+ plugAccelerators(m_midiMixer, m_midiMixer->getAccelerators());
+
+ m_midiMixer->show();
+}
+
+void
+RosegardenGUIApp::slotEditControlParameters(DeviceId device)
+{
+ for (std::set
+ <ControlEditorDialog *>::iterator i = m_controlEditors.begin();
+ i != m_controlEditors.end(); ++i) {
+ if ((*i)->getDevice() == device) {
+ (*i)->show();
+ (*i)->raise();
+ (*i)->setActiveWindow();
+ return ;
+ }
+ }
+
+ ControlEditorDialog *controlEditor = new ControlEditorDialog(this, m_doc,
+ device);
+ m_controlEditors.insert(controlEditor);
+
+ RG_DEBUG << "inserting control editor dialog, have " << m_controlEditors.size() << " now" << endl;
+
+ connect(controlEditor, SIGNAL(closing()),
+ SLOT(slotControlEditorClosed()));
+
+ connect(this, SIGNAL(documentAboutToChange()),
+ controlEditor, SLOT(close()));
+
+ connect(m_doc, SIGNAL(devicesResyncd()),
+ controlEditor, SLOT(slotUpdate()));
+
+ controlEditor->show();
+}
+
+void
+RosegardenGUIApp::slotEditBanks()
+{
+ slotEditBanks(Device::NO_DEVICE);
+}
+
+void
+RosegardenGUIApp::slotEditBanks(DeviceId device)
+{
+ if (m_bankEditor) {
+ if (device != Device::NO_DEVICE)
+ m_bankEditor->setCurrentDevice(device);
+ m_bankEditor->show();
+ m_bankEditor->raise();
+ m_bankEditor->setActiveWindow();
+ return ;
+ }
+
+ m_bankEditor = new BankEditorDialog(this, m_doc, device);
+
+ connect(m_bankEditor, SIGNAL(closing()),
+ this, SLOT(slotBankEditorClosed()));
+
+ connect(this, SIGNAL(documentAboutToChange()),
+ m_bankEditor, SLOT(slotFileClose()));
+
+ // Cheating way of updating the track/instrument list
+ //
+ connect(m_bankEditor, SIGNAL(deviceNamesChanged()),
+ m_view, SLOT(slotSynchroniseWithComposition()));
+
+ m_bankEditor->show();
+}
+
+void
+RosegardenGUIApp::slotManageTriggerSegments()
+{
+ if (m_triggerSegmentManager) {
+ m_triggerSegmentManager->show();
+ m_triggerSegmentManager->raise();
+ m_triggerSegmentManager->setActiveWindow();
+ return ;
+ }
+
+ m_triggerSegmentManager = new TriggerSegmentManager(this, m_doc);
+
+ connect(m_triggerSegmentManager, SIGNAL(closing()),
+ SLOT(slotTriggerManagerClosed()));
+
+ connect(m_triggerSegmentManager, SIGNAL(editTriggerSegment(int)),
+ m_view, SLOT(slotEditTriggerSegment(int)));
+
+ m_triggerSegmentManager->show();
+}
+
+void
+RosegardenGUIApp::slotTriggerManagerClosed()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotTriggerManagerClosed" << endl;
+
+ m_triggerSegmentManager = 0;
+}
+
+void
+RosegardenGUIApp::slotEditMarkers()
+{
+ if (m_markerEditor) {
+ m_markerEditor->show();
+ m_markerEditor->raise();
+ m_markerEditor->setActiveWindow();
+ return ;
+ }
+
+ m_markerEditor = new MarkerEditor(this, m_doc);
+
+ connect(m_markerEditor, SIGNAL(closing()),
+ SLOT(slotMarkerEditorClosed()));
+
+ connect(m_markerEditor, SIGNAL(jumpToMarker(timeT)),
+ m_doc, SLOT(slotSetPointerPosition(timeT)));
+
+ plugAccelerators(m_markerEditor, m_markerEditor->getAccelerators());
+
+ m_markerEditor->show();
+}
+
+void
+RosegardenGUIApp::slotMarkerEditorClosed()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotMarkerEditorClosed" << endl;
+
+ m_markerEditor = 0;
+}
+
+void
+RosegardenGUIApp::slotEditTempos(timeT t)
+{
+ if (m_tempoView) {
+ m_tempoView->show();
+ m_tempoView->raise();
+ m_tempoView->setActiveWindow();
+ return ;
+ }
+
+ m_tempoView = new TempoView(m_doc, getView(), t);
+
+ connect(m_tempoView, SIGNAL(closing()),
+ SLOT(slotTempoViewClosed()));
+
+ connect(m_tempoView, SIGNAL(windowActivated()),
+ getView(), SLOT(slotActiveMainWindowChanged()));
+
+ connect(m_tempoView,
+ SIGNAL(changeTempo(timeT,
+ tempoT,
+ tempoT,
+ TempoDialog::TempoDialogAction)),
+ this,
+ SLOT(slotChangeTempo(timeT,
+ tempoT,
+ tempoT,
+ TempoDialog::TempoDialogAction)));
+
+ connect(m_tempoView, SIGNAL(saveFile()), this, SLOT(slotFileSave()));
+
+ plugAccelerators(m_tempoView, m_tempoView->getAccelerators());
+
+ m_tempoView->show();
+}
+
+void
+RosegardenGUIApp::slotTempoViewClosed()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotTempoViewClosed" << endl;
+
+ m_tempoView = 0;
+}
+
+void
+RosegardenGUIApp::slotControlEditorClosed()
+{
+ const QObject *s = sender();
+
+ RG_DEBUG << "RosegardenGUIApp::slotControlEditorClosed" << endl;
+
+ for (std::set
+ <ControlEditorDialog *>::iterator i = m_controlEditors.begin();
+ i != m_controlEditors.end(); ++i) {
+ if (*i == s) {
+ m_controlEditors.erase(i);
+ RG_DEBUG << "removed control editor dialog, have " << m_controlEditors.size() << " left" << endl;
+ return ;
+ }
+ }
+
+ std::cerr << "WARNING: control editor " << s << " closed, but couldn't find it in our control editor list (we have " << m_controlEditors.size() << " editors)" << std::endl;
+}
+
+void
+RosegardenGUIApp::slotShowPluginDialog(QWidget *parent,
+ InstrumentId instrumentId,
+ int index)
+{
+ if (!parent)
+ parent = this;
+
+ int key = (index << 16) + instrumentId;
+
+ if (m_pluginDialogs[key]) {
+ m_pluginDialogs[key]->show();
+ m_pluginDialogs[key]->raise();
+ m_pluginDialogs[key]->setActiveWindow();
+ return ;
+ }
+
+ PluginContainer *container = 0;
+
+ container = m_doc->getStudio().getContainerById(instrumentId);
+ if (!container) {
+ RG_DEBUG << "RosegardenGUIApp::slotShowPluginDialog - "
+ << "no instrument or buss of id " << instrumentId << endl;
+ return ;
+ }
+
+ // only create a dialog if we've got a plugin instance
+ AudioPluginInstance *inst =
+ container->getPlugin(index);
+
+ if (!inst) {
+ RG_DEBUG << "RosegardenGUIApp::slotShowPluginDialog - "
+ << "no AudioPluginInstance found for index "
+ << index << endl;
+ return ;
+ }
+
+ // Create the plugin dialog
+ //
+ AudioPluginDialog *dialog =
+ new AudioPluginDialog(parent,
+ m_doc->getPluginManager(),
+#ifdef HAVE_LIBLO
+ m_pluginGUIManager,
+#endif
+ container,
+ index);
+
+ connect(dialog, SIGNAL(windowActivated()),
+ m_view, SLOT(slotActiveMainWindowChanged()));
+
+/* This feature isn't provided by the plugin dialog
+ connect(m_view, SIGNAL(controllerDeviceEventReceived(MappedEvent *, const void *)),
+ dialog, SLOT(slotControllerDeviceEventReceived(MappedEvent *, const void *)));
+*/
+
+ // Plug the new dialog into the standard keyboard accelerators so
+ // that we can use them still while the plugin has focus.
+ //
+ plugAccelerators(dialog, dialog->getAccelerators());
+
+ connect(dialog,
+ SIGNAL(pluginSelected(InstrumentId, int, int)),
+ this,
+ SLOT(slotPluginSelected(InstrumentId, int, int)));
+
+ connect(dialog,
+ SIGNAL(pluginPortChanged(InstrumentId, int, int)),
+ this,
+ SLOT(slotPluginPortChanged(InstrumentId, int, int)));
+
+ connect(dialog,
+ SIGNAL(pluginProgramChanged(InstrumentId, int)),
+ this,
+ SLOT(slotPluginProgramChanged(InstrumentId, int)));
+
+ connect(dialog,
+ SIGNAL(changePluginConfiguration(InstrumentId, int, bool, QString, QString)),
+ this,
+ SLOT(slotChangePluginConfiguration(InstrumentId, int, bool, QString, QString)));
+
+ connect(dialog,
+ SIGNAL(showPluginGUI(InstrumentId, int)),
+ this,
+ SLOT(slotShowPluginGUI(InstrumentId, int)));
+
+ connect(dialog,
+ SIGNAL(stopPluginGUI(InstrumentId, int)),
+ this,
+ SLOT(slotStopPluginGUI(InstrumentId, int)));
+
+ connect(dialog,
+ SIGNAL(bypassed(InstrumentId, int, bool)),
+ this,
+ SLOT(slotPluginBypassed(InstrumentId, int, bool)));
+
+ connect(dialog,
+ SIGNAL(destroyed(InstrumentId, int)),
+ this,
+ SLOT(slotPluginDialogDestroyed(InstrumentId, int)));
+
+ connect(this, SIGNAL(documentAboutToChange()), dialog, SLOT(close()));
+
+ m_pluginDialogs[key] = dialog;
+ m_pluginDialogs[key]->show();
+
+ // Set modified
+ m_doc->slotDocumentModified();
+}
+
+void
+RosegardenGUIApp::slotPluginSelected(InstrumentId instrumentId,
+ int index, int plugin)
+{
+ const QObject *s = sender();
+
+ bool fromSynthMgr = (s == m_synthManager);
+
+ // It's assumed that ports etc will already have been set up on
+ // the AudioPluginInstance before this is invoked.
+
+ PluginContainer *container = 0;
+
+ container = m_doc->getStudio().getContainerById(instrumentId);
+ if (!container) {
+ RG_DEBUG << "RosegardenGUIApp::slotPluginSelected - "
+ << "no instrument or buss of id " << instrumentId << endl;
+ return ;
+ }
+
+ AudioPluginInstance *inst =
+ container->getPlugin(index);
+
+ if (!inst) {
+ RG_DEBUG << "RosegardenGUIApp::slotPluginSelected - "
+ << "got index of unknown plugin!" << endl;
+ return ;
+ }
+
+ if (plugin == -1) {
+ // Destroy plugin instance
+ //!!! seems iffy -- why can't we just unassign it?
+
+ if (StudioControl::
+ destroyStudioObject(inst->getMappedId())) {
+ RG_DEBUG << "RosegardenGUIApp::slotPluginSelected - "
+ << "cannot destroy Studio object "
+ << inst->getMappedId() << endl;
+ }
+
+ inst->setAssigned(false);
+ } else {
+ // If unassigned then create a sequencer instance of this
+ // AudioPluginInstance.
+ //
+ if (inst->isAssigned()) {
+ RG_DEBUG << "RosegardenGUIApp::slotPluginSelected - "
+ << " setting identifier for mapper id " << inst->getMappedId()
+ << " to " << inst->getIdentifier() << endl;
+
+ StudioControl::setStudioObjectProperty
+ (inst->getMappedId(),
+ MappedPluginSlot::Identifier,
+ strtoqstr(inst->getIdentifier()));
+ } else {
+ // create a studio object at the sequencer
+ MappedObjectId newId =
+ StudioControl::createStudioObject
+ (MappedObject::PluginSlot);
+
+ RG_DEBUG << "RosegardenGUIApp::slotPluginSelected - "
+ << " new MappedObjectId = " << newId << endl;
+
+ // set the new Mapped ID and that this instance
+ // is assigned
+ inst->setMappedId(newId);
+ inst->setAssigned(true);
+
+ // set the instrument id
+ StudioControl::setStudioObjectProperty
+ (newId,
+ MappedObject::Instrument,
+ MappedObjectValue(instrumentId));
+
+ // set the position
+ StudioControl::setStudioObjectProperty
+ (newId,
+ MappedObject::Position,
+ MappedObjectValue(index));
+
+ // set the plugin id
+ StudioControl::setStudioObjectProperty
+ (newId,
+ MappedPluginSlot::Identifier,
+ strtoqstr(inst->getIdentifier()));
+ }
+ }
+
+ int pluginMappedId = inst->getMappedId();
+
+ //!!! much code duplicated here from RosegardenGUIDoc::initialiseStudio
+
+ inst->setConfigurationValue
+ (qstrtostr(PluginIdentifier::RESERVED_PROJECT_DIRECTORY_KEY),
+ m_doc->getAudioFileManager().getAudioPath());
+
+ // Set opaque string configuration data (e.g. for DSSI plugin)
+ //
+ MappedObjectPropertyList config;
+ for (AudioPluginInstance::ConfigMap::const_iterator
+ i = inst->getConfiguration().begin();
+ i != inst->getConfiguration().end(); ++i) {
+ config.push_back(strtoqstr(i->first));
+ config.push_back(strtoqstr(i->second));
+ }
+ StudioControl::setStudioObjectPropertyList
+ (pluginMappedId,
+ MappedPluginSlot::Configuration,
+ config);
+
+ // Set the bypass
+ //
+ StudioControl::setStudioObjectProperty
+ (pluginMappedId,
+ MappedPluginSlot::Bypassed,
+ MappedObjectValue(inst->isBypassed()));
+
+ // Set the program
+ //
+ if (inst->getProgram() != "") {
+ StudioControl::setStudioObjectProperty
+ (pluginMappedId,
+ MappedPluginSlot::Program,
+ strtoqstr(inst->getProgram()));
+ }
+
+ // Set all the port values
+ //
+ PortInstanceIterator portIt;
+
+ for (portIt = inst->begin();
+ portIt != inst->end(); ++portIt) {
+ StudioControl::setStudioPluginPort
+ (pluginMappedId,
+ (*portIt)->number,
+ (*portIt)->value);
+ }
+
+ if (fromSynthMgr) {
+ int key = (index << 16) + instrumentId;
+ if (m_pluginDialogs[key]) {
+ m_pluginDialogs[key]->updatePlugin(plugin);
+ }
+ } else if (m_synthManager) {
+ m_synthManager->updatePlugin(instrumentId, plugin);
+ }
+
+ emit pluginSelected(instrumentId, index, plugin);
+
+ // Set modified
+ m_doc->slotDocumentModified();
+}
+
+void
+RosegardenGUIApp::slotChangePluginPort(InstrumentId instrumentId,
+ int pluginIndex,
+ int portIndex,
+ float value)
+{
+ PluginContainer *container = 0;
+
+ container = m_doc->getStudio().getContainerById(instrumentId);
+ if (!container) {
+ RG_DEBUG << "RosegardenGUIApp::slotChangePluginPort - "
+ << "no instrument or buss of id " << instrumentId << endl;
+ return ;
+ }
+
+ AudioPluginInstance *inst = container->getPlugin(pluginIndex);
+ if (!inst) {
+ RG_DEBUG << "RosegardenGUIApp::slotChangePluginPort - "
+ << "no plugin at index " << pluginIndex << " on " << instrumentId << endl;
+ return ;
+ }
+
+ PluginPortInstance *port = inst->getPort(portIndex);
+ if (!port) {
+ RG_DEBUG << "RosegardenGUIApp::slotChangePluginPort - no port "
+ << portIndex << endl;
+ return ;
+ }
+
+ RG_DEBUG << "RosegardenGUIApp::slotPluginPortChanged - "
+ << "setting plugin port (" << inst->getMappedId()
+ << ", " << portIndex << ") from " << port->value
+ << " to " << value << endl;
+
+ port->setValue(value);
+
+ StudioControl::setStudioPluginPort(inst->getMappedId(),
+ portIndex, port->value);
+
+ m_doc->slotDocumentModified();
+
+ // This modification came from The Outside!
+ int key = (pluginIndex << 16) + instrumentId;
+ if (m_pluginDialogs[key]) {
+ m_pluginDialogs[key]->updatePluginPortControl(portIndex);
+ }
+}
+
+void
+RosegardenGUIApp::slotPluginPortChanged(InstrumentId instrumentId,
+ int pluginIndex,
+ int portIndex)
+{
+ PluginContainer *container = 0;
+
+ container = m_doc->getStudio().getContainerById(instrumentId);
+ if (!container) {
+ RG_DEBUG << "RosegardenGUIApp::slotPluginPortChanged - "
+ << "no instrument or buss of id " << instrumentId << endl;
+ return ;
+ }
+
+ AudioPluginInstance *inst = container->getPlugin(pluginIndex);
+ if (!inst) {
+ RG_DEBUG << "RosegardenGUIApp::slotPluginPortChanged - "
+ << "no plugin at index " << pluginIndex << " on " << instrumentId << endl;
+ return ;
+ }
+
+ PluginPortInstance *port = inst->getPort(portIndex);
+ if (!port) {
+ RG_DEBUG << "RosegardenGUIApp::slotPluginPortChanged - no port "
+ << portIndex << endl;
+ return ;
+ }
+
+ RG_DEBUG << "RosegardenGUIApp::slotPluginPortChanged - "
+ << "setting plugin port (" << inst->getMappedId()
+ << ", " << portIndex << ") to " << port->value << endl;
+
+ StudioControl::setStudioPluginPort(inst->getMappedId(),
+ portIndex, port->value);
+
+ m_doc->slotDocumentModified();
+
+#ifdef HAVE_LIBLO
+ // This modification came from our own plugin dialog, so update
+ // any external GUIs
+ if (m_pluginGUIManager) {
+ m_pluginGUIManager->updatePort(instrumentId,
+ pluginIndex,
+ portIndex);
+ }
+#endif
+}
+
+void
+RosegardenGUIApp::slotChangePluginProgram(InstrumentId instrumentId,
+ int pluginIndex,
+ QString program)
+{
+ PluginContainer *container = 0;
+
+ container = m_doc->getStudio().getContainerById(instrumentId);
+ if (!container) {
+ RG_DEBUG << "RosegardenGUIApp::slotChangePluginProgram - "
+ << "no instrument or buss of id " << instrumentId << endl;
+ return ;
+ }
+
+ AudioPluginInstance *inst = container->getPlugin(pluginIndex);
+ if (!inst) {
+ RG_DEBUG << "RosegardenGUIApp::slotChangePluginProgram - "
+ << "no plugin at index " << pluginIndex << " on " << instrumentId << endl;
+ return ;
+ }
+
+ RG_DEBUG << "RosegardenGUIApp::slotChangePluginProgram - "
+ << "setting plugin program ("
+ << inst->getMappedId() << ") from " << inst->getProgram()
+ << " to " << program << endl;
+
+ inst->setProgram(qstrtostr(program));
+
+ StudioControl::
+ setStudioObjectProperty(inst->getMappedId(),
+ MappedPluginSlot::Program,
+ program);
+
+ PortInstanceIterator portIt;
+
+ for (portIt = inst->begin();
+ portIt != inst->end(); ++portIt) {
+ float value = StudioControl::getStudioPluginPort
+ (inst->getMappedId(),
+ (*portIt)->number);
+ (*portIt)->value = value;
+ }
+
+ // Set modified
+ m_doc->slotDocumentModified();
+
+ int key = (pluginIndex << 16) + instrumentId;
+ if (m_pluginDialogs[key]) {
+ m_pluginDialogs[key]->updatePluginProgramControl();
+ }
+}
+
+void
+RosegardenGUIApp::slotPluginProgramChanged(InstrumentId instrumentId,
+ int pluginIndex)
+{
+ PluginContainer *container = 0;
+
+ container = m_doc->getStudio().getContainerById(instrumentId);
+ if (!container) {
+ RG_DEBUG << "RosegardenGUIApp::slotPluginProgramChanged - "
+ << "no instrument or buss of id " << instrumentId << endl;
+ return ;
+ }
+
+ AudioPluginInstance *inst = container->getPlugin(pluginIndex);
+ if (!inst) {
+ RG_DEBUG << "RosegardenGUIApp::slotPluginProgramChanged - "
+ << "no plugin at index " << pluginIndex << " on " << instrumentId << endl;
+ return ;
+ }
+
+ QString program = strtoqstr(inst->getProgram());
+
+ RG_DEBUG << "RosegardenGUIApp::slotPluginProgramChanged - "
+ << "setting plugin program ("
+ << inst->getMappedId() << ") to " << program << endl;
+
+ StudioControl::
+ setStudioObjectProperty(inst->getMappedId(),
+ MappedPluginSlot::Program,
+ program);
+
+ PortInstanceIterator portIt;
+
+ for (portIt = inst->begin();
+ portIt != inst->end(); ++portIt) {
+ float value = StudioControl::getStudioPluginPort
+ (inst->getMappedId(),
+ (*portIt)->number);
+ (*portIt)->value = value;
+ }
+
+ // Set modified
+ m_doc->slotDocumentModified();
+
+#ifdef HAVE_LIBLO
+
+ if (m_pluginGUIManager)
+ m_pluginGUIManager->updateProgram(instrumentId,
+ pluginIndex);
+#endif
+}
+
+void
+RosegardenGUIApp::slotChangePluginConfiguration(InstrumentId instrumentId,
+ int index,
+ bool global,
+ QString key,
+ QString value)
+{
+ PluginContainer *container = 0;
+
+ container = m_doc->getStudio().getContainerById(instrumentId);
+ if (!container) {
+ RG_DEBUG << "RosegardenGUIApp::slotChangePluginConfiguration - "
+ << "no instrument or buss of id " << instrumentId << endl;
+ return ;
+ }
+
+ AudioPluginInstance *inst = container->getPlugin(index);
+
+ if (global && inst) {
+
+ // Set the same configuration on other plugins in the same
+ // instance group
+
+ AudioPlugin *pl =
+ m_pluginManager->getPluginByIdentifier(strtoqstr(inst->getIdentifier()));
+
+ if (pl && pl->isGrouped()) {
+
+ InstrumentList il =
+ m_doc->getStudio().getAllInstruments();
+
+ for (InstrumentList::iterator i = il.begin();
+ i != il.end(); ++i) {
+
+ for (PluginInstanceIterator pli =
+ (*i)->beginPlugins();
+ pli != (*i)->endPlugins(); ++pli) {
+
+ if (*pli && (*pli)->isAssigned() &&
+ (*pli)->getIdentifier() == inst->getIdentifier() &&
+ (*pli) != inst) {
+
+ slotChangePluginConfiguration
+ ((*i)->getId(), (*pli)->getPosition(),
+ false, key, value);
+
+#ifdef HAVE_LIBLO
+
+ m_pluginGUIManager->updateConfiguration
+ ((*i)->getId(), (*pli)->getPosition(), key);
+#endif
+
+ }
+ }
+ }
+ }
+ }
+
+ if (inst) {
+
+ inst->setConfigurationValue(qstrtostr(key), qstrtostr(value));
+
+ MappedObjectPropertyList config;
+ for (AudioPluginInstance::ConfigMap::const_iterator
+ i = inst->getConfiguration().begin();
+ i != inst->getConfiguration().end(); ++i) {
+ config.push_back(strtoqstr(i->first));
+ config.push_back(strtoqstr(i->second));
+ }
+
+ RG_DEBUG << "RosegardenGUIApp::slotChangePluginConfiguration: setting new config on mapped id " << inst->getMappedId() << endl;
+
+ StudioControl::setStudioObjectPropertyList
+ (inst->getMappedId(),
+ MappedPluginSlot::Configuration,
+ config);
+
+ // Set modified
+ m_doc->slotDocumentModified();
+
+ int key = (index << 16) + instrumentId;
+ if (m_pluginDialogs[key]) {
+ m_pluginDialogs[key]->updatePluginProgramList();
+ }
+ }
+}
+
+void
+RosegardenGUIApp::slotPluginDialogDestroyed(InstrumentId instrumentId,
+ int index)
+{
+ int key = (index << 16) + instrumentId;
+ m_pluginDialogs[key] = 0;
+}
+
+void
+RosegardenGUIApp::slotPluginBypassed(InstrumentId instrumentId,
+ int pluginIndex, bool bp)
+{
+ PluginContainer *container = 0;
+
+ container = m_doc->getStudio().getContainerById(instrumentId);
+ if (!container) {
+ RG_DEBUG << "RosegardenGUIApp::slotPluginBypassed - "
+ << "no instrument or buss of id " << instrumentId << endl;
+ return ;
+ }
+
+ AudioPluginInstance *inst = container->getPlugin(pluginIndex);
+
+ if (inst) {
+ StudioControl::setStudioObjectProperty
+ (inst->getMappedId(),
+ MappedPluginSlot::Bypassed,
+ MappedObjectValue(bp));
+
+ // Set the bypass on the instance
+ //
+ inst->setBypass(bp);
+
+ // Set modified
+ m_doc->slotDocumentModified();
+ }
+
+ emit pluginBypassed(instrumentId, pluginIndex, bp);
+}
+
+void
+RosegardenGUIApp::slotShowPluginGUI(InstrumentId instrument,
+ int index)
+{
+#ifdef HAVE_LIBLO
+ m_pluginGUIManager->showGUI(instrument, index);
+#endif
+}
+
+void
+RosegardenGUIApp::slotStopPluginGUI(InstrumentId instrument,
+ int index)
+{
+#ifdef HAVE_LIBLO
+ m_pluginGUIManager->stopGUI(instrument, index);
+#endif
+}
+
+void
+RosegardenGUIApp::slotPluginGUIExited(InstrumentId instrument,
+ int index)
+{
+ int key = (index << 16) + instrument;
+ if (m_pluginDialogs[key]) {
+ m_pluginDialogs[key]->guiExited();
+ }
+}
+
+void
+RosegardenGUIApp::slotPlayList()
+{
+ if (!m_playList) {
+ m_playList = new PlayListDialog(i18n("Play List"), this);
+ connect(m_playList, SIGNAL(closing()),
+ SLOT(slotPlayListClosed()));
+ connect(m_playList->getPlayList(), SIGNAL(play(QString)),
+ SLOT(slotPlayListPlay(QString)));
+ }
+
+ m_playList->show();
+}
+
+void
+RosegardenGUIApp::slotPlayListPlay(QString url)
+{
+ slotStop();
+ openURL(url);
+ slotPlay();
+}
+
+void
+RosegardenGUIApp::slotPlayListClosed()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotPlayListClosed()\n";
+ m_playList = 0;
+}
+
+void
+RosegardenGUIApp::slotTutorial()
+{
+ QString exe = KStandardDirs::findExe( "x-www-browser" );
+
+ if( exe )
+ {
+ KProcess *proc = new KProcess;
+ *proc << "x-www-browser";
+ *proc << "http://rosegarden.sourceforge.net/tutorial/en/chapter-0.html";
+
+ proc->start(KProcess::DontCare);
+ proc->detach();
+ delete proc;
+ }
+ else
+ {
+ QString tutorialURL = i18n("http://rosegarden.sourceforge.net/tutorial/en/chapter-0.html");
+ kapp->invokeBrowser(tutorialURL);
+ }
+}
+
+void
+RosegardenGUIApp::slotBugGuidelines()
+{
+ QString exe = KStandardDirs::findExe( "x-www-browser" );
+
+ if( exe )
+ {
+ KProcess *proc = new KProcess;
+ *proc << "x-www-browser";
+ *proc << "http://rosegarden.sourceforge.net/tutorial/bug-guidelines.html";
+
+ proc->start(KProcess::DontCare);
+ proc->detach();
+ delete proc;
+ }
+ else
+ {
+ QString tutorialURL = i18n("http://rosegarden.sourceforge.net/tutorial/bug-guidelines.html");
+ kapp->invokeBrowser(tutorialURL);
+ }
+}
+
+void
+RosegardenGUIApp::slotBankEditorClosed()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotBankEditorClosed()\n";
+
+ if (m_doc->isModified()) {
+ if (m_view)
+ m_view->slotSelectTrackSegments(m_doc->getComposition().getSelectedTrack());
+ }
+
+ m_bankEditor = 0;
+}
+
+void
+RosegardenGUIApp::slotDeviceManagerClosed()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotDeviceManagerClosed()\n";
+
+ if (m_doc->isModified()) {
+ if (m_view)
+ m_view->slotSelectTrackSegments(m_doc->getComposition().getSelectedTrack());
+ }
+
+ m_deviceManager = 0;
+}
+
+void
+RosegardenGUIApp::slotSynthPluginManagerClosed()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotSynthPluginManagerClosed()\n";
+
+ m_synthManager = 0;
+}
+
+void
+RosegardenGUIApp::slotAudioMixerClosed()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotAudioMixerClosed()\n";
+
+ m_audioMixer = 0;
+}
+
+void
+RosegardenGUIApp::slotMidiMixerClosed()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotMidiMixerClosed()\n";
+
+ m_midiMixer = 0;
+}
+
+void
+RosegardenGUIApp::slotAudioManagerClosed()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotAudioManagerClosed()\n";
+
+ if (m_doc->isModified()) {
+ if (m_view)
+ m_view->slotSelectTrackSegments(m_doc->getComposition().getSelectedTrack());
+ }
+
+ m_audioManagerDialog = 0;
+}
+
+void
+RosegardenGUIApp::slotPanic()
+{
+ if (m_seqManager) {
+ // Stop the transport before we send a panic as the
+ // playback goes all to hell anyway.
+ //
+ slotStop();
+
+ ProgressDialog progressDlg(i18n("Queueing MIDI panic events for tranmission..."),
+ 100,
+ this);
+ CurrentProgressDialog::set
+ (&progressDlg);
+ ProgressDialog::processEvents();
+
+ connect(m_seqManager, SIGNAL(setProgress(int)),
+ progressDlg.progressBar(), SLOT(setValue(int)));
+ connect(m_seqManager, SIGNAL(incrementProgress(int)),
+ progressDlg.progressBar(), SLOT(advance(int)));
+
+ m_seqManager->panic();
+
+ }
+}
+
+void
+RosegardenGUIApp::slotPopulateTrackInstrumentPopup()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotSetTrackInstrument\n";
+ Composition &comp = m_doc->getComposition();
+ Track *track = comp.getTrackById(comp.getSelectedTrack());
+
+ if (!track) {
+ RG_DEBUG << "Weird: no track available for instrument popup!" << endl;
+ return ;
+ }
+
+ Instrument* instrument = m_doc->getStudio().getInstrumentById(track->getInstrument());
+
+ QPopupMenu* popup = dynamic_cast<QPopupMenu*>(factory()->container("set_track_instrument", this));
+
+ m_view->getTrackEditor()->getTrackButtons()->populateInstrumentPopup(instrument, popup);
+}
+
+void
+RosegardenGUIApp::slotRemapInstruments()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotRemapInstruments\n";
+ RemapInstrumentDialog dialog(this, m_doc);
+
+ connect(&dialog, SIGNAL(applyClicked()),
+ m_view->getTrackEditor()->getTrackButtons(),
+ SLOT(slotSynchroniseWithComposition()));
+
+ if (dialog.exec() == QDialog::Accepted) {
+ RG_DEBUG << "slotRemapInstruments - accepted\n";
+ }
+
+}
+
+void
+RosegardenGUIApp::slotSaveDefaultStudio()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotSaveDefaultStudio\n";
+
+ int reply = KMessageBox::warningYesNo
+ (this, i18n("Are you sure you want to save this as your default studio?"));
+
+ if (reply != KMessageBox::Yes)
+ return ;
+
+ KTmpStatusMsg msg(i18n("Saving current document as default studio..."), this);
+
+ QString autoloadFile = ::locateLocal("appdata", "autoload.rg");
+
+ RG_DEBUG << "RosegardenGUIApp::slotSaveDefaultStudio : saving studio in "
+ << autoloadFile << endl;
+
+ SetWaitCursor waitCursor;
+ QString errMsg;
+ bool res = m_doc->saveDocument(autoloadFile, errMsg);
+ if (!res) {
+ if (errMsg)
+ KMessageBox::error(this, i18n(QString("Could not auto-save document at %1\nError was : %2")
+ .arg(autoloadFile).arg(errMsg)));
+ else
+ KMessageBox::error(this, i18n(QString("Could not auto-save document at %1")
+ .arg(autoloadFile)));
+
+ }
+}
+
+void
+RosegardenGUIApp::slotImportDefaultStudio()
+{
+ int reply = KMessageBox::warningYesNo
+ (this, i18n("Are you sure you want to import your default studio and lose the current one?"));
+
+ if (reply != KMessageBox::Yes)
+ return ;
+
+ QString autoloadFile =
+ KGlobal::dirs()->findResource("appdata", "autoload.rg");
+
+ QFileInfo autoloadFileInfo(autoloadFile);
+
+ if (!autoloadFileInfo.isReadable()) {
+ RG_DEBUG << "RosegardenGUIDoc::slotImportDefaultStudio - "
+ << "can't find autoload file - defaulting" << endl;
+ return ;
+ }
+
+ slotImportStudioFromFile(autoloadFile);
+}
+
+void
+RosegardenGUIApp::slotImportStudio()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotImportStudio()\n";
+
+ QString studioDir = KGlobal::dirs()->findResource("appdata", "library/");
+ QDir dir(studioDir);
+ if (!dir.exists()) {
+ studioDir = ":ROSEGARDENDEVICE";
+ } else {
+ studioDir = "file://" + studioDir;
+ }
+
+ KURL url = KFileDialog::getOpenURL
+ (studioDir,
+ "audio/x-rosegarden-device audio/x-rosegarden",
+ this, i18n("Import Studio from File"));
+
+ if (url.isEmpty())
+ return ;
+
+ QString target;
+ if (KIO::NetAccess::download(url, target, this) == false) {
+ KMessageBox::error(this, i18n("Cannot download file %1")
+ .arg(url.prettyURL()));
+ return ;
+ }
+
+ slotImportStudioFromFile(target);
+}
+
+void
+RosegardenGUIApp::slotImportStudioFromFile(const QString &file)
+{
+ RosegardenGUIDoc *doc = new RosegardenGUIDoc(this, 0, true); // skipAutoload
+
+ Studio &oldStudio = m_doc->getStudio();
+ Studio &newStudio = doc->getStudio();
+
+ // Add some dummy devices for when we open the document. We guess
+ // that the file won't have more than 32 devices.
+ //
+ // for (unsigned int i = 0; i < 32; i++) {
+ // newStudio.addDevice("", i, Device::Midi);
+ // }
+
+ if (doc->openDocument(file, true)) { // true because we actually
+ // do want to create devices
+ // on the sequencer here
+
+ KMacroCommand *command = new KMacroCommand(i18n("Import Studio"));
+ doc->syncDevices();
+
+ // We actually only copy across MIDI play devices... for now
+ std::vector<DeviceId> midiPlayDevices;
+
+ for (DeviceList::const_iterator i =
+ oldStudio.begin(); i != oldStudio.end(); ++i) {
+
+ MidiDevice *md =
+ dynamic_cast<MidiDevice *>(*i);
+
+ if (md && (md->getDirection() == MidiDevice::Play)) {
+ midiPlayDevices.push_back((*i)->getId());
+ }
+ }
+
+ std::vector<DeviceId>::iterator di(midiPlayDevices.begin());
+
+ for (DeviceList::const_iterator i =
+ newStudio.begin(); i != newStudio.end(); ++i) {
+
+ MidiDevice *md =
+ dynamic_cast<MidiDevice *>(*i);
+
+ if (md && (md->getDirection() == MidiDevice::Play)) {
+ if (di != midiPlayDevices.end()) {
+ MidiDevice::VariationType variation
+ (md->getVariationType());
+ BankList bl(md->getBanks());
+ ProgramList pl(md->getPrograms());
+ ControlList cl(md->getControlParameters());
+
+ ModifyDeviceCommand* mdCommand = new ModifyDeviceCommand(&oldStudio,
+ *di,
+ md->getName(),
+ md->getLibrarianName(),
+ md->getLibrarianEmail());
+ mdCommand->setVariation(variation);
+ mdCommand->setBankList(bl);
+ mdCommand->setProgramList(pl);
+ mdCommand->setControlList(cl);
+ mdCommand->setOverwrite(true);
+ mdCommand->setRename(md->getName() != "");
+
+ command->addCommand(mdCommand);
+ ++di;
+ }
+ }
+ }
+
+ while (di != midiPlayDevices.end()) {
+ command->addCommand(new CreateOrDeleteDeviceCommand
+ (&oldStudio,
+ *di));
+ }
+
+ oldStudio.setMIDIThruFilter(newStudio.getMIDIThruFilter());
+ oldStudio.setMIDIRecordFilter(newStudio.getMIDIRecordFilter());
+
+ m_doc->getCommandHistory()->addCommand(command);
+ m_doc->syncDevices();
+ m_doc->initialiseStudio(); // The other document will have reset it
+ }
+
+ delete doc;
+}
+
+void
+RosegardenGUIApp::slotResetMidiNetwork()
+{
+ if (m_seqManager) {
+
+ m_seqManager->preparePlayback(true);
+
+ m_seqManager->resetMidiNetwork();
+ }
+
+}
+
+void
+RosegardenGUIApp::slotModifyMIDIFilters()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotModifyMIDIFilters" << endl;
+
+ MidiFilterDialog dialog(this, m_doc);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ RG_DEBUG << "slotModifyMIDIFilters - accepted" << endl;
+ }
+}
+
+void
+RosegardenGUIApp::slotManageMetronome()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotManageMetronome" << endl;
+
+ ManageMetronomeDialog dialog(this, m_doc);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ RG_DEBUG << "slotManageMetronome - accepted" << endl;
+ }
+}
+
+void
+RosegardenGUIApp::slotAutoSave()
+{
+ if (!m_seqManager ||
+ m_seqManager->getTransportStatus() == PLAYING ||
+ m_seqManager->getTransportStatus() == RECORDING)
+ return ;
+
+ KConfig* config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+ if (!config->readBoolEntry("autosave", true))
+ return ;
+
+ m_doc->slotAutoSave();
+}
+
+void
+RosegardenGUIApp::slotUpdateAutoSaveInterval(unsigned int interval)
+{
+ RG_DEBUG << "RosegardenGUIApp::slotUpdateAutoSaveInterval - "
+ << "changed interval to " << interval << endl;
+ m_autoSaveTimer->changeInterval(int(interval) * 1000);
+}
+
+void
+RosegardenGUIApp::slotUpdateSidebarStyle(unsigned int style)
+{
+ RG_DEBUG << "RosegardenGUIApp::slotUpdateSidebarStyle - "
+ << "changed style to " << style << endl;
+ m_parameterArea->setArrangement((RosegardenParameterArea::Arrangement) style);
+}
+
+void
+RosegardenGUIApp::slotShowTip()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotShowTip" << endl;
+ KTipDialog::showTip(this, locate("data", "rosegarden/tips"), true);
+}
+
+void RosegardenGUIApp::slotShowToolHelp(const QString &s)
+{
+ QString msg = s;
+ if (msg != "") msg = " " + msg;
+ slotStatusMsg(msg);
+}
+
+void
+RosegardenGUIApp::slotEnableMIDIThruRouting()
+{
+ m_seqManager->enableMIDIThruRouting(m_enableMIDIrouting->isChecked());
+}
+
+TransportDialog* RosegardenGUIApp::getTransport()
+{
+ if (m_transport == 0)
+ createAndSetupTransport();
+
+ return m_transport;
+}
+
+RosegardenGUIDoc *RosegardenGUIApp::getDocument() const
+{
+ return m_doc;
+}
+
+void
+RosegardenGUIApp::awaitDialogClearance()
+{
+ bool haveDialog = true;
+
+ std::cerr << "RosegardenGUIApp::awaitDialogClearance: entering" << std::endl;
+
+ while (haveDialog) {
+
+ const QObjectList *c = children();
+ if (!c) return;
+
+ haveDialog = false;
+ for (QObjectList::const_iterator i = c->begin(); i != c->end(); ++i) {
+ QDialog *dialog = dynamic_cast<QDialog *>(*i);
+ if (dialog && dialog->isVisible()) {
+ haveDialog = true;
+ break;
+ }
+ }
+
+// std::cerr << "RosegardenGUIApp::awaitDialogClearance: have dialog = "
+// << haveDialog << std::endl;
+
+ if (haveDialog) kapp->processEvents();
+ }
+
+ std::cerr << "RosegardenGUIApp::awaitDialogClearance: exiting" << std::endl;
+}
+
+void
+RosegardenGUIApp::slotNewerVersionAvailable(QString v)
+{
+ if (m_firstRun) return;
+ KStartupLogo::hideIfStillThere();
+ CurrentProgressDialog::freeze();
+ awaitDialogClearance();
+ KMessageBox::information
+ (this,
+ i18n("<h3>Newer version available</h3><p>A newer version of Rosegarden may be available.<br>Please consult the <a href=\"http://www.rosegardenmusic.com/getting/\">Rosegarden website</a> for more information.</p>"),
+ i18n("Newer version available"),
+ QString("version-%1-available-show").arg(v),
+ KMessageBox::AllowLink);
+ CurrentProgressDialog::thaw();
+}
+
+void
+RosegardenGUIApp::slotSetQuickMarker()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotSetQuickMarker" << endl;
+
+ m_doc->setQuickMarker();
+ getView()->getTrackEditor()->updateRulers();
+}
+
+void
+RosegardenGUIApp::slotJumpToQuickMarker()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotJumpToQuickMarker" << endl;
+
+ m_doc->jumpToQuickMarker();
+}
+
+const void* RosegardenGUIApp::SequencerExternal = (void*)-1;
+RosegardenGUIApp *RosegardenGUIApp::m_myself = 0;
+
+}
+#include "RosegardenGUIApp.moc"
diff --git a/src/gui/application/RosegardenGUIApp.cpp.orig b/src/gui/application/RosegardenGUIApp.cpp.orig
new file mode 100644
index 0000000..fa98530
--- /dev/null
+++ b/src/gui/application/RosegardenGUIApp.cpp.orig
@@ -0,0 +1,8043 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "RosegardenGUIApp.h"
+#include <kapplication.h>
+
+#include "gui/editors/segment/TrackEditor.h"
+#include "gui/editors/segment/TrackButtons.h"
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "gui/application/RosegardenDCOP.h"
+#include "base/AnalysisTypes.h"
+#include "base/AudioPluginInstance.h"
+#include "base/Clipboard.h"
+#include "base/Composition.h"
+#include "base/CompositionTimeSliceAdapter.h"
+#include "base/Configuration.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/Profiler.h"
+#include "base/RealTime.h"
+#include "base/Segment.h"
+#include "base/SegmentNotationHelper.h"
+#include "base/Selection.h"
+#include "base/Studio.h"
+#include "base/Track.h"
+#include "commands/edit/CopyCommand.h"
+#include "commands/edit/CutCommand.h"
+#include "commands/edit/EventQuantizeCommand.h"
+#include "commands/edit/PasteSegmentsCommand.h"
+#include "commands/edit/TransposeCommand.h"
+#include "commands/edit/AddMarkerCommand.h"
+#include "commands/edit/ModifyMarkerCommand.h"
+#include "commands/edit/RemoveMarkerCommand.h"
+#include "commands/notation/KeyInsertionCommand.h"
+#include "commands/segment/AddTempoChangeCommand.h"
+#include "commands/segment/AddTimeSignatureAndNormalizeCommand.h"
+#include "commands/segment/AddTimeSignatureCommand.h"
+#include "commands/segment/AudioSegmentAutoSplitCommand.h"
+#include "commands/segment/AudioSegmentRescaleCommand.h"
+#include "commands/segment/AudioSegmentSplitCommand.h"
+#include "commands/segment/ChangeCompositionLengthCommand.h"
+#include "commands/segment/CreateTempoMapFromSegmentCommand.h"
+#include "commands/segment/CutRangeCommand.h"
+#include "commands/segment/DeleteRangeCommand.h"
+#include "commands/segment/InsertRangeCommand.h"
+#include "commands/segment/ModifyDefaultTempoCommand.h"
+#include "commands/segment/MoveTracksCommand.h"
+#include "commands/segment/PasteRangeCommand.h"
+#include "commands/segment/RemoveTempoChangeCommand.h"
+#include "commands/segment/SegmentAutoSplitCommand.h"
+#include "commands/segment/SegmentChangeTransposeCommand.h"
+#include "commands/segment/SegmentJoinCommand.h"
+#include "commands/segment/SegmentLabelCommand.h"
+#include "commands/segment/SegmentReconfigureCommand.h"
+#include "commands/segment/SegmentRescaleCommand.h"
+#include "commands/segment/SegmentSplitByPitchCommand.h"
+#include "commands/segment/SegmentSplitByRecordingSrcCommand.h"
+#include "commands/segment/SegmentSplitCommand.h"
+#include "commands/segment/SegmentTransposeCommand.h"
+#include "commands/studio/CreateOrDeleteDeviceCommand.h"
+#include "commands/studio/ModifyDeviceCommand.h"
+#include "document/io/CsoundExporter.h"
+#include "document/io/HydrogenLoader.h"
+#include "document/io/LilyPondExporter.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/io/RG21Loader.h"
+#include "document/io/MupExporter.h"
+#include "document/io/MusicXmlExporter.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "gui/application/RosegardenApplication.h"
+#include "gui/dialogs/AddTracksDialog.h"
+#include "gui/dialogs/AudioManagerDialog.h"
+#include "gui/dialogs/AudioPluginDialog.h"
+#include "gui/dialogs/AudioSplitDialog.h"
+#include "gui/dialogs/BeatsBarsDialog.h"
+#include "gui/dialogs/CompositionLengthDialog.h"
+#include "gui/dialogs/ConfigureDialog.h"
+#include "gui/dialogs/CountdownDialog.h"
+#include "gui/dialogs/DocumentConfigureDialog.h"
+#include "gui/dialogs/FileMergeDialog.h"
+#include "gui/dialogs/IdentifyTextCodecDialog.h"
+#include "gui/dialogs/IntervalDialog.h"
+#include "gui/dialogs/LilyPondOptionsDialog.h"
+#include "gui/dialogs/ManageMetronomeDialog.h"
+#include "gui/dialogs/QuantizeDialog.h"
+#include "gui/dialogs/RescaleDialog.h"
+#include "gui/dialogs/SplitByPitchDialog.h"
+#include "gui/dialogs/SplitByRecordingSrcDialog.h"
+#include "gui/dialogs/TempoDialog.h"
+#include "gui/dialogs/TimeDialog.h"
+#include "gui/dialogs/TimeSignatureDialog.h"
+#include "gui/dialogs/TransportDialog.h"
+#include "gui/editors/parameters/InstrumentParameterBox.h"
+#include "gui/editors/parameters/RosegardenParameterArea.h"
+#include "gui/editors/parameters/SegmentParameterBox.h"
+#include "gui/editors/parameters/TrackParameterBox.h"
+#include "gui/editors/segment/segmentcanvas/CompositionView.h"
+#include "gui/editors/segment/ControlEditorDialog.h"
+#include "gui/editors/segment/MarkerEditor.h"
+#include "gui/editors/segment/PlayListDialog.h"
+#include "gui/editors/segment/PlayList.h"
+#include "gui/editors/segment/segmentcanvas/SegmentEraser.h"
+#include "gui/editors/segment/segmentcanvas/SegmentJoiner.h"
+#include "gui/editors/segment/segmentcanvas/SegmentMover.h"
+#include "gui/editors/segment/segmentcanvas/SegmentPencil.h"
+#include "gui/editors/segment/segmentcanvas/SegmentResizer.h"
+#include "gui/editors/segment/segmentcanvas/SegmentSelector.h"
+#include "gui/editors/segment/segmentcanvas/SegmentSplitter.h"
+#include "gui/editors/segment/segmentcanvas/SegmentToolBox.h"
+#include "gui/editors/segment/TrackLabel.h"
+#include "gui/editors/segment/TriggerSegmentManager.h"
+#include "gui/editors/tempo/TempoView.h"
+#include "gui/general/EditViewBase.h"
+#include "gui/kdeext/KStartupLogo.h"
+#include "gui/kdeext/KTmpStatusMsg.h"
+#include "gui/seqmanager/MidiFilterDialog.h"
+#include "gui/seqmanager/SequenceManager.h"
+#include "gui/seqmanager/SequencerMapper.h"
+#include "gui/studio/AudioMixerWindow.h"
+#include "gui/studio/AudioPlugin.h"
+#include "gui/studio/AudioPluginManager.h"
+#include "gui/studio/AudioPluginOSCGUIManager.h"
+#include "gui/studio/BankEditorDialog.h"
+#include "gui/studio/DeviceManagerDialog.h"
+#include "gui/studio/MidiMixerWindow.h"
+#include "gui/studio/RemapInstrumentDialog.h"
+#include "gui/studio/StudioControl.h"
+#include "gui/studio/SynthPluginManagerDialog.h"
+#include "gui/widgets/CurrentProgressDialog.h"
+#include "gui/widgets/ProgressBar.h"
+#include "gui/widgets/ProgressDialog.h"
+#include "LircClient.h"
+#include "LircCommander.h"
+#include "RosegardenGUIView.h"
+#include "RosegardenIface.h"
+#include "SetWaitCursor.h"
+#include "sound/AudioFile.h"
+#include "sound/AudioFileManager.h"
+#include "sound/MappedCommon.h"
+#include "sound/MappedComposition.h"
+#include "sound/MappedEvent.h"
+#include "sound/MappedStudio.h"
+#include "sound/MidiFile.h"
+#include "sound/PluginIdentifier.h"
+#include "sound/SoundDriver.h"
+#include "StartupTester.h"
+#include <dcopclient.h>
+#include <dcopobject.h>
+#include <dcopref.h>
+#include <kaction.h>
+#include <kconfig.h>
+#include <kdcopactionproxy.h>
+#include <kdockwidget.h>
+#include <kedittoolbar.h>
+#include <kfiledialog.h>
+#include <kglobal.h>
+#include <kinputdialog.h>
+#include <kio/netaccess.h>
+#include <kkeydialog.h>
+#include <klocale.h>
+#include <kmainwindow.h>
+#include <kmessagebox.h>
+#include <kmimetype.h>
+#include <kprocess.h>
+#include <kstatusbar.h>
+#include <kstdaccel.h>
+#include <kstdaction.h>
+#include <kstddirs.h>
+#include <ktempfile.h>
+#include <ktip.h>
+#include <ktoolbar.h>
+#include <kurl.h>
+#include <kxmlguiclient.h>
+#include <qaccel.h>
+#include <qcanvas.h>
+#include <qcstring.h>
+#include <qcursor.h>
+#include <qdatastream.h>
+#include <qdialog.h>
+#include <qdir.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qiconset.h>
+#include <qinputdialog.h>
+#include <qlabel.h>
+#include <qobject.h>
+#include <qobjectlist.h>
+#include <qpixmap.h>
+#include <qpopupmenu.h>
+#include <qpushbutton.h>
+#include <qregexp.h>
+#include <qslider.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qtextcodec.h>
+#include <qtimer.h>
+#include <qvaluevector.h>
+#include <qwidget.h>
+
+#ifdef HAVE_LIBJACK
+#include <jack/jack.h>
+#endif
+
+
+namespace Rosegarden
+{
+
+RosegardenGUIApp::RosegardenGUIApp(bool useSequencer,
+ bool useExistingSequencer,
+ QObject *startupStatusMessageReceiver)
+ : DCOPObject("RosegardenIface"), RosegardenIface(this), KDockMainWindow(0),
+ m_actionsSetup(false),
+ m_fileRecent(0),
+ m_view(0),
+ m_swapView(0),
+ m_mainDockWidget(0),
+ m_dockLeft(0),
+ m_doc(0),
+ m_sequencerProcess(0),
+ m_sequencerCheckedIn(false),
+#ifdef HAVE_LIBJACK
+ m_jackProcess(0),
+#endif
+ m_zoomSlider(0),
+ m_seqManager(0),
+ m_transport(0),
+ m_audioManagerDialog(0),
+ m_originatingJump(false),
+ m_storedLoopStart(0),
+ m_storedLoopEnd(0),
+ m_useSequencer(useSequencer),
+ m_dockVisible(true),
+ m_autoSaveTimer(new QTimer(this)),
+ m_clipboard(new Clipboard),
+ m_playList(0),
+ m_deviceManager(0),
+ m_synthManager(0),
+ m_audioMixer(0),
+ m_midiMixer(0),
+ m_bankEditor(0),
+ m_markerEditor(0),
+ m_tempoView(0),
+ m_triggerSegmentManager(0),
+#ifdef HAVE_LIBLO
+ m_pluginGUIManager(new AudioPluginOSCGUIManager(this)),
+#endif
+ m_playTimer(new QTimer(this)),
+ m_stopTimer(new QTimer(this)),
+ m_startupTester(0),
+#ifdef HAVE_LIRC
+ m_lircClient(0),
+ m_lircCommander(0),
+#endif
+ m_haveAudioImporter(false),
+ m_firstRun(false),
+ m_parameterArea(0)
+{
+ m_myself = this;
+
+
+ if (startupStatusMessageReceiver) {
+ QObject::connect(this, SIGNAL(startupStatusMessage(QString)),
+ startupStatusMessageReceiver,
+ SLOT(slotShowStatusMessage(QString)));
+ }
+
+ // Try to start the sequencer
+ //
+ if (m_useSequencer) {
+
+#ifdef HAVE_LIBJACK
+#define OFFER_JACK_START_OPTION 1
+#ifdef OFFER_JACK_START_OPTION
+ // First we check if jackd is running allready
+
+ std::string jackClientName = "rosegarden";
+
+ // attempt connection to JACK server
+ //
+ jack_client_t* testJackClient;
+ testJackClient = jack_client_new(jackClientName.c_str());
+ if (testJackClient == 0 ) {
+
+ // If no connection to JACK
+ // try to launch JACK - if the configuration wants us to.
+ if (!launchJack()) {
+ KStartupLogo::hideIfStillThere();
+ KMessageBox::error(this, i18n("Attempted to launch JACK audio daemon failed. Audio will be disabled.\nPlease check configuration (Settings -> Configure Rosegarden -> Audio -> Startup)\n and restart."));
+ }
+ } else {
+ //this client was just for testing
+ jack_client_close(testJackClient);
+ }
+#endif // OFFER_JACK_START_OPTION
+#endif // HAVE_LIBJACK
+
+ emit startupStatusMessage(i18n("Starting sequencer..."));
+ launchSequencer(useExistingSequencer);
+
+ } else
+ RG_DEBUG << "RosegardenGUIApp : don't use sequencer\n";
+
+ // Plugin manager
+ //
+ emit startupStatusMessage(i18n("Initializing plugin manager..."));
+ m_pluginManager = new AudioPluginManager();
+
+ // call inits to invoke all other construction parts
+ //
+ emit startupStatusMessage(i18n("Initializing view..."));
+ initStatusBar();
+ setupActions();
+ iFaceDelayedInit(this);
+ initZoomToolbar();
+
+ QPixmap dummyPixmap; // any icon will do
+ m_mainDockWidget = createDockWidget("Rosegarden MainDockWidget", dummyPixmap, 0L, "main_dock_widget");
+ // allow others to dock to the left and right sides only
+ m_mainDockWidget->setDockSite(KDockWidget::DockLeft | KDockWidget::DockRight);
+ // forbit docking abilities of m_mainDockWidget itself
+ m_mainDockWidget->setEnableDocking(KDockWidget::DockNone);
+ setView(m_mainDockWidget); // central widget in a KDE mainwindow
+ setMainDockWidget(m_mainDockWidget); // master dockwidget
+
+ m_dockLeft = createDockWidget("params dock", dummyPixmap, 0L,
+ i18n("Special 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)));
+
+ stateChanged("parametersbox_closed", KXMLGUIClient::StateReverse);
+
+ RosegardenGUIDoc* doc = new RosegardenGUIDoc(this, m_pluginManager);
+
+ m_parameterArea = new RosegardenParameterArea(m_dockLeft);
+ m_dockLeft->setWidget(m_parameterArea);
+
+ // Populate the parameter-box area with the respective
+ // parameter box widgets.
+
+ m_segmentParameterBox = new SegmentParameterBox(doc, m_parameterArea);
+ m_parameterArea->addRosegardenParameterBox(m_segmentParameterBox);
+ m_trackParameterBox = new TrackParameterBox(doc, m_parameterArea);
+ m_parameterArea->addRosegardenParameterBox(m_trackParameterBox);
+ m_instrumentParameterBox = new InstrumentParameterBox(doc, m_parameterArea);
+ m_parameterArea->addRosegardenParameterBox(m_instrumentParameterBox);
+
+ // Lookup the configuration parameter that specifies the default
+ // arrangement, and instantiate it.
+
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+ m_parameterArea->setArrangement((RosegardenParameterArea::Arrangement)
+ kapp->config()->readUnsignedNumEntry("sidebarstyle",
+ RosegardenParameterArea::CLASSIC_STYLE));
+
+ m_dockLeft->update();
+
+ connect(m_instrumentParameterBox,
+ SIGNAL(selectPlugin(QWidget *, InstrumentId, int)),
+ this,
+ SLOT(slotShowPluginDialog(QWidget *, InstrumentId, int)));
+
+ connect(m_instrumentParameterBox,
+ SIGNAL(showPluginGUI(InstrumentId, int)),
+ this,
+ SLOT(slotShowPluginGUI(InstrumentId, int)));
+
+ // relay this through our own signal so that others can use it too
+ connect(m_instrumentParameterBox,
+ SIGNAL(instrumentParametersChanged(InstrumentId)),
+ this,
+ SIGNAL(instrumentParametersChanged(InstrumentId)));
+
+ connect(this,
+ SIGNAL(instrumentParametersChanged(InstrumentId)),
+ m_instrumentParameterBox,
+ SLOT(slotInstrumentParametersChanged(InstrumentId)));
+
+ connect(this,
+ SIGNAL(pluginSelected(InstrumentId, int, int)),
+ m_instrumentParameterBox,
+ SLOT(slotPluginSelected(InstrumentId, int, int)));
+
+ connect(this,
+ SIGNAL(pluginBypassed(InstrumentId, int, bool)),
+ m_instrumentParameterBox,
+ SLOT(slotPluginBypassed(InstrumentId, int, bool)));
+
+ // Load the initial document (this includes doc's own autoload)
+ //
+ setDocument(doc);
+
+ emit startupStatusMessage(i18n("Starting sequence manager..."));
+
+ // transport is created by setupActions()
+ m_seqManager = new SequenceManager(m_doc, getTransport());
+
+ if (m_useSequencer) {
+ // Check the sound driver status and warn the user of any
+ // problems. This warning has to happen early, in case it
+ // affects the ability to load plugins etc from a file on the
+ // command line.
+ m_seqManager->checkSoundDriverStatus(true);
+ }
+
+ if (m_view) {
+ connect(m_seqManager, SIGNAL(controllerDeviceEventReceived(MappedEvent *)),
+ m_view, SLOT(slotControllerDeviceEventReceived(MappedEvent *)));
+ }
+
+ if (m_seqManager->getSoundDriverStatus() & AUDIO_OK) {
+ slotStateChanged("got_audio", true);
+ } else {
+ slotStateChanged("got_audio", false);
+ }
+
+ // If we're restarting the gui then make sure any transient
+ // studio objects are cleared away.
+ emit startupStatusMessage(i18n("Clearing studio data..."));
+ m_seqManager->reinitialiseSequencerStudio();
+
+ // Send the transport control statuses for MMC and JACK
+ //
+ m_seqManager->sendTransportControlStatuses();
+
+ // Now autoload
+ //
+ stateChanged("new_file");
+ stateChanged("have_segments", KXMLGUIClient::StateReverse);
+ stateChanged("have_selection", KXMLGUIClient::StateReverse);
+ slotTestClipboard();
+
+ // Check for lack of MIDI devices and disable Studio options accordingly
+ //
+ if (!m_doc->getStudio().haveMidiDevices())
+ stateChanged("got_midi_devices", KXMLGUIClient::StateReverse);
+
+ emit startupStatusMessage(i18n("Starting..."));
+
+ setupFileDialogSpeedbar();
+ readOptions();
+
+ // All toolbars should be created before this is called
+ setAutoSaveSettings(MainWindowConfigGroup, true);
+
+#ifdef HAVE_LIRC
+
+ try {
+ m_lircClient = new LircClient();
+ } catch (Exception e) {
+ RG_DEBUG << e.getMessage().c_str() << endl;
+ // continue without
+ m_lircClient = 0;
+ }
+ if (m_lircClient) {
+ m_lircCommander = new LircCommander(m_lircClient, this);
+ }
+#endif
+
+ stateChanged("have_project_packager", KXMLGUIClient::StateReverse);
+ stateChanged("have_lilypondview", KXMLGUIClient::StateReverse);
+ QTimer::singleShot(1000, this, SLOT(slotTestStartupTester()));
+}
+
+RosegardenGUIApp::~RosegardenGUIApp()
+{
+ RG_DEBUG << "~RosegardenGUIApp()\n";
+
+ if (getView() &&
+ getView()->getTrackEditor() &&
+ getView()->getTrackEditor()->getSegmentCanvas()) {
+ getView()->getTrackEditor()->getSegmentCanvas()->endAudioPreviewGeneration();
+ }
+
+#ifdef HAVE_LIBLO
+ delete m_pluginGUIManager;
+#endif
+
+ if (isSequencerRunning() && !isSequencerExternal()) {
+ m_sequencerProcess->blockSignals(true);
+ rgapp->sequencerSend("quit()");
+ usleep(300000);
+ delete m_sequencerProcess;
+ }
+
+ delete m_jumpToQuickMarkerAction;
+ delete m_setQuickMarkerAction;
+
+ delete m_transport;
+
+ delete m_seqManager;
+
+#ifdef HAVE_LIRC
+
+ delete m_lircCommander;
+ delete m_lircClient;
+#endif
+
+ delete m_doc;
+ Profiles::getInstance()->dump();
+}
+
+void RosegardenGUIApp::setupActions()
+{
+ // setup File menu
+ // New Window ?
+ KStdAction::openNew (this, SLOT(slotFileNew()), actionCollection());
+ KStdAction::open (this, SLOT(slotFileOpen()), actionCollection());
+ m_fileRecent = KStdAction::openRecent(this,
+ SLOT(slotFileOpenRecent(const KURL&)),
+ actionCollection());
+ KStdAction::save (this, SLOT(slotFileSave()), actionCollection());
+ KStdAction::saveAs(this, SLOT(slotFileSaveAs()), actionCollection());
+ KStdAction::revert(this, SLOT(slotRevertToSaved()), actionCollection());
+ KStdAction::close (this, SLOT(slotFileClose()), actionCollection());
+ KStdAction::print (this, SLOT(slotFilePrint()), actionCollection());
+ KStdAction::printPreview (this, SLOT(slotFilePrintPreview()), actionCollection());
+
+ new KAction(i18n("Import Rosegarden &Project file..."), 0, 0, this,
+ SLOT(slotImportProject()), actionCollection(),
+ "file_import_project");
+
+ new KAction(i18n("Import &MIDI file..."), 0, 0, this,
+ SLOT(slotImportMIDI()), actionCollection(),
+ "file_import_midi");
+
+ new KAction(i18n("Import &Rosegarden 2.1 file..."), 0, 0, this,
+ SLOT(slotImportRG21()), actionCollection(),
+ "file_import_rg21");
+
+ new KAction(i18n("Import &Hydrogen file..."), 0, 0, this,
+ SLOT(slotImportHydrogen()), actionCollection(),
+ "file_import_hydrogen");
+
+ new KAction(i18n("Merge &File..."), 0, 0, this,
+ SLOT(slotMerge()), actionCollection(),
+ "file_merge");
+
+ new KAction(i18n("Merge &MIDI file..."), 0, 0, this,
+ SLOT(slotMergeMIDI()), actionCollection(),
+ "file_merge_midi");
+
+ new KAction(i18n("Merge &Rosegarden 2.1 file..."), 0, 0, this,
+ SLOT(slotMergeRG21()), actionCollection(),
+ "file_merge_rg21");
+
+ new KAction(i18n("Merge &Hydrogen file..."), 0, 0, this,
+ SLOT(slotMergeHydrogen()), actionCollection(),
+ "file_merge_hydrogen");
+
+ new KAction(i18n("Export Rosegarden &Project file..."), 0, 0, this,
+ SLOT(slotExportProject()), actionCollection(),
+ "file_export_project");
+
+ new KAction(i18n("Export &MIDI file..."), 0, 0, this,
+ SLOT(slotExportMIDI()), actionCollection(),
+ "file_export_midi");
+
+ new KAction(i18n("Export &LilyPond file..."), 0, 0, this,
+ SLOT(slotExportLilyPond()), actionCollection(),
+ "file_export_lilypond");
+
+ new KAction(i18n("Export Music&XML file..."), 0, 0, this,
+ SLOT(slotExportMusicXml()), actionCollection(),
+ "file_export_musicxml");
+
+ new KAction(i18n("Export &Csound score file..."), 0, 0, this,
+ SLOT(slotExportCsound()), actionCollection(),
+ "file_export_csound");
+
+ new KAction(i18n("Export M&up file..."), 0, 0, this,
+ SLOT(slotExportMup()), actionCollection(),
+ "file_export_mup");
+
+ 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");
+
+ new KAction(i18n("Play&list"), 0, 0, this,
+ SLOT(slotPlayList()), actionCollection(),
+ "file_show_playlist");
+
+ KStdAction::quit (this, SLOT(slotQuit()), actionCollection());
+
+ // help menu
+ new KAction(i18n("Rosegarden &Tutorial"), 0, 0, this,
+ SLOT(slotTutorial()), actionCollection(),
+ "tutorial");
+
+ new KAction(i18n("&Bug Reporting Guidelines"), 0, 0, this,
+ SLOT(slotBugGuidelines()), actionCollection(),
+ "guidelines");
+
+ // setup edit menu
+ KStdAction::cut (this, SLOT(slotEditCut()), actionCollection());
+ KStdAction::copy (this, SLOT(slotEditCopy()), actionCollection());
+ KStdAction::paste (this, SLOT(slotEditPaste()), actionCollection());
+
+ //
+ // undo/redo actions are special in that they are connected to
+ // slots later on, when the current document is set up - see
+ // MultiViewCommandHistory::attachView
+ //
+ new KToolBarPopupAction(i18n("Und&o"),
+ "undo",
+ KStdAccel::shortcut(KStdAccel::Undo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Undo));
+
+ new KToolBarPopupAction(i18n("Re&do"),
+ "redo",
+ KStdAccel::shortcut(KStdAccel::Redo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Redo));
+ /////
+
+
+
+ // setup Settings menu
+ //
+ m_viewToolBar = KStdAction::showToolbar (this, SLOT(slotToggleToolBar()), actionCollection(),
+ "show_stock_toolbar");
+
+ m_viewToolsToolBar = new KToggleAction(i18n("Show T&ools Toolbar"), 0, this,
+ SLOT(slotToggleToolsToolBar()), actionCollection(),
+ "show_tools_toolbar");
+
+ m_viewTracksToolBar = new KToggleAction(i18n("Show Trac&ks Toolbar"), 0, this,
+ SLOT(slotToggleTracksToolBar()), actionCollection(),
+ "show_tracks_toolbar");
+
+ m_viewEditorsToolBar = new KToggleAction(i18n("Show &Editors Toolbar"), 0, this,
+ SLOT(slotToggleEditorsToolBar()), actionCollection(),
+ "show_editors_toolbar");
+
+ m_viewTransportToolBar = new KToggleAction(i18n("Show Trans&port Toolbar"), 0, this,
+ SLOT(slotToggleTransportToolBar()), actionCollection(),
+ "show_transport_toolbar");
+
+ m_viewZoomToolBar = new KToggleAction(i18n("Show &Zoom Toolbar"), 0, this,
+ SLOT(slotToggleZoomToolBar()), actionCollection(),
+ "show_zoom_toolbar");
+
+ m_viewStatusBar = KStdAction::showStatusbar(this, SLOT(slotToggleStatusBar()),
+ actionCollection(), "show_status_bar");
+
+ m_viewTransport = new KToggleAction(i18n("Show Tra&nsport"), Key_T, this,
+ SLOT(slotToggleTransport()),
+ actionCollection(),
+ "show_transport");
+
+ m_viewTrackLabels = new KToggleAction(i18n("Show Track &Labels"), 0, this,
+ SLOT(slotToggleTrackLabels()),
+ actionCollection(),
+ "show_tracklabels");
+
+ m_viewRulers = new KToggleAction(i18n("Show Playback Position R&uler"), 0, this,
+ SLOT(slotToggleRulers()),
+ actionCollection(),
+ "show_rulers");
+
+ m_viewTempoRuler = new KToggleAction(i18n("Show Te&mpo Ruler"), 0, this,
+ SLOT(slotToggleTempoRuler()),
+ actionCollection(),
+ "show_tempo_ruler");
+
+ m_viewChordNameRuler = new KToggleAction(i18n("Show Cho&rd Name Ruler"), 0, this,
+ SLOT(slotToggleChordNameRuler()),
+ actionCollection(),
+ "show_chord_name_ruler");
+
+
+ m_viewPreviews = new KToggleAction(i18n("Show Segment Pre&views"), 0, this,
+ SLOT(slotTogglePreviews()),
+ actionCollection(),
+ "show_previews");
+
+ new KAction(i18n("Show Special &Parameters"), Key_P, this,
+ SLOT(slotDockParametersBack()),
+ actionCollection(),
+ "show_inst_segment_parameters");
+
+ KStdAction::tipOfDay( this, SLOT( slotShowTip() ), actionCollection() );
+
+ // Standard Actions
+ //
+ KStdAction::saveOptions(this,
+ SLOT(slotSaveOptions()),
+ actionCollection());
+
+ KStdAction::preferences(this,
+ SLOT(slotConfigure()),
+ actionCollection());
+
+ KStdAction::keyBindings(this,
+ SLOT(slotEditKeys()),
+ actionCollection());
+
+ KStdAction::configureToolbars(this,
+ SLOT(slotEditToolbars()),
+ actionCollection());
+
+ KRadioAction *action = 0;
+
+ // Create the select icon
+ //
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QCanvasPixmap pixmap(pixmapDir + "/toolbar/select.xpm");
+ QIconSet icon = QIconSet(pixmap);
+
+ // TODO : add some shortcuts here
+ action = new KRadioAction(i18n("&Select and Edit"), icon, Key_F2,
+ this, SLOT(slotPointerSelected()),
+ actionCollection(), "select");
+ action->setExclusiveGroup("segmenttools");
+
+ action = new KRadioAction(i18n("&Draw"), "pencil", Key_F3,
+ this, SLOT(slotDrawSelected()),
+ actionCollection(), "draw");
+ action->setExclusiveGroup("segmenttools");
+
+ action = new KRadioAction(i18n("&Erase"), "eraser", Key_F4,
+ this, SLOT(slotEraseSelected()),
+ actionCollection(), "erase");
+ action->setExclusiveGroup("segmenttools");
+
+ action = new KRadioAction(i18n("&Move"), "move", Key_F5,
+ this, SLOT(slotMoveSelected()),
+ actionCollection(), "move");
+ action->setExclusiveGroup("segmenttools");
+
+ pixmap.load(pixmapDir + "/toolbar/resize.xpm");
+ icon = QIconSet(pixmap);
+ action = new KRadioAction(i18n("&Resize"), icon, Key_F6,
+ this, SLOT(slotResizeSelected()),
+ actionCollection(), "resize");
+ action->setExclusiveGroup("segmenttools");
+
+ pixmap.load(pixmapDir + "/toolbar/split.xpm");
+ icon = QIconSet(pixmap);
+ action = new KRadioAction(i18n("&Split"), icon, Key_F7,
+ this, SLOT(slotSplitSelected()),
+ actionCollection(), "split");
+ action->setExclusiveGroup("segmenttools");
+
+ pixmap.load(pixmapDir + "/toolbar/join.xpm");
+ icon = QIconSet(pixmap);
+ action = new KRadioAction(i18n("&Join"), icon, 0,
+ this, SLOT(slotJoinSelected()),
+ actionCollection(), "join");
+ action->setExclusiveGroup("segmenttools");
+
+
+ new KAction(i18n("&Harmonize"), 0, this,
+ SLOT(slotHarmonizeSelection()), actionCollection(),
+ "harmonize_selection");
+
+ pixmap.load(pixmapDir + "/toolbar/event-insert-timesig.png");
+ icon = QIconSet(pixmap);
+ new KAction(AddTimeSignatureCommand::getGlobalName(),
+ icon, 0,
+ this, SLOT(slotEditTimeSignature()),
+ actionCollection(), "add_time_signature");
+
+ new KAction(i18n("Open Tempo and Time Signature Editor"), 0, this,
+ SLOT(slotEditTempos()), actionCollection(), "edit_tempos");
+
+ //
+ // Edit menu
+ //
+ new KAction(i18n("Cut Range"), Key_X + CTRL + SHIFT, this,
+ SLOT(slotCutRange()), actionCollection(),
+ "cut_range");
+
+ new KAction(i18n("Copy Range"), Key_C + CTRL + SHIFT, this,
+ SLOT(slotCopyRange()), actionCollection(),
+ "copy_range");
+
+ new KAction(i18n("Paste Range"), Key_V + CTRL + SHIFT, this,
+ SLOT(slotPasteRange()), actionCollection(),
+ "paste_range");
+/*
+ new KAction(i18n("Delete Range"), Key_Delete + SHIFT, this,
+ SLOT(slotDeleteRange()), actionCollection(),
+ "delete_range");
+*/
+ new KAction(i18n("Insert Range..."), Key_Insert + SHIFT, this,
+ SLOT(slotInsertRange()), actionCollection(),
+ "insert_range");
+
+ new KAction(i18n("De&lete"), Key_Delete, this,
+ SLOT(slotDeleteSelectedSegments()), actionCollection(),
+ "delete");
+
+ new KAction(i18n("Select &All Segments"), Key_A + CTRL, this,
+ SLOT(slotSelectAll()), actionCollection(),
+ "select_all");
+
+ pixmap.load(pixmapDir + "/toolbar/event-insert-tempo.png");
+ icon = QIconSet(pixmap);
+ new KAction(AddTempoChangeCommand::getGlobalName(),
+ icon, 0,
+ this, SLOT(slotEditTempo()),
+ actionCollection(), "add_tempo");
+
+ new KAction(ChangeCompositionLengthCommand::getGlobalName(),
+ 0,
+ this, SLOT(slotChangeCompositionLength()),
+ actionCollection(), "change_composition_length");
+
+ new KAction(i18n("Edit Mar&kers..."), Key_K + CTRL, this,
+ SLOT(slotEditMarkers()),
+ actionCollection(), "edit_markers");
+
+ new KAction(i18n("Edit Document P&roperties..."), 0, this,
+ SLOT(slotEditDocumentProperties()),
+ actionCollection(), "edit_doc_properties");
+
+
+ //
+ // Segments menu
+ //
+ new KAction(i18n("Open in &Default Editor"), Key_Return, this,
+ SLOT(slotEdit()), actionCollection(),
+ "edit_default");
+
+ pixmap.load(pixmapDir + "/toolbar/matrix.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Open in Matri&x Editor"), icon, Key_M, this,
+ SLOT(slotEditInMatrix()), actionCollection(),
+ "edit_matrix");
+
+ pixmap.load(pixmapDir + "/toolbar/matrix-percussion.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Open in &Percussion Matrix Editor"), icon, Key_D, this,
+ SLOT(slotEditInPercussionMatrix()), actionCollection(),
+ "edit_percussion_matrix");
+
+ pixmap.load(pixmapDir + "/toolbar/notation.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Open in &Notation Editor"), icon, Key_N, this,
+ SLOT(slotEditAsNotation()), actionCollection(),
+ "edit_notation");
+
+ pixmap.load(pixmapDir + "/toolbar/eventlist.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Open in &Event List Editor"), icon, Key_E, this,
+ SLOT(slotEditInEventList()), actionCollection(),
+ "edit_event_list");
+
+ pixmap.load(pixmapDir + "/toolbar/quantize.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("&Quantize..."), icon, Key_Equal, this,
+ SLOT(slotQuantizeSelection()), actionCollection(),
+ "quantize_selection");
+
+ new KAction(SegmentLabelCommand::getGlobalName(),
+ 0,
+ this, SLOT(slotRelabelSegments()),
+ actionCollection(), "relabel_segment");
+
+ new KAction(SegmentTransposeCommand::getGlobalName(),
+ 0,
+ this, SLOT(slotTransposeSegments()),
+ actionCollection(), "transpose");
+
+ new KAction(i18n("Repeat Last Quantize"), Key_Plus, this,
+ SLOT(slotRepeatQuantizeSelection()), actionCollection(),
+ "repeat_quantize");
+
+ new KAction(SegmentRescaleCommand::getGlobalName(), 0, this,
+ SLOT(slotRescaleSelection()), actionCollection(),
+ "rescale");
+
+ new KAction(SegmentAutoSplitCommand::getGlobalName(), 0, this,
+ SLOT(slotAutoSplitSelection()), actionCollection(),
+ "auto_split");
+
+ new KAction(SegmentSplitByPitchCommand::getGlobalName(), 0, this,
+ SLOT(slotSplitSelectionByPitch()), actionCollection(),
+ "split_by_pitch");
+
+ new KAction(SegmentSplitByRecordingSrcCommand::getGlobalName(), 0, this,
+ SLOT(slotSplitSelectionByRecordedSrc()), actionCollection(),
+ "split_by_recording");
+
+ new KAction(i18n("Split at Time..."), 0, this,
+ SLOT(slotSplitSelectionAtTime()), actionCollection(),
+ "split_at_time");
+
+ new KAction(i18n("Jog &Left"), Key_Left + ALT, this,
+ SLOT(slotJogLeft()), actionCollection(),
+ "jog_left");
+
+ new KAction(i18n("Jog &Right"), Key_Right + ALT, this,
+ SLOT(slotJogRight()), actionCollection(),
+ "jog_right");
+
+ new KAction(i18n("Set Start Time..."), 0, this,
+ SLOT(slotSetSegmentStartTimes()), actionCollection(),
+ "set_segment_start");
+
+ new KAction(i18n("Set Duration..."), 0, this,
+ SLOT(slotSetSegmentDurations()), actionCollection(),
+ "set_segment_duration");
+
+ new KAction(SegmentJoinCommand::getGlobalName(),
+ Key_J + CTRL,
+ this, SLOT(slotJoinSegments()),
+ actionCollection(), "join_segments");
+
+ new KAction(i18n("Turn Re&peats into Copies"),
+ 0,
+ this, SLOT(slotRepeatingSegments()),
+ actionCollection(), "repeats_to_real_copies");
+
+ new KAction(i18n("Manage Tri&ggered Segments"), 0,
+ this, SLOT(slotManageTriggerSegments()),
+ actionCollection(), "manage_trigger_segments");
+
+ new KAction(i18n("Set Tempos from &Beat Segment"), 0, this,
+ SLOT(slotGrooveQuantize()), actionCollection(),
+ "groove_quantize");
+
+ new KAction(i18n("Set &Tempo to Audio Segment Duration"), 0, this,
+ SLOT(slotTempoToSegmentLength()), actionCollection(),
+ "set_tempo_to_segment_length");
+
+ pixmap.load(pixmapDir + "/toolbar/manage-audio-segments.xpm");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Manage A&udio Files"), icon,
+ Key_U + CTRL,
+ this, SLOT(slotAudioManager()),
+ actionCollection(), "audio_manager");
+
+ m_viewSegmentLabels = new KToggleAction(i18n("Show Segment Labels"), 0, this,
+ SLOT(slotToggleSegmentLabels()), actionCollection(),
+ "show_segment_labels");
+
+ //
+ // Tracks menu
+ //
+ pixmap.load(pixmapDir + "/toolbar/add_tracks.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Add &Track"), icon, CTRL + Key_T,
+ this, SLOT(slotAddTrack()),
+ actionCollection(), "add_track");
+
+ new KAction(i18n("&Add Tracks..."), 0,
+ this, SLOT(slotAddTracks()),
+ actionCollection(), "add_tracks");
+
+ pixmap.load(pixmapDir + "/toolbar/delete_track.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("D&elete Track"), icon, CTRL + Key_D,
+ this, SLOT(slotDeleteTrack()),
+ actionCollection(), "delete_track");
+
+ pixmap.load(pixmapDir + "/toolbar/move_track_down.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Move Track &Down"), icon, SHIFT + Key_Down,
+ this, SLOT(slotMoveTrackDown()),
+ actionCollection(), "move_track_down");
+
+ pixmap.load(pixmapDir + "/toolbar/move_track_up.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Move Track &Up"), icon, SHIFT + Key_Up,
+ this, SLOT(slotMoveTrackUp()),
+ actionCollection(), "move_track_up");
+
+ new KAction(i18n("Select &Next Track"),
+ Key_Down,
+ this, SLOT(slotTrackDown()),
+ actionCollection(), "select_next_track");
+
+ new KAction(i18n("Select &Previous Track"),
+ Key_Up,
+ this, SLOT(slotTrackUp()),
+ actionCollection(), "select_previous_track");
+
+ new KAction(i18n("Mute or Unmute Track"),
+ Key_U,
+ this, SLOT(slotToggleMutedCurrentTrack()),
+ actionCollection(), "toggle_mute_track");
+
+ new KAction(i18n("Arm or Un-arm Track for Record"),
+ Key_R,
+ this, SLOT(slotToggleRecordCurrentTrack()),
+ actionCollection(), "toggle_arm_track");
+
+ pixmap.load(pixmapDir + "/toolbar/mute-all.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("&Mute all Tracks"), icon, 0,
+ this, SLOT(slotMuteAllTracks()),
+ actionCollection(), "mute_all_tracks");
+
+ pixmap.load(pixmapDir + "/toolbar/un-mute-all.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("&Unmute all Tracks"), icon, 0,
+ this, SLOT(slotUnmuteAllTracks()),
+ actionCollection(), "unmute_all_tracks");
+
+ new KAction(i18n("&Remap Instruments..."), 0, this,
+ SLOT(slotRemapInstruments()),
+ actionCollection(), "remap_instruments");
+
+ //
+ // Studio menu
+ //
+ pixmap.load(pixmapDir + "/toolbar/mixer.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("&Audio Mixer"), icon, 0, this,
+ SLOT(slotOpenAudioMixer()),
+ actionCollection(), "audio_mixer");
+
+ pixmap.load(pixmapDir + "/toolbar/midimixer.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Midi Mi&xer"), icon, 0, this,
+ SLOT(slotOpenMidiMixer()),
+ actionCollection(), "midi_mixer");
+
+ pixmap.load(pixmapDir + "/toolbar/manage-midi-devices.xpm");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Manage MIDI &Devices"), icon, 0, this,
+ SLOT(slotManageMIDIDevices()),
+ actionCollection(), "manage_devices");
+
+ pixmap.load(pixmapDir + "/toolbar/manage-synth-plugins.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Manage S&ynth Plugins"), icon, 0, this,
+ SLOT(slotManageSynths()),
+ actionCollection(), "manage_synths");
+
+ new KAction(i18n("Modify MIDI &Filters"), "filter", 0, this,
+ SLOT(slotModifyMIDIFilters()),
+ actionCollection(), "modify_midi_filters");
+
+ m_enableMIDIrouting = new KToggleAction(i18n("MIDI Thru Routing"), 0, this,
+ SLOT(slotEnableMIDIThruRouting()),
+ actionCollection(), "enable_midi_routing");
+
+ pixmap.load(pixmapDir + "/toolbar/time-musical.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Manage &Metronome"), 0, this,
+ SLOT(slotManageMetronome()),
+ actionCollection(), "manage_metronome");
+
+ new KAction(i18n("&Save Current Document as Default Studio"), 0, this,
+ SLOT(slotSaveDefaultStudio()),
+ actionCollection(), "save_default_studio");
+
+ new KAction(i18n("&Import Default Studio"), 0, this,
+ SLOT(slotImportDefaultStudio()),
+ actionCollection(), "load_default_studio");
+
+ new KAction(i18n("Im&port Studio from File..."), 0, this,
+ SLOT(slotImportStudio()),
+ actionCollection(), "load_studio");
+
+ new KAction(i18n("&Reset MIDI Network"), 0, this,
+ SLOT(slotResetMidiNetwork()),
+ actionCollection(), "reset_midi_network");
+
+ m_setQuickMarkerAction = new KAction(i18n("Set Quick Marker at Playback Position"), 0, CTRL + Key_1, this,
+ SLOT(slotSetQuickMarker()), actionCollection(),
+ "set_quick_marker");
+
+ m_jumpToQuickMarkerAction = new KAction(i18n("Jump to Quick Marker"), 0, Key_1, this,
+ SLOT(slotJumpToQuickMarker()), actionCollection(),
+ "jump_to_quick_marker");
+
+ //
+ // Marker Ruler popup menu
+ //
+// new KAction(i18n("Insert Marker"), 0, 0, this,
+// SLOT(slotInsertMarkerHere()), actionCollection(),
+// "insert_marker_here");
+//
+// new KAction(i18n("Insert Marker at Playback Position"), 0, 0, this,
+// SLOT(slotInsertMarkerAtPointer()), actionCollection(),
+// "insert_marker_at_pointer");
+//
+// new KAction(i18n("Delete Marker"), 0, 0, this,
+// SLOT(slotDeleteMarker()), actionCollection(),
+// "delete_marker");
+
+
+
+ //
+ // Transport menu
+ //
+
+ // Transport controls [rwb]
+ //
+ // We set some default key bindings - with numlock off
+ // use 1 (End) and 3 (Page Down) for Rwd and Ffwd and
+ // 0 (insert) and keypad Enter for Play and Stop
+ //
+ pixmap.load(pixmapDir + "/toolbar/transport-play.png");
+ icon = QIconSet(pixmap);
+ m_playTransport = new KAction(i18n("&Play"), icon, Key_Enter, this,
+ SLOT(slotPlay()), actionCollection(),
+ "play");
+ // Alternative shortcut for Play
+ KShortcut playShortcut = m_playTransport->shortcut();
+ playShortcut.append( KKey(Key_Return + CTRL) );
+ m_playTransport->setShortcut(playShortcut);
+ m_playTransport->setGroup(TransportDialogConfigGroup);
+
+ pixmap.load(pixmapDir + "/toolbar/transport-stop.png");
+ icon = QIconSet(pixmap);
+ m_stopTransport = new KAction(i18n("&Stop"), icon, Key_Insert, this,
+ SLOT(slotStop()), actionCollection(),
+ "stop");
+ m_stopTransport->setGroup(TransportDialogConfigGroup);
+
+ pixmap.load(pixmapDir + "/toolbar/transport-ffwd.png");
+ icon = QIconSet(pixmap);
+ m_ffwdTransport = new KAction(i18n("&Fast Forward"), icon, Key_PageDown,
+ this,
+ SLOT(slotFastforward()), actionCollection(),
+ "fast_forward");
+ m_ffwdTransport->setGroup(TransportDialogConfigGroup);
+
+ pixmap.load(pixmapDir + "/toolbar/transport-rewind.png");
+ icon = QIconSet(pixmap);
+ m_rewindTransport = new KAction(i18n("Re&wind"), icon, Key_End, this,
+ SLOT(slotRewind()), actionCollection(),
+ "rewind");
+ m_rewindTransport->setGroup(TransportDialogConfigGroup);
+
+ pixmap.load(pixmapDir + "/toolbar/transport-record.png");
+ icon = QIconSet(pixmap);
+ m_recordTransport = new KAction(i18n("P&unch in Record"), icon, Key_Space, this,
+ SLOT(slotToggleRecord()), actionCollection(),
+ "recordtoggle");
+ m_recordTransport->setGroup(TransportDialogConfigGroup);
+
+ pixmap.load(pixmapDir + "/toolbar/transport-record.png");
+ icon = QIconSet(pixmap);
+ m_recordTransport = new KAction(i18n("&Record"), icon, 0, this,
+ SLOT(slotRecord()), actionCollection(),
+ "record");
+ m_recordTransport->setGroup(TransportDialogConfigGroup);
+
+ pixmap.load(pixmapDir + "/toolbar/transport-rewind-end.png");
+ icon = QIconSet(pixmap);
+ m_rewindEndTransport = new KAction(i18n("Rewind to &Beginning"), icon, 0, this,
+ SLOT(slotRewindToBeginning()), actionCollection(),
+ "rewindtobeginning");
+ m_rewindEndTransport->setGroup(TransportDialogConfigGroup);
+
+ pixmap.load(pixmapDir + "/toolbar/transport-ffwd-end.png");
+ icon = QIconSet(pixmap);
+ m_ffwdEndTransport = new KAction(i18n("Fast Forward to &End"), icon, 0, this,
+ SLOT(slotFastForwardToEnd()), actionCollection(),
+ "fastforwardtoend");
+ m_ffwdEndTransport->setGroup(TransportDialogConfigGroup);
+
+ pixmap.load(pixmapDir + "/toolbar/transport-tracking.png");
+ icon = QIconSet(pixmap);
+ (new KToggleAction(i18n("Scro&ll to Follow Playback"), icon, Key_Pause, this,
+ SLOT(slotToggleTracking()), actionCollection(),
+ "toggle_tracking"))->setChecked(true);
+
+ pixmap.load(pixmapDir + "/toolbar/transport-panic.png");
+ icon = QIconSet(pixmap);
+ new KAction( i18n("Panic"), icon, Key_P + CTRL + ALT, this, SLOT(slotPanic()),
+ actionCollection(), "panic");
+
+ // DEBUG FACILITY
+ new KAction(i18n("Segment Debug Dump "), 0, this,
+ SLOT(slotDebugDump()), actionCollection(),
+ "debug_dump_segments");
+
+ // create main gui
+ //
+ createGUI("rosegardenui.rc", false);
+
+ createAndSetupTransport();
+
+ // transport toolbar is hidden by default - TODO : this should be in options
+ //
+ //toolBar("Transport Toolbar")->hide();
+
+ QPopupMenu* setTrackInstrumentMenu = dynamic_cast<QPopupMenu*>(factory()->container("set_track_instrument", this));
+
+ if (setTrackInstrumentMenu) {
+ connect(setTrackInstrumentMenu, SIGNAL(aboutToShow()),
+ this, SLOT(slotPopulateTrackInstrumentPopup()));
+ } else {
+ RG_DEBUG << "RosegardenGUIApp::setupActions() : couldn't find set_track_instrument menu - check rosegardenui.rcn\n";
+ }
+
+ setRewFFwdToAutoRepeat();
+}
+
+void RosegardenGUIApp::setRewFFwdToAutoRepeat()
+{
+ QWidget* transportToolbar = factory()->container("Transport Toolbar", this);
+
+ if (transportToolbar) {
+ QObjectList *l = transportToolbar->queryList();
+ QObjectListIt it(*l); // iterate over the buttons
+ QObject *obj;
+
+ while ( (obj = it.current()) != 0 ) {
+ // for each found object...
+ ++it;
+ // RG_DEBUG << "obj name : " << obj->name() << endl;
+ QString objName = obj->name();
+
+ if (objName.endsWith("rewind") || objName.endsWith("fast_forward")) {
+ QButton* btn = dynamic_cast<QButton*>(obj);
+ if (!btn) {
+ RG_DEBUG << "Very strange - found widgets in transport_toolbar which aren't buttons\n";
+
+ continue;
+ }
+ btn->setAutoRepeat(true);
+ }
+
+
+ }
+ delete l;
+
+ } else {
+ RG_DEBUG << "transportToolbar == 0\n";
+ }
+
+}
+
+void RosegardenGUIApp::initZoomToolbar()
+{
+ KToolBar *zoomToolbar = toolBar("Zoom Toolbar");
+ if (!zoomToolbar) {
+ RG_DEBUG << "RosegardenGUIApp::initZoomToolbar() : "
+ << "zoom toolbar not found" << endl;
+ return ;
+ }
+
+ new QLabel(i18n(" Zoom: "), zoomToolbar, "kde toolbar widget");
+
+ 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 };
+
+ for (unsigned int i = 0; i < sizeof(factors) / sizeof(factors[0]); ++i) {
+ zoomSizes.push_back(duration44 / (defaultBarWidth44 * factors[i]));
+ }
+
+ // zoom labels
+ QString minZoom = QString("%1%").arg(factors[0] * 100.0);
+ QString maxZoom = QString("%1%").arg(factors[(sizeof(factors) / sizeof(factors[0])) - 1] * 100.0);
+
+ m_zoomSlider = new ZoomSlider<double>
+ (zoomSizes, -1, QSlider::Horizontal, zoomToolbar, "kde toolbar widget");
+ m_zoomSlider->setTracking(true);
+ m_zoomSlider->setFocusPolicy(QWidget::NoFocus);
+ m_zoomLabel = new QLabel(minZoom, zoomToolbar, "kde toolbar widget");
+ m_zoomLabel->setIndent(10);
+
+ connect(m_zoomSlider, SIGNAL(valueChanged(int)),
+ this, SLOT(slotChangeZoom(int)));
+
+ // set initial zoom - we might want to make this a config option
+ // m_zoomSlider->setToDefault();
+
+}
+
+void RosegardenGUIApp::initStatusBar()
+{
+ KTmpStatusMsg::setDefaultMsg("");
+ statusBar()->insertItem(KTmpStatusMsg::getDefaultMsg(),
+ KTmpStatusMsg::getDefaultId(), 1);
+ statusBar()->setItemAlignment(KTmpStatusMsg::getDefaultId(),
+ AlignLeft | AlignVCenter);
+
+ m_progressBar = new ProgressBar(100, true, statusBar());
+ // m_progressBar->setMinimumWidth(100);
+ m_progressBar->setFixedWidth(60);
+ m_progressBar->setFixedHeight(18);
+ m_progressBar->setTextEnabled(false);
+ statusBar()->addWidget(m_progressBar);
+}
+
+void RosegardenGUIApp::initView()
+{
+ ////////////////////////////////////////////////////////////////////
+ // create the main widget here that is managed by KTMainWindow's view-region and
+ // connect the widget to your document to display document contents.
+
+ RG_DEBUG << "RosegardenGUIApp::initView()" << endl;
+
+ Composition &comp = m_doc->getComposition();
+
+ // Ensure that the start and end markers for the piece are set
+ // to something reasonable
+ //
+ if (comp.getStartMarker() == 0 &&
+ comp.getEndMarker() == 0) {
+ int endMarker = comp.getBarRange(100 + comp.getNbBars()).second;
+ comp.setEndMarker(endMarker);
+ }
+
+ m_swapView = new RosegardenGUIView(m_viewTrackLabels->isChecked(),
+ m_segmentParameterBox,
+ m_instrumentParameterBox,
+ m_trackParameterBox, this);
+
+ // Connect up this signal so that we can force tool mode
+ // changes from the view
+ connect(m_swapView, SIGNAL(activateTool(QString)),
+ this, SLOT(slotActivateTool(QString)));
+
+ connect(m_swapView,
+ SIGNAL(segmentsSelected(const SegmentSelection &)),
+ SIGNAL(segmentsSelected(const SegmentSelection &)));
+
+ connect(m_swapView,
+ SIGNAL(addAudioFile(AudioFileId)),
+ SLOT(slotAddAudioFile(AudioFileId)));
+
+ connect(m_swapView, SIGNAL(toggleSolo(bool)), SLOT(slotToggleSolo(bool)));
+
+ m_doc->attachView(m_swapView);
+
+ m_mainDockWidget->setWidget(m_swapView);
+
+ // setCentralWidget(m_swapView);
+ setCaption(m_doc->getTitle());
+
+
+ // Transport setup
+ //
+ std::string transportMode = m_doc->getConfiguration().
+ get
+ <String>
+ (DocumentConfiguration::TransportMode);
+
+
+ slotEnableTransport(true);
+
+ // and the time signature
+ //
+ getTransport()->setTimeSignature(comp.getTimeSignatureAt(comp.getPosition()));
+
+ // set the tempo in the transport
+ //
+ getTransport()->setTempo(comp.getCurrentTempo());
+
+ // bring the transport to the front
+ //
+ getTransport()->raise();
+
+ // set the play metronome button
+ getTransport()->MetronomeButton()->setOn(comp.usePlayMetronome());
+
+ // Set the solo button
+ getTransport()->SoloButton()->setOn(comp.isSolo());
+
+ // set the transport mode found in the configuration
+ getTransport()->setNewMode(transportMode);
+
+ // set the pointer position
+ //
+ slotSetPointerPosition(m_doc->getComposition().getPosition());
+
+ // make sure we show
+ //
+ RosegardenGUIView *oldView = m_view;
+ m_view = m_swapView;
+
+ connect(m_view, SIGNAL(stateChange(QString, bool)),
+ this, SLOT (slotStateChanged(QString, bool)));
+
+ connect(m_view, SIGNAL(instrumentParametersChanged(InstrumentId)),
+ this, SIGNAL(instrumentParametersChanged(InstrumentId)));
+
+ // We only check for the SequenceManager to make sure
+ // we're not on the first pass though - we don't want
+ // to send these toggles twice on initialisation.
+ //
+ // Clunky but we just about get away with it for the
+ // moment.
+ //
+ if (m_seqManager != 0) {
+ slotToggleChordNameRuler();
+ slotToggleRulers();
+ slotToggleTempoRuler();
+ slotTogglePreviews();
+ slotToggleSegmentLabels();
+
+ // Reset any loop on the sequencer
+ //
+ try {
+ if (isUsingSequencer())
+ m_seqManager->setLoop(0, 0);
+ stateChanged("have_range", KXMLGUIClient::StateReverse);
+ } catch (QString s) {
+ KStartupLogo::hideIfStillThere();
+ CurrentProgressDialog::freeze();
+ KMessageBox::error(this, s);
+ CurrentProgressDialog::thaw();
+ }
+
+ connect(m_seqManager, SIGNAL(controllerDeviceEventReceived(MappedEvent *)),
+ m_view, SLOT(slotControllerDeviceEventReceived(MappedEvent *)));
+ }
+
+ // delete m_playList;
+ // m_playList = 0;
+
+ delete m_deviceManager;
+ m_deviceManager = 0;
+
+ delete m_synthManager;
+ m_synthManager = 0;
+
+ delete m_audioMixer;
+ m_audioMixer = 0;
+
+ delete m_bankEditor;
+ m_bankEditor = 0;
+
+ delete m_markerEditor;
+ m_markerEditor = 0;
+
+ delete m_tempoView;
+ m_tempoView = 0;
+
+ delete m_triggerSegmentManager;
+ m_triggerSegmentManager = 0;
+
+ delete oldView;
+
+ // set the highlighted track
+ m_view->slotSelectTrackSegments(comp.getSelectedTrack());
+
+ // play tracking on in the editor by default: turn off if need be
+ KToggleAction *trackingAction = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("toggle_tracking"));
+ if (trackingAction && !trackingAction->isChecked()) {
+ m_view->getTrackEditor()->slotToggleTracking();
+ }
+
+ m_view->show();
+
+ connect(m_view->getTrackEditor()->getSegmentCanvas(),
+ SIGNAL(showContextHelp(const QString &)),
+ this,
+ SLOT(slotShowToolHelp(const QString &)));
+
+ // We have to do this to make sure that the 2nd call ("select")
+ // actually has any effect. Activating the same radio action
+ // doesn't work the 2nd time (like pressing down the same radio
+ // button twice - it doesn't have any effect), so if you load two
+ // files in a row, on the 2nd file a new SegmentCanvas will be
+ // created but its tool won't be set, even though it will appear
+ // to be selected.
+ //
+ actionCollection()->action("move")->activate();
+ if (m_doc->getComposition().getNbSegments() > 0)
+ actionCollection()->action("select")->activate();
+ else
+ actionCollection()->action("draw")->activate();
+
+ int zoomLevel = m_doc->getConfiguration().
+ get
+ <Int>
+ (DocumentConfiguration::ZoomLevel);
+
+ m_zoomSlider->setSize(double(zoomLevel) / 1000.0);
+ slotChangeZoom(zoomLevel);
+
+ //slotChangeZoom(int(m_zoomSlider->getCurrentSize()));
+
+ stateChanged("new_file");
+
+ ProgressDialog::processEvents();
+
+ if (m_viewChordNameRuler->isChecked()) {
+ SetWaitCursor swc;
+ m_view->initChordNameRuler();
+ } else {
+ m_view->initChordNameRuler();
+ }
+}
+
+void RosegardenGUIApp::setDocument(RosegardenGUIDoc* newDocument)
+{
+ if (m_doc == newDocument)
+ return ;
+
+ emit documentAboutToChange();
+ kapp->processEvents(); // to make sure all opened dialogs (mixer, midi devices...) are closed
+
+ // Take care of all subparts which depend on the document
+
+ // Caption
+ //
+ QString caption = kapp->caption();
+ setCaption(caption + ": " + newDocument->getTitle());
+
+ // // reset AudioManagerDialog
+ // //
+ // delete m_audioManagerDialog; // TODO : replace this with a connection to documentAboutToChange() sig.
+ // m_audioManagerDialog = 0;
+
+ RosegardenGUIDoc* oldDoc = m_doc;
+
+ m_doc = newDocument;
+
+ if (m_seqManager) // when we're called at startup, the seq. man. isn't created yet
+ m_seqManager->setDocument(m_doc);
+
+ if (m_markerEditor)
+ m_markerEditor->setDocument(m_doc);
+ if (m_tempoView) {
+ delete m_tempoView;
+ m_tempoView = 0;
+ }
+ if (m_triggerSegmentManager)
+ m_triggerSegmentManager->setDocument(m_doc);
+
+ m_trackParameterBox->setDocument(m_doc);
+ m_segmentParameterBox->setDocument(m_doc);
+ m_instrumentParameterBox->setDocument(m_doc);
+
+#ifdef HAVE_LIBLO
+
+ if (m_pluginGUIManager) {
+ m_pluginGUIManager->stopAllGUIs();
+ m_pluginGUIManager->setStudio(&m_doc->getStudio());
+ }
+#endif
+
+ if (getView() &&
+ getView()->getTrackEditor() &&
+ getView()->getTrackEditor()->getSegmentCanvas()) {
+ getView()->getTrackEditor()->getSegmentCanvas()->endAudioPreviewGeneration();
+ }
+
+ // this will delete all edit views
+ //
+ delete oldDoc;
+
+ // connect needed signals
+ //
+ connect(m_segmentParameterBox, SIGNAL(documentModified()),
+ m_doc, SLOT(slotDocumentModified()));
+
+ connect(m_doc, SIGNAL(pointerPositionChanged(timeT)),
+ this, SLOT(slotSetPointerPosition(timeT)));
+
+ connect(m_doc, SIGNAL(documentModified(bool)),
+ this, SLOT(slotDocumentModified(bool)));
+
+ connect(m_doc, SIGNAL(loopChanged(timeT, timeT)),
+ this, SLOT(slotSetLoop(timeT, timeT)));
+
+ m_doc->getCommandHistory()->attachView(actionCollection());
+
+ connect(m_doc->getCommandHistory(), SIGNAL(commandExecuted()),
+ SLOT(update()));
+ connect(m_doc->getCommandHistory(), SIGNAL(commandExecuted()),
+ SLOT(slotTestClipboard()));
+
+ // connect and start the autosave timer
+ connect(m_autoSaveTimer, SIGNAL(timeout()), this, SLOT(slotAutoSave()));
+ m_autoSaveTimer->start(m_doc->getAutoSavePeriod() * 1000);
+
+ // Connect the playback timer
+ //
+ connect(m_playTimer, SIGNAL(timeout()), this, SLOT(slotUpdatePlaybackPosition()));
+ connect(m_stopTimer, SIGNAL(timeout()), this, SLOT(slotUpdateMonitoring()));
+
+ // finally recreate the main view
+ //
+ initView();
+
+ if (getView() && getView()->getTrackEditor()) {
+ connect(m_doc, SIGNAL(makeTrackVisible(int)),
+ getView()->getTrackEditor(), SLOT(slotScrollToTrack(int)));
+ }
+
+ connect(m_doc, SIGNAL(devicesResyncd()),
+ this, SLOT(slotDocumentDevicesResyncd()));
+
+ m_doc->syncDevices();
+ m_doc->clearModifiedStatus();
+
+ if (newDocument->getStudio().haveMidiDevices()) {
+ stateChanged("got_midi_devices");
+ } else {
+ stateChanged("got_midi_devices", KXMLGUIClient::StateReverse);
+ }
+
+ // Ensure the sequencer knows about any audio files
+ // we've loaded as part of the new Composition
+ //
+ m_doc->prepareAudio();
+
+ // Do not reset instrument prog. changes after all.
+ // if (m_seqManager)
+ // m_seqManager->preparePlayback(true);
+
+ Composition &comp = m_doc->getComposition();
+
+ // Set any loaded loop at the Composition and
+ // on the marker on SegmentCanvas and clients
+ //
+ if (m_seqManager)
+ m_doc->setLoop(comp.getLoopStart(), comp.getLoopEnd());
+
+ emit documentChanged(m_doc);
+
+ m_doc->clearModifiedStatus(); // because it's set as modified by the various
+ // init operations
+ // TODO: this sucks, have to sort it out somehow.
+
+ // Readjust canvas size
+ //
+ m_view->getTrackEditor()->slotReadjustCanvasSize();
+
+ m_stopTimer->start(100);
+}
+
+void
+RosegardenGUIApp::openFile(QString filePath, ImportType type)
+{
+ RG_DEBUG << "RosegardenGUIApp::openFile " << filePath << endl;
+
+ if (type == ImportCheckType && filePath.endsWith(".rgp")) {
+ importProject(filePath);
+ return ;
+ }
+
+ RosegardenGUIDoc *doc = createDocument(filePath, type);
+ if (doc) {
+ setDocument(doc);
+
+ // fix # 1235755, "SPB combo not updating after document swap"
+ RG_DEBUG << "RosegardenGUIApp::openFile(): calling slotDocColoursChanged() in doc" << endl;
+ doc->slotDocColoursChanged();
+
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+ if (kapp->config()->readBoolEntry("alwaysusedefaultstudio", false)) {
+
+ QString autoloadFile =
+ KGlobal::dirs()->findResource("appdata", "autoload.rg");
+
+ QFileInfo autoloadFileInfo(autoloadFile);
+ if (autoloadFileInfo.isReadable()) {
+
+ RG_DEBUG << "Importing default studio from " << autoloadFile << endl;
+
+ slotImportStudioFromFile(autoloadFile);
+ }
+ }
+
+ QFileInfo fInfo(filePath);
+ m_fileRecent->addURL(fInfo.absFilePath());
+ }
+}
+
+RosegardenGUIDoc*
+RosegardenGUIApp::createDocument(QString filePath, ImportType importType)
+{
+ QFileInfo info(filePath);
+ RosegardenGUIDoc *doc = 0;
+
+ if (!info.exists()) {
+ // can happen with command-line arg, so...
+ KStartupLogo::hideIfStillThere();
+ KMessageBox::sorry(this, i18n("File \"%1\" does not exist").arg(filePath));
+ return 0;
+ }
+
+ if (info.isDir()) {
+ KStartupLogo::hideIfStillThere();
+ KMessageBox::sorry(this, i18n("File \"%1\" is actually a directory"));
+ return 0;
+ }
+
+ QFile file(filePath);
+
+ if (!file.open(IO_ReadOnly)) {
+ KStartupLogo::hideIfStillThere();
+ QString errStr =
+ i18n("You do not have read permission for \"%1\"").arg(filePath);
+
+ KMessageBox::sorry(this, errStr);
+ return 0;
+ }
+
+ // Stop if playing
+ //
+ if (m_seqManager && m_seqManager->getTransportStatus() == PLAYING)
+ slotStop();
+
+ slotEnableTransport(false);
+
+ if (importType == ImportCheckType) {
+ KMimeType::Ptr fileMimeType = KMimeType::findByPath(filePath);
+ if (fileMimeType->name() == "audio/x-midi")
+ importType = ImportMIDI;
+ else if (fileMimeType->name() == "audio/x-rosegarden")
+ importType = ImportRG4;
+ else if (filePath.endsWith(".rose"))
+ importType = ImportRG21;
+ else if (filePath.endsWith(".h2song"))
+ importType = ImportHydrogen;
+ }
+
+
+ switch (importType) {
+ case ImportMIDI:
+ doc = createDocumentFromMIDIFile(filePath);
+ break;
+ case ImportRG21:
+ doc = createDocumentFromRG21File(filePath);
+ break;
+ case ImportHydrogen:
+ doc = createDocumentFromHydrogenFile(filePath);
+ break;
+ default:
+ doc = createDocumentFromRGFile(filePath);
+ }
+
+ slotEnableTransport(true);
+
+ return doc;
+}
+
+RosegardenGUIDoc*
+RosegardenGUIApp::createDocumentFromRGFile(QString filePath)
+{
+ // Check for an autosaved file to recover
+ QString effectiveFilePath = filePath;
+ bool canRecover = false;
+ QString autoSaveFileName = kapp->checkRecoverFile(filePath, canRecover);
+
+ if (canRecover) {
+ // First check if the auto-save file is more recent than the doc
+ QFileInfo docFileInfo(filePath), autoSaveFileInfo(autoSaveFileName);
+
+ if (docFileInfo.lastModified() < autoSaveFileInfo.lastModified()) {
+
+ RG_DEBUG << "RosegardenGUIApp::openFile : "
+ << "found a more recent autosave file\n";
+
+ // At this point the splash screen may still be there, hide it if
+ // it's the case
+ KStartupLogo::hideIfStillThere();
+
+ // It is, so ask the user if he wants to use the autosave file
+ int reply = KMessageBox::questionYesNo(this,
+ i18n("An auto-save file for this document has been found\nDo you want to open it instead ?"));
+
+ if (reply == KMessageBox::Yes)
+ // open the autosave file instead
+ effectiveFilePath = autoSaveFileName;
+ else {
+ // user doesn't want the autosave, so delete it
+ // so it won't bother us again if we reload
+ canRecover = false;
+ QFile::remove
+ (autoSaveFileName);
+ }
+
+ } else
+ canRecover = false;
+ }
+
+ // Create a new blank document
+ //
+ RosegardenGUIDoc *newDoc = new RosegardenGUIDoc(this, m_pluginManager,
+ true); // skipAutoload
+
+ // ignore return thingy
+ //
+ if (newDoc->openDocument(effectiveFilePath)) {
+ if (canRecover) {
+ // Mark the document as modified,
+ // set the "regular" filepath and name (not those of
+ // the autosaved doc)
+ //
+ newDoc->slotDocumentModified();
+ QFileInfo info(filePath);
+ newDoc->setAbsFilePath(info.absFilePath());
+ newDoc->setTitle(info.fileName());
+ } else {
+ newDoc->clearModifiedStatus();
+ }
+ } else {
+ delete newDoc;
+ return 0;
+ }
+
+ return newDoc;
+}
+
+void RosegardenGUIApp::slotSaveOptions()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotSaveOptions()\n";
+
+#ifdef SETTING_LOG_DEBUG
+
+ _settingLog(QString("SETTING 2 : transport flap extended = %1").arg(getTransport()->isExpanded()));
+ _settingLog(QString("SETTING 2 : show track labels = %1").arg(m_viewTrackLabels->isChecked()));
+#endif
+
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+ kapp->config()->writeEntry("Show Transport", m_viewTransport->isChecked());
+ kapp->config()->writeEntry("Expanded Transport", m_transport ? getTransport()->isExpanded() : true);
+ kapp->config()->writeEntry("Show Track labels", m_viewTrackLabels->isChecked());
+ kapp->config()->writeEntry("Show Rulers", m_viewRulers->isChecked());
+ kapp->config()->writeEntry("Show Tempo Ruler", m_viewTempoRuler->isChecked());
+ kapp->config()->writeEntry("Show Chord Name Ruler", m_viewChordNameRuler->isChecked());
+ kapp->config()->writeEntry("Show Previews", m_viewPreviews->isChecked());
+ kapp->config()->writeEntry("Show Segment Labels", m_viewSegmentLabels->isChecked());
+ kapp->config()->writeEntry("Show Parameters", m_dockVisible);
+ kapp->config()->writeEntry("MIDI Thru Routing", m_enableMIDIrouting->isChecked());
+
+#ifdef SETTING_LOG_DEBUG
+
+ RG_DEBUG << "SHOW PARAMETERS = " << m_dockVisible << endl;
+#endif
+
+ m_fileRecent->saveEntries(kapp->config());
+
+ // saveMainWindowSettings(kapp->config(), RosegardenGUIApp::MainWindowConfigGroup); - no need to, done by KMainWindow
+ kapp->config()->sync();
+}
+
+void RosegardenGUIApp::setupFileDialogSpeedbar()
+{
+ KConfig *config = kapp->config();
+
+ config->setGroup("KFileDialog Speedbar");
+
+ RG_DEBUG << "RosegardenGUIApp::setupFileDialogSpeedbar" << endl;
+
+ bool hasSetExamplesItem = config->readBoolEntry("Examples Set", false);
+
+ RG_DEBUG << "RosegardenGUIApp::setupFileDialogSpeedbar: examples set " << hasSetExamplesItem << endl;
+
+ if (!hasSetExamplesItem) {
+
+ unsigned int n = config->readUnsignedNumEntry("Number of Entries", 0);
+
+ config->writeEntry(QString("Description_%1").arg(n), i18n("Example Files"));
+ config->writeEntry(QString("IconGroup_%1").arg(n), 4);
+ config->writeEntry(QString("Icon_%1").arg(n), "folder");
+ config->writeEntry(QString("URL_%1").arg(n),
+ KGlobal::dirs()->findResource("appdata", "examples/"));
+
+ RG_DEBUG << "wrote url " << config->readEntry(QString("URL_%1").arg(n)) << endl;
+
+ config->writeEntry("Examples Set", true);
+ config->writeEntry("Number of Entries", n + 1);
+ config->sync();
+ }
+
+}
+
+void RosegardenGUIApp::readOptions()
+{
+ applyMainWindowSettings(kapp->config(), MainWindowConfigGroup);
+
+ kapp->config()->reparseConfiguration();
+
+ // Statusbar and toolbars toggling action status
+ //
+ m_viewStatusBar ->setChecked(!statusBar() ->isHidden());
+ m_viewToolBar ->setChecked(!toolBar() ->isHidden());
+ m_viewToolsToolBar ->setChecked(!toolBar("Tools Toolbar") ->isHidden());
+ m_viewTracksToolBar ->setChecked(!toolBar("Tracks Toolbar") ->isHidden());
+ m_viewEditorsToolBar ->setChecked(!toolBar("Editors Toolbar") ->isHidden());
+ m_viewTransportToolBar->setChecked(!toolBar("Transport Toolbar")->isHidden());
+ m_viewZoomToolBar ->setChecked(!toolBar("Zoom Toolbar") ->isHidden());
+
+ bool opt;
+
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+
+ opt = kapp->config()->readBoolEntry("Show Transport", true);
+ m_viewTransport->setChecked(opt);
+ slotToggleTransport();
+
+ opt = kapp->config()->readBoolEntry("Expanded Transport", true);
+
+#ifdef SETTING_LOG_DEBUG
+
+ _settingLog(QString("SETTING 3 : transport flap extended = %1").arg(opt));
+#endif
+
+ if (opt)
+ getTransport()->slotPanelOpenButtonClicked();
+ else
+ getTransport()->slotPanelCloseButtonClicked();
+
+ opt = kapp->config()->readBoolEntry("Show Track labels", true);
+
+#ifdef SETTING_LOG_DEBUG
+
+ _settingLog(QString("SETTING 3 : show track labels = %1").arg(opt));
+#endif
+
+ m_viewTrackLabels->setChecked(opt);
+ slotToggleTrackLabels();
+
+ opt = kapp->config()->readBoolEntry("Show Rulers", true);
+ m_viewRulers->setChecked(opt);
+ slotToggleRulers();
+
+ opt = kapp->config()->readBoolEntry("Show Tempo Ruler", true);
+ m_viewTempoRuler->setChecked(opt);
+ slotToggleTempoRuler();
+
+ opt = kapp->config()->readBoolEntry("Show Chord Name Ruler", false);
+ m_viewChordNameRuler->setChecked(opt);
+ slotToggleChordNameRuler();
+
+ opt = kapp->config()->readBoolEntry("Show Previews", true);
+ m_viewPreviews->setChecked(opt);
+ slotTogglePreviews();
+
+ opt = kapp->config()->readBoolEntry("Show Segment Labels", true);
+ m_viewSegmentLabels->setChecked(opt);
+ slotToggleSegmentLabels();
+
+ opt = kapp->config()->readBoolEntry("Show Parameters", true);
+ if (!opt) {
+ m_dockLeft->undock();
+ m_dockLeft->hide();
+ stateChanged("parametersbox_closed", KXMLGUIClient::StateNoReverse);
+ m_dockVisible = false;
+ }
+
+ // MIDI Thru routing
+ opt = kapp->config()->readBoolEntry("MIDI Thru Routing", true);
+ m_enableMIDIrouting->setChecked(opt);
+ slotEnableMIDIThruRouting();
+
+ // initialise the recent file list
+ //
+ m_fileRecent->loadEntries(kapp->config());
+
+ m_actionsSetup = true;
+
+}
+
+void RosegardenGUIApp::saveGlobalProperties(KConfig *cfg)
+{
+ if (m_doc->getTitle() != i18n("Untitled") && !m_doc->isModified()) {
+ // saving to tempfile not necessary
+ } else {
+ QString filename = m_doc->getAbsFilePath();
+ cfg->writeEntry("filename", filename);
+ cfg->writeEntry("modified", m_doc->isModified());
+
+ QString tempname = kapp->tempSaveName(filename);
+ QString errMsg;
+ bool res = m_doc->saveDocument(tempname, errMsg);
+ if (!res) {
+ if (errMsg)
+ KMessageBox::error(this, i18n(QString("Could not save document at %1\nError was : %2")
+ .arg(tempname).arg(errMsg)));
+ else
+ KMessageBox::error(this, i18n(QString("Could not save document at %1")
+ .arg(tempname)));
+ }
+ }
+}
+
+void RosegardenGUIApp::readGlobalProperties(KConfig* _cfg)
+{
+ QString filename = _cfg->readEntry("filename", "");
+ bool modified = _cfg->readBoolEntry("modified", false);
+
+ if (modified) {
+ bool canRecover;
+ QString tempname = kapp->checkRecoverFile(filename, canRecover);
+
+ if (canRecover) {
+ slotEnableTransport(false);
+ m_doc->openDocument(tempname);
+ slotEnableTransport(true);
+ m_doc->slotDocumentModified();
+ QFileInfo info(filename);
+ m_doc->setAbsFilePath(info.absFilePath());
+ m_doc->setTitle(info.fileName());
+ }
+ } else {
+ if (!filename.isEmpty()) {
+ slotEnableTransport(false);
+ m_doc->openDocument(filename);
+ slotEnableTransport(true);
+ }
+ }
+
+ QString caption = kapp->caption();
+ setCaption(caption + ": " + m_doc->getTitle());
+}
+
+void RosegardenGUIApp::showEvent(QShowEvent* e)
+{
+ RG_DEBUG << "RosegardenGUIApp::showEvent()\n";
+
+ getTransport()->raise();
+ KMainWindow::showEvent(e);
+}
+
+bool RosegardenGUIApp::queryClose()
+{
+ RG_DEBUG << "RosegardenGUIApp::queryClose" << endl;
+#ifdef SETTING_LOG_DEBUG
+
+ _settingLog(QString("SETTING 1 : transport flap extended = %1").arg(getTransport()->isExpanded()));
+ _settingLog(QString("SETTING 1 : show track labels = %1").arg(m_viewTrackLabels->isChecked()));
+#endif
+
+ QString errMsg;
+
+ bool canClose = m_doc->saveIfModified();
+
+ /*
+ if (canClose && m_transport) {
+
+ // or else the closing of the transport will toggle off the
+ // 'view transport' action, and its state will be saved as
+ // 'off'
+ //
+
+ disconnect(m_transport, SIGNAL(closed()),
+ this, SLOT(slotCloseTransport()));
+ }
+ */
+
+ return canClose;
+
+}
+
+bool RosegardenGUIApp::queryExit()
+{
+ RG_DEBUG << "RosegardenGUIApp::queryExit" << endl;
+ if (m_actionsSetup)
+ slotSaveOptions();
+
+ return true;
+}
+
+void RosegardenGUIApp::slotFileNewWindow()
+{
+ KTmpStatusMsg msg(i18n("Opening a new application window..."), this);
+
+ RosegardenGUIApp *new_window = new RosegardenGUIApp();
+ new_window->show();
+}
+
+void RosegardenGUIApp::slotFileNew()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotFileNew()\n";
+
+ KTmpStatusMsg msg(i18n("Creating new document..."), this);
+
+ bool makeNew = false;
+
+ if (!m_doc->isModified()) {
+ makeNew = true;
+ // m_doc->closeDocument();
+ } else if (m_doc->saveIfModified()) {
+ makeNew = true;
+ }
+
+ if (makeNew) {
+
+ setDocument(new RosegardenGUIDoc(this, m_pluginManager));
+ }
+}
+
+void RosegardenGUIApp::slotOpenDroppedURL(QString url)
+{
+ ProgressDialog::processEvents(); // or else we get a crash because the
+ // track editor is erased too soon - it is the originator of the signal
+ // this slot is connected to.
+
+ if (!m_doc->saveIfModified())
+ return ;
+
+ openURL(KURL(url));
+}
+
+void RosegardenGUIApp::openURL(QString url)
+{
+ RG_DEBUG << "RosegardenGUIApp::openURL: QString " << url << endl;
+ openURL(KURL(url));
+}
+
+void RosegardenGUIApp::openURL(const KURL& url)
+{
+ SetWaitCursor waitCursor;
+
+ QString netFile = url.prettyURL();
+ RG_DEBUG << "RosegardenGUIApp::openURL: KURL " << netFile << endl;
+
+ if (!url.isValid()) {
+ QString string;
+ string = i18n( "Malformed URL\n%1").arg(netFile);
+
+ KMessageBox::sorry(this, string);
+ return ;
+ }
+
+ QString target, caption(url.path());
+
+ if (KIO::NetAccess::download(url, target, this) == false) {
+ KMessageBox::error(this, i18n("Cannot download file %1").arg(url.prettyURL()));
+ return ;
+ }
+
+ RG_DEBUG << "RosegardenGUIApp::openURL: target : " << target << endl;
+
+ if (!m_doc->saveIfModified())
+ return ;
+
+ openFile(target);
+
+ setCaption(caption);
+}
+
+void RosegardenGUIApp::slotFileOpen()
+{
+ slotStatusHelpMsg(i18n("Opening file..."));
+
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+
+ QString lastOpenedVersion =
+ kapp->config()->readEntry("Last File Opened Version", "none");
+
+ if (lastOpenedVersion != VERSION) {
+
+ // We haven't opened any files with this version of the
+ // program before. Default to the examples directory.
+
+ QString examplesDir = KGlobal::dirs()->findResource("appdata", "examples/");
+ kapp->config()->setGroup("Recent Dirs");
+ QString recentString = kapp->config()->readEntry("ROSEGARDEN", "");
+ kapp->config()->writeEntry
+ ("ROSEGARDEN", QString("file:%1,%2").arg(examplesDir).arg(recentString));
+ }
+
+ KURL url = KFileDialog::getOpenURL
+ (":ROSEGARDEN",
+ "audio/x-rosegarden audio/x-midi audio/x-rosegarden21", this,
+ i18n("Open File"));
+ if ( url.isEmpty() ) {
+ return ;
+ }
+
+ if (m_doc && !m_doc->saveIfModified())
+ return ;
+
+ kapp->config()->setGroup(GeneralOptionsConfigGroup);
+ kapp->config()->writeEntry("Last File Opened Version", VERSION);
+
+ openURL(url);
+}
+
+void RosegardenGUIApp::slotMerge()
+{
+ KURL url = KFileDialog::getOpenURL
+ (":ROSEGARDEN",
+ "audio/x-rosegarden audio/x-midi audio/x-rosegarden21", this,
+ i18n("Open File"));
+ if ( url.isEmpty() ) {
+ return ;
+ }
+
+
+ QString target;
+
+ if (KIO::NetAccess::download(url, target, this) == false) {
+ KMessageBox::error(this, i18n("Cannot download file %1").arg(url.prettyURL()));
+ return ;
+ }
+
+ mergeFile(target);
+
+ KIO::NetAccess::removeTempFile( target );
+}
+
+void RosegardenGUIApp::slotFileOpenRecent(const KURL &url)
+{
+ KTmpStatusMsg msg(i18n("Opening file..."), this);
+
+ if (m_doc) {
+
+ if (!m_doc->saveIfModified()) {
+ return ;
+
+ }
+ }
+
+ openURL(url);
+}
+
+void RosegardenGUIApp::slotFileSave()
+{
+ if (!m_doc /*|| !m_doc->isModified()*/)
+ return ; // ALWAYS save, even if doc is not modified.
+
+ KTmpStatusMsg msg(i18n("Saving file..."), this);
+
+ // if it's a new file (no file path), or an imported file
+ // (file path doesn't end with .rg), call saveAs
+ //
+ if (!m_doc->isRegularDotRGFile()) {
+
+ slotFileSaveAs();
+
+ } else {
+
+ SetWaitCursor waitCursor;
+ QString errMsg, docFilePath = m_doc->getAbsFilePath();
+
+ bool res = m_doc->saveDocument(docFilePath, errMsg);
+ if (!res) {
+ if (errMsg)
+ KMessageBox::error(this, i18n(QString("Could not save document at %1\nError was : %2")
+ .arg(docFilePath).arg(errMsg)));
+ else
+ KMessageBox::error(this, i18n(QString("Could not save document at %1")
+ .arg(docFilePath)));
+ }
+ }
+}
+
+QString
+RosegardenGUIApp::getValidWriteFile(QString descriptiveExtension,
+ QString label)
+{
+ // extract first extension listed in descriptiveExtension, for instance,
+ // ".rg" from "*.rg|Rosegarden files", or ".mid" from "*.mid *.midi|MIDI Files"
+ //
+ QString extension = descriptiveExtension.left(descriptiveExtension.find('|')).mid(1).section(' ', 0, 0);
+
+ RG_DEBUG << "RosegardenGUIApp::getValidWriteFile() : extension = " << extension << endl;
+
+ // It's too bad there isn't this functionality within
+ // KFileDialog::getSaveFileName
+ KFileDialog saveFileDialog(":ROSEGARDEN", descriptiveExtension, this, label, true);
+ saveFileDialog.setOperationMode(KFileDialog::Saving);
+ if (m_doc) {
+ QString saveFileName = m_doc->getAbsFilePath();
+ // Show filename without the old extension
+ int dotLoc = saveFileName.findRev('.');
+ if (dotLoc >= int(saveFileName.length() - 4)) {
+ saveFileName = saveFileName.left(dotLoc);
+ }
+ saveFileDialog.setSelection(saveFileName);
+ }
+ saveFileDialog.exec();
+ QString name = saveFileDialog.selectedFile();
+
+ // RG_DEBUG << "RosegardenGUIApp::getValidWriteFile() : KFileDialog::getSaveFileName returned "
+ // << name << endl;
+
+
+ if (name.isEmpty())
+ return name;
+
+ // Append extension if we don't have one
+ //
+ if (!extension.isEmpty()) {
+ static QRegExp rgFile("\\..{1,4}$");
+ if (rgFile.match(name) == -1) {
+ name += extension;
+ }
+ }
+
+ KURL *u = new KURL(name);
+
+ if (!u->isValid()) {
+ KMessageBox::sorry(this, i18n("This is not a valid filename.\n"));
+ return "";
+ }
+
+ if (!u->isLocalFile()) {
+ KMessageBox::sorry(this, i18n("This is not a local file.\n"));
+ return "";
+ }
+
+ QFileInfo info(name);
+
+ if (info.isDir()) {
+ KMessageBox::sorry(this, i18n("You have specified a directory"));
+ return "";
+ }
+
+ if (info.exists()) {
+ int overwrite = KMessageBox::questionYesNo
+ (this, i18n("The specified file exists. Overwrite?"));
+
+ if (overwrite != KMessageBox::Yes)
+ return "";
+ }
+
+ return name;
+}
+
+bool RosegardenGUIApp::slotFileSaveAs()
+{
+ if (!m_doc)
+ return false;
+
+ KTmpStatusMsg msg(i18n("Saving file with a new filename..."), this);
+
+ QString newName = getValidWriteFile("*.rg|" + i18n("Rosegarden files") +
+ "\n*|" + i18n("All files"),
+ i18n("Save as..."));
+ if (newName.isEmpty())
+ return false;
+
+ SetWaitCursor waitCursor;
+ QFileInfo saveAsInfo(newName);
+ m_doc->setTitle(saveAsInfo.fileName());
+ m_doc->setAbsFilePath(saveAsInfo.absFilePath());
+ QString errMsg;
+ bool res = m_doc->saveDocument(newName, errMsg);
+ if (!res) {
+ if (errMsg)
+ KMessageBox::error(this, i18n(QString("Could not save document at %1\nError was : %2")
+ .arg(newName).arg(errMsg)));
+ else
+ KMessageBox::error(this, i18n(QString("Could not save document at %1")
+ .arg(newName)));
+
+ } else {
+
+ m_fileRecent->addURL(newName);
+
+ QString caption = kapp->caption();
+ setCaption(caption + ": " + m_doc->getTitle());
+ // update the edit view's captions too
+ emit compositionStateUpdate();
+ }
+
+ return res;
+}
+
+void RosegardenGUIApp::slotFileClose()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotFileClose()" << endl;
+
+ if (!m_doc)
+ return ;
+
+ KTmpStatusMsg msg(i18n("Closing file..."), this);
+
+ if (m_doc->saveIfModified()) {
+ setDocument(new RosegardenGUIDoc(this, m_pluginManager));
+ }
+
+ // Don't close the whole view (i.e. Quit), just close the doc.
+ // close();
+}
+
+void RosegardenGUIApp::slotFilePrint()
+{
+ if (m_doc->getComposition().getNbSegments() == 0) {
+ KMessageBox::sorry(0, "Please create some tracks first (until we implement menu state management)");
+ return ;
+ }
+
+ KTmpStatusMsg msg(i18n("Printing..."), this);
+
+ m_view->print(&m_doc->getComposition());
+}
+
+void RosegardenGUIApp::slotFilePrintPreview()
+{
+ if (m_doc->getComposition().getNbSegments() == 0) {
+ KMessageBox::sorry(0, "Please create some tracks first (until we implement menu state management)");
+ return ;
+ }
+
+ KTmpStatusMsg msg(i18n("Previewing..."), this);
+
+ m_view->print(&m_doc->getComposition(), true);
+}
+
+void RosegardenGUIApp::slotQuit()
+{
+ slotStatusMsg(i18n("Exiting..."));
+
+ Profiles::getInstance()->dump();
+
+ // close the first window, the list makes the next one the first again.
+ // This ensures that queryClose() is called on each window to ask for closing
+ KMainWindow* w;
+ if (memberList) {
+
+ for (w = memberList->first(); w != 0; w = memberList->next()) {
+ // only close the window if the closeEvent is accepted. If
+ // the user presses Cancel on the saveIfModified() dialog,
+ // the window and the application stay open.
+ if (!w->close())
+ break;
+ }
+ }
+}
+
+void RosegardenGUIApp::slotEditCut()
+{
+ if (!m_view->haveSelection())
+ return ;
+ KTmpStatusMsg msg(i18n("Cutting selection..."), this);
+
+ SegmentSelection selection(m_view->getSelection());
+ m_doc->getCommandHistory()->addCommand
+ (new CutCommand(selection, m_clipboard));
+}
+
+void RosegardenGUIApp::slotEditCopy()
+{
+ if (!m_view->haveSelection())
+ return ;
+ KTmpStatusMsg msg(i18n("Copying selection to clipboard..."), this);
+
+ SegmentSelection selection(m_view->getSelection());
+ m_doc->getCommandHistory()->addCommand
+ (new CopyCommand(selection, m_clipboard));
+}
+
+void RosegardenGUIApp::slotEditPaste()
+{
+ if (m_clipboard->isEmpty()) {
+ KTmpStatusMsg msg(i18n("Clipboard is empty"), this);
+ return ;
+ }
+ KTmpStatusMsg msg(i18n("Inserting clipboard contents..."), this);
+
+ // for now, but we could paste at the time of the first copied
+ // segment and then do ghosting drag or something
+ timeT insertionTime = m_doc->getComposition().getPosition();
+ m_doc->getCommandHistory()->addCommand
+ (new PasteSegmentsCommand(&m_doc->getComposition(),
+ m_clipboard, insertionTime,
+ m_doc->getComposition().getSelectedTrack(),
+ false));
+
+ // User preference? Update song pointer position on paste
+ m_doc->slotSetPointerPosition(m_doc->getComposition().getPosition());
+}
+
+void RosegardenGUIApp::slotCutRange()
+{
+ timeT t0 = m_doc->getComposition().getLoopStart();
+ timeT t1 = m_doc->getComposition().getLoopEnd();
+
+ if (t0 == t1)
+ return ;
+
+ m_doc->getCommandHistory()->addCommand
+ (new CutRangeCommand(&m_doc->getComposition(), t0, t1, m_clipboard));
+}
+
+void RosegardenGUIApp::slotCopyRange()
+{
+ timeT t0 = m_doc->getComposition().getLoopStart();
+ timeT t1 = m_doc->getComposition().getLoopEnd();
+
+ if (t0 == t1)
+ return ;
+
+ m_doc->getCommandHistory()->addCommand
+ (new CopyCommand(&m_doc->getComposition(), t0, t1, m_clipboard));
+}
+
+void RosegardenGUIApp::slotPasteRange()
+{
+ if (m_clipboard->isEmpty())
+ return ;
+
+ m_doc->getCommandHistory()->addCommand
+ (new PasteRangeCommand(&m_doc->getComposition(), m_clipboard,
+ m_doc->getComposition().getPosition()));
+
+ m_doc->setLoop(0, 0);
+}
+
+void RosegardenGUIApp::slotDeleteRange()
+{
+ timeT t0 = m_doc->getComposition().getLoopStart();
+ timeT t1 = m_doc->getComposition().getLoopEnd();
+
+ if (t0 == t1)
+ return ;
+
+ m_doc->getCommandHistory()->addCommand
+ (new DeleteRangeCommand(&m_doc->getComposition(), t0, t1));
+
+ m_doc->setLoop(0, 0);
+}
+
+void RosegardenGUIApp::slotInsertRange()
+{
+ timeT t0 = m_doc->getComposition().getPosition();
+ std::pair<timeT, timeT> r = m_doc->getComposition().getBarRangeForTime(t0);
+ TimeDialog dialog(m_view, i18n("Duration of empty range to insert"),
+ &m_doc->getComposition(), t0, r.second - r.first, false);
+ if (dialog.exec() == QDialog::Accepted) {
+ m_doc->getCommandHistory()->addCommand
+ (new InsertRangeCommand(&m_doc->getComposition(), t0, dialog.getTime()));
+ m_doc->setLoop(0, 0);
+ }
+}
+
+void RosegardenGUIApp::slotSelectAll()
+{
+ m_view->slotSelectAllSegments();
+}
+
+void RosegardenGUIApp::slotDeleteSelectedSegments()
+{
+ m_view->getTrackEditor()->slotDeleteSelectedSegments();
+}
+
+void RosegardenGUIApp::slotQuantizeSelection()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ //!!! this should all be in rosegardenguiview
+
+ QuantizeDialog dialog(m_view);
+ if (dialog.exec() != QDialog::Accepted)
+ return ;
+
+ SegmentSelection selection = m_view->getSelection();
+
+ KMacroCommand *command = new KMacroCommand
+ (EventQuantizeCommand::getGlobalName());
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+ command->addCommand(new EventQuantizeCommand
+ (**i, (*i)->getStartTime(), (*i)->getEndTime(),
+ dialog.getQuantizer()));
+ }
+
+ m_view->slotAddCommandToHistory(command);
+}
+
+void RosegardenGUIApp::slotRepeatQuantizeSelection()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ //!!! this should all be in rosegardenguiview
+
+ SegmentSelection selection = m_view->getSelection();
+
+ KMacroCommand *command = new KMacroCommand
+ (EventQuantizeCommand::getGlobalName());
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+ command->addCommand(new EventQuantizeCommand
+ (**i, (*i)->getStartTime(), (*i)->getEndTime(),
+ "Quantize Dialog Grid", false)); // no i18n (config group name)
+ }
+
+ m_view->slotAddCommandToHistory(command);
+}
+
+void RosegardenGUIApp::slotGrooveQuantize()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ SegmentSelection selection = m_view->getSelection();
+
+ if (selection.size() != 1) {
+ KMessageBox::sorry(this, i18n("This function needs no more than one segment to be selected."));
+ return ;
+ }
+
+ Segment *s = *selection.begin();
+ m_view->slotAddCommandToHistory(new CreateTempoMapFromSegmentCommand(s));
+}
+
+void RosegardenGUIApp::slotJoinSegments()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ //!!! this should all be in rosegardenguiview
+ //!!! should it?
+
+ SegmentSelection selection = m_view->getSelection();
+ if (selection.size() == 0)
+ return ;
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+ if ((*i)->getType() != Segment::Internal) {
+ KMessageBox::sorry(this, i18n("Can't join Audio segments"));
+ return ;
+ }
+ }
+
+ m_view->slotAddCommandToHistory(new SegmentJoinCommand(selection));
+ m_view->updateSelectionContents();
+}
+
+void RosegardenGUIApp::slotRescaleSelection()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ //!!! this should all be in rosegardenguiview
+ //!!! should it?
+
+ SegmentSelection selection = m_view->getSelection();
+
+ timeT startTime = 0, endTime = 0;
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+ if ((i == selection.begin()) || ((*i)->getStartTime() < startTime)) {
+ startTime = (*i)->getStartTime();
+ }
+ if ((i == selection.begin()) || ((*i)->getEndMarkerTime() > endTime)) {
+ endTime = (*i)->getEndMarkerTime();
+ }
+ }
+
+ RescaleDialog dialog(m_view, &m_doc->getComposition(),
+ startTime, endTime - startTime,
+ false, false);
+ if (dialog.exec() != QDialog::Accepted)
+ return ;
+
+ std::vector<AudioSegmentRescaleCommand *> asrcs;
+
+ int mult = dialog.getNewDuration();
+ int div = endTime - startTime;
+ float ratio = float(mult) / float(div);
+
+ std::cerr << "slotRescaleSelection: mult = " << mult << ", div = " << div << ", ratio = " << ratio << std::endl;
+
+ KMacroCommand *command = new KMacroCommand
+ (SegmentRescaleCommand::getGlobalName());
+
+ bool pathTested = false;
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+ if ((*i)->getType() == Segment::Audio) {
+ if (!pathTested) {
+ testAudioPath(i18n("rescaling an audio file"));
+ pathTested = true;
+ }
+ AudioSegmentRescaleCommand *asrc = new AudioSegmentRescaleCommand
+ (m_doc, *i, ratio);
+ command->addCommand(asrc);
+ asrcs.push_back(asrc);
+ } else {
+ command->addCommand(new SegmentRescaleCommand(*i, mult, div));
+ }
+ }
+
+ ProgressDialog *progressDlg = 0;
+
+ if (!asrcs.empty()) {
+ progressDlg = new ProgressDialog
+ (i18n("Rescaling audio file..."), 100, this);
+ progressDlg->setAutoClose(false);
+ progressDlg->setAutoReset(false);
+ progressDlg->show();
+ for (size_t i = 0; i < asrcs.size(); ++i) {
+ asrcs[i]->connectProgressDialog(progressDlg);
+ }
+ }
+
+ m_view->slotAddCommandToHistory(command);
+
+ if (!asrcs.empty()) {
+
+ progressDlg->setLabel(i18n("Generating audio preview..."));
+
+ for (size_t i = 0; i < asrcs.size(); ++i) {
+ asrcs[i]->disconnectProgressDialog(progressDlg);
+ }
+
+ connect(&m_doc->getAudioFileManager(), SIGNAL(setProgress(int)),
+ progressDlg->progressBar(), SLOT(setValue(int)));
+ connect(progressDlg, SIGNAL(cancelClicked()),
+ &m_doc->getAudioFileManager(), SLOT(slotStopPreview()));
+
+ for (size_t i = 0; i < asrcs.size(); ++i) {
+ int fid = asrcs[i]->getNewAudioFileId();
+ if (fid >= 0) {
+ slotAddAudioFile(fid);
+ m_doc->getAudioFileManager().generatePreview(fid);
+ }
+ }
+ }
+
+ if (progressDlg) delete progressDlg;
+}
+
+bool
+RosegardenGUIApp::testAudioPath(QString op)
+{
+ try {
+ m_doc->getAudioFileManager().testAudioPath();
+ } catch (AudioFileManager::BadAudioPathException) {
+ if (KMessageBox::warningContinueCancel
+ (this,
+ 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 %1.\nWould you like to set it now?").arg(op),
+ i18n("Warning"),
+ i18n("Set audio file path")) == KMessageBox::Continue) {
+ slotOpenAudioPathSettings();
+ }
+ return false;
+ }
+ return true;
+}
+
+void RosegardenGUIApp::slotAutoSplitSelection()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ //!!! this should all be in rosegardenguiview
+ //!!! or should it?
+
+ SegmentSelection selection = m_view->getSelection();
+
+ KMacroCommand *command = new KMacroCommand
+ (SegmentAutoSplitCommand::getGlobalName());
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+
+ if ((*i)->getType() == Segment::Audio) {
+ AudioSplitDialog aSD(this, (*i), m_doc);
+
+ if (aSD.exec() == QDialog::Accepted) {
+ // split to threshold
+ //
+ command->addCommand(
+ new AudioSegmentAutoSplitCommand(m_doc,
+ *i,
+ aSD.getThreshold()));
+ // dmm - verifying that widget->value() accessors *can* work without crashing
+ // std::cout << "SILVAN: getThreshold() = " << aSD.getThreshold() << std::endl;
+ }
+ } else {
+ command->addCommand(new SegmentAutoSplitCommand(*i));
+ }
+ }
+
+ m_view->slotAddCommandToHistory(command);
+}
+
+void RosegardenGUIApp::slotJogLeft()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotJogLeft" << endl;
+ jogSelection( -Note(Note::Demisemiquaver).getDuration());
+}
+
+void RosegardenGUIApp::slotJogRight()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotJogRight" << endl;
+ jogSelection(Note(Note::Demisemiquaver).getDuration());
+}
+
+void RosegardenGUIApp::jogSelection(timeT amount)
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ SegmentSelection selection = m_view->getSelection();
+
+ SegmentReconfigureCommand *command = new SegmentReconfigureCommand(i18n("Jog Selection"));
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+
+ command->addSegment((*i),
+ (*i)->getStartTime() + amount,
+ (*i)->getEndMarkerTime() + amount,
+ (*i)->getTrack());
+ }
+
+ m_view->slotAddCommandToHistory(command);
+}
+
+void RosegardenGUIApp::createAndSetupTransport()
+{
+ // create the Transport GUI and add the callbacks to the
+ // buttons and keyboard accelerators
+ //
+ m_transport =
+ new TransportDialog(this);
+ plugAccelerators(m_transport, m_transport->getAccelerators());
+
+ m_transport->getAccelerators()->connectItem
+ (m_transport->getAccelerators()->insertItem(Key_T),
+ this,
+ SLOT(slotHideTransport()));
+
+ // Ensure that the checkbox is unchecked if the dialog
+ // is closed
+ connect(m_transport, SIGNAL(closed()),
+ SLOT(slotCloseTransport()));
+
+ // Handle loop setting and unsetting from the transport loop button
+ //
+
+ connect(m_transport, SIGNAL(setLoop()), SLOT(slotSetLoop()));
+ connect(m_transport, SIGNAL(unsetLoop()), SLOT(slotUnsetLoop()));
+ connect(m_transport, SIGNAL(panic()), SLOT(slotPanic()));
+
+ connect(m_transport, SIGNAL(editTempo(QWidget*)),
+ SLOT(slotEditTempo(QWidget*)));
+
+ connect(m_transport, SIGNAL(editTimeSignature(QWidget*)),
+ SLOT(slotEditTimeSignature(QWidget*)));
+
+ connect(m_transport, SIGNAL(editTransportTime(QWidget*)),
+ SLOT(slotEditTransportTime(QWidget*)));
+
+ // Handle set loop start/stop time buttons.
+ //
+ connect(m_transport, SIGNAL(setLoopStartTime()), SLOT(slotSetLoopStart()));
+ connect(m_transport, SIGNAL(setLoopStopTime()), SLOT(slotSetLoopStop()));
+
+ if (m_seqManager != 0)
+ m_seqManager->setTransport(m_transport);
+
+}
+
+void RosegardenGUIApp::slotSplitSelectionByPitch()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ SplitByPitchDialog dialog(m_view);
+ if (dialog.exec() != QDialog::Accepted)
+ return ;
+
+ SegmentSelection selection = m_view->getSelection();
+
+ KMacroCommand *command = new KMacroCommand
+ (SegmentSplitByPitchCommand::getGlobalName());
+
+ bool haveSomething = false;
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+
+ if ((*i)->getType() == Segment::Audio) {
+ // nothing
+ } else {
+ command->addCommand
+ (new SegmentSplitByPitchCommand
+ (*i,
+ dialog.getPitch(),
+ dialog.getShouldRange(),
+ dialog.getShouldDuplicateNonNoteEvents(),
+ (SegmentSplitByPitchCommand::ClefHandling)
+ dialog.getClefHandling()));
+ haveSomething = true;
+ }
+ }
+
+ if (haveSomething)
+ m_view->slotAddCommandToHistory(command);
+ //!!! else complain
+}
+
+void
+RosegardenGUIApp::slotSplitSelectionByRecordedSrc()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ SplitByRecordingSrcDialog dialog(m_view, m_doc);
+ if (dialog.exec() != QDialog::Accepted)
+ return ;
+
+ SegmentSelection selection = m_view->getSelection();
+
+ KMacroCommand *command = new KMacroCommand
+ (SegmentSplitByRecordingSrcCommand::getGlobalName());
+
+ bool haveSomething = false;
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+
+ if ((*i)->getType() == Segment::Audio) {
+ // nothing
+ } else {
+ command->addCommand
+ (new SegmentSplitByRecordingSrcCommand(*i,
+ dialog.getChannel(),
+ dialog.getDevice()));
+ haveSomething = true;
+ }
+ }
+ if (haveSomething)
+ m_view->slotAddCommandToHistory(command);
+}
+
+void
+RosegardenGUIApp::slotSplitSelectionAtTime()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ SegmentSelection selection = m_view->getSelection();
+ if (selection.empty())
+ return ;
+
+ timeT now = m_doc->getComposition().getPosition();
+
+ QString title = i18n("Split Segment at Time",
+ "Split %n Segments at Time",
+ selection.size());
+
+ TimeDialog dialog(m_view, title,
+ &m_doc->getComposition(),
+ now, true);
+
+ KMacroCommand *command = new KMacroCommand( title );
+
+ if (dialog.exec() == QDialog::Accepted) {
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+
+ if ((*i)->getType() == Segment::Audio) {
+ command->addCommand(new AudioSegmentSplitCommand(*i, dialog.getTime()));
+ } else {
+ command->addCommand(new SegmentSplitCommand(*i, dialog.getTime()));
+ }
+ }
+ m_view->slotAddCommandToHistory(command);
+ }
+}
+
+void
+RosegardenGUIApp::slotSetSegmentStartTimes()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ SegmentSelection selection = m_view->getSelection();
+ if (selection.empty())
+ return ;
+
+ timeT someTime = (*selection.begin())->getStartTime();
+
+ TimeDialog dialog(m_view, i18n("Segment Start Time"),
+ &m_doc->getComposition(),
+ someTime, false);
+
+ if (dialog.exec() == QDialog::Accepted) {
+
+ bool plural = (selection.size() > 1);
+
+ SegmentReconfigureCommand *command =
+ new SegmentReconfigureCommand(plural ?
+ i18n("Set Segment Start Times") :
+ i18n("Set Segment Start Time"));
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+
+ command->addSegment
+ (*i, dialog.getTime(),
+ (*i)->getEndMarkerTime() - (*i)->getStartTime() + dialog.getTime(),
+ (*i)->getTrack());
+ }
+
+ m_view->slotAddCommandToHistory(command);
+ }
+}
+
+void
+RosegardenGUIApp::slotSetSegmentDurations()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ SegmentSelection selection = m_view->getSelection();
+ if (selection.empty())
+ return ;
+
+ timeT someTime =
+ (*selection.begin())->getStartTime();
+
+ timeT someDuration =
+ (*selection.begin())->getEndMarkerTime() -
+ (*selection.begin())->getStartTime();
+
+ TimeDialog dialog(m_view, i18n("Segment Duration"),
+ &m_doc->getComposition(),
+ someTime,
+ someDuration,
+ false);
+
+ if (dialog.exec() == QDialog::Accepted) {
+
+ bool plural = (selection.size() > 1);
+
+ SegmentReconfigureCommand *command =
+ new SegmentReconfigureCommand(plural ?
+ i18n("Set Segment Durations") :
+ i18n("Set Segment Duration"));
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+
+ command->addSegment
+ (*i, (*i)->getStartTime(),
+ (*i)->getStartTime() + dialog.getTime(),
+ (*i)->getTrack());
+ }
+
+ m_view->slotAddCommandToHistory(command);
+ }
+}
+
+void RosegardenGUIApp::slotHarmonizeSelection()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ SegmentSelection selection = m_view->getSelection();
+ //!!! This should be somewhere else too
+
+ CompositionTimeSliceAdapter adapter(&m_doc->getComposition(),
+ &selection);
+
+ AnalysisHelper helper;
+ Segment *segment = new Segment;
+ helper.guessHarmonies(adapter, *segment);
+
+ //!!! do nothing with the results yet
+ delete segment;
+}
+
+void RosegardenGUIApp::slotTempoToSegmentLength()
+{
+ slotTempoToSegmentLength(this);
+}
+
+void RosegardenGUIApp::slotTempoToSegmentLength(QWidget* parent)
+{
+ RG_DEBUG << "RosegardenGUIApp::slotTempoToSegmentLength" << endl;
+
+ if (!m_view->haveSelection())
+ return ;
+
+ SegmentSelection selection = m_view->getSelection();
+
+ // Only set for a single selection
+ //
+ if (selection.size() == 1 &&
+ (*selection.begin())->getType() == Segment::Audio) {
+ Composition &comp = m_doc->getComposition();
+ Segment *seg = *selection.begin();
+
+ TimeSignature timeSig =
+ comp.getTimeSignatureAt( seg->getStartTime());
+
+ timeT endTime = seg->getEndTime();
+
+ if (seg->getRawEndMarkerTime())
+ endTime = seg->getEndMarkerTime();
+
+ RealTime segDuration =
+ seg->getAudioEndTime() - seg->getAudioStartTime();
+
+ int beats = 0;
+
+ // Get user to tell us how many beats or bars the segment contains
+ BeatsBarsDialog dialog(parent);
+ if (dialog.exec() == QDialog::Accepted) {
+ beats = dialog.getQuantity(); // beats (or bars)
+ if (dialog.getMode() == 1) // bars (multiply by time sig)
+ beats *= timeSig.getBeatsPerBar();
+#ifdef DEBUG_TEMPO_FROM_AUDIO
+
+ RG_DEBUG << "RosegardenGUIApp::slotTempoToSegmentLength - beats = " << beats
+ << " mode = " << ((dialog.getMode() == 0) ? "bars" : "beats") << endl
+ << " beats per bar = " << timeSig.getBeatsPerBar()
+ << " user quantity = " << dialog.getQuantity()
+ << " user mode = " << dialog.getMode() << endl;
+#endif
+
+ } else {
+ RG_DEBUG << "RosegardenGUIApp::slotTempoToSegmentLength - BeatsBarsDialog aborted"
+ << endl;
+ return ;
+ }
+
+ double beatLengthUsec =
+ double(segDuration.sec * 1000000 + segDuration.usec()) /
+ double(beats);
+
+ // New tempo is a minute divided by time of beat
+ // converted up (#1414252) to a sane value via getTempoFoQpm()
+ //
+ tempoT newTempo =
+ comp.getTempoForQpm(60.0 * 1000000.0 / beatLengthUsec);
+
+#ifdef DEBUG_TEMPO_FROM_AUDIO
+
+ RG_DEBUG << "RosegardenGUIApp::slotTempoToSegmentLength info: " << endl
+ << " beatLengthUsec = " << beatLengthUsec << endl
+ << " segDuration.usec = " << segDuration.usec() << endl
+ << " newTempo = " << newTempo << endl;
+#endif
+
+ KMacroCommand *macro = new KMacroCommand(i18n("Set Global Tempo"));
+
+ // Remove all tempo changes in reverse order so as the index numbers
+ // don't becoming meaningless as the command gets unwound.
+ //
+ for (int i = 0; i < comp.getTempoChangeCount(); i++)
+ macro->addCommand(new RemoveTempoChangeCommand(&comp,
+ (comp.getTempoChangeCount() - 1 - i)));
+
+ // add tempo change at time zero
+ //
+ macro->addCommand(new AddTempoChangeCommand(&comp, 0, newTempo));
+
+ // execute
+ m_doc->getCommandHistory()->addCommand(macro);
+ }
+}
+
+void RosegardenGUIApp::slotToggleSegmentLabels()
+{
+ KToggleAction* act = dynamic_cast<KToggleAction*>(actionCollection()->action("show_segment_labels"));
+ if (act) {
+ m_view->slotShowSegmentLabels(act->isChecked());
+ }
+}
+
+void RosegardenGUIApp::slotEdit()
+{
+ m_view->slotEditSegment(0);
+}
+
+void RosegardenGUIApp::slotEditAsNotation()
+{
+ m_view->slotEditSegmentNotation(0);
+}
+
+void RosegardenGUIApp::slotEditInMatrix()
+{
+ m_view->slotEditSegmentMatrix(0);
+}
+
+void RosegardenGUIApp::slotEditInPercussionMatrix()
+{
+ m_view->slotEditSegmentPercussionMatrix(0);
+}
+
+void RosegardenGUIApp::slotEditInEventList()
+{
+ m_view->slotEditSegmentEventList(0);
+}
+
+void RosegardenGUIApp::slotEditTempos()
+{
+ slotEditTempos(m_doc->getComposition().getPosition());
+}
+
+void RosegardenGUIApp::slotToggleToolBar()
+{
+ KTmpStatusMsg msg(i18n("Toggle the toolbar..."), this);
+
+ if (m_viewToolBar->isChecked())
+ toolBar("mainToolBar")->show();
+ else
+ toolBar("mainToolBar")->hide();
+}
+
+void RosegardenGUIApp::slotToggleToolsToolBar()
+{
+ KTmpStatusMsg msg(i18n("Toggle the tools toolbar..."), this);
+
+ if (m_viewToolsToolBar->isChecked())
+ toolBar("Tools Toolbar")->show();
+ else
+ toolBar("Tools Toolbar")->hide();
+}
+
+void RosegardenGUIApp::slotToggleTracksToolBar()
+{
+ KTmpStatusMsg msg(i18n("Toggle the tracks toolbar..."), this);
+
+ if (m_viewTracksToolBar->isChecked())
+ toolBar("Tracks Toolbar")->show();
+ else
+ toolBar("Tracks Toolbar")->hide();
+}
+
+void RosegardenGUIApp::slotToggleEditorsToolBar()
+{
+ KTmpStatusMsg msg(i18n("Toggle the editor toolbar..."), this);
+
+ if (m_viewEditorsToolBar->isChecked())
+ toolBar("Editors Toolbar")->show();
+ else
+ toolBar("Editors Toolbar")->hide();
+}
+
+void RosegardenGUIApp::slotToggleTransportToolBar()
+{
+ KTmpStatusMsg msg(i18n("Toggle the transport toolbar..."), this);
+
+ if (m_viewTransportToolBar->isChecked())
+ toolBar("Transport Toolbar")->show();
+ else
+ toolBar("Transport Toolbar")->hide();
+}
+
+void RosegardenGUIApp::slotToggleZoomToolBar()
+{
+ KTmpStatusMsg msg(i18n("Toggle the zoom toolbar..."), this);
+
+ if (m_viewZoomToolBar->isChecked())
+ toolBar("Zoom Toolbar")->show();
+ else
+ toolBar("Zoom Toolbar")->hide();
+}
+
+void RosegardenGUIApp::slotToggleTransport()
+{
+ KTmpStatusMsg msg(i18n("Toggle the Transport"), this);
+
+ if (m_viewTransport->isChecked()) {
+ getTransport()->show();
+ getTransport()->raise();
+ getTransport()->blockSignals(false);
+ } else {
+ getTransport()->hide();
+ getTransport()->blockSignals(true);
+ }
+}
+
+void RosegardenGUIApp::slotHideTransport()
+{
+ if (m_viewTransport->isChecked()) {
+ m_viewTransport->blockSignals(true);
+ m_viewTransport->setChecked(false);
+ m_viewTransport->blockSignals(false);
+ }
+ getTransport()->hide();
+ getTransport()->blockSignals(true);
+}
+
+void RosegardenGUIApp::slotToggleTrackLabels()
+{
+ if (m_viewTrackLabels->isChecked()) {
+#ifdef SETTING_LOG_DEBUG
+ _settingLog("toggle track labels on");
+#endif
+
+ m_view->getTrackEditor()->getTrackButtons()->
+ changeTrackInstrumentLabels(TrackLabel::ShowTrack);
+ } else {
+#ifdef SETTING_LOG_DEBUG
+ _settingLog("toggle track labels off");
+#endif
+
+ m_view->getTrackEditor()->getTrackButtons()->
+ changeTrackInstrumentLabels(TrackLabel::ShowInstrument);
+ }
+}
+
+void RosegardenGUIApp::slotToggleRulers()
+{
+ m_view->slotShowRulers(m_viewRulers->isChecked());
+}
+
+void RosegardenGUIApp::slotToggleTempoRuler()
+{
+ m_view->slotShowTempoRuler(m_viewTempoRuler->isChecked());
+}
+
+void RosegardenGUIApp::slotToggleChordNameRuler()
+{
+ m_view->slotShowChordNameRuler(m_viewChordNameRuler->isChecked());
+}
+
+void RosegardenGUIApp::slotTogglePreviews()
+{
+ m_view->slotShowPreviews(m_viewPreviews->isChecked());
+}
+
+void RosegardenGUIApp::slotDockParametersBack()
+{
+ m_dockLeft->dockBack();
+}
+
+void RosegardenGUIApp::slotParametersClosed()
+{
+ stateChanged("parametersbox_closed");
+ m_dockVisible = false;
+}
+
+void RosegardenGUIApp::slotParametersDockedBack(KDockWidget* dw, KDockWidget::DockPosition)
+{
+ if (dw == m_dockLeft) {
+ stateChanged("parametersbox_closed", KXMLGUIClient::StateReverse);
+ m_dockVisible = true;
+ }
+}
+
+void RosegardenGUIApp::slotToggleStatusBar()
+{
+ KTmpStatusMsg msg(i18n("Toggle the statusbar..."), this);
+
+ if (!m_viewStatusBar->isChecked())
+ statusBar()->hide();
+ else
+ statusBar()->show();
+}
+
+void RosegardenGUIApp::slotStatusMsg(QString text)
+{
+ ///////////////////////////////////////////////////////////////////
+ // change status message permanently
+ statusBar()->clear();
+ statusBar()->changeItem(text, EditViewBase::ID_STATUS_MSG);
+}
+
+void RosegardenGUIApp::slotStatusHelpMsg(QString text)
+{
+ ///////////////////////////////////////////////////////////////////
+ // change status message of whole statusbar temporary (text, msec)
+ statusBar()->message(text, 2000);
+}
+
+void RosegardenGUIApp::slotEnableTransport(bool enable)
+{
+ if (m_transport)
+ getTransport()->setEnabled(enable);
+}
+
+void RosegardenGUIApp::slotPointerSelected()
+{
+ m_view->selectTool(SegmentSelector::ToolName);
+}
+
+void RosegardenGUIApp::slotEraseSelected()
+{
+ m_view->selectTool(SegmentEraser::ToolName);
+}
+
+void RosegardenGUIApp::slotDrawSelected()
+{
+ m_view->selectTool(SegmentPencil::ToolName);
+}
+
+void RosegardenGUIApp::slotMoveSelected()
+{
+ m_view->selectTool(SegmentMover::ToolName);
+}
+
+void RosegardenGUIApp::slotResizeSelected()
+{
+ m_view->selectTool(SegmentResizer::ToolName);
+}
+
+void RosegardenGUIApp::slotJoinSelected()
+{
+ KMessageBox::information(this,
+ i18n("The join tool isn't implemented yet. Instead please highlight "
+ "the segments you want to join and then use the menu option:\n\n"
+ " Segments->Collapse Segments.\n"),
+ i18n("Join tool not yet implemented"));
+
+ m_view->selectTool(SegmentJoiner::ToolName);
+}
+
+void RosegardenGUIApp::slotSplitSelected()
+{
+ m_view->selectTool(SegmentSplitter::ToolName);
+}
+
+void RosegardenGUIApp::slotAddTrack()
+{
+ if (!m_view)
+ return ;
+
+ // default to the base number - might not actually exist though
+ //
+ InstrumentId id = MidiInstrumentBase;
+
+ // Get the first Internal/MIDI instrument
+ //
+ DeviceList *devices = m_doc->getStudio().getDevices();
+ bool have = false;
+
+ for (DeviceList::iterator it = devices->begin();
+ it != devices->end() && !have; it++) {
+
+ if ((*it)->getType() != Device::Midi)
+ continue;
+
+ InstrumentList instruments = (*it)->getAllInstruments();
+ for (InstrumentList::iterator iit = instruments.begin();
+ iit != instruments.end(); iit++) {
+
+ if ((*iit)->getId() >= MidiInstrumentBase) {
+ id = (*iit)->getId();
+ have = true;
+ break;
+ }
+ }
+ }
+
+ Composition &comp = m_doc->getComposition();
+ TrackId trackId = comp.getSelectedTrack();
+ Track *track = comp.getTrackById(trackId);
+
+ int pos = -1;
+ if (track) pos = track->getPosition() + 1;
+
+ m_view->slotAddTracks(1, id, pos);
+}
+
+void RosegardenGUIApp::slotAddTracks()
+{
+ if (!m_view)
+ return ;
+
+ // default to the base number - might not actually exist though
+ //
+ InstrumentId id = MidiInstrumentBase;
+
+ // Get the first Internal/MIDI instrument
+ //
+ DeviceList *devices = m_doc->getStudio().getDevices();
+ bool have = false;
+
+ for (DeviceList::iterator it = devices->begin();
+ it != devices->end() && !have; it++) {
+
+ if ((*it)->getType() != Device::Midi)
+ continue;
+
+ InstrumentList instruments = (*it)->getAllInstruments();
+ for (InstrumentList::iterator iit = instruments.begin();
+ iit != instruments.end(); iit++) {
+
+ if ((*iit)->getId() >= MidiInstrumentBase) {
+ id = (*iit)->getId();
+ have = true;
+ break;
+ }
+ }
+ }
+
+ Composition &comp = m_doc->getComposition();
+ TrackId trackId = comp.getSelectedTrack();
+ Track *track = comp.getTrackById(trackId);
+
+ int pos = 0;
+ if (track) pos = track->getPosition();
+
+ bool ok = false;
+
+ AddTracksDialog dialog(this, pos);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ m_view->slotAddTracks(dialog.getTracks(), id,
+ dialog.getInsertPosition());
+ }
+}
+
+void RosegardenGUIApp::slotDeleteTrack()
+{
+ if (!m_view)
+ return ;
+
+ Composition &comp = m_doc->getComposition();
+ TrackId trackId = comp.getSelectedTrack();
+ Track *track = comp.getTrackById(trackId);
+
+ RG_DEBUG << "RosegardenGUIApp::slotDeleteTrack() : about to delete track id "
+ << trackId << endl;
+
+ if (track == 0)
+ return ;
+
+ // Always have at least one track in a composition
+ //
+ if (comp.getNbTracks() == 1)
+ return ;
+
+ // VLADA
+ if (m_view->haveSelection()) {
+
+ SegmentSelection selection = m_view->getSelection();
+ m_view->slotSelectTrackSegments(trackId);
+ m_view->getTrackEditor()->slotDeleteSelectedSegments();
+ m_view->slotPropagateSegmentSelection(selection);
+
+ } else {
+
+ m_view->slotSelectTrackSegments(trackId);
+ m_view->getTrackEditor()->slotDeleteSelectedSegments();
+ }
+ //VLADA
+
+ int position = track->getPosition();
+
+ // Delete the track
+ //
+ std::vector<TrackId> tracks;
+ tracks.push_back(trackId);
+
+ m_view->slotDeleteTracks(tracks);
+
+ // Select a new valid track
+ //
+ if (comp.getTrackByPosition(position))
+ trackId = comp.getTrackByPosition(position)->getId();
+ else if (comp.getTrackByPosition(position - 1))
+ trackId = comp.getTrackByPosition(position - 1)->getId();
+ else {
+ RG_DEBUG << "RosegardenGUIApp::slotDeleteTrack - "
+ << "can't select a highlighted track after delete"
+ << endl;
+ }
+
+ comp.setSelectedTrack(trackId);
+
+ Instrument *inst = m_doc->getStudio().
+ getInstrumentById(comp.getTrackById(trackId)->getInstrument());
+
+ //VLADA
+ // m_view->slotSelectTrackSegments(trackId);
+ //VLADA
+}
+
+void RosegardenGUIApp::slotMoveTrackDown()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotMoveTrackDown" << endl;
+
+ Composition &comp = m_doc->getComposition();
+ Track *srcTrack = comp.getTrackById(comp.getSelectedTrack());
+
+ // Check for track object
+ //
+ if (srcTrack == 0)
+ return ;
+
+ // Check destination track exists
+ //
+ Track *destTrack =
+ comp.getTrackByPosition(srcTrack->getPosition() + 1);
+
+ if (destTrack == 0)
+ return ;
+
+ MoveTracksCommand *command =
+ new MoveTracksCommand(&comp, srcTrack->getId(), destTrack->getId());
+
+ m_doc->getCommandHistory()->addCommand(command);
+
+ // make sure we're showing the right selection
+ m_view->slotSelectTrackSegments(comp.getSelectedTrack());
+
+}
+
+void RosegardenGUIApp::slotMoveTrackUp()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotMoveTrackUp" << endl;
+
+ Composition &comp = m_doc->getComposition();
+ Track *srcTrack = comp.getTrackById(comp.getSelectedTrack());
+
+ // Check for track object
+ //
+ if (srcTrack == 0)
+ return ;
+
+ // Check we're not at the top already
+ //
+ if (srcTrack->getPosition() == 0)
+ return ;
+
+ // Check destination track exists
+ //
+ Track *destTrack =
+ comp.getTrackByPosition(srcTrack->getPosition() - 1);
+
+ if (destTrack == 0)
+ return ;
+
+ MoveTracksCommand *command =
+ new MoveTracksCommand(&comp, srcTrack->getId(), destTrack->getId());
+
+ m_doc->getCommandHistory()->addCommand(command);
+
+ // make sure we're showing the right selection
+ m_view->slotSelectTrackSegments(comp.getSelectedTrack());
+}
+
+void RosegardenGUIApp::slotRevertToSaved()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotRevertToSaved" << endl;
+
+ if (m_doc->isModified()) {
+ int revert =
+ KMessageBox::questionYesNo(this,
+ i18n("Revert modified document to previous saved version?"));
+
+ if (revert == KMessageBox::No)
+ return ;
+
+ openFile(m_doc->getAbsFilePath());
+ }
+}
+
+void RosegardenGUIApp::slotImportProject()
+{
+ if (m_doc && !m_doc->saveIfModified())
+ return ;
+
+ KURL url = KFileDialog::getOpenURL
+ (":RGPROJECT",
+ i18n("*.rgp|Rosegarden Project files\n*|All files"), this,
+ i18n("Import Rosegarden Project File"));
+ if (url.isEmpty()) {
+ return ;
+ }
+
+ QString tmpfile;
+ KIO::NetAccess::download(url, tmpfile, this);
+
+ importProject(tmpfile);
+
+ KIO::NetAccess::removeTempFile(tmpfile);
+}
+
+void RosegardenGUIApp::importProject(QString filePath)
+{
+ KProcess *proc = new KProcess;
+ *proc << "rosegarden-project-package";
+ *proc << "--unpack";
+ *proc << filePath;
+
+ KStartupLogo::hideIfStillThere();
+ proc->start(KProcess::Block, KProcess::All);
+
+ if (!proc->normalExit() || proc->exitStatus()) {
+ CurrentProgressDialog::freeze();
+ KMessageBox::sorry(this, i18n("Failed to import project file \"%1\"").arg(filePath));
+ CurrentProgressDialog::thaw();
+ delete proc;
+ return ;
+ }
+
+ delete proc;
+
+ QString rgFile = filePath;
+ rgFile.replace(QRegExp(".rg.rgp$"), ".rg");
+ rgFile.replace(QRegExp(".rgp$"), ".rg");
+ openURL(rgFile);
+}
+
+void RosegardenGUIApp::slotImportMIDI()
+{
+ if (m_doc && !m_doc->saveIfModified())
+ return ;
+
+ KURL url = KFileDialog::getOpenURL
+ (":MIDI",
+ "audio/x-midi", this,
+ i18n("Open MIDI File"));
+ if (url.isEmpty()) {
+ return ;
+ }
+
+ QString tmpfile;
+ KIO::NetAccess::download(url, tmpfile, this);
+ openFile(tmpfile, ImportMIDI); // does everything including setting the document
+
+ KIO::NetAccess::removeTempFile( tmpfile );
+}
+
+void RosegardenGUIApp::slotMergeMIDI()
+{
+ KURL url = KFileDialog::getOpenURL
+ (":MIDI",
+ "audio/x-midi", this,
+ i18n("Merge MIDI File"));
+ if (url.isEmpty()) {
+ return ;
+ }
+
+ QString tmpfile;
+ KIO::NetAccess::download(url, tmpfile, this);
+ mergeFile(tmpfile, ImportMIDI);
+
+ KIO::NetAccess::removeTempFile( tmpfile );
+}
+
+QTextCodec *
+RosegardenGUIApp::guessTextCodec(std::string text)
+{
+ QTextCodec *codec = 0;
+
+ for (int c = 0; c < text.length(); ++c) {
+ if (text[c] & 0x80) {
+
+ CurrentProgressDialog::freeze();
+ KStartupLogo::hideIfStillThere();
+
+ IdentifyTextCodecDialog dialog(0, text);
+ dialog.exec();
+
+ std::string codecName = dialog.getCodec();
+
+ CurrentProgressDialog::thaw();
+
+ if (codecName != "") {
+ codec = QTextCodec::codecForName(codecName.c_str());
+ }
+ break;
+ }
+ }
+
+ return codec;
+}
+
+void
+RosegardenGUIApp::fixTextEncodings(Composition *c)
+
+{
+ QTextCodec *codec = 0;
+
+ for (Composition::iterator i = c->begin();
+ i != c->end(); ++i) {
+
+ for (Segment::iterator j = (*i)->begin();
+ j != (*i)->end(); ++j) {
+
+ if ((*j)->isa(Text::EventType)) {
+
+ std::string text;
+
+ if ((*j)->get
+ <String>
+ (Text::TextPropertyName, text)) {
+
+ if (!codec)
+ codec = guessTextCodec(text);
+
+ if (codec) {
+ (*j)->set
+ <String>
+ (Text::TextPropertyName,
+ convertFromCodec(text, codec));
+ }
+ }
+ }
+ }
+ }
+
+ if (!codec)
+ codec = guessTextCodec(c->getCopyrightNote());
+ if (codec)
+ c->setCopyrightNote(convertFromCodec(c->getCopyrightNote(), codec));
+
+ for (Composition::trackcontainer::iterator i =
+ c->getTracks().begin(); i != c->getTracks().end(); ++i) {
+ if (!codec)
+ codec = guessTextCodec(i->second->getLabel());
+ if (codec)
+ i->second->setLabel(convertFromCodec(i->second->getLabel(), codec));
+ }
+
+ for (Composition::iterator i = c->begin(); i != c->end(); ++i) {
+ if (!codec)
+ codec = guessTextCodec((*i)->getLabel());
+ if (codec)
+ (*i)->setLabel(convertFromCodec((*i)->getLabel(), codec));
+ }
+}
+
+RosegardenGUIDoc*
+RosegardenGUIApp::createDocumentFromMIDIFile(QString file)
+{
+ //if (!merge && !m_doc->saveIfModified()) return;
+
+ // Create new document (autoload is inherent)
+ //
+ RosegardenGUIDoc *newDoc = new RosegardenGUIDoc(this, m_pluginManager);
+
+ std::string fname(QFile::encodeName(file));
+
+ MidiFile midiFile(fname,
+ &newDoc->getStudio());
+
+ KStartupLogo::hideIfStillThere();
+ ProgressDialog progressDlg(i18n("Importing MIDI file..."),
+ 200,
+ this);
+
+ CurrentProgressDialog::set
+ (&progressDlg);
+
+ connect(&midiFile, SIGNAL(setProgress(int)),
+ progressDlg.progressBar(), SLOT(setValue(int)));
+
+ connect(&midiFile, SIGNAL(incrementProgress(int)),
+ progressDlg.progressBar(), SLOT(advance(int)));
+
+ if (!midiFile.open()) {
+ CurrentProgressDialog::freeze();
+ KMessageBox::error(this, strtoqstr(midiFile.getError())); //!!! i18n
+ delete newDoc;
+ return 0;
+ }
+
+ midiFile.convertToRosegarden(newDoc->getComposition(),
+ MidiFile::CONVERT_REPLACE);
+
+ fixTextEncodings(&newDoc->getComposition());
+
+ // Set modification flag
+ //
+ newDoc->slotDocumentModified();
+
+ // Set the caption
+ //
+ newDoc->setTitle(QFileInfo(file).fileName());
+ newDoc->setAbsFilePath(QFileInfo(file).absFilePath());
+
+ // Clean up for notation purposes (after reinitialise, because that
+ // sets the composition's end marker time which is needed here)
+
+ progressDlg.slotSetOperationName(i18n("Calculating notation..."));
+ ProgressDialog::processEvents();
+
+ Composition *comp = &newDoc->getComposition();
+
+ for (Composition::iterator i = comp->begin();
+ i != comp->end(); ++i) {
+
+ Segment &segment = **i;
+ SegmentNotationHelper helper(segment);
+ segment.insert(helper.guessClef(segment.begin(),
+ segment.getEndMarker()).getAsEvent
+ (segment.getStartTime()));
+ }
+
+ progressDlg.progressBar()->setProgress(100);
+
+ for (Composition::iterator i = comp->begin();
+ i != comp->end(); ++i) {
+
+ // find first key event in each segment (we'd have done the
+ // same for clefs, except there is no MIDI clef event)
+
+ Segment &segment = **i;
+ timeT firstKeyTime = segment.getEndMarkerTime();
+
+ for (Segment::iterator si = segment.begin();
+ segment.isBeforeEndMarker(si); ++si) {
+ if ((*si)->isa(Rosegarden::Key::EventType)) {
+ firstKeyTime = (*si)->getAbsoluteTime();
+ break;
+ }
+ }
+
+ if (firstKeyTime > segment.getStartTime()) {
+ CompositionTimeSliceAdapter adapter
+ (comp, timeT(0), firstKeyTime);
+ AnalysisHelper helper;
+ segment.insert(helper.guessKey(adapter).getAsEvent
+ (segment.getStartTime()));
+ }
+ }
+
+ int progressPer = 100;
+ if (comp->getNbSegments() > 0)
+ progressPer = (int)(100.0 / double(comp->getNbSegments()));
+
+ KMacroCommand *command = new KMacroCommand(i18n("Calculate Notation"));
+
+ for (Composition::iterator i = comp->begin();
+ i != comp->end(); ++i) {
+
+ Segment &segment = **i;
+ timeT startTime(segment.getStartTime());
+ timeT endTime(segment.getEndMarkerTime());
+
+// std::cerr << "segment: start time " << segment.getStartTime() << ", end time " << segment.getEndTime() << ", end marker time " << segment.getEndMarkerTime() << ", events " << segment.size() << std::endl;
+
+ EventQuantizeCommand *subCommand = new EventQuantizeCommand
+ (segment, startTime, endTime, "Notation Options", true);
+
+ subCommand->setProgressTotal(progressPer + 1);
+ QObject::connect(subCommand, SIGNAL(incrementProgress(int)),
+ progressDlg.progressBar(), SLOT(advance(int)));
+
+ command->addCommand(subCommand);
+ }
+
+ newDoc->getCommandHistory()->addCommand(command);
+
+ if (comp->getTimeSignatureCount() == 0) {
+ CompositionTimeSliceAdapter adapter(comp);
+ AnalysisHelper analysisHelper;
+ TimeSignature timeSig =
+ analysisHelper.guessTimeSignature(adapter);
+ comp->addTimeSignature(0, timeSig);
+ }
+
+ return newDoc;
+}
+
+void RosegardenGUIApp::slotImportRG21()
+{
+ if (m_doc && !m_doc->saveIfModified())
+ return ;
+
+ KURL url = KFileDialog::getOpenURL
+ (":ROSEGARDEN21",
+ i18n("*.rose|Rosegarden-2 files\n*|All files"), this,
+ i18n("Open Rosegarden 2.1 File"));
+ if (url.isEmpty()) {
+ return ;
+ }
+
+ QString tmpfile;
+ KIO::NetAccess::download(url, tmpfile, this);
+ openFile(tmpfile, ImportRG21);
+
+ KIO::NetAccess::removeTempFile(tmpfile);
+}
+
+void RosegardenGUIApp::slotMergeRG21()
+{
+ KURL url = KFileDialog::getOpenURL
+ (":ROSEGARDEN21",
+ i18n("*.rose|Rosegarden-2 files\n*|All files"), this,
+ i18n("Open Rosegarden 2.1 File"));
+ if (url.isEmpty()) {
+ return ;
+ }
+
+ QString tmpfile;
+ KIO::NetAccess::download(url, tmpfile, this);
+ mergeFile(tmpfile, ImportRG21);
+
+ KIO::NetAccess::removeTempFile( tmpfile );
+}
+
+RosegardenGUIDoc*
+RosegardenGUIApp::createDocumentFromRG21File(QString file)
+{
+ KStartupLogo::hideIfStillThere();
+ ProgressDialog progressDlg(
+ i18n("Importing Rosegarden 2.1 file..."), 100, this);
+
+ CurrentProgressDialog::set
+ (&progressDlg);
+
+ // Inherent autoload
+ //
+ RosegardenGUIDoc *newDoc = new RosegardenGUIDoc(this, m_pluginManager);
+
+ RG21Loader rg21Loader(&newDoc->getStudio());
+
+ // TODO: make RG21Loader to actually emit these signals
+ //
+ connect(&rg21Loader, SIGNAL(setProgress(int)),
+ progressDlg.progressBar(), SLOT(setValue(int)));
+
+ connect(&rg21Loader, SIGNAL(incrementProgress(int)),
+ progressDlg.progressBar(), SLOT(advance(int)));
+
+ // "your starter for 40%" - helps the "freeze" work
+ //
+ progressDlg.progressBar()->advance(40);
+
+ if (!rg21Loader.load(file, newDoc->getComposition())) {
+ CurrentProgressDialog::freeze();
+ KMessageBox::error(this,
+ i18n("Can't load Rosegarden 2.1 file. It appears to be corrupted."));
+ delete newDoc;
+ return 0;
+ }
+
+ // Set modification flag
+ //
+ newDoc->slotDocumentModified();
+
+ // Set the caption and add recent
+ //
+ newDoc->setTitle(QFileInfo(file).fileName());
+ newDoc->setAbsFilePath(QFileInfo(file).absFilePath());
+
+ return newDoc;
+
+}
+
+void
+RosegardenGUIApp::slotImportHydrogen()
+{
+ if (m_doc && !m_doc->saveIfModified())
+ return ;
+
+ KURL url = KFileDialog::getOpenURL
+ (":HYDROGEN",
+ i18n("*.h2song|Hydrogen files\n*|All files"), this,
+ i18n("Open Hydrogen File"));
+ if (url.isEmpty()) {
+ return ;
+ }
+
+ QString tmpfile;
+ KIO::NetAccess::download(url, tmpfile, this);
+ openFile(tmpfile, ImportHydrogen);
+
+ KIO::NetAccess::removeTempFile(tmpfile);
+}
+
+void RosegardenGUIApp::slotMergeHydrogen()
+{
+ KURL url = KFileDialog::getOpenURL
+ (":HYDROGEN",
+ i18n("*.h2song|Hydrogen files\n*|All files"), this,
+ i18n("Open Hydrogen File"));
+ if (url.isEmpty()) {
+ return ;
+ }
+
+ QString tmpfile;
+ KIO::NetAccess::download(url, tmpfile, this);
+ mergeFile(tmpfile, ImportHydrogen);
+
+ KIO::NetAccess::removeTempFile( tmpfile );
+}
+
+RosegardenGUIDoc*
+RosegardenGUIApp::createDocumentFromHydrogenFile(QString file)
+{
+ KStartupLogo::hideIfStillThere();
+ ProgressDialog progressDlg(
+ i18n("Importing Hydrogen file..."), 100, this);
+
+ CurrentProgressDialog::set
+ (&progressDlg);
+
+ // Inherent autoload
+ //
+ RosegardenGUIDoc *newDoc = new RosegardenGUIDoc(this, m_pluginManager);
+
+ HydrogenLoader hydrogenLoader(&newDoc->getStudio());
+
+ // TODO: make RG21Loader to actually emit these signals
+ //
+ connect(&hydrogenLoader, SIGNAL(setProgress(int)),
+ progressDlg.progressBar(), SLOT(setValue(int)));
+
+ connect(&hydrogenLoader, SIGNAL(incrementProgress(int)),
+ progressDlg.progressBar(), SLOT(advance(int)));
+
+ // "your starter for 40%" - helps the "freeze" work
+ //
+ progressDlg.progressBar()->advance(40);
+
+ if (!hydrogenLoader.load(file, newDoc->getComposition())) {
+ CurrentProgressDialog::freeze();
+ KMessageBox::error(this,
+ i18n("Can't load Hydrogen file. It appears to be corrupted."));
+ delete newDoc;
+ return 0;
+ }
+
+ // Set modification flag
+ //
+ newDoc->slotDocumentModified();
+
+ // Set the caption and add recent
+ //
+ newDoc->setTitle(QFileInfo(file).fileName());
+ newDoc->setAbsFilePath(QFileInfo(file).absFilePath());
+
+ return newDoc;
+
+}
+
+void
+RosegardenGUIApp::mergeFile(QString filePath, ImportType type)
+{
+ RosegardenGUIDoc *doc = createDocument(filePath, type);
+
+ if (doc) {
+ if (m_doc) {
+
+ bool timingsDiffer = false;
+ Composition &c1 = m_doc->getComposition();
+ Composition &c2 = doc->getComposition();
+
+ // compare tempos and time sigs in the two -- rather laborious
+
+ if (c1.getTimeSignatureCount() != c2.getTimeSignatureCount()) {
+ timingsDiffer = true;
+ } else {
+ for (int i = 0; i < c1.getTimeSignatureCount(); ++i) {
+ std::pair<timeT, TimeSignature> t1 =
+ c1.getTimeSignatureChange(i);
+ std::pair<timeT, TimeSignature> t2 =
+ c2.getTimeSignatureChange(i);
+ if (t1.first != t2.first || t1.second != t2.second) {
+ timingsDiffer = true;
+ break;
+ }
+ }
+ }
+
+ if (c1.getTempoChangeCount() != c2.getTempoChangeCount()) {
+ timingsDiffer = true;
+ } else {
+ for (int i = 0; i < c1.getTempoChangeCount(); ++i) {
+ std::pair<timeT, tempoT> t1 = c1.getTempoChange(i);
+ std::pair<timeT, tempoT> t2 = c2.getTempoChange(i);
+ if (t1.first != t2.first || t1.second != t2.second) {
+ timingsDiffer = true;
+ break;
+ }
+ }
+ }
+
+ FileMergeDialog dialog(this, filePath, timingsDiffer);
+ if (dialog.exec() == QDialog::Accepted) {
+ m_doc->mergeDocument(doc, dialog.getMergeOptions());
+ }
+
+ delete doc;
+
+ } else {
+ setDocument(doc);
+ }
+ }
+}
+
+void
+RosegardenGUIApp::slotUpdatePlaybackPosition()
+{
+ static int callbackCount = 0;
+
+ // Either sequencer mappper or the sequence manager could be missing at
+ // this point.
+ //
+ if (!m_seqManager || !m_seqManager->getSequencerMapper())
+ return ;
+
+ SequencerMapper *mapper = m_seqManager->getSequencerMapper();
+
+ MappedEvent ev;
+ bool haveEvent = mapper->getVisual(ev);
+ if (haveEvent)
+ getTransport()->setMidiOutLabel(&ev);
+
+ RealTime position = mapper->getPositionPointer();
+
+ // std::cerr << "RosegardenGUIApp::slotUpdatePlaybackPosition: mapper pos = " << position << std::endl;
+
+ Composition &comp = m_doc->getComposition();
+ timeT elapsedTime = comp.getElapsedTimeForRealTime(position);
+
+ // std::cerr << "RosegardenGUIApp::slotUpdatePlaybackPosition: mapper timeT = " << elapsedTime << std::endl;
+
+ if (m_seqManager->getTransportStatus() == RECORDING) {
+
+ MappedComposition mC;
+ if (mapper->getRecordedEvents(mC) > 0) {
+ m_seqManager->processAsynchronousMidi(mC, 0);
+ m_doc->insertRecordedMidi(mC);
+ }
+
+ m_doc->updateRecordingMIDISegment();
+ m_doc->updateRecordingAudioSegments();
+ }
+
+ m_originatingJump = true;
+ m_doc->slotSetPointerPosition(elapsedTime);
+ m_originatingJump = false;
+
+ if (m_audioMixer && m_audioMixer->isVisible())
+ m_audioMixer->updateMeters(mapper);
+
+ if (m_midiMixer && m_midiMixer->isVisible())
+ m_midiMixer->updateMeters(mapper);
+
+ m_view->updateMeters(mapper);
+
+ if (++callbackCount == 60) {
+ slotUpdateCPUMeter(true);
+ callbackCount = 0;
+ }
+
+ // if (elapsedTime >= comp.getEndMarker())
+ // slotStop();
+}
+
+void
+RosegardenGUIApp::slotUpdateCPUMeter(bool playing)
+{
+ static std::ifstream *statstream = 0;
+ static bool modified = false;
+ static unsigned long lastBusy = 0, lastIdle = 0;
+
+ if (playing) {
+
+ if (!statstream) {
+ statstream = new std::ifstream("/proc/stat", std::ios::in);
+ }
+
+ if (!statstream || !*statstream)
+ return ;
+ statstream->seekg(0, std::ios::beg);
+
+ std::string cpu;
+ unsigned long user, nice, sys, idle;
+ *statstream >> cpu;
+ *statstream >> user;
+ *statstream >> nice;
+ *statstream >> sys;
+ *statstream >> idle;
+
+ unsigned long busy = user + nice + sys;
+ unsigned long count = 0;
+
+ if (lastBusy > 0) {
+ unsigned long bd = busy - lastBusy;
+ unsigned long id = idle - lastIdle;
+ if (bd + id > 0)
+ count = bd * 100 / (bd + id);
+ if (count > 100)
+ count = 100;
+ }
+
+ lastBusy = busy;
+ lastIdle = idle;
+
+ if (m_progressBar) {
+ if (!modified) {
+ m_progressBar->setTextEnabled(true);
+ m_progressBar->setFormat("CPU");
+ }
+ m_progressBar->setProgress(count);
+ }
+
+ modified = true;
+
+ } else if (modified) {
+ if (m_progressBar) {
+ m_progressBar->setTextEnabled(false);
+ m_progressBar->setFormat("%p%");
+ m_progressBar->setProgress(0);
+ }
+ modified = false;
+ }
+}
+
+void
+RosegardenGUIApp::slotUpdateMonitoring()
+{
+ // Either sequencer mappper or the sequence manager could be missing at
+ // this point.
+ //
+ if (!m_seqManager || !m_seqManager->getSequencerMapper())
+ return ;
+
+ SequencerMapper *mapper = m_seqManager->getSequencerMapper();
+
+ if (m_audioMixer && m_audioMixer->isVisible())
+ m_audioMixer->updateMonitorMeters(mapper);
+
+ if (m_midiMixer && m_midiMixer->isVisible())
+ m_midiMixer->updateMonitorMeter(mapper);
+
+ m_view->updateMonitorMeters(mapper);
+
+ slotUpdateCPUMeter(false);
+}
+
+void RosegardenGUIApp::slotSetPointerPosition(timeT t)
+{
+ Composition &comp = m_doc->getComposition();
+
+ // std::cerr << "RosegardenGUIApp::slotSetPointerPosition: t = " << t << std::endl;
+
+ if (m_seqManager) {
+ if ( m_seqManager->getTransportStatus() == PLAYING ||
+ m_seqManager->getTransportStatus() == RECORDING ) {
+ if (t > comp.getEndMarker()) {
+ if (m_seqManager->getTransportStatus() == PLAYING) {
+
+ slotStop();
+ t = comp.getEndMarker();
+ m_doc->slotSetPointerPosition(t); //causes this method to be re-invoked
+ return ;
+
+ } else { // if recording, increase composition duration
+ std::pair<timeT, timeT> timeRange = comp.getBarRangeForTime(t);
+ timeT barDuration = timeRange.second - timeRange.first;
+ timeT newEndMarker = t + 10 * barDuration;
+ comp.setEndMarker(newEndMarker);
+ getView()->getTrackEditor()->slotReadjustCanvasSize();
+ getView()->getTrackEditor()->updateRulers();
+ }
+ }
+ }
+
+ // cc 20050520 - jump at the sequencer even if we're not playing,
+ // because we might be a transport master of some kind
+ try {
+ if (!m_originatingJump) {
+ m_seqManager->sendSequencerJump(comp.getElapsedRealTime(t));
+ }
+ } catch (QString s) {
+ KMessageBox::error(this, s);
+ }
+ }
+
+ // set the time sig
+ getTransport()->setTimeSignature(comp.getTimeSignatureAt(t));
+
+ // and the tempo
+ getTransport()->setTempo(comp.getTempoAtTime(t));
+
+ // and the time
+ //
+ TransportDialog::TimeDisplayMode mode =
+ getTransport()->getCurrentMode();
+
+ if (mode == TransportDialog::BarMode ||
+ mode == TransportDialog::BarMetronomeMode) {
+
+ slotDisplayBarTime(t);
+
+ } else {
+
+ RealTime rT(comp.getElapsedRealTime(t));
+
+ if (getTransport()->isShowingTimeToEnd()) {
+ rT = rT - comp.getElapsedRealTime(comp.getDuration());
+ }
+
+ if (mode == TransportDialog::RealMode) {
+
+ getTransport()->displayRealTime(rT);
+
+ } else if (mode == TransportDialog::SMPTEMode) {
+
+ getTransport()->displaySMPTETime(rT);
+
+ } else {
+
+ getTransport()->displayFrameTime(rT);
+ }
+ }
+
+ // handle transport mode configuration changes
+ std::string modeAsString = getTransport()->getCurrentModeAsString();
+
+ if (m_doc->getConfiguration().get<String>
+ (DocumentConfiguration::TransportMode) != modeAsString) {
+
+ m_doc->getConfiguration().set<String>
+ (DocumentConfiguration::TransportMode, modeAsString);
+
+ //m_doc->slotDocumentModified(); to avoid being prompted for a file change when merely changing the transport display
+ }
+
+ // Update position on the marker editor if it's available
+ //
+ if (m_markerEditor)
+ m_markerEditor->updatePosition();
+}
+
+void RosegardenGUIApp::slotDisplayBarTime(timeT t)
+{
+ Composition &comp = m_doc->getComposition();
+
+ int barNo = comp.getBarNumber(t);
+ timeT barStart = comp.getBarStart(barNo);
+
+ TimeSignature timeSig = comp.getTimeSignatureAt(t);
+ timeT beatDuration = timeSig.getBeatDuration();
+
+ int beatNo = (t - barStart) / beatDuration;
+ int unitNo = (t - barStart) - (beatNo * beatDuration);
+
+ if (getTransport()->isShowingTimeToEnd()) {
+ barNo = barNo + 1 - comp.getNbBars();
+ beatNo = timeSig.getBeatsPerBar() - 1 - beatNo;
+ unitNo = timeSig.getBeatDuration() - 1 - unitNo;
+ } else {
+ // convert to 1-based display bar numbers
+ barNo += 1;
+ beatNo += 1;
+ }
+
+ // show units in hemidemis (or whatever), not in raw time ticks
+ unitNo /= Note(Note::Shortest).getDuration();
+
+ getTransport()->displayBarTime(barNo, beatNo, unitNo);
+}
+
+void RosegardenGUIApp::slotRefreshTimeDisplay()
+{
+ if ( m_seqManager->getTransportStatus() == PLAYING ||
+ m_seqManager->getTransportStatus() == RECORDING ) {
+ return ; // it'll be refreshed in a moment anyway
+ }
+ slotSetPointerPosition(m_doc->getComposition().getPosition());
+}
+
+bool
+RosegardenGUIApp::isTrackEditorPlayTracking() const
+{
+ return m_view->getTrackEditor()->isTracking();
+}
+
+void RosegardenGUIApp::slotToggleTracking()
+{
+ m_view->getTrackEditor()->slotToggleTracking();
+}
+
+void RosegardenGUIApp::slotTestStartupTester()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotTestStartupTester" << endl;
+
+ if (!m_startupTester) {
+ m_startupTester = new StartupTester();
+ connect(m_startupTester, SIGNAL(newerVersionAvailable(QString)),
+ this, SLOT(slotNewerVersionAvailable(QString)));
+ m_startupTester->start();
+ QTimer::singleShot(100, this, SLOT(slotTestStartupTester()));
+ return ;
+ }
+
+ if (!m_startupTester->isReady()) {
+ QTimer::singleShot(100, this, SLOT(slotTestStartupTester()));
+ return ;
+ }
+
+ QStringList missingFeatures;
+ QStringList allMissing;
+
+ QStringList missing;
+ bool have = m_startupTester->haveProjectPackager(&missing);
+
+ stateChanged("have_project_packager",
+ have ?
+ KXMLGUIClient::StateNoReverse : KXMLGUIClient::StateReverse);
+
+ if (!have) {
+ missingFeatures.push_back(i18n("Export and import of Rosegarden Project files"));
+ if (missing.count() == 0) {
+ allMissing.push_back(i18n("The Rosegarden Project Packager helper script"));
+ } else {
+ for (int i = 0; i < missing.count(); ++i) {
+// if (missingFeatures.count() > 1) {
+ allMissing.push_back(i18n("%1 - for project file support").arg(missing[i]));
+// } else {
+// allMissing.push_back(missing[i]);
+// }
+ }
+ }
+ }
+
+ have = m_startupTester->haveLilyPondView(&missing);
+
+ stateChanged("have_lilypondview",
+ have ?
+ KXMLGUIClient::StateNoReverse : KXMLGUIClient::StateReverse);
+
+ if (!have) {
+ missingFeatures.push_back("Notation previews through LilyPond");
+ if (missing.count() == 0) {
+ allMissing.push_back(i18n("The Rosegarden LilyPondView helper script"));
+ } else {
+ for (int i = 0; i < missing.count(); ++i) {
+ if (missingFeatures.count() > 1) {
+ allMissing.push_back(i18n("%1 - for LilyPond preview support").arg(missing[i]));
+ } else {
+ allMissing.push_back(missing[i]);
+ }
+ }
+ }
+ }
+
+#ifdef HAVE_LIBJACK
+ if (m_seqManager && (m_seqManager->getSoundDriverStatus() & AUDIO_OK)) {
+
+ m_haveAudioImporter = m_startupTester->haveAudioFileImporter(&missing);
+
+ if (!m_haveAudioImporter) {
+ missingFeatures.push_back("General audio file import and conversion");
+ if (missing.count() == 0) {
+ allMissing.push_back(i18n("The Rosegarden Audio File Importer helper script"));
+ } else {
+ for (int i = 0; i < missing.count(); ++i) {
+ if (missingFeatures.count() > 1) {
+ allMissing.push_back(i18n("%1 - for audio file import").arg(missing[i]));
+ } else {
+ allMissing.push_back(missing[i]);
+ }
+ }
+ }
+ }
+ }
+#endif
+
+ if (missingFeatures.count() > 0) {
+ QString message = i18n("<h3>Helper programs not found</h3><p>Rosegarden could not find one or more helper programs which it needs to provide some features. The following features will not be available:</p>");
+ message += i18n("<ul>");
+ for (int i = 0; i < missingFeatures.count(); ++i) {
+ message += i18n("<li>%1</li>").arg(missingFeatures[i]);
+ }
+ message += i18n("</ul>");
+ message += i18n("<p>To fix this, you should install the following additional programs:</p>");
+ message += i18n("<ul>");
+ for (int i = 0; i < allMissing.count(); ++i) {
+ message += i18n("<li>%1</li>").arg(allMissing[i]);
+ }
+ message += i18n("</ul>");
+
+ awaitDialogClearance();
+
+ KMessageBox::information
+ (m_view,
+ message,
+ i18n("Helper programs not found"),
+ "startup-helpers-missing");
+ }
+
+ delete m_startupTester;
+ m_startupTester = 0;
+}
+
+void RosegardenGUIApp::slotDebugDump()
+{
+ Composition &comp = m_doc->getComposition();
+ comp.dump(std::cerr);
+}
+
+bool RosegardenGUIApp::launchSequencer(bool useExisting)
+{
+ if (!isUsingSequencer()) {
+ RG_DEBUG << "RosegardenGUIApp::launchSequencer() - not using seq. - returning\n";
+ return false; // no need to launch anything
+ }
+
+ if (isSequencerRunning()) {
+ RG_DEBUG << "RosegardenGUIApp::launchSequencer() - sequencer already running - returning\n";
+ if (m_seqManager) m_seqManager->checkSoundDriverStatus(false);
+ return true;
+ }
+
+ // Check to see if we're clearing down sequencer processes -
+ // if we're not we check DCOP for an existing sequencer and
+ // try to talk to use that (that's the "developer" mode).
+ //
+ // User mode should clear down sequencer processes.
+ //
+ if (kapp->dcopClient()->isApplicationRegistered(
+ QCString(ROSEGARDEN_SEQUENCER_APP_NAME))) {
+ RG_DEBUG << "RosegardenGUIApp::launchSequencer() - "
+ << "existing DCOP registered sequencer found\n";
+
+ if (useExisting) {
+ if (m_seqManager) m_seqManager->checkSoundDriverStatus(false);
+ m_sequencerProcess = (KProcess*)SequencerExternal;
+ return true;
+ }
+
+ KProcess *proc = new KProcess;
+ *proc << "/usr/bin/killall";
+ *proc << "rosegardensequencer";
+ *proc << "lt-rosegardensequencer";
+
+ proc->start(KProcess::Block, KProcess::All);
+
+ if (!proc->normalExit() || proc->exitStatus()) {
+ RG_DEBUG << "couldn't kill any sequencer processes" << endl;
+ }
+ delete proc;
+
+ sleep(1);
+
+ if (kapp->dcopClient()->isApplicationRegistered(
+ QCString(ROSEGARDEN_SEQUENCER_APP_NAME))) {
+ RG_DEBUG << "RosegardenGUIApp::launchSequencer() - "
+ << "failed to kill existing sequencer\n";
+
+ KProcess *proc = new KProcess;
+ *proc << "/usr/bin/killall";
+ *proc << "-9";
+ *proc << "rosegardensequencer";
+ *proc << "lt-rosegardensequencer";
+
+ proc->start(KProcess::Block, KProcess::All);
+
+ if (proc->exitStatus()) {
+ RG_DEBUG << "couldn't kill any sequencer processes" << endl;
+ }
+ delete proc;
+
+ sleep(1);
+ }
+ }
+
+ //
+ // No sequencer is running, so start one
+ //
+ KTmpStatusMsg msg(i18n("Starting the sequencer..."), this);
+
+ if (!m_sequencerProcess) {
+ m_sequencerProcess = new KProcess;
+
+ (*m_sequencerProcess) << "rosegardensequencer";
+
+ // Command line arguments
+ //
+ KConfig *config = kapp->config();
+ config->setGroup(SequencerOptionsConfigGroup);
+ QString options = config->readEntry("commandlineoptions");
+ if (!options.isEmpty()) {
+ (*m_sequencerProcess) << options;
+ RG_DEBUG << "sequencer options \"" << options << "\"" << endl;
+ }
+
+ } else {
+ RG_DEBUG << "RosegardenGUIApp::launchSequencer() - sequencer KProcess already created\n";
+ m_sequencerProcess->disconnect(); // disconnect processExit signal
+ // it will be reconnected later on
+ }
+
+ bool res = m_sequencerProcess->start();
+
+ if (!res) {
+ KMessageBox::error(0, i18n("Couldn't start the sequencer"));
+ RG_DEBUG << "Couldn't start the sequencer\n";
+ m_sequencerProcess = 0;
+ // If starting it didn't even work, fall back to no sequencer mode
+ m_useSequencer = false;
+ } else {
+ // connect processExited only after start, otherwise
+ // a failed startup will call slotSequencerExited()
+ // right away and we don't get to check the result
+ // of m_sequencerProcess->start() and thus make the distinction
+ // between the case where the sequencer was successfully launched
+ // but crashed right away, or the case where the process couldn't
+ // be launched at all (missing executable, etc...)
+ //
+ // We also re-check that the process is still running at this
+ // point in case it crashed between the moment we check res above
+ // and now.
+ //
+ //usleep(1000 * 1000); // even wait half a sec. to make sure it's actually running
+ if (m_sequencerProcess->isRunning()) {
+
+ try {
+ // if (m_seqManager) {
+ // RG_DEBUG << "RosegardenGUIApp::launchSequencer : checking sound driver status\n";
+ // m_seqManager->checkSoundDriverStatus();
+ // }
+
+ stateChanged("sequencer_running");
+ slotEnableTransport(true);
+
+ connect(m_sequencerProcess, SIGNAL(processExited(KProcess*)),
+ this, SLOT(slotSequencerExited(KProcess*)));
+
+ } catch (Exception e) {
+ m_sequencerProcess = 0;
+ m_useSequencer = false;
+ stateChanged("sequencer_running", KXMLGUIClient::StateReverse);
+ slotEnableTransport(false);
+ }
+
+ } else { // if it crashed so fast, it's probably pointless
+ // to try restarting it later, so also fall back to no
+ // sequencer mode
+ m_sequencerProcess = 0;
+ m_useSequencer = false;
+ stateChanged("sequencer_running", KXMLGUIClient::StateReverse);
+ slotEnableTransport(false);
+ }
+
+ }
+
+ // Sync current devices with the sequencer
+ //
+ if (m_doc)
+ m_doc->syncDevices();
+
+ if (m_doc && m_doc->getStudio().haveMidiDevices()) {
+ stateChanged("got_midi_devices");
+ } else {
+ stateChanged("got_midi_devices", KXMLGUIClient::StateReverse);
+ }
+
+ return res;
+}
+
+#ifdef HAVE_LIBJACK
+bool RosegardenGUIApp::launchJack()
+{
+ KConfig* config = kapp->config();
+ config->setGroup(SequencerOptionsConfigGroup);
+
+ bool startJack = config->readBoolEntry("jackstart", false);
+ if (!startJack)
+ return true; // we don't do anything
+
+ QString jackPath = config->readEntry("jackcommand", "");
+
+ emit startupStatusMessage(i18n("Clearing down jackd..."));
+
+ KProcess *proc = new KProcess; // TODO: do it in a less clumsy way
+ *proc << "/usr/bin/killall";
+ *proc << "-9";
+ *proc << "jackd";
+
+ proc->start(KProcess::Block, KProcess::All);
+
+ if (proc->exitStatus())
+ RG_DEBUG << "couldn't kill any jackd processes" << endl;
+ else
+ RG_DEBUG << "killed old jackd processes" << endl;
+
+ emit startupStatusMessage(i18n("Starting jackd..."));
+
+ if (jackPath != "") {
+
+ RG_DEBUG << "starting jack \"" << jackPath << "\"" << endl;
+
+ QStringList splitCommand;
+ splitCommand = QStringList::split(" ", jackPath);
+
+ RG_DEBUG << "RosegardenGUIApp::launchJack() : splitCommand length : "
+ << splitCommand.size() << endl;
+
+ // start jack process
+ m_jackProcess = new KProcess;
+
+ *m_jackProcess << splitCommand;
+
+ m_jackProcess->start();
+ }
+
+
+ return m_jackProcess != 0 ? m_jackProcess->isRunning() : true;
+}
+#endif
+
+void RosegardenGUIApp::slotDocumentDevicesResyncd()
+{
+ m_sequencerCheckedIn = true;
+ m_trackParameterBox->populateDeviceLists();
+}
+
+void RosegardenGUIApp::slotSequencerExited(KProcess*)
+{
+ RG_DEBUG << "RosegardenGUIApp::slotSequencerExited Sequencer exited\n";
+
+ KStartupLogo::hideIfStillThere();
+
+ if (m_sequencerCheckedIn) {
+
+ KMessageBox::error(0, i18n("The Rosegarden sequencer process has exited unexpectedly. Sound and recording will no longer be available for this session.\nPlease exit and restart Rosegarden to restore sound capability."));
+
+ } else {
+
+ KMessageBox::error(0, i18n("The Rosegarden sequencer could not be started, so sound and recording will be unavailable for this session.\nFor assistance with correct audio and MIDI configuration, go to http://rosegardenmusic.com."));
+ }
+
+ m_sequencerProcess = 0; // isSequencerRunning() will return false
+ // but isUsingSequencer() will keep returning true
+ // so pressing the play button may attempt to restart the sequencer
+}
+
+void RosegardenGUIApp::slotExportProject()
+{
+ KTmpStatusMsg msg(i18n("Exporting Rosegarden Project file..."), this);
+
+ QString fileName = getValidWriteFile
+ ("*.rgp|" + i18n("Rosegarden Project files\n") +
+ "\n*|" + i18n("All files"),
+ i18n("Export as..."));
+
+ if (fileName.isEmpty())
+ return ;
+
+ QString rgFile = fileName;
+ rgFile.replace(QRegExp(".rg.rgp$"), ".rg");
+ rgFile.replace(QRegExp(".rgp$"), ".rg");
+
+ CurrentProgressDialog::freeze();
+
+ QString errMsg;
+ if (!m_doc->saveDocument(rgFile, errMsg,
+ true)) { // pretend it's autosave
+ KMessageBox::sorry(this, i18n("Saving Rosegarden file to package failed: %1").arg(errMsg));
+ CurrentProgressDialog::thaw();
+ return ;
+ }
+
+ KProcess *proc = new KProcess;
+ *proc << "rosegarden-project-package";
+ *proc << "--pack";
+ *proc << rgFile;
+ *proc << fileName;
+
+ proc->start(KProcess::Block, KProcess::All);
+
+ if (!proc->normalExit() || proc->exitStatus()) {
+ KMessageBox::sorry(this, i18n("Failed to export to project file \"%1\"").arg(fileName));
+ CurrentProgressDialog::thaw();
+ delete proc;
+ return ;
+ }
+
+ delete proc;
+}
+
+void RosegardenGUIApp::slotExportMIDI()
+{
+ KTmpStatusMsg msg(i18n("Exporting MIDI file..."), this);
+
+ QString fileName = getValidWriteFile
+ ("*.mid *.midi|" + i18n("Standard MIDI files\n") +
+ "\n*|" + i18n("All files"),
+ i18n("Export as..."));
+
+ if (fileName.isEmpty())
+ return ;
+
+ exportMIDIFile(fileName);
+}
+
+void RosegardenGUIApp::exportMIDIFile(QString file)
+{
+ ProgressDialog progressDlg(i18n("Exporting MIDI file..."),
+ 100,
+ this);
+
+ std::string fname(QFile::encodeName(file));
+
+ MidiFile midiFile(fname,
+ &m_doc->getStudio());
+
+ connect(&midiFile, SIGNAL(setProgress(int)),
+ progressDlg.progressBar(), SLOT(setValue(int)));
+
+ connect(&midiFile, SIGNAL(incrementProgress(int)),
+ progressDlg.progressBar(), SLOT(advance(int)));
+
+ midiFile.convertToMidi(m_doc->getComposition());
+
+ if (!midiFile.write()) {
+ CurrentProgressDialog::freeze();
+ KMessageBox::sorry(this, i18n("Export failed. The file could not be opened for writing."));
+ }
+}
+
+void RosegardenGUIApp::slotExportCsound()
+{
+ KTmpStatusMsg msg(i18n("Exporting Csound score file..."), this);
+
+ QString fileName = getValidWriteFile(QString("*|") + i18n("All files"),
+ i18n("Export as..."));
+ if (fileName.isEmpty())
+ return ;
+
+ exportCsoundFile(fileName);
+}
+
+void RosegardenGUIApp::exportCsoundFile(QString file)
+{
+ ProgressDialog progressDlg(i18n("Exporting Csound score file..."),
+ 100,
+ this);
+
+ CsoundExporter e(this, &m_doc->getComposition(), 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."));
+ }
+}
+
+void RosegardenGUIApp::slotExportMup()
+{
+ KTmpStatusMsg msg(i18n("Exporting Mup file..."), this);
+
+ QString fileName = getValidWriteFile
+ ("*.mup|" + i18n("Mup files\n") + "\n*|" + i18n("All files"),
+ i18n("Export as..."));
+ if (fileName.isEmpty())
+ return ;
+
+ exportMupFile(fileName);
+}
+
+void RosegardenGUIApp::exportMupFile(QString file)
+{
+ ProgressDialog progressDlg(i18n("Exporting Mup file..."),
+ 100,
+ this);
+
+ MupExporter e(this, &m_doc->getComposition(), 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."));
+ }
+}
+
+void RosegardenGUIApp::slotExportLilyPond()
+{
+ KTmpStatusMsg msg(i18n("Exporting LilyPond file..."), this);
+
+ QString fileName = getValidWriteFile
+ (QString("*.ly|") + i18n("LilyPond files") +
+ "\n*|" + i18n("All files"),
+ i18n("Export as..."));
+
+ if (fileName.isEmpty())
+ return ;
+
+ exportLilyPondFile(fileName);
+}
+
+std::map<KProcess *, KTempFile *> RosegardenGUIApp::m_lilyTempFileMap;
+
+
+void RosegardenGUIApp::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 RosegardenGUIApp::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 RosegardenGUIApp::slotLilyPondViewProcessExited(KProcess *p)
+{
+ delete m_lilyTempFileMap[p];
+ m_lilyTempFileMap.erase(p);
+ delete p;
+}
+
+bool RosegardenGUIApp::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 RosegardenGUIApp::slotExportMusicXml()
+{
+ KTmpStatusMsg msg(i18n("Exporting MusicXML file..."), this);
+
+ QString fileName = getValidWriteFile
+ (QString("*.xml|") + i18n("XML files") +
+ "\n*|" + i18n("All files"), i18n("Export as..."));
+
+ if (fileName.isEmpty())
+ return ;
+
+ exportMusicXmlFile(fileName);
+}
+
+void RosegardenGUIApp::exportMusicXmlFile(QString file)
+{
+ ProgressDialog progressDlg(i18n("Exporting MusicXML file..."),
+ 100,
+ this);
+
+ MusicXmlExporter 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."));
+ }
+}
+
+void
+RosegardenGUIApp::slotCloseTransport()
+{
+ m_viewTransport->setChecked(false);
+ slotToggleTransport(); // hides the transport
+}
+
+void
+RosegardenGUIApp::slotDeleteTransport()
+{
+ delete m_transport;
+ m_transport = 0;
+}
+
+void
+RosegardenGUIApp::slotActivateTool(QString toolName)
+{
+ if (toolName == SegmentSelector::ToolName) {
+ actionCollection()->action("select")->activate();
+ }
+}
+
+void
+RosegardenGUIApp::slotToggleMetronome()
+{
+ Composition &comp = m_doc->getComposition();
+
+ if (m_seqManager->getTransportStatus() == STARTING_TO_RECORD ||
+ m_seqManager->getTransportStatus() == RECORDING ||
+ m_seqManager->getTransportStatus() == RECORDING_ARMED) {
+ if (comp.useRecordMetronome())
+ comp.setRecordMetronome(false);
+ else
+ comp.setRecordMetronome(true);
+
+ getTransport()->MetronomeButton()->setOn(comp.useRecordMetronome());
+ } else {
+ if (comp.usePlayMetronome())
+ comp.setPlayMetronome(false);
+ else
+ comp.setPlayMetronome(true);
+
+ getTransport()->MetronomeButton()->setOn(comp.usePlayMetronome());
+ }
+}
+
+void
+RosegardenGUIApp::slotRewindToBeginning()
+{
+ // ignore requests if recording
+ //
+ if (m_seqManager->getTransportStatus() == RECORDING)
+ return ;
+
+ m_seqManager->rewindToBeginning();
+}
+
+void
+RosegardenGUIApp::slotFastForwardToEnd()
+{
+ // ignore requests if recording
+ //
+ if (m_seqManager->getTransportStatus() == RECORDING)
+ return ;
+
+ m_seqManager->fastForwardToEnd();
+}
+
+void
+RosegardenGUIApp::slotSetPlayPosition(timeT time)
+{
+ RG_DEBUG << "RosegardenGUIApp::slotSetPlayPosition(" << time << ")" << endl;
+ if (m_seqManager->getTransportStatus() == RECORDING)
+ return ;
+
+ m_doc->slotSetPointerPosition(time);
+
+ if (m_seqManager->getTransportStatus() == PLAYING)
+ return ;
+
+ slotPlay();
+}
+
+void RosegardenGUIApp::notifySequencerStatus(int status)
+{
+ stateChanged("not_playing",
+ (status == PLAYING ||
+ status == RECORDING) ?
+ KXMLGUIClient::StateReverse : KXMLGUIClient::StateNoReverse);
+
+ if (m_seqManager)
+ m_seqManager->setTransportStatus((TransportStatus) status);
+}
+
+void RosegardenGUIApp::processAsynchronousMidi(const MappedComposition &mC)
+{
+ if (!m_seqManager) {
+ return ; // probably getting this from a not-yet-killed runaway sequencer
+ }
+
+ m_seqManager->processAsynchronousMidi(mC, 0);
+ SequencerMapper *mapper = m_seqManager->getSequencerMapper();
+ if (mapper)
+ m_view->updateMeters(mapper);
+}
+
+void
+RosegardenGUIApp::slotRecord()
+{
+ if (!isUsingSequencer())
+ return ;
+
+ if (!isSequencerRunning()) {
+
+ // Try to launch sequencer and return if we fail
+ //
+ if (!launchSequencer(false))
+ return ;
+ }
+
+ if (m_seqManager->getTransportStatus() == RECORDING) {
+ slotStop();
+ return ;
+ } else if (m_seqManager->getTransportStatus() == PLAYING) {
+ slotToggleRecord();
+ return ;
+ }
+
+ // Attempt to start recording
+ //
+ try {
+ m_seqManager->record(false);
+ } catch (QString s) {
+ // We should already be stopped by this point so just unset
+ // the buttons after clicking the dialog.
+ //
+ KMessageBox::error(this, s);
+
+ getTransport()->MetronomeButton()->setOn(false);
+ getTransport()->RecordButton()->setOn(false);
+ getTransport()->PlayButton()->setOn(false);
+ return ;
+ } 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) {
+ slotOpenAudioPathSettings();
+ }
+ getTransport()->MetronomeButton()->setOn(false);
+ getTransport()->RecordButton()->setOn(false);
+ getTransport()->PlayButton()->setOn(false);
+ return ;
+ } catch (Exception e) {
+ KMessageBox::error(this, strtoqstr(e.getMessage()));
+
+ getTransport()->MetronomeButton()->setOn(false);
+ getTransport()->RecordButton()->setOn(false);
+ getTransport()->PlayButton()->setOn(false);
+ return ;
+ }
+
+ // plugin the keyboard accelerators for focus on this dialog
+ plugAccelerators(m_seqManager->getCountdownDialog(),
+ m_seqManager->getCountdownDialog()->getAccelerators());
+
+ connect(m_seqManager->getCountdownDialog(), SIGNAL(stopped()),
+ this, SLOT(slotStop()));
+
+ // Start the playback timer - this fetches the current sequencer position &c
+ //
+ m_stopTimer->stop();
+ m_playTimer->start(23); // avoid multiples of 10 just so as
+ // to avoid always having the same digit
+ // in one place on the transport. How
+ // shallow.)
+}
+
+void
+RosegardenGUIApp::slotToggleRecord()
+{
+ if (!isUsingSequencer() ||
+ (!isSequencerRunning() && !launchSequencer(false)))
+ return ;
+
+ try {
+ m_seqManager->record(true);
+ } catch (QString s) {
+ KMessageBox::error(this, s);
+ } 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 you start to record audio.\nWould you like to set it now?"),
+ i18n("Error"),
+ i18n("Set audio file path")) == KMessageBox::Continue) {
+ slotOpenAudioPathSettings();
+ }
+ } catch (Exception e) {
+ KMessageBox::error(this, strtoqstr(e.getMessage()));
+ }
+
+}
+
+void
+RosegardenGUIApp::slotSetLoop(timeT lhs, timeT rhs)
+{
+ try {
+ m_doc->slotDocumentModified();
+
+ m_seqManager->setLoop(lhs, rhs);
+
+ // toggle the loop button
+ if (lhs != rhs) {
+ getTransport()->LoopButton()->setOn(true);
+ stateChanged("have_range", KXMLGUIClient::StateNoReverse);
+ } else {
+ getTransport()->LoopButton()->setOn(false);
+ stateChanged("have_range", KXMLGUIClient::StateReverse);
+ }
+ } catch (QString s) {
+ KMessageBox::error(this, s);
+ }
+}
+
+void RosegardenGUIApp::alive()
+{
+ if (m_doc)
+ m_doc->syncDevices();
+
+ if (m_doc && m_doc->getStudio().haveMidiDevices()) {
+ stateChanged("got_midi_devices");
+ } else {
+ stateChanged("got_midi_devices", KXMLGUIClient::StateReverse);
+ }
+}
+
+void RosegardenGUIApp::slotPlay()
+{
+ if (!isUsingSequencer())
+ return ;
+
+ if (!isSequencerRunning()) {
+
+ // Try to launch sequencer and return if it fails
+ //
+ if (!launchSequencer(false))
+ return ;
+ }
+
+ if (!m_seqManager)
+ return ;
+
+ // If we're armed and ready to record then do this instead (calling
+ // slotRecord ensures we don't toggle the recording state in
+ // SequenceManager)
+ //
+ if (m_seqManager->getTransportStatus() == RECORDING_ARMED) {
+ slotRecord();
+ return ;
+ }
+
+ // Send the controllers at start of playback if required
+ //
+ KConfig *config = kapp->config();
+ config->setGroup(SequencerOptionsConfigGroup);
+ bool sendControllers = config->readBoolEntry("alwayssendcontrollers", false);
+
+ if (sendControllers)
+ m_doc->initialiseControllers();
+
+ bool pausedPlayback = false;
+
+ try {
+ pausedPlayback = m_seqManager->play(); // this will stop playback (pause) if it's already running
+ // Check the new state of the transport and start or stop timer
+ // accordingly
+ //
+ if (!pausedPlayback) {
+
+ // Start the playback timer - this fetches the current sequencer position &c
+ //
+ m_stopTimer->stop();
+ m_playTimer->start(23);
+ } else {
+ m_playTimer->stop();
+ m_stopTimer->start(100);
+ }
+ } catch (QString s) {
+ KMessageBox::error(this, s);
+ m_playTimer->stop();
+ m_stopTimer->start(100);
+ } catch (Exception e) {
+ KMessageBox::error(this, e.getMessage());
+ m_playTimer->stop();
+ m_stopTimer->start(100);
+ }
+
+}
+
+void RosegardenGUIApp::slotJumpToTime(int sec, int usec)
+{
+ Composition *comp = &m_doc->getComposition();
+ timeT t = comp->getElapsedTimeForRealTime
+ (RealTime(sec, usec * 1000));
+ m_doc->slotSetPointerPosition(t);
+}
+
+void RosegardenGUIApp::slotStartAtTime(int sec, int usec)
+{
+ slotJumpToTime(sec, usec);
+ slotPlay();
+}
+
+void RosegardenGUIApp::slotStop()
+{
+ if (m_seqManager &&
+ m_seqManager->getCountdownDialog()) {
+ disconnect(m_seqManager->getCountdownDialog(), SIGNAL(stopped()),
+ this, SLOT(slotStop()));
+ disconnect(m_seqManager->getCountdownDialog(), SIGNAL(completed()),
+ this, SLOT(slotStop()));
+ }
+
+ try {
+ if (m_seqManager)
+ m_seqManager->stopping();
+ } catch (Exception e) {
+ KMessageBox::error(this, strtoqstr(e.getMessage()));
+ }
+
+ // stop the playback timer
+ m_playTimer->stop();
+ m_stopTimer->start(100);
+}
+
+void RosegardenGUIApp::slotRewind()
+{
+ // ignore requests if recording
+ //
+ if (m_seqManager->getTransportStatus() == RECORDING)
+ return ;
+ if (m_seqManager)
+ m_seqManager->rewind();
+}
+
+void RosegardenGUIApp::slotFastforward()
+{
+ // ignore requests if recording
+ //
+ if (m_seqManager->getTransportStatus() == RECORDING)
+ return ;
+
+ if (m_seqManager)
+ m_seqManager->fastforward();
+}
+
+void
+RosegardenGUIApp::slotSetLoop()
+{
+ // restore loop
+ m_doc->setLoop(m_storedLoopStart, m_storedLoopEnd);
+}
+
+void
+RosegardenGUIApp::slotUnsetLoop()
+{
+ Composition &comp = m_doc->getComposition();
+
+ // store the loop
+ m_storedLoopStart = comp.getLoopStart();
+ m_storedLoopEnd = comp.getLoopEnd();
+
+ // clear the loop at the composition and propagate to the rest
+ // of the display items
+ m_doc->setLoop(0, 0);
+}
+
+void
+RosegardenGUIApp::slotSetLoopStart()
+{
+ // Check so that start time is before endtime, othervise move upp the
+ // endtime to that same pos.
+ if ( m_doc->getComposition().getPosition() < m_doc->getComposition().getLoopEnd() ) {
+ m_doc->setLoop(m_doc->getComposition().getPosition(), m_doc->getComposition().getLoopEnd());
+ } else {
+ m_doc->setLoop(m_doc->getComposition().getPosition(), m_doc->getComposition().getPosition());
+ }
+}
+
+void
+RosegardenGUIApp::slotSetLoopStop()
+{
+ // Check so that end time is after start time, othervise move upp the
+ // start time to that same pos.
+ if ( m_doc->getComposition().getLoopStart() < m_doc->getComposition().getPosition() ) {
+ m_doc->setLoop(m_doc->getComposition().getLoopStart(), m_doc->getComposition().getPosition());
+ } else {
+ m_doc->setLoop(m_doc->getComposition().getPosition(), m_doc->getComposition().getPosition());
+ }
+}
+
+void RosegardenGUIApp::slotToggleSolo(bool value)
+{
+ RG_DEBUG << "RosegardenGUIApp::slotToggleSolo value = " << value << endl;
+
+ m_doc->getComposition().setSolo(value);
+ getTransport()->SoloButton()->setOn(value);
+
+ m_doc->slotDocumentModified();
+
+ emit compositionStateUpdate();
+}
+
+void RosegardenGUIApp::slotTrackUp()
+{
+ Composition &comp = m_doc->getComposition();
+
+ TrackId tid = comp.getSelectedTrack();
+ TrackId pos = comp.getTrackById(tid)->getPosition();
+
+ // If at top already
+ if (pos == 0)
+ return ;
+
+ Track *track = comp.getTrackByPosition(pos - 1);
+
+ // If the track exists
+ if (track) {
+ comp.setSelectedTrack(track->getId());
+ m_view->slotSelectTrackSegments(comp.getSelectedTrack());
+ }
+
+}
+
+void RosegardenGUIApp::slotTrackDown()
+{
+ Composition &comp = m_doc->getComposition();
+
+ TrackId tid = comp.getSelectedTrack();
+ TrackId pos = comp.getTrackById(tid)->getPosition();
+
+ Track *track = comp.getTrackByPosition(pos + 1);
+
+ // If the track exists
+ if (track) {
+ comp.setSelectedTrack(track->getId());
+ m_view->slotSelectTrackSegments(comp.getSelectedTrack());
+ }
+
+}
+
+void RosegardenGUIApp::slotMuteAllTracks()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotMuteAllTracks" << endl;
+
+ Composition &comp = m_doc->getComposition();
+
+ Composition::trackcontainer tracks = comp.getTracks();
+ Composition::trackiterator tit;
+ for (tit = tracks.begin(); tit != tracks.end(); ++tit)
+ m_view->slotSetMute((*tit).second->getInstrument(), true);
+}
+
+void RosegardenGUIApp::slotUnmuteAllTracks()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotUnmuteAllTracks" << endl;
+
+ Composition &comp = m_doc->getComposition();
+
+ Composition::trackcontainer tracks = comp.getTracks();
+ Composition::trackiterator tit;
+ for (tit = tracks.begin(); tit != tracks.end(); ++tit)
+ m_view->slotSetMute((*tit).second->getInstrument(), false);
+}
+
+void RosegardenGUIApp::slotToggleMutedCurrentTrack()
+{
+ Composition &comp = m_doc->getComposition();
+ TrackId tid = comp.getSelectedTrack();
+ Track *track = comp.getTrackById(tid);
+ // If the track exists
+ if (track) {
+ bool isMuted = track->isMuted();
+ m_view->slotSetMuteButton(tid, !isMuted);
+ }
+}
+
+void RosegardenGUIApp::slotToggleRecordCurrentTrack()
+{
+ Composition &comp = m_doc->getComposition();
+ TrackId tid = comp.getSelectedTrack();
+ int pos = comp.getTrackPositionById(tid);
+ m_view->getTrackEditor()->getTrackButtons()->slotToggleRecordTrack(pos);
+}
+
+
+void RosegardenGUIApp::slotConfigure()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotConfigure\n";
+
+ ConfigureDialog *configDlg =
+ new ConfigureDialog(m_doc, kapp->config(), this);
+
+ connect(configDlg, SIGNAL(updateAutoSaveInterval(unsigned int)),
+ this, SLOT(slotUpdateAutoSaveInterval(unsigned int)));
+ connect(configDlg, SIGNAL(updateSidebarStyle(unsigned int)),
+ this, SLOT(slotUpdateSidebarStyle(unsigned int)));
+
+ configDlg->show();
+}
+
+void RosegardenGUIApp::slotEditDocumentProperties()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotEditDocumentProperties\n";
+
+ DocumentConfigureDialog *configDlg =
+ new DocumentConfigureDialog(m_doc, this);
+
+ configDlg->show();
+}
+
+void RosegardenGUIApp::slotOpenAudioPathSettings()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotOpenAudioPathSettings\n";
+
+ DocumentConfigureDialog *configDlg =
+ new DocumentConfigureDialog(m_doc, this);
+
+ configDlg->showAudioPage();
+ configDlg->show();
+}
+
+void RosegardenGUIApp::slotEditKeys()
+{
+ KKeyDialog::configure(actionCollection());
+}
+
+void RosegardenGUIApp::slotEditToolbars()
+{
+ KEditToolbar dlg(actionCollection(), "rosegardenui.rc");
+
+ connect(&dlg, SIGNAL(newToolbarConfig()),
+ SLOT(slotUpdateToolbars()));
+
+ dlg.exec();
+}
+
+void RosegardenGUIApp::slotUpdateToolbars()
+{
+ createGUI("rosegardenui.rc");
+ m_viewToolBar->setChecked(!toolBar()->isHidden());
+}
+
+void RosegardenGUIApp::slotEditTempo()
+{
+ slotEditTempo(this);
+}
+
+void RosegardenGUIApp::slotEditTempo(timeT atTime)
+{
+ slotEditTempo(this, atTime);
+}
+
+void RosegardenGUIApp::slotEditTempo(QWidget *parent)
+{
+ slotEditTempo(parent, m_doc->getComposition().getPosition());
+}
+
+void RosegardenGUIApp::slotEditTempo(QWidget *parent, timeT atTime)
+{
+ RG_DEBUG << "RosegardenGUIApp::slotEditTempo\n";
+
+ TempoDialog tempoDialog(parent, m_doc);
+
+ connect(&tempoDialog,
+ SIGNAL(changeTempo(timeT,
+ tempoT,
+ tempoT,
+ TempoDialog::TempoDialogAction)),
+ SLOT(slotChangeTempo(timeT,
+ tempoT,
+ tempoT,
+ TempoDialog::TempoDialogAction)));
+
+ tempoDialog.setTempoPosition(atTime);
+ tempoDialog.exec();
+}
+
+void RosegardenGUIApp::slotEditTimeSignature()
+{
+ slotEditTimeSignature(this);
+}
+
+void RosegardenGUIApp::slotEditTimeSignature(timeT atTime)
+{
+ slotEditTimeSignature(this, atTime);
+}
+
+void RosegardenGUIApp::slotEditTimeSignature(QWidget *parent)
+{
+ slotEditTimeSignature(parent, m_doc->getComposition().getPosition());
+}
+
+void RosegardenGUIApp::slotEditTimeSignature(QWidget *parent,
+ timeT time)
+{
+ Composition &composition(m_doc->getComposition());
+
+ TimeSignature sig = composition.getTimeSignatureAt(time);
+
+ TimeSignatureDialog dialog(parent, &composition, time, sig);
+
+ if (dialog.exec() == QDialog::Accepted) {
+
+ time = dialog.getTime();
+
+ if (dialog.shouldNormalizeRests()) {
+ m_doc->getCommandHistory()->addCommand
+ (new AddTimeSignatureAndNormalizeCommand
+ (&composition, time, dialog.getTimeSignature()));
+ } else {
+ m_doc->getCommandHistory()->addCommand
+ (new AddTimeSignatureCommand
+ (&composition, time, dialog.getTimeSignature()));
+ }
+ }
+}
+
+void RosegardenGUIApp::slotEditTransportTime()
+{
+ slotEditTransportTime(this);
+}
+
+void RosegardenGUIApp::slotEditTransportTime(QWidget *parent)
+{
+ TimeDialog dialog(parent, i18n("Move playback pointer to time"),
+ &m_doc->getComposition(),
+ m_doc->getComposition().getPosition(),
+ true);
+ if (dialog.exec() == QDialog::Accepted) {
+ m_doc->slotSetPointerPosition(dialog.getTime());
+ }
+}
+
+void RosegardenGUIApp::slotChangeZoom(int)
+{
+ double duration44 = TimeSignature(4, 4).getBarDuration();
+ double value = double(m_zoomSlider->getCurrentSize());
+ m_zoomLabel->setText(i18n("%1%").arg(duration44 / value));
+
+ RG_DEBUG << "RosegardenGUIApp::slotChangeZoom : zoom size = "
+ << m_zoomSlider->getCurrentSize() << endl;
+
+ // initZoomToolbar sets the zoom value. With some old versions of
+ // Qt3.0, this can cause slotChangeZoom() to be called while the
+ // view hasn't been initialized yet, so we need to check it's not
+ // null
+ //
+ if (m_view)
+ m_view->setZoomSize(m_zoomSlider->getCurrentSize());
+
+ long newZoom = int(m_zoomSlider->getCurrentSize() * 1000.0);
+
+ if (m_doc->getConfiguration().get<Int>
+ (DocumentConfiguration::ZoomLevel) != newZoom) {
+
+ m_doc->getConfiguration().set<Int>
+ (DocumentConfiguration::ZoomLevel, newZoom);
+
+ m_doc->slotDocumentModified();
+ }
+}
+
+void
+RosegardenGUIApp::slotZoomIn()
+{
+ m_zoomSlider->increment();
+}
+
+void
+RosegardenGUIApp::slotZoomOut()
+{
+ m_zoomSlider->decrement();
+}
+
+void
+RosegardenGUIApp::slotChangeTempo(timeT time,
+ tempoT value,
+ tempoT target,
+ TempoDialog::TempoDialogAction action)
+{
+ //!!! handle target
+
+ Composition &comp = m_doc->getComposition();
+
+ // We define a macro command here and build up the command
+ // label as we add commands on.
+ //
+ if (action == TempoDialog::AddTempo) {
+ m_doc->getCommandHistory()->addCommand
+ (new AddTempoChangeCommand(&comp, time, value, target));
+ } else if (action == TempoDialog::ReplaceTempo) {
+ int index = comp.getTempoChangeNumberAt(time);
+
+ // if there's no previous tempo change then just set globally
+ //
+ if (index == -1) {
+ m_doc->getCommandHistory()->addCommand
+ (new AddTempoChangeCommand(&comp, 0, value, target));
+ return ;
+ }
+
+ // get time of previous tempo change
+ timeT prevTime = comp.getTempoChange(index).first;
+
+ KMacroCommand *macro =
+ new KMacroCommand(i18n("Replace Tempo Change at %1").arg(time));
+
+ macro->addCommand(new RemoveTempoChangeCommand(&comp, index));
+ macro->addCommand(new AddTempoChangeCommand(&comp, prevTime, value,
+ target));
+
+ m_doc->getCommandHistory()->addCommand(macro);
+
+ } else if (action == TempoDialog::AddTempoAtBarStart) {
+ m_doc->getCommandHistory()->addCommand(new
+ AddTempoChangeCommand(&comp, comp.getBarStartForTime(time),
+ value, target));
+ } else if (action == TempoDialog::GlobalTempo ||
+ action == TempoDialog::GlobalTempoWithDefault) {
+ KMacroCommand *macro = new KMacroCommand(i18n("Set Global Tempo"));
+
+ // Remove all tempo changes in reverse order so as the index numbers
+ // don't becoming meaningless as the command gets unwound.
+ //
+ for (int i = 0; i < comp.getTempoChangeCount(); i++)
+ macro->addCommand(new RemoveTempoChangeCommand(&comp,
+ (comp.getTempoChangeCount() - 1 - i)));
+
+ // add tempo change at time zero
+ //
+ macro->addCommand(new AddTempoChangeCommand(&comp, 0, value, target));
+
+ // are we setting default too?
+ //
+ if (action == TempoDialog::GlobalTempoWithDefault) {
+ macro->setName(i18n("Set Global and Default Tempo"));
+ macro->addCommand(new ModifyDefaultTempoCommand(&comp, value));
+ }
+
+ m_doc->getCommandHistory()->addCommand(macro);
+
+ } else {
+ RG_DEBUG << "RosegardenGUIApp::slotChangeTempo() - "
+ << "unrecognised tempo command" << endl;
+ }
+}
+
+void
+RosegardenGUIApp::slotMoveTempo(timeT oldTime,
+ timeT newTime)
+{
+ Composition &comp = m_doc->getComposition();
+ int index = comp.getTempoChangeNumberAt(oldTime);
+
+ if (index < 0)
+ return ;
+
+ KMacroCommand *macro =
+ new KMacroCommand(i18n("Move Tempo Change"));
+
+ std::pair<timeT, tempoT> tc =
+ comp.getTempoChange(index);
+ std::pair<bool, tempoT> tr =
+ comp.getTempoRamping(index, false);
+
+ macro->addCommand(new RemoveTempoChangeCommand(&comp, index));
+ macro->addCommand(new AddTempoChangeCommand(&comp,
+ newTime,
+ tc.second,
+ tr.first ? tr.second : -1));
+
+ m_doc->getCommandHistory()->addCommand(macro);
+}
+
+void
+RosegardenGUIApp::slotDeleteTempo(timeT t)
+{
+ Composition &comp = m_doc->getComposition();
+ int index = comp.getTempoChangeNumberAt(t);
+
+ if (index < 0)
+ return ;
+
+ m_doc->getCommandHistory()->addCommand(new RemoveTempoChangeCommand
+ (&comp, index));
+}
+
+void
+RosegardenGUIApp::slotAddMarker(timeT time)
+{
+ AddMarkerCommand *command =
+ new AddMarkerCommand(&m_doc->getComposition(),
+ time,
+ i18n("new marker"),
+ i18n("no description"));
+
+ m_doc->getCommandHistory()->addCommand(command);
+}
+
+void
+RosegardenGUIApp::slotDeleteMarker(int id, timeT time, QString name, QString description)
+{
+ RemoveMarkerCommand *command =
+ new RemoveMarkerCommand(&m_doc->getComposition(),
+ id,
+ time,
+ qstrtostr(name),
+ qstrtostr(description));
+
+ m_doc->getCommandHistory()->addCommand(command);
+}
+
+void
+RosegardenGUIApp::slotDocumentModified(bool m)
+{
+ RG_DEBUG << "RosegardenGUIApp::slotDocumentModified(" << m << ") - doc path = "
+ << m_doc->getAbsFilePath() << endl;
+
+ if (!m_doc->getAbsFilePath().isEmpty()) {
+ slotStateChanged("saved_file_modified", m);
+ } else {
+ slotStateChanged("new_file_modified", m);
+ }
+
+}
+
+void
+RosegardenGUIApp::slotStateChanged(QString s,
+ bool noReverse)
+{
+ // RG_DEBUG << "RosegardenGUIApp::slotStateChanged " << s << "," << noReverse << endl;
+
+ stateChanged(s, noReverse ? KXMLGUIClient::StateNoReverse : KXMLGUIClient::StateReverse);
+}
+
+void
+RosegardenGUIApp::slotTestClipboard()
+{
+ if (m_clipboard->isEmpty()) {
+ stateChanged("have_clipboard", KXMLGUIClient::StateReverse);
+ stateChanged("have_clipboard_single_segment",
+ KXMLGUIClient::StateReverse);
+ } else {
+ stateChanged("have_clipboard", KXMLGUIClient::StateNoReverse);
+ stateChanged("have_clipboard_single_segment",
+ (m_clipboard->isSingleSegment() ?
+ KXMLGUIClient::StateNoReverse :
+ KXMLGUIClient::StateReverse));
+ }
+}
+
+void
+RosegardenGUIApp::plugAccelerators(QWidget *widget, QAccel *acc)
+{
+
+ acc->connectItem(acc->insertItem(Key_Enter),
+ this,
+ SLOT(slotPlay()));
+ // Alternative shortcut for Play
+ acc->connectItem(acc->insertItem(Key_Return + CTRL),
+ this,
+ SLOT(slotPlay()));
+
+ acc->connectItem(acc->insertItem(Key_Insert),
+ this,
+ SLOT(slotStop()));
+
+ acc->connectItem(acc->insertItem(Key_PageDown),
+ this,
+ SLOT(slotFastforward()));
+
+ acc->connectItem(acc->insertItem(Key_End),
+ this,
+ SLOT(slotRewind()));
+
+ acc->connectItem(acc->insertItem(Key_Space),
+ this,
+ SLOT(slotToggleRecord()));
+
+ TransportDialog *transport =
+ dynamic_cast<TransportDialog*>(widget);
+
+ if (transport) {
+ acc->connectItem(acc->insertItem(m_jumpToQuickMarkerAction->shortcut()),
+ this,
+ SLOT(slotJumpToQuickMarker()));
+
+ acc->connectItem(acc->insertItem(m_setQuickMarkerAction->shortcut()),
+ this,
+ SLOT(slotSetQuickMarker()));
+
+ connect(transport->PlayButton(),
+ SIGNAL(clicked()),
+ this,
+ SLOT(slotPlay()));
+
+ connect(transport->StopButton(),
+ SIGNAL(clicked()),
+ this,
+ SLOT(slotStop()));
+
+ connect(transport->FfwdButton(),
+ SIGNAL(clicked()),
+ SLOT(slotFastforward()));
+
+ connect(transport->RewindButton(),
+ SIGNAL(clicked()),
+ this,
+ SLOT(slotRewind()));
+
+ connect(transport->RecordButton(),
+ SIGNAL(clicked()),
+ this,
+ SLOT(slotRecord()));
+
+ connect(transport->RewindEndButton(),
+ SIGNAL(clicked()),
+ this,
+ SLOT(slotRewindToBeginning()));
+
+ connect(transport->FfwdEndButton(),
+ SIGNAL(clicked()),
+ this,
+ SLOT(slotFastForwardToEnd()));
+
+ connect(transport->MetronomeButton(),
+ SIGNAL(clicked()),
+ this,
+ SLOT(slotToggleMetronome()));
+
+ connect(transport->SoloButton(),
+ SIGNAL(toggled(bool)),
+ this,
+ SLOT(slotToggleSolo(bool)));
+
+ connect(transport->TimeDisplayButton(),
+ SIGNAL(clicked()),
+ this,
+ SLOT(slotRefreshTimeDisplay()));
+
+ connect(transport->ToEndButton(),
+ SIGNAL(clicked()),
+ SLOT(slotRefreshTimeDisplay()));
+ }
+}
+
+void
+RosegardenGUIApp::setCursor(const QCursor& cursor)
+{
+ KDockMainWindow::setCursor(cursor);
+
+ // play it safe, so we can use this class at anytime even very early in the app init
+ if ((getView() &&
+ getView()->getTrackEditor() &&
+ getView()->getTrackEditor()->getSegmentCanvas() &&
+ getView()->getTrackEditor()->getSegmentCanvas()->viewport())) {
+
+ getView()->getTrackEditor()->getSegmentCanvas()->viewport()->setCursor(cursor);
+ }
+
+ // view, main window...
+ //
+ getView()->setCursor(cursor);
+
+ // toolbars...
+ //
+ QPtrListIterator<KToolBar> tbIter = toolBarIterator();
+ KToolBar* tb = 0;
+ while ((tb = tbIter.current()) != 0) {
+ tb->setCursor(cursor);
+ ++tbIter;
+ }
+
+ m_dockLeft->setCursor(cursor);
+}
+
+QString
+RosegardenGUIApp::createNewAudioFile()
+{
+ AudioFile *aF = 0;
+ try {
+ aF = m_doc->getAudioFileManager().createRecordingAudioFile();
+ if (!aF) {
+ // createRecordingAudioFile doesn't actually write to the disk,
+ // and in principle it shouldn't fail
+ std::cerr << "ERROR: RosegardenGUIApp::createNewAudioFile: Failed to create recording audio file" << std::endl;
+ return "";
+ } else {
+ return aF->getFilename().c_str();
+ }
+ } catch (AudioFileManager::BadAudioPathException e) {
+ delete aF;
+ std::cerr << "ERROR: RosegardenGUIApp::createNewAudioFile: Failed to create recording audio file: " << e.getMessage() << std::endl;
+ return "";
+ }
+}
+
+QValueVector<QString>
+RosegardenGUIApp::createRecordAudioFiles(const QValueVector<InstrumentId> &recordInstruments)
+{
+ QValueVector<QString> qv;
+ for (unsigned int i = 0; i < recordInstruments.size(); ++i) {
+ AudioFile *aF = 0;
+ try {
+ aF = m_doc->getAudioFileManager().createRecordingAudioFile();
+ if (aF) {
+ // createRecordingAudioFile doesn't actually write to the disk,
+ // and in principle it shouldn't fail
+ qv.push_back(aF->getFilename().c_str());
+ m_doc->addRecordAudioSegment(recordInstruments[i],
+ aF->getId());
+ } else {
+ std::cerr << "ERROR: RosegardenGUIApp::createRecordAudioFiles: Failed to create recording audio file" << std::endl;
+ return qv;
+ }
+ } catch (AudioFileManager::BadAudioPathException e) {
+ delete aF;
+ std::cerr << "ERROR: RosegardenGUIApp::createRecordAudioFiles: Failed to create recording audio file: " << e.getMessage() << std::endl;
+ return qv;
+ }
+ }
+ return qv;
+}
+
+QString
+RosegardenGUIApp::getAudioFilePath()
+{
+ return QString(m_doc->getAudioFileManager().getAudioPath().c_str());
+}
+
+QValueVector<InstrumentId>
+RosegardenGUIApp::getArmedInstruments()
+{
+ std::set
+ <InstrumentId> iid;
+
+ const Composition::recordtrackcontainer &tr =
+ m_doc->getComposition().getRecordTracks();
+
+ for (Composition::recordtrackcontainer::const_iterator i =
+ tr.begin(); i != tr.end(); ++i) {
+ TrackId tid = (*i);
+ Track *track = m_doc->getComposition().getTrackById(tid);
+ if (track) {
+ iid.insert(track->getInstrument());
+ } else {
+ std::cerr << "Warning: RosegardenGUIApp::getArmedInstruments: Armed track " << tid << " not found in Composition" << std::endl;
+ }
+ }
+
+ QValueVector<InstrumentId> iv;
+ for (std::set
+ <InstrumentId>::iterator ii = iid.begin();
+ ii != iid.end(); ++ii) {
+ iv.push_back(*ii);
+ }
+ return iv;
+}
+
+void
+RosegardenGUIApp::showError(QString error)
+{
+ KStartupLogo::hideIfStillThere();
+ CurrentProgressDialog::freeze();
+
+ // This is principally used for return values from DSSI plugin
+ // configure() calls. It seems some plugins return a string
+ // telling you when everything's OK, as well as error strings, but
+ // dssi.h does make it reasonably clear that configure() should
+ // only return a string when there is actually a problem, so we're
+ // going to stick with a sorry dialog here rather than an
+ // information one
+
+ KMessageBox::sorry(0, error);
+
+ CurrentProgressDialog::thaw();
+}
+
+void
+RosegardenGUIApp::slotAudioManager()
+{
+ if (m_audioManagerDialog) {
+ m_audioManagerDialog->show();
+ m_audioManagerDialog->raise();
+ m_audioManagerDialog->setActiveWindow();
+ return ;
+ }
+
+ m_audioManagerDialog =
+ new AudioManagerDialog(this, m_doc);
+
+ connect(m_audioManagerDialog,
+ SIGNAL(playAudioFile(AudioFileId,
+ const RealTime &,
+ const RealTime&)),
+ SLOT(slotPlayAudioFile(AudioFileId,
+ const RealTime &,
+ const RealTime &)));
+
+ connect(m_audioManagerDialog,
+ SIGNAL(addAudioFile(AudioFileId)),
+ SLOT(slotAddAudioFile(AudioFileId)));
+
+ connect(m_audioManagerDialog,
+ SIGNAL(deleteAudioFile(AudioFileId)),
+ SLOT(slotDeleteAudioFile(AudioFileId)));
+
+ //
+ // Sync segment selection between audio man. dialog and main window
+ //
+
+ // from dialog to us...
+ connect(m_audioManagerDialog,
+ SIGNAL(segmentsSelected(const SegmentSelection&)),
+ m_view,
+ SLOT(slotPropagateSegmentSelection(const SegmentSelection&)));
+
+ // and from us to dialog
+ connect(this, SIGNAL(segmentsSelected(const SegmentSelection&)),
+ m_audioManagerDialog,
+ SLOT(slotSegmentSelection(const SegmentSelection&)));
+
+
+ connect(m_audioManagerDialog,
+ SIGNAL(deleteSegments(const SegmentSelection&)),
+ SLOT(slotDeleteSegments(const SegmentSelection&)));
+
+ connect(m_audioManagerDialog,
+ SIGNAL(insertAudioSegment(AudioFileId,
+ const RealTime&,
+ const RealTime&)),
+ m_view,
+ SLOT(slotAddAudioSegmentDefaultPosition(AudioFileId,
+ const RealTime&,
+ const RealTime&)));
+ connect(m_audioManagerDialog,
+ SIGNAL(cancelPlayingAudioFile(AudioFileId)),
+ SLOT(slotCancelAudioPlayingFile(AudioFileId)));
+
+ connect(m_audioManagerDialog,
+ SIGNAL(deleteAllAudioFiles()),
+ SLOT(slotDeleteAllAudioFiles()));
+
+ // Make sure we know when the audio man. dialog is closing
+ //
+ connect(m_audioManagerDialog,
+ SIGNAL(closing()),
+ SLOT(slotAudioManagerClosed()));
+
+ // And that it goes away when the current document is changing
+ //
+ connect(this, SIGNAL(documentAboutToChange()),
+ m_audioManagerDialog, SLOT(close()));
+
+ m_audioManagerDialog->setAudioSubsystemStatus(
+ m_seqManager->getSoundDriverStatus() & AUDIO_OK);
+
+ plugAccelerators(m_audioManagerDialog,
+ m_audioManagerDialog->getAccelerators());
+
+ m_audioManagerDialog->show();
+}
+
+void
+RosegardenGUIApp::slotPlayAudioFile(unsigned int id,
+ const RealTime &startTime,
+ const RealTime &duration)
+{
+ AudioFile *aF = m_doc->getAudioFileManager().getAudioFile(id);
+
+ if (aF == 0)
+ return ;
+
+ MappedEvent mE(m_doc->getStudio().
+ getAudioPreviewInstrument(),
+ id,
+ RealTime( -120, 0),
+ duration, // duration
+ startTime); // start index
+
+ StudioControl::sendMappedEvent(mE);
+
+}
+
+void
+RosegardenGUIApp::slotAddAudioFile(unsigned int id)
+{
+ AudioFile *aF = m_doc->getAudioFileManager().getAudioFile(id);
+
+ if (aF == 0)
+ return ;
+
+ QCString replyType;
+ QByteArray replyData;
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ // We have to pass the filename as a QString
+ //
+ streamOut << QString(strtoqstr(aF->getFilename()));
+ streamOut << (int)aF->getId();
+
+ if (rgapp->sequencerCall("addAudioFile(QString, int)", replyType, replyData, data)) {
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ int result;
+ streamIn >> result;
+ if (!result) {
+ KMessageBox::error(this, i18n("Sequencer failed to add audio file %1").arg(aF->getFilename().c_str()));
+ }
+ }
+}
+
+void
+RosegardenGUIApp::slotDeleteAudioFile(unsigned int id)
+{
+ if (m_doc->getAudioFileManager().removeFile(id) == false)
+ return ;
+
+ QCString replyType;
+ QByteArray replyData;
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ // file id
+ //
+ streamOut << (int)id;
+
+ if (rgapp->sequencerCall("removeAudioFile(int)", replyType, replyData, data)) {
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ int result;
+ streamIn >> result;
+ if (!result) {
+ KMessageBox::error(this, i18n("Sequencer failed to remove audio file id %1").arg(id));
+ }
+ }
+}
+
+void
+RosegardenGUIApp::slotDeleteSegments(const SegmentSelection &selection)
+{
+ m_view->slotPropagateSegmentSelection(selection);
+ slotDeleteSelectedSegments();
+}
+
+void
+RosegardenGUIApp::slotCancelAudioPlayingFile(AudioFileId id)
+{
+ AudioFile *aF = m_doc->getAudioFileManager().getAudioFile(id);
+
+ if (aF == 0)
+ return ;
+
+ MappedEvent mE(m_doc->getStudio().
+ getAudioPreviewInstrument(),
+ MappedEvent::AudioCancel,
+ id);
+
+ StudioControl::sendMappedEvent(mE);
+}
+
+void
+RosegardenGUIApp::slotDeleteAllAudioFiles()
+{
+ m_doc->getAudioFileManager().clear();
+
+ // Clear at the sequencer
+ //
+ QCString replyType;
+ QByteArray replyData;
+ QByteArray data;
+
+ rgapp->sequencerCall("clearAllAudioFiles()", replyType, replyData, data);
+}
+
+void
+RosegardenGUIApp::slotRepeatingSegments()
+{
+ m_view->getTrackEditor()->slotTurnRepeatingSegmentToRealCopies();
+}
+
+void
+RosegardenGUIApp::slotRelabelSegments()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ SegmentSelection selection(m_view->getSelection());
+ QString editLabel;
+
+ if (selection.size() == 0)
+ return ;
+ else if (selection.size() == 1)
+ editLabel = i18n("Modify Segment label");
+ else
+ editLabel = i18n("Modify Segments label");
+
+ KTmpStatusMsg msg(i18n("Relabelling selection..."), this);
+
+ // Generate label
+ QString label = strtoqstr((*selection.begin())->getLabel());
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+ if (strtoqstr((*i)->getLabel()) != label)
+ label = "";
+ }
+
+ bool ok = false;
+
+ QString newLabel = KInputDialog::getText(editLabel,
+ i18n("Enter new label"),
+ label,
+ &ok,
+ this);
+
+ if (ok) {
+ m_doc->getCommandHistory()->addCommand
+ (new SegmentLabelCommand(selection, newLabel));
+ m_view->getTrackEditor()->getSegmentCanvas()->slotUpdateSegmentsDrawBuffer();
+ }
+}
+
+void
+RosegardenGUIApp::slotTransposeSegments()
+{
+ if (!m_view->haveSelection())
+ return ;
+
+ IntervalDialog intervalDialog(this, true, true);
+ int ok = intervalDialog.exec();
+
+ int semitones = intervalDialog.getChromaticDistance();
+ int steps = intervalDialog.getDiatonicDistance();
+
+ if (!ok || (semitones == 0 && steps == 0)) return;
+
+ m_doc->getCommandHistory()->addCommand
+ (new SegmentTransposeCommand(m_view->getSelection(), intervalDialog.getChangeKey(), steps, semitones, intervalDialog.getTransposeSegmentBack()));
+}
+
+void
+RosegardenGUIApp::slotChangeCompositionLength()
+{
+ CompositionLengthDialog dialog(this, &m_doc->getComposition());
+
+ if (dialog.exec() == QDialog::Accepted) {
+ ChangeCompositionLengthCommand *command
+ = new ChangeCompositionLengthCommand(
+ &m_doc->getComposition(),
+ dialog.getStartMarker(),
+ dialog.getEndMarker());
+
+ m_view->getTrackEditor()->getSegmentCanvas()->clearSegmentRectsCache(true);
+ m_doc->getCommandHistory()->addCommand(command);
+ }
+}
+
+void
+RosegardenGUIApp::slotManageMIDIDevices()
+{
+ if (m_deviceManager) {
+ m_deviceManager->show();
+ m_deviceManager->raise();
+ m_deviceManager->setActiveWindow();
+ return ;
+ }
+
+ m_deviceManager = new DeviceManagerDialog(this, m_doc);
+
+ connect(m_deviceManager, SIGNAL(closing()),
+ this, SLOT(slotDeviceManagerClosed()));
+
+ connect(this, SIGNAL(documentAboutToChange()),
+ m_deviceManager, SLOT(close()));
+
+ // Cheating way of updating the track/instrument list
+ //
+ connect(m_deviceManager, SIGNAL(deviceNamesChanged()),
+ m_view, SLOT(slotSynchroniseWithComposition()));
+
+ connect(m_deviceManager, SIGNAL(editBanks(DeviceId)),
+ this, SLOT(slotEditBanks(DeviceId)));
+
+ connect(m_deviceManager, SIGNAL(editControllers(DeviceId)),
+ this, SLOT(slotEditControlParameters(DeviceId)));
+
+ if (m_midiMixer) {
+ connect(m_deviceManager, SIGNAL(deviceNamesChanged()),
+ m_midiMixer, SLOT(slotSynchronise()));
+
+ }
+
+
+ m_deviceManager->show();
+}
+
+void
+RosegardenGUIApp::slotManageSynths()
+{
+ if (m_synthManager) {
+ m_synthManager->show();
+ m_synthManager->raise();
+ m_synthManager->setActiveWindow();
+ return ;
+ }
+
+ m_synthManager = new SynthPluginManagerDialog(this, m_doc
+#ifdef HAVE_LIBLO
+ , m_pluginGUIManager
+#endif
+ );
+
+ connect(m_synthManager, SIGNAL(closing()),
+ this, SLOT(slotSynthPluginManagerClosed()));
+
+ connect(this, SIGNAL(documentAboutToChange()),
+ m_synthManager, SLOT(close()));
+
+ connect(m_synthManager,
+ SIGNAL(pluginSelected(InstrumentId, int, int)),
+ this,
+ SLOT(slotPluginSelected(InstrumentId, int, int)));
+
+ connect(m_synthManager,
+ SIGNAL(showPluginDialog(QWidget *, InstrumentId, int)),
+ this,
+ SLOT(slotShowPluginDialog(QWidget *, InstrumentId, int)));
+
+ connect(m_synthManager,
+ SIGNAL(showPluginGUI(InstrumentId, int)),
+ this,
+ SLOT(slotShowPluginGUI(InstrumentId, int)));
+
+ m_synthManager->show();
+}
+
+void
+RosegardenGUIApp::slotOpenAudioMixer()
+{
+ if (m_audioMixer) {
+ m_audioMixer->show();
+ m_audioMixer->raise();
+ m_audioMixer->setActiveWindow();
+ return ;
+ }
+
+ m_audioMixer = new AudioMixerWindow(this, m_doc);
+
+ connect(m_audioMixer, SIGNAL(windowActivated()),
+ m_view, SLOT(slotActiveMainWindowChanged()));
+
+ connect(m_view, SIGNAL(controllerDeviceEventReceived(MappedEvent *, const void *)),
+ m_audioMixer, SLOT(slotControllerDeviceEventReceived(MappedEvent *, const void *)));
+
+ connect(m_audioMixer, SIGNAL(closing()),
+ this, SLOT(slotAudioMixerClosed()));
+
+ connect(m_audioMixer, SIGNAL(selectPlugin(QWidget *, InstrumentId, int)),
+ this, SLOT(slotShowPluginDialog(QWidget *, InstrumentId, int)));
+
+ connect(this,
+ SIGNAL(pluginSelected(InstrumentId, int, int)),
+ m_audioMixer,
+ SLOT(slotPluginSelected(InstrumentId, int, int)));
+
+ connect(this,
+ SIGNAL(pluginBypassed(InstrumentId, int, bool)),
+ m_audioMixer,
+ SLOT(slotPluginBypassed(InstrumentId, int, bool)));
+
+ connect(this, SIGNAL(documentAboutToChange()),
+ m_audioMixer, SLOT(close()));
+
+ connect(m_view, SIGNAL(checkTrackAssignments()),
+ m_audioMixer, SLOT(slotTrackAssignmentsChanged()));
+
+ connect(m_audioMixer, SIGNAL(play()),
+ this, SLOT(slotPlay()));
+ connect(m_audioMixer, SIGNAL(stop()),
+ this, SLOT(slotStop()));
+ connect(m_audioMixer, SIGNAL(fastForwardPlayback()),
+ this, SLOT(slotFastforward()));
+ connect(m_audioMixer, SIGNAL(rewindPlayback()),
+ this, SLOT(slotRewind()));
+ connect(m_audioMixer, SIGNAL(fastForwardPlaybackToEnd()),
+ this, SLOT(slotFastForwardToEnd()));
+ connect(m_audioMixer, SIGNAL(rewindPlaybackToBeginning()),
+ this, SLOT(slotRewindToBeginning()));
+ connect(m_audioMixer, SIGNAL(record()),
+ this, SLOT(slotRecord()));
+ connect(m_audioMixer, SIGNAL(panic()),
+ this, SLOT(slotPanic()));
+
+ connect(m_audioMixer,
+ SIGNAL(instrumentParametersChanged(InstrumentId)),
+ this,
+ SIGNAL(instrumentParametersChanged(InstrumentId)));
+
+ connect(this,
+ SIGNAL(instrumentParametersChanged(InstrumentId)),
+ m_audioMixer,
+ SLOT(slotUpdateInstrument(InstrumentId)));
+
+ if (m_synthManager) {
+ connect(m_synthManager,
+ SIGNAL(pluginSelected(InstrumentId, int, int)),
+ m_audioMixer,
+ SLOT(slotPluginSelected(InstrumentId, int, int)));
+ }
+
+ plugAccelerators(m_audioMixer, m_audioMixer->getAccelerators());
+
+ m_audioMixer->show();
+}
+
+void
+RosegardenGUIApp::slotOpenMidiMixer()
+{
+ if (m_midiMixer) {
+ m_midiMixer->show();
+ m_midiMixer->raise();
+ m_midiMixer->setActiveWindow();
+ return ;
+ }
+
+ m_midiMixer = new MidiMixerWindow(this, m_doc);
+
+ connect(m_midiMixer, SIGNAL(windowActivated()),
+ m_view, SLOT(slotActiveMainWindowChanged()));
+
+ connect(m_view, SIGNAL(controllerDeviceEventReceived(MappedEvent *, const void *)),
+ m_midiMixer, SLOT(slotControllerDeviceEventReceived(MappedEvent *, const void *)));
+
+ connect(m_midiMixer, SIGNAL(closing()),
+ this, SLOT(slotMidiMixerClosed()));
+
+ connect(this, SIGNAL(documentAboutToChange()),
+ m_midiMixer, SLOT(close()));
+
+ connect(m_midiMixer, SIGNAL(play()),
+ this, SLOT(slotPlay()));
+ connect(m_midiMixer, SIGNAL(stop()),
+ this, SLOT(slotStop()));
+ connect(m_midiMixer, SIGNAL(fastForwardPlayback()),
+ this, SLOT(slotFastforward()));
+ connect(m_midiMixer, SIGNAL(rewindPlayback()),
+ this, SLOT(slotRewind()));
+ connect(m_midiMixer, SIGNAL(fastForwardPlaybackToEnd()),
+ this, SLOT(slotFastForwardToEnd()));
+ connect(m_midiMixer, SIGNAL(rewindPlaybackToBeginning()),
+ this, SLOT(slotRewindToBeginning()));
+ connect(m_midiMixer, SIGNAL(record()),
+ this, SLOT(slotRecord()));
+ connect(m_midiMixer, SIGNAL(panic()),
+ this, SLOT(slotPanic()));
+
+ connect(m_midiMixer,
+ SIGNAL(instrumentParametersChanged(InstrumentId)),
+ this,
+ SIGNAL(instrumentParametersChanged(InstrumentId)));
+
+ connect(this,
+ SIGNAL(instrumentParametersChanged(InstrumentId)),
+ m_midiMixer,
+ SLOT(slotUpdateInstrument(InstrumentId)));
+
+ plugAccelerators(m_midiMixer, m_midiMixer->getAccelerators());
+
+ m_midiMixer->show();
+}
+
+void
+RosegardenGUIApp::slotEditControlParameters(DeviceId device)
+{
+ for (std::set
+ <ControlEditorDialog *>::iterator i = m_controlEditors.begin();
+ i != m_controlEditors.end(); ++i) {
+ if ((*i)->getDevice() == device) {
+ (*i)->show();
+ (*i)->raise();
+ (*i)->setActiveWindow();
+ return ;
+ }
+ }
+
+ ControlEditorDialog *controlEditor = new ControlEditorDialog(this, m_doc,
+ device);
+ m_controlEditors.insert(controlEditor);
+
+ RG_DEBUG << "inserting control editor dialog, have " << m_controlEditors.size() << " now" << endl;
+
+ connect(controlEditor, SIGNAL(closing()),
+ SLOT(slotControlEditorClosed()));
+
+ connect(this, SIGNAL(documentAboutToChange()),
+ controlEditor, SLOT(close()));
+
+ connect(m_doc, SIGNAL(devicesResyncd()),
+ controlEditor, SLOT(slotUpdate()));
+
+ controlEditor->show();
+}
+
+void
+RosegardenGUIApp::slotEditBanks()
+{
+ slotEditBanks(Device::NO_DEVICE);
+}
+
+void
+RosegardenGUIApp::slotEditBanks(DeviceId device)
+{
+ if (m_bankEditor) {
+ if (device != Device::NO_DEVICE)
+ m_bankEditor->setCurrentDevice(device);
+ m_bankEditor->show();
+ m_bankEditor->raise();
+ m_bankEditor->setActiveWindow();
+ return ;
+ }
+
+ m_bankEditor = new BankEditorDialog(this, m_doc, device);
+
+ connect(m_bankEditor, SIGNAL(closing()),
+ this, SLOT(slotBankEditorClosed()));
+
+ connect(this, SIGNAL(documentAboutToChange()),
+ m_bankEditor, SLOT(slotFileClose()));
+
+ // Cheating way of updating the track/instrument list
+ //
+ connect(m_bankEditor, SIGNAL(deviceNamesChanged()),
+ m_view, SLOT(slotSynchroniseWithComposition()));
+
+ m_bankEditor->show();
+}
+
+void
+RosegardenGUIApp::slotManageTriggerSegments()
+{
+ if (m_triggerSegmentManager) {
+ m_triggerSegmentManager->show();
+ m_triggerSegmentManager->raise();
+ m_triggerSegmentManager->setActiveWindow();
+ return ;
+ }
+
+ m_triggerSegmentManager = new TriggerSegmentManager(this, m_doc);
+
+ connect(m_triggerSegmentManager, SIGNAL(closing()),
+ SLOT(slotTriggerManagerClosed()));
+
+ connect(m_triggerSegmentManager, SIGNAL(editTriggerSegment(int)),
+ m_view, SLOT(slotEditTriggerSegment(int)));
+
+ m_triggerSegmentManager->show();
+}
+
+void
+RosegardenGUIApp::slotTriggerManagerClosed()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotTriggerManagerClosed" << endl;
+
+ m_triggerSegmentManager = 0;
+}
+
+void
+RosegardenGUIApp::slotEditMarkers()
+{
+ if (m_markerEditor) {
+ m_markerEditor->show();
+ m_markerEditor->raise();
+ m_markerEditor->setActiveWindow();
+ return ;
+ }
+
+ m_markerEditor = new MarkerEditor(this, m_doc);
+
+ connect(m_markerEditor, SIGNAL(closing()),
+ SLOT(slotMarkerEditorClosed()));
+
+ connect(m_markerEditor, SIGNAL(jumpToMarker(timeT)),
+ m_doc, SLOT(slotSetPointerPosition(timeT)));
+
+ plugAccelerators(m_markerEditor, m_markerEditor->getAccelerators());
+
+ m_markerEditor->show();
+}
+
+void
+RosegardenGUIApp::slotMarkerEditorClosed()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotMarkerEditorClosed" << endl;
+
+ m_markerEditor = 0;
+}
+
+void
+RosegardenGUIApp::slotEditTempos(timeT t)
+{
+ if (m_tempoView) {
+ m_tempoView->show();
+ m_tempoView->raise();
+ m_tempoView->setActiveWindow();
+ return ;
+ }
+
+ m_tempoView = new TempoView(m_doc, getView(), t);
+
+ connect(m_tempoView, SIGNAL(closing()),
+ SLOT(slotTempoViewClosed()));
+
+ connect(m_tempoView, SIGNAL(windowActivated()),
+ getView(), SLOT(slotActiveMainWindowChanged()));
+
+ connect(m_tempoView,
+ SIGNAL(changeTempo(timeT,
+ tempoT,
+ tempoT,
+ TempoDialog::TempoDialogAction)),
+ this,
+ SLOT(slotChangeTempo(timeT,
+ tempoT,
+ tempoT,
+ TempoDialog::TempoDialogAction)));
+
+ connect(m_tempoView, SIGNAL(saveFile()), this, SLOT(slotFileSave()));
+
+ plugAccelerators(m_tempoView, m_tempoView->getAccelerators());
+
+ m_tempoView->show();
+}
+
+void
+RosegardenGUIApp::slotTempoViewClosed()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotTempoViewClosed" << endl;
+
+ m_tempoView = 0;
+}
+
+void
+RosegardenGUIApp::slotControlEditorClosed()
+{
+ const QObject *s = sender();
+
+ RG_DEBUG << "RosegardenGUIApp::slotControlEditorClosed" << endl;
+
+ for (std::set
+ <ControlEditorDialog *>::iterator i = m_controlEditors.begin();
+ i != m_controlEditors.end(); ++i) {
+ if (*i == s) {
+ m_controlEditors.erase(i);
+ RG_DEBUG << "removed control editor dialog, have " << m_controlEditors.size() << " left" << endl;
+ return ;
+ }
+ }
+
+ std::cerr << "WARNING: control editor " << s << " closed, but couldn't find it in our control editor list (we have " << m_controlEditors.size() << " editors)" << std::endl;
+}
+
+void
+RosegardenGUIApp::slotShowPluginDialog(QWidget *parent,
+ InstrumentId instrumentId,
+ int index)
+{
+ if (!parent)
+ parent = this;
+
+ int key = (index << 16) + instrumentId;
+
+ if (m_pluginDialogs[key]) {
+ m_pluginDialogs[key]->show();
+ m_pluginDialogs[key]->raise();
+ m_pluginDialogs[key]->setActiveWindow();
+ return ;
+ }
+
+ PluginContainer *container = 0;
+
+ container = m_doc->getStudio().getContainerById(instrumentId);
+ if (!container) {
+ RG_DEBUG << "RosegardenGUIApp::slotShowPluginDialog - "
+ << "no instrument or buss of id " << instrumentId << endl;
+ return ;
+ }
+
+ // only create a dialog if we've got a plugin instance
+ AudioPluginInstance *inst =
+ container->getPlugin(index);
+
+ if (!inst) {
+ RG_DEBUG << "RosegardenGUIApp::slotShowPluginDialog - "
+ << "no AudioPluginInstance found for index "
+ << index << endl;
+ return ;
+ }
+
+ // Create the plugin dialog
+ //
+ AudioPluginDialog *dialog =
+ new AudioPluginDialog(parent,
+ m_doc->getPluginManager(),
+#ifdef HAVE_LIBLO
+ m_pluginGUIManager,
+#endif
+ container,
+ index);
+
+ connect(dialog, SIGNAL(windowActivated()),
+ m_view, SLOT(slotActiveMainWindowChanged()));
+
+/* This feature isn't provided by the plugin dialog
+ connect(m_view, SIGNAL(controllerDeviceEventReceived(MappedEvent *, const void *)),
+ dialog, SLOT(slotControllerDeviceEventReceived(MappedEvent *, const void *)));
+*/
+
+ // Plug the new dialog into the standard keyboard accelerators so
+ // that we can use them still while the plugin has focus.
+ //
+ plugAccelerators(dialog, dialog->getAccelerators());
+
+ connect(dialog,
+ SIGNAL(pluginSelected(InstrumentId, int, int)),
+ this,
+ SLOT(slotPluginSelected(InstrumentId, int, int)));
+
+ connect(dialog,
+ SIGNAL(pluginPortChanged(InstrumentId, int, int)),
+ this,
+ SLOT(slotPluginPortChanged(InstrumentId, int, int)));
+
+ connect(dialog,
+ SIGNAL(pluginProgramChanged(InstrumentId, int)),
+ this,
+ SLOT(slotPluginProgramChanged(InstrumentId, int)));
+
+ connect(dialog,
+ SIGNAL(changePluginConfiguration(InstrumentId, int, bool, QString, QString)),
+ this,
+ SLOT(slotChangePluginConfiguration(InstrumentId, int, bool, QString, QString)));
+
+ connect(dialog,
+ SIGNAL(showPluginGUI(InstrumentId, int)),
+ this,
+ SLOT(slotShowPluginGUI(InstrumentId, int)));
+
+ connect(dialog,
+ SIGNAL(stopPluginGUI(InstrumentId, int)),
+ this,
+ SLOT(slotStopPluginGUI(InstrumentId, int)));
+
+ connect(dialog,
+ SIGNAL(bypassed(InstrumentId, int, bool)),
+ this,
+ SLOT(slotPluginBypassed(InstrumentId, int, bool)));
+
+ connect(dialog,
+ SIGNAL(destroyed(InstrumentId, int)),
+ this,
+ SLOT(slotPluginDialogDestroyed(InstrumentId, int)));
+
+ connect(this, SIGNAL(documentAboutToChange()), dialog, SLOT(close()));
+
+ m_pluginDialogs[key] = dialog;
+ m_pluginDialogs[key]->show();
+
+ // Set modified
+ m_doc->slotDocumentModified();
+}
+
+void
+RosegardenGUIApp::slotPluginSelected(InstrumentId instrumentId,
+ int index, int plugin)
+{
+ const QObject *s = sender();
+
+ bool fromSynthMgr = (s == m_synthManager);
+
+ // It's assumed that ports etc will already have been set up on
+ // the AudioPluginInstance before this is invoked.
+
+ PluginContainer *container = 0;
+
+ container = m_doc->getStudio().getContainerById(instrumentId);
+ if (!container) {
+ RG_DEBUG << "RosegardenGUIApp::slotPluginSelected - "
+ << "no instrument or buss of id " << instrumentId << endl;
+ return ;
+ }
+
+ AudioPluginInstance *inst =
+ container->getPlugin(index);
+
+ if (!inst) {
+ RG_DEBUG << "RosegardenGUIApp::slotPluginSelected - "
+ << "got index of unknown plugin!" << endl;
+ return ;
+ }
+
+ if (plugin == -1) {
+ // Destroy plugin instance
+ //!!! seems iffy -- why can't we just unassign it?
+
+ if (StudioControl::
+ destroyStudioObject(inst->getMappedId())) {
+ RG_DEBUG << "RosegardenGUIApp::slotPluginSelected - "
+ << "cannot destroy Studio object "
+ << inst->getMappedId() << endl;
+ }
+
+ inst->setAssigned(false);
+ } else {
+ // If unassigned then create a sequencer instance of this
+ // AudioPluginInstance.
+ //
+ if (inst->isAssigned()) {
+ RG_DEBUG << "RosegardenGUIApp::slotPluginSelected - "
+ << " setting identifier for mapper id " << inst->getMappedId()
+ << " to " << inst->getIdentifier() << endl;
+
+ StudioControl::setStudioObjectProperty
+ (inst->getMappedId(),
+ MappedPluginSlot::Identifier,
+ strtoqstr(inst->getIdentifier()));
+ } else {
+ // create a studio object at the sequencer
+ MappedObjectId newId =
+ StudioControl::createStudioObject
+ (MappedObject::PluginSlot);
+
+ RG_DEBUG << "RosegardenGUIApp::slotPluginSelected - "
+ << " new MappedObjectId = " << newId << endl;
+
+ // set the new Mapped ID and that this instance
+ // is assigned
+ inst->setMappedId(newId);
+ inst->setAssigned(true);
+
+ // set the instrument id
+ StudioControl::setStudioObjectProperty
+ (newId,
+ MappedObject::Instrument,
+ MappedObjectValue(instrumentId));
+
+ // set the position
+ StudioControl::setStudioObjectProperty
+ (newId,
+ MappedObject::Position,
+ MappedObjectValue(index));
+
+ // set the plugin id
+ StudioControl::setStudioObjectProperty
+ (newId,
+ MappedPluginSlot::Identifier,
+ strtoqstr(inst->getIdentifier()));
+ }
+ }
+
+ int pluginMappedId = inst->getMappedId();
+
+ //!!! much code duplicated here from RosegardenGUIDoc::initialiseStudio
+
+ inst->setConfigurationValue
+ (qstrtostr(PluginIdentifier::RESERVED_PROJECT_DIRECTORY_KEY),
+ m_doc->getAudioFileManager().getAudioPath());
+
+ // Set opaque string configuration data (e.g. for DSSI plugin)
+ //
+ MappedObjectPropertyList config;
+ for (AudioPluginInstance::ConfigMap::const_iterator
+ i = inst->getConfiguration().begin();
+ i != inst->getConfiguration().end(); ++i) {
+ config.push_back(strtoqstr(i->first));
+ config.push_back(strtoqstr(i->second));
+ }
+ StudioControl::setStudioObjectPropertyList
+ (pluginMappedId,
+ MappedPluginSlot::Configuration,
+ config);
+
+ // Set the bypass
+ //
+ StudioControl::setStudioObjectProperty
+ (pluginMappedId,
+ MappedPluginSlot::Bypassed,
+ MappedObjectValue(inst->isBypassed()));
+
+ // Set the program
+ //
+ if (inst->getProgram() != "") {
+ StudioControl::setStudioObjectProperty
+ (pluginMappedId,
+ MappedPluginSlot::Program,
+ strtoqstr(inst->getProgram()));
+ }
+
+ // Set all the port values
+ //
+ PortInstanceIterator portIt;
+
+ for (portIt = inst->begin();
+ portIt != inst->end(); ++portIt) {
+ StudioControl::setStudioPluginPort
+ (pluginMappedId,
+ (*portIt)->number,
+ (*portIt)->value);
+ }
+
+ if (fromSynthMgr) {
+ int key = (index << 16) + instrumentId;
+ if (m_pluginDialogs[key]) {
+ m_pluginDialogs[key]->updatePlugin(plugin);
+ }
+ } else if (m_synthManager) {
+ m_synthManager->updatePlugin(instrumentId, plugin);
+ }
+
+ emit pluginSelected(instrumentId, index, plugin);
+
+ // Set modified
+ m_doc->slotDocumentModified();
+}
+
+void
+RosegardenGUIApp::slotChangePluginPort(InstrumentId instrumentId,
+ int pluginIndex,
+ int portIndex,
+ float value)
+{
+ PluginContainer *container = 0;
+
+ container = m_doc->getStudio().getContainerById(instrumentId);
+ if (!container) {
+ RG_DEBUG << "RosegardenGUIApp::slotChangePluginPort - "
+ << "no instrument or buss of id " << instrumentId << endl;
+ return ;
+ }
+
+ AudioPluginInstance *inst = container->getPlugin(pluginIndex);
+ if (!inst) {
+ RG_DEBUG << "RosegardenGUIApp::slotChangePluginPort - "
+ << "no plugin at index " << pluginIndex << " on " << instrumentId << endl;
+ return ;
+ }
+
+ PluginPortInstance *port = inst->getPort(portIndex);
+ if (!port) {
+ RG_DEBUG << "RosegardenGUIApp::slotChangePluginPort - no port "
+ << portIndex << endl;
+ return ;
+ }
+
+ RG_DEBUG << "RosegardenGUIApp::slotPluginPortChanged - "
+ << "setting plugin port (" << inst->getMappedId()
+ << ", " << portIndex << ") from " << port->value
+ << " to " << value << endl;
+
+ port->setValue(value);
+
+ StudioControl::setStudioPluginPort(inst->getMappedId(),
+ portIndex, port->value);
+
+ m_doc->slotDocumentModified();
+
+ // This modification came from The Outside!
+ int key = (pluginIndex << 16) + instrumentId;
+ if (m_pluginDialogs[key]) {
+ m_pluginDialogs[key]->updatePluginPortControl(portIndex);
+ }
+}
+
+void
+RosegardenGUIApp::slotPluginPortChanged(InstrumentId instrumentId,
+ int pluginIndex,
+ int portIndex)
+{
+ PluginContainer *container = 0;
+
+ container = m_doc->getStudio().getContainerById(instrumentId);
+ if (!container) {
+ RG_DEBUG << "RosegardenGUIApp::slotPluginPortChanged - "
+ << "no instrument or buss of id " << instrumentId << endl;
+ return ;
+ }
+
+ AudioPluginInstance *inst = container->getPlugin(pluginIndex);
+ if (!inst) {
+ RG_DEBUG << "RosegardenGUIApp::slotPluginPortChanged - "
+ << "no plugin at index " << pluginIndex << " on " << instrumentId << endl;
+ return ;
+ }
+
+ PluginPortInstance *port = inst->getPort(portIndex);
+ if (!port) {
+ RG_DEBUG << "RosegardenGUIApp::slotPluginPortChanged - no port "
+ << portIndex << endl;
+ return ;
+ }
+
+ RG_DEBUG << "RosegardenGUIApp::slotPluginPortChanged - "
+ << "setting plugin port (" << inst->getMappedId()
+ << ", " << portIndex << ") to " << port->value << endl;
+
+ StudioControl::setStudioPluginPort(inst->getMappedId(),
+ portIndex, port->value);
+
+ m_doc->slotDocumentModified();
+
+#ifdef HAVE_LIBLO
+ // This modification came from our own plugin dialog, so update
+ // any external GUIs
+ if (m_pluginGUIManager) {
+ m_pluginGUIManager->updatePort(instrumentId,
+ pluginIndex,
+ portIndex);
+ }
+#endif
+}
+
+void
+RosegardenGUIApp::slotChangePluginProgram(InstrumentId instrumentId,
+ int pluginIndex,
+ QString program)
+{
+ PluginContainer *container = 0;
+
+ container = m_doc->getStudio().getContainerById(instrumentId);
+ if (!container) {
+ RG_DEBUG << "RosegardenGUIApp::slotChangePluginProgram - "
+ << "no instrument or buss of id " << instrumentId << endl;
+ return ;
+ }
+
+ AudioPluginInstance *inst = container->getPlugin(pluginIndex);
+ if (!inst) {
+ RG_DEBUG << "RosegardenGUIApp::slotChangePluginProgram - "
+ << "no plugin at index " << pluginIndex << " on " << instrumentId << endl;
+ return ;
+ }
+
+ RG_DEBUG << "RosegardenGUIApp::slotChangePluginProgram - "
+ << "setting plugin program ("
+ << inst->getMappedId() << ") from " << inst->getProgram()
+ << " to " << program << endl;
+
+ inst->setProgram(qstrtostr(program));
+
+ StudioControl::
+ setStudioObjectProperty(inst->getMappedId(),
+ MappedPluginSlot::Program,
+ program);
+
+ PortInstanceIterator portIt;
+
+ for (portIt = inst->begin();
+ portIt != inst->end(); ++portIt) {
+ float value = StudioControl::getStudioPluginPort
+ (inst->getMappedId(),
+ (*portIt)->number);
+ (*portIt)->value = value;
+ }
+
+ // Set modified
+ m_doc->slotDocumentModified();
+
+ int key = (pluginIndex << 16) + instrumentId;
+ if (m_pluginDialogs[key]) {
+ m_pluginDialogs[key]->updatePluginProgramControl();
+ }
+}
+
+void
+RosegardenGUIApp::slotPluginProgramChanged(InstrumentId instrumentId,
+ int pluginIndex)
+{
+ PluginContainer *container = 0;
+
+ container = m_doc->getStudio().getContainerById(instrumentId);
+ if (!container) {
+ RG_DEBUG << "RosegardenGUIApp::slotPluginProgramChanged - "
+ << "no instrument or buss of id " << instrumentId << endl;
+ return ;
+ }
+
+ AudioPluginInstance *inst = container->getPlugin(pluginIndex);
+ if (!inst) {
+ RG_DEBUG << "RosegardenGUIApp::slotPluginProgramChanged - "
+ << "no plugin at index " << pluginIndex << " on " << instrumentId << endl;
+ return ;
+ }
+
+ QString program = strtoqstr(inst->getProgram());
+
+ RG_DEBUG << "RosegardenGUIApp::slotPluginProgramChanged - "
+ << "setting plugin program ("
+ << inst->getMappedId() << ") to " << program << endl;
+
+ StudioControl::
+ setStudioObjectProperty(inst->getMappedId(),
+ MappedPluginSlot::Program,
+ program);
+
+ PortInstanceIterator portIt;
+
+ for (portIt = inst->begin();
+ portIt != inst->end(); ++portIt) {
+ float value = StudioControl::getStudioPluginPort
+ (inst->getMappedId(),
+ (*portIt)->number);
+ (*portIt)->value = value;
+ }
+
+ // Set modified
+ m_doc->slotDocumentModified();
+
+#ifdef HAVE_LIBLO
+
+ if (m_pluginGUIManager)
+ m_pluginGUIManager->updateProgram(instrumentId,
+ pluginIndex);
+#endif
+}
+
+void
+RosegardenGUIApp::slotChangePluginConfiguration(InstrumentId instrumentId,
+ int index,
+ bool global,
+ QString key,
+ QString value)
+{
+ PluginContainer *container = 0;
+
+ container = m_doc->getStudio().getContainerById(instrumentId);
+ if (!container) {
+ RG_DEBUG << "RosegardenGUIApp::slotChangePluginConfiguration - "
+ << "no instrument or buss of id " << instrumentId << endl;
+ return ;
+ }
+
+ AudioPluginInstance *inst = container->getPlugin(index);
+
+ if (global && inst) {
+
+ // Set the same configuration on other plugins in the same
+ // instance group
+
+ AudioPlugin *pl =
+ m_pluginManager->getPluginByIdentifier(strtoqstr(inst->getIdentifier()));
+
+ if (pl && pl->isGrouped()) {
+
+ InstrumentList il =
+ m_doc->getStudio().getAllInstruments();
+
+ for (InstrumentList::iterator i = il.begin();
+ i != il.end(); ++i) {
+
+ for (PluginInstanceIterator pli =
+ (*i)->beginPlugins();
+ pli != (*i)->endPlugins(); ++pli) {
+
+ if (*pli && (*pli)->isAssigned() &&
+ (*pli)->getIdentifier() == inst->getIdentifier() &&
+ (*pli) != inst) {
+
+ slotChangePluginConfiguration
+ ((*i)->getId(), (*pli)->getPosition(),
+ false, key, value);
+
+#ifdef HAVE_LIBLO
+
+ m_pluginGUIManager->updateConfiguration
+ ((*i)->getId(), (*pli)->getPosition(), key);
+#endif
+
+ }
+ }
+ }
+ }
+ }
+
+ if (inst) {
+
+ inst->setConfigurationValue(qstrtostr(key), qstrtostr(value));
+
+ MappedObjectPropertyList config;
+ for (AudioPluginInstance::ConfigMap::const_iterator
+ i = inst->getConfiguration().begin();
+ i != inst->getConfiguration().end(); ++i) {
+ config.push_back(strtoqstr(i->first));
+ config.push_back(strtoqstr(i->second));
+ }
+
+ RG_DEBUG << "RosegardenGUIApp::slotChangePluginConfiguration: setting new config on mapped id " << inst->getMappedId() << endl;
+
+ StudioControl::setStudioObjectPropertyList
+ (inst->getMappedId(),
+ MappedPluginSlot::Configuration,
+ config);
+
+ // Set modified
+ m_doc->slotDocumentModified();
+
+ int key = (index << 16) + instrumentId;
+ if (m_pluginDialogs[key]) {
+ m_pluginDialogs[key]->updatePluginProgramList();
+ }
+ }
+}
+
+void
+RosegardenGUIApp::slotPluginDialogDestroyed(InstrumentId instrumentId,
+ int index)
+{
+ int key = (index << 16) + instrumentId;
+ m_pluginDialogs[key] = 0;
+}
+
+void
+RosegardenGUIApp::slotPluginBypassed(InstrumentId instrumentId,
+ int pluginIndex, bool bp)
+{
+ PluginContainer *container = 0;
+
+ container = m_doc->getStudio().getContainerById(instrumentId);
+ if (!container) {
+ RG_DEBUG << "RosegardenGUIApp::slotPluginBypassed - "
+ << "no instrument or buss of id " << instrumentId << endl;
+ return ;
+ }
+
+ AudioPluginInstance *inst = container->getPlugin(pluginIndex);
+
+ if (inst) {
+ StudioControl::setStudioObjectProperty
+ (inst->getMappedId(),
+ MappedPluginSlot::Bypassed,
+ MappedObjectValue(bp));
+
+ // Set the bypass on the instance
+ //
+ inst->setBypass(bp);
+
+ // Set modified
+ m_doc->slotDocumentModified();
+ }
+
+ emit pluginBypassed(instrumentId, pluginIndex, bp);
+}
+
+void
+RosegardenGUIApp::slotShowPluginGUI(InstrumentId instrument,
+ int index)
+{
+#ifdef HAVE_LIBLO
+ m_pluginGUIManager->showGUI(instrument, index);
+#endif
+}
+
+void
+RosegardenGUIApp::slotStopPluginGUI(InstrumentId instrument,
+ int index)
+{
+#ifdef HAVE_LIBLO
+ m_pluginGUIManager->stopGUI(instrument, index);
+#endif
+}
+
+void
+RosegardenGUIApp::slotPluginGUIExited(InstrumentId instrument,
+ int index)
+{
+ int key = (index << 16) + instrument;
+ if (m_pluginDialogs[key]) {
+ m_pluginDialogs[key]->guiExited();
+ }
+}
+
+void
+RosegardenGUIApp::slotPlayList()
+{
+ if (!m_playList) {
+ m_playList = new PlayListDialog(i18n("Play List"), this);
+ connect(m_playList, SIGNAL(closing()),
+ SLOT(slotPlayListClosed()));
+ connect(m_playList->getPlayList(), SIGNAL(play(QString)),
+ SLOT(slotPlayListPlay(QString)));
+ }
+
+ m_playList->show();
+}
+
+void
+RosegardenGUIApp::slotPlayListPlay(QString url)
+{
+ slotStop();
+ openURL(url);
+ slotPlay();
+}
+
+void
+RosegardenGUIApp::slotPlayListClosed()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotPlayListClosed()\n";
+ m_playList = 0;
+}
+
+void
+RosegardenGUIApp::slotTutorial()
+{
+ QString tutorialURL = i18n("http://rosegarden.sourceforge.net/tutorial/en/chapter-0.html");
+ kapp->invokeBrowser(tutorialURL);
+}
+
+void
+RosegardenGUIApp::slotBugGuidelines()
+{
+ QString glURL = i18n("http://rosegarden.sourceforge.net/tutorial/bug-guidelines.html");
+ kapp->invokeBrowser(glURL);
+}
+
+void
+RosegardenGUIApp::slotBankEditorClosed()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotBankEditorClosed()\n";
+
+ if (m_doc->isModified()) {
+ if (m_view)
+ m_view->slotSelectTrackSegments(m_doc->getComposition().getSelectedTrack());
+ }
+
+ m_bankEditor = 0;
+}
+
+void
+RosegardenGUIApp::slotDeviceManagerClosed()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotDeviceManagerClosed()\n";
+
+ if (m_doc->isModified()) {
+ if (m_view)
+ m_view->slotSelectTrackSegments(m_doc->getComposition().getSelectedTrack());
+ }
+
+ m_deviceManager = 0;
+}
+
+void
+RosegardenGUIApp::slotSynthPluginManagerClosed()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotSynthPluginManagerClosed()\n";
+
+ m_synthManager = 0;
+}
+
+void
+RosegardenGUIApp::slotAudioMixerClosed()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotAudioMixerClosed()\n";
+
+ m_audioMixer = 0;
+}
+
+void
+RosegardenGUIApp::slotMidiMixerClosed()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotMidiMixerClosed()\n";
+
+ m_midiMixer = 0;
+}
+
+void
+RosegardenGUIApp::slotAudioManagerClosed()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotAudioManagerClosed()\n";
+
+ if (m_doc->isModified()) {
+ if (m_view)
+ m_view->slotSelectTrackSegments(m_doc->getComposition().getSelectedTrack());
+ }
+
+ m_audioManagerDialog = 0;
+}
+
+void
+RosegardenGUIApp::slotPanic()
+{
+ if (m_seqManager) {
+ // Stop the transport before we send a panic as the
+ // playback goes all to hell anyway.
+ //
+ slotStop();
+
+ ProgressDialog progressDlg(i18n("Queueing MIDI panic events for tranmission..."),
+ 100,
+ this);
+ CurrentProgressDialog::set
+ (&progressDlg);
+ ProgressDialog::processEvents();
+
+ connect(m_seqManager, SIGNAL(setProgress(int)),
+ progressDlg.progressBar(), SLOT(setValue(int)));
+ connect(m_seqManager, SIGNAL(incrementProgress(int)),
+ progressDlg.progressBar(), SLOT(advance(int)));
+
+ m_seqManager->panic();
+
+ }
+}
+
+void
+RosegardenGUIApp::slotPopulateTrackInstrumentPopup()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotSetTrackInstrument\n";
+ Composition &comp = m_doc->getComposition();
+ Track *track = comp.getTrackById(comp.getSelectedTrack());
+
+ if (!track) {
+ RG_DEBUG << "Weird: no track available for instrument popup!" << endl;
+ return ;
+ }
+
+ Instrument* instrument = m_doc->getStudio().getInstrumentById(track->getInstrument());
+
+ QPopupMenu* popup = dynamic_cast<QPopupMenu*>(factory()->container("set_track_instrument", this));
+
+ m_view->getTrackEditor()->getTrackButtons()->populateInstrumentPopup(instrument, popup);
+}
+
+void
+RosegardenGUIApp::slotRemapInstruments()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotRemapInstruments\n";
+ RemapInstrumentDialog dialog(this, m_doc);
+
+ connect(&dialog, SIGNAL(applyClicked()),
+ m_view->getTrackEditor()->getTrackButtons(),
+ SLOT(slotSynchroniseWithComposition()));
+
+ if (dialog.exec() == QDialog::Accepted) {
+ RG_DEBUG << "slotRemapInstruments - accepted\n";
+ }
+
+}
+
+void
+RosegardenGUIApp::slotSaveDefaultStudio()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotSaveDefaultStudio\n";
+
+ int reply = KMessageBox::warningYesNo
+ (this, i18n("Are you sure you want to save this as your default studio?"));
+
+ if (reply != KMessageBox::Yes)
+ return ;
+
+ KTmpStatusMsg msg(i18n("Saving current document as default studio..."), this);
+
+ QString autoloadFile = ::locateLocal("appdata", "autoload.rg");
+
+ RG_DEBUG << "RosegardenGUIApp::slotSaveDefaultStudio : saving studio in "
+ << autoloadFile << endl;
+
+ SetWaitCursor waitCursor;
+ QString errMsg;
+ bool res = m_doc->saveDocument(autoloadFile, errMsg);
+ if (!res) {
+ if (errMsg)
+ KMessageBox::error(this, i18n(QString("Could not auto-save document at %1\nError was : %2")
+ .arg(autoloadFile).arg(errMsg)));
+ else
+ KMessageBox::error(this, i18n(QString("Could not auto-save document at %1")
+ .arg(autoloadFile)));
+
+ }
+}
+
+void
+RosegardenGUIApp::slotImportDefaultStudio()
+{
+ int reply = KMessageBox::warningYesNo
+ (this, i18n("Are you sure you want to import your default studio and lose the current one?"));
+
+ if (reply != KMessageBox::Yes)
+ return ;
+
+ QString autoloadFile =
+ KGlobal::dirs()->findResource("appdata", "autoload.rg");
+
+ QFileInfo autoloadFileInfo(autoloadFile);
+
+ if (!autoloadFileInfo.isReadable()) {
+ RG_DEBUG << "RosegardenGUIDoc::slotImportDefaultStudio - "
+ << "can't find autoload file - defaulting" << endl;
+ return ;
+ }
+
+ slotImportStudioFromFile(autoloadFile);
+}
+
+void
+RosegardenGUIApp::slotImportStudio()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotImportStudio()\n";
+
+ QString studioDir = KGlobal::dirs()->findResource("appdata", "library/");
+ QDir dir(studioDir);
+ if (!dir.exists()) {
+ studioDir = ":ROSEGARDENDEVICE";
+ } else {
+ studioDir = "file://" + studioDir;
+ }
+
+ KURL url = KFileDialog::getOpenURL
+ (studioDir,
+ "audio/x-rosegarden-device audio/x-rosegarden",
+ this, i18n("Import Studio from File"));
+
+ if (url.isEmpty())
+ return ;
+
+ QString target;
+ if (KIO::NetAccess::download(url, target, this) == false) {
+ KMessageBox::error(this, i18n("Cannot download file %1")
+ .arg(url.prettyURL()));
+ return ;
+ }
+
+ slotImportStudioFromFile(target);
+}
+
+void
+RosegardenGUIApp::slotImportStudioFromFile(const QString &file)
+{
+ RosegardenGUIDoc *doc = new RosegardenGUIDoc(this, 0, true); // skipAutoload
+
+ Studio &oldStudio = m_doc->getStudio();
+ Studio &newStudio = doc->getStudio();
+
+ // Add some dummy devices for when we open the document. We guess
+ // that the file won't have more than 32 devices.
+ //
+ // for (unsigned int i = 0; i < 32; i++) {
+ // newStudio.addDevice("", i, Device::Midi);
+ // }
+
+ if (doc->openDocument(file, true)) { // true because we actually
+ // do want to create devices
+ // on the sequencer here
+
+ KMacroCommand *command = new KMacroCommand(i18n("Import Studio"));
+ doc->syncDevices();
+
+ // We actually only copy across MIDI play devices... for now
+ std::vector<DeviceId> midiPlayDevices;
+
+ for (DeviceList::const_iterator i =
+ oldStudio.begin(); i != oldStudio.end(); ++i) {
+
+ MidiDevice *md =
+ dynamic_cast<MidiDevice *>(*i);
+
+ if (md && (md->getDirection() == MidiDevice::Play)) {
+ midiPlayDevices.push_back((*i)->getId());
+ }
+ }
+
+ std::vector<DeviceId>::iterator di(midiPlayDevices.begin());
+
+ for (DeviceList::const_iterator i =
+ newStudio.begin(); i != newStudio.end(); ++i) {
+
+ MidiDevice *md =
+ dynamic_cast<MidiDevice *>(*i);
+
+ if (md && (md->getDirection() == MidiDevice::Play)) {
+ if (di != midiPlayDevices.end()) {
+ MidiDevice::VariationType variation
+ (md->getVariationType());
+ BankList bl(md->getBanks());
+ ProgramList pl(md->getPrograms());
+ ControlList cl(md->getControlParameters());
+
+ ModifyDeviceCommand* mdCommand = new ModifyDeviceCommand(&oldStudio,
+ *di,
+ md->getName(),
+ md->getLibrarianName(),
+ md->getLibrarianEmail());
+ mdCommand->setVariation(variation);
+ mdCommand->setBankList(bl);
+ mdCommand->setProgramList(pl);
+ mdCommand->setControlList(cl);
+ mdCommand->setOverwrite(true);
+ mdCommand->setRename(md->getName() != "");
+
+ command->addCommand(mdCommand);
+ ++di;
+ }
+ }
+ }
+
+ while (di != midiPlayDevices.end()) {
+ command->addCommand(new CreateOrDeleteDeviceCommand
+ (&oldStudio,
+ *di));
+ }
+
+ oldStudio.setMIDIThruFilter(newStudio.getMIDIThruFilter());
+ oldStudio.setMIDIRecordFilter(newStudio.getMIDIRecordFilter());
+
+ m_doc->getCommandHistory()->addCommand(command);
+ m_doc->syncDevices();
+ m_doc->initialiseStudio(); // The other document will have reset it
+ }
+
+ delete doc;
+}
+
+void
+RosegardenGUIApp::slotResetMidiNetwork()
+{
+ if (m_seqManager) {
+
+ m_seqManager->preparePlayback(true);
+
+ m_seqManager->resetMidiNetwork();
+ }
+
+}
+
+void
+RosegardenGUIApp::slotModifyMIDIFilters()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotModifyMIDIFilters" << endl;
+
+ MidiFilterDialog dialog(this, m_doc);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ RG_DEBUG << "slotModifyMIDIFilters - accepted" << endl;
+ }
+}
+
+void
+RosegardenGUIApp::slotManageMetronome()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotManageMetronome" << endl;
+
+ ManageMetronomeDialog dialog(this, m_doc);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ RG_DEBUG << "slotManageMetronome - accepted" << endl;
+ }
+}
+
+void
+RosegardenGUIApp::slotAutoSave()
+{
+ if (!m_seqManager ||
+ m_seqManager->getTransportStatus() == PLAYING ||
+ m_seqManager->getTransportStatus() == RECORDING)
+ return ;
+
+ KConfig* config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+ if (!config->readBoolEntry("autosave", true))
+ return ;
+
+ m_doc->slotAutoSave();
+}
+
+void
+RosegardenGUIApp::slotUpdateAutoSaveInterval(unsigned int interval)
+{
+ RG_DEBUG << "RosegardenGUIApp::slotUpdateAutoSaveInterval - "
+ << "changed interval to " << interval << endl;
+ m_autoSaveTimer->changeInterval(int(interval) * 1000);
+}
+
+void
+RosegardenGUIApp::slotUpdateSidebarStyle(unsigned int style)
+{
+ RG_DEBUG << "RosegardenGUIApp::slotUpdateSidebarStyle - "
+ << "changed style to " << style << endl;
+ m_parameterArea->setArrangement((RosegardenParameterArea::Arrangement) style);
+}
+
+void
+RosegardenGUIApp::slotShowTip()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotShowTip" << endl;
+ KTipDialog::showTip(this, locate("data", "rosegarden/tips"), true);
+}
+
+void RosegardenGUIApp::slotShowToolHelp(const QString &s)
+{
+ QString msg = s;
+ if (msg != "") msg = " " + msg;
+ slotStatusMsg(msg);
+}
+
+void
+RosegardenGUIApp::slotEnableMIDIThruRouting()
+{
+ m_seqManager->enableMIDIThruRouting(m_enableMIDIrouting->isChecked());
+}
+
+TransportDialog* RosegardenGUIApp::getTransport()
+{
+ if (m_transport == 0)
+ createAndSetupTransport();
+
+ return m_transport;
+}
+
+RosegardenGUIDoc *RosegardenGUIApp::getDocument() const
+{
+ return m_doc;
+}
+
+void
+RosegardenGUIApp::awaitDialogClearance()
+{
+ bool haveDialog = true;
+
+ std::cerr << "RosegardenGUIApp::awaitDialogClearance: entering" << std::endl;
+
+ while (haveDialog) {
+
+ const QObjectList *c = children();
+ if (!c) return;
+
+ haveDialog = false;
+ for (QObjectList::const_iterator i = c->begin(); i != c->end(); ++i) {
+ QDialog *dialog = dynamic_cast<QDialog *>(*i);
+ if (dialog && dialog->isVisible()) {
+ haveDialog = true;
+ break;
+ }
+ }
+
+// std::cerr << "RosegardenGUIApp::awaitDialogClearance: have dialog = "
+// << haveDialog << std::endl;
+
+ if (haveDialog) kapp->processEvents();
+ }
+
+ std::cerr << "RosegardenGUIApp::awaitDialogClearance: exiting" << std::endl;
+}
+
+void
+RosegardenGUIApp::slotNewerVersionAvailable(QString v)
+{
+ if (m_firstRun) return;
+ KStartupLogo::hideIfStillThere();
+ CurrentProgressDialog::freeze();
+ awaitDialogClearance();
+ KMessageBox::information
+ (this,
+ i18n("<h3>Newer version available</h3><p>A newer version of Rosegarden may be available.<br>Please consult the <a href=\"http://www.rosegardenmusic.com/getting/\">Rosegarden website</a> for more information.</p>"),
+ i18n("Newer version available"),
+ QString("version-%1-available-show").arg(v),
+ KMessageBox::AllowLink);
+ CurrentProgressDialog::thaw();
+}
+
+void
+RosegardenGUIApp::slotSetQuickMarker()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotSetQuickMarker" << endl;
+
+ m_doc->setQuickMarker();
+ getView()->getTrackEditor()->updateRulers();
+}
+
+void
+RosegardenGUIApp::slotJumpToQuickMarker()
+{
+ RG_DEBUG << "RosegardenGUIApp::slotJumpToQuickMarker" << endl;
+
+ m_doc->jumpToQuickMarker();
+}
+
+const void* RosegardenGUIApp::SequencerExternal = (void*)-1;
+RosegardenGUIApp *RosegardenGUIApp::m_myself = 0;
+
+}
+#include "RosegardenGUIApp.moc"
diff --git a/src/gui/application/RosegardenGUIApp.h b/src/gui/application/RosegardenGUIApp.h
new file mode 100644
index 0000000..502d195
--- /dev/null
+++ b/src/gui/application/RosegardenGUIApp.h
@@ -0,0 +1,1691 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ROSEGARDENGUIAPP_H_
+#define _RG_ROSEGARDENGUIAPP_H_
+
+#include <map>
+#include <set>
+#include "base/MidiProgram.h"
+#include "gui/dialogs/TempoDialog.h"
+#include "gui/widgets/ZoomSlider.h"
+#include "RosegardenIface.h"
+#include "base/Event.h"
+#include "sound/AudioFile.h"
+#include "sound/Midi.h"
+#include <kdockwidget.h>
+#include <qstring.h>
+#include <qvaluevector.h>
+
+
+class QWidget;
+class QTimer;
+class QTextCodec;
+class QShowEvent;
+class QObject;
+class QLabel;
+class QCursor;
+class QAccel;
+class KURL;
+class KTempFile;
+class KToggleAction;
+class KRecentFilesAction;
+class KProcess;
+class KConfig;
+class KAction;
+
+
+namespace Rosegarden
+{
+
+class TriggerSegmentManager;
+class TransportDialog;
+class TrackParameterBox;
+class TempoView;
+class SynthPluginManagerDialog;
+class StartupTester;
+class SequenceManager;
+class SegmentSelection;
+class SegmentParameterBox;
+class RosegardenParameterArea;
+class RosegardenGUIView;
+class RosegardenGUIDoc;
+class RealTime;
+class ProgressBar;
+class PlayListDialog;
+class MidiMixerWindow;
+class MarkerEditor;
+class MappedComposition;
+class LircCommander;
+class LircClient;
+class InstrumentParameterBox;
+class DeviceManagerDialog;
+class ControlEditorDialog;
+class Composition;
+class Clipboard;
+class BankEditorDialog;
+class AudioPluginOSCGUIManager;
+class AudioPluginManager;
+class AudioPluginDialog;
+class AudioMixerWindow;
+class AudioManagerDialog;
+
+/**
+ * The base class for RosegardenGUI application windows. It sets up the main
+ * window and reads the config file as well as providing a menubar, toolbar
+ * and statusbar. An instance of RosegardenGUIView creates your center view, which is connected
+ * to the window's Doc object.
+ * RosegardenGUIApp reimplements the methods that KTMainWindow provides for main window handling and supports
+ * full session management as well as keyboard accelerator configuration by using KAccel.
+ * @see KTMainWindow
+ * @see KApplication
+ * @see KConfig
+ * @see KAccel
+ *
+ * @author Source Framework Automatically Generated by KDevelop, (c) The KDevelop Team.
+ * @version KDevelop version 0.4 code generation
+ */
+class RosegardenGUIApp : public KDockMainWindow, virtual public RosegardenIface
+{
+ Q_OBJECT
+
+ friend class RosegardenGUIView;
+
+public:
+
+ /**
+ * construtor of RosegardenGUIApp, calls all init functions to
+ * create the application.
+ * \arg useSequencer : if true, the sequencer is launched
+ * @see initMenuBar initToolBar
+ */
+ RosegardenGUIApp(bool useSequencer = true,
+ bool useExistingSequencer = false,
+ QObject *startupStatusMessageReceiver = 0);
+
+ virtual ~RosegardenGUIApp();
+
+ /*
+ * Get the current copy of the app object
+ */
+ static RosegardenGUIApp *self() { return m_myself; }
+
+ /**
+ * returns a pointer to the current document connected to the
+ * KTMainWindow instance and is used by * the View class to access
+ * the document object's methods
+ */
+ RosegardenGUIDoc *getDocument() const;
+
+ RosegardenGUIView* getView() { return m_view; }
+
+ TransportDialog* getTransport();
+
+ enum ImportType { ImportRG4, ImportMIDI, ImportRG21, ImportHydrogen, ImportCheckType };
+
+ /**
+ * open a Rosegarden file
+ */
+ virtual void openFile(QString filePath) { openFile(filePath, ImportCheckType); }
+
+ /**
+ * open a file, explicitly specifying its type
+ */
+ void openFile(QString filePath, ImportType type);
+
+ /**
+ * decode and open a project file
+ */
+ void importProject(QString filePath);
+
+ /**
+ * open a URL
+ */
+ virtual void openURL(QString url);
+
+ /**
+ * merge a file with the existing document
+ */
+ virtual void mergeFile(QString filePath) { mergeFile(filePath, ImportCheckType); }
+
+ /**
+ * merge a file, explicitly specifying its type
+ */
+ void mergeFile(QString filePath, ImportType type);
+
+ /**
+ * open a URL
+ */
+ void openURL(const KURL &url);
+
+ /**
+ * export a MIDI file
+ */
+ void exportMIDIFile(QString url);
+
+ /**
+ * export a Csound scorefile
+ */
+ void exportCsoundFile(QString url);
+
+ /**
+ * export a Mup file
+ */
+ void exportMupFile(QString url);
+
+ /**
+ * export a LilyPond file
+ */
+ bool exportLilyPondFile(QString url, bool forPreview = false);
+
+ /**
+ * export a MusicXml file
+ */
+ void exportMusicXmlFile(QString url);
+
+ /**
+ * Get the sequence manager object
+ */
+ SequenceManager* getSequenceManager() { return m_seqManager; }
+
+ /**
+ * Get a progress bar
+ */
+ ProgressBar *getProgressBar() { return m_progressBar; }
+
+ /**
+ * Equivalents of the GUI slots, for DCOP use
+ */
+ virtual void fileNew() { slotFileNew(); }
+ virtual void fileSave() { slotFileSave(); }
+ virtual void fileClose() { slotFileClose(); }
+ virtual void quit() { slotQuit(); }
+
+ virtual void play() { slotPlay(); }
+ virtual void stop() { slotStop(); }
+ virtual void rewind() { slotRewind(); }
+ virtual void fastForward() { slotFastforward(); }
+ virtual void record() { slotRecord(); }
+ virtual void rewindToBeginning() { slotRewindToBeginning(); }
+ virtual void fastForwardToEnd() { slotFastForwardToEnd(); }
+ virtual void jumpToTime(int sec, int usec) { slotJumpToTime(sec, usec); }
+ virtual void startAtTime(int sec, int usec) { slotStartAtTime(sec, usec); }
+
+ virtual void trackUp() { slotTrackUp(); }
+ virtual void trackDown() { slotTrackDown(); }
+ virtual void toggleMutedCurrentTrack() { slotToggleMutedCurrentTrack(); }
+ virtual void toggleRecordCurrentTrack() { slotToggleRecordCurrentTrack(); }
+
+ /**
+ * Start the sequencer auxiliary process
+ * (built in the 'sequencer' directory)
+ *
+ * @see slotSequencerExited()
+ */
+ bool launchSequencer(bool useExistingSequencer);
+
+#ifdef HAVE_LIBJACK
+ /*
+ * Launch and control JACK if required to by configuration
+ */
+ bool launchJack();
+
+#endif // HAVE_LIBJACK
+
+
+ /**
+ * Returns whether we're using a sequencer.
+ * false if the '--nosequencer' option was given
+ * true otherwise.
+ * This doesn't give the state of the sequencer
+ * @see #isSequencerRunning
+ */
+ bool isUsingSequencer() { return m_useSequencer; }
+
+ /**
+ * Returns whether there's a sequencer running.
+ * The result is dynamically updated depending on the sequencer's
+ * status.
+ */
+ bool isSequencerRunning() { return m_useSequencer && (m_sequencerProcess != 0); }
+
+ /**
+ * Returns true if the sequencer wasn't started by us
+ */
+ bool isSequencerExternal() { return m_useSequencer && (m_sequencerProcess == SequencerExternal); }
+
+ /**
+ * Set the sequencer status - pass through DCOP as an int
+ */
+ virtual void notifySequencerStatus(int status);
+
+ /**
+ * Handle some random incoming MIDI events.
+ */
+ virtual void processAsynchronousMidi(const MappedComposition &);
+
+ /*
+ * The sequencer calls this method when it's running to
+ * allow us to sync data with it.
+ *
+ */
+ virtual void alive();
+
+ /*
+ * Tell the application whether this is the first time this
+ * version of RG has been run
+ */
+ void setIsFirstRun(bool first) { m_firstRun = first; }
+
+ /*
+ * Wait in a sub-event-loop until all modal dialogs from the main
+ * window have been cleared
+ */
+ void awaitDialogClearance();
+
+ /*
+ * Return the clipboard
+ */
+ Clipboard* getClipboard() { return m_clipboard; }
+
+#ifdef HAVE_LIBLO
+ /**
+ * Return the plugin native GUI manager, if we have one
+ */
+ AudioPluginOSCGUIManager *getPluginGUIManager() { return m_pluginGUIManager; }
+#endif
+
+ /**
+ * Plug a widget into our common accelerators
+ */
+ void plugAccelerators(QWidget *widget, QAccel *accel);
+
+ /**
+ * Override from QWidget
+ * Toolbars and docks need special treatment
+ */
+ virtual void setCursor(const QCursor&);
+
+ bool isTrackEditorPlayTracking() const;
+
+ bool testAudioPath(QString op); // and open the dialog to set it if unset
+ bool haveAudioImporter() const { return m_haveAudioImporter; }
+
+protected:
+
+ /**** File handling code that we don't want the outside world to use ****/
+ /**/
+ /**/
+
+ /**
+ * Create document from a file
+ */
+ RosegardenGUIDoc* createDocument(QString filePath, ImportType type = ImportRG4);
+
+ /**
+ * Create a document from RG file
+ */
+ RosegardenGUIDoc* createDocumentFromRGFile(QString filePath);
+
+ /**
+ * Create document from MIDI file
+ */
+ RosegardenGUIDoc* createDocumentFromMIDIFile(QString filePath);
+
+ /**
+ * Create document from RG21 file
+ */
+ RosegardenGUIDoc* createDocumentFromRG21File(QString filePath);
+
+ /**
+ * Create document from Hydrogen drum machine file
+ */
+ RosegardenGUIDoc* createDocumentFromHydrogenFile(QString filePath);
+
+ /**/
+ /**/
+ /***********************************************************************/
+
+ /**
+ * Set the 'Rewind' and 'Fast Forward' buttons in the transport
+ * toolbar to AutoRepeat
+ */
+ void setRewFFwdToAutoRepeat();
+
+ static const void* SequencerExternal;
+
+ /// Raise the transport along
+ virtual void showEvent(QShowEvent*);
+
+ /**
+ * read general Options again and initialize all variables like
+ * the recent file list
+ */
+ void readOptions();
+
+ /**
+ * add an item pointing to the example files in the KFileDialog speedbar
+ */
+ void setupFileDialogSpeedbar();
+
+ /**
+ * create menus and toolbars
+ */
+ void setupActions();
+
+ /**
+ * sets up the zoom toolbar
+ */
+ void initZoomToolbar();
+
+ /**
+ * sets up the statusbar for the main window by initialzing a
+ * statuslabel.
+ */
+ void initStatusBar();
+
+ /**
+ * creates the centerwidget of the KTMainWindow instance and sets
+ * it as the view
+ */
+ void initView();
+
+ /**
+ * queryClose is called by KTMainWindow on each closeEvent of a
+ * window. Against the default implementation (only returns true),
+ * this calls saveModified() on the document object to ask if the
+ * document shall be saved if Modified; on cancel the closeEvent
+ * is rejected.
+ *
+ * @see KTMainWindow#queryClose
+ * @see KTMainWindow#closeEvent
+ */
+ virtual bool queryClose();
+
+ /**
+ * queryExit is called by KTMainWindow when the last window of the
+ * application is going to be closed during the closeEvent().
+ * Against the default implementation that just returns true, this
+ * calls saveOptions() to save the settings of the last window's
+ * properties.
+ *
+ * @see KTMainWindow#queryExit
+ * @see KTMainWindow#closeEvent
+ */
+ virtual bool queryExit();
+
+ /**
+ * saves the window properties for each open window during session
+ * end to the session config file, including saving the currently
+ * opened file by a temporary filename provided by KApplication.
+ *
+ * @see KTMainWindow#saveProperties
+ */
+ virtual void saveGlobalProperties(KConfig *_cfg);
+
+ /**
+ * reads the session config file and restores the application's
+ * state including the last opened files and documents by reading
+ * the temporary files saved by saveProperties()
+ *
+ * @see KTMainWindow#readProperties
+ */
+ virtual void readGlobalProperties(KConfig *_cfg);
+
+ /**
+ * Create a new audio file for the sequencer and return the
+ * path to it as a QString.
+ */
+ QString createNewAudioFile();
+ QValueVector<QString> createRecordAudioFiles(const QValueVector<InstrumentId> &);
+
+ QString getAudioFilePath();
+
+ //!!!mtr
+ QValueVector<InstrumentId> getArmedInstruments();
+
+ /**
+ * Show a sequencer error to the user. This is for errors from
+ * the framework code; the playback code uses mapped compositions
+ * to send these things back asynchronously.
+ */
+ void showError(QString error);
+
+ /*
+ * Return AudioManagerDialog
+ */
+ AudioManagerDialog* getAudioManagerDialog() { return m_audioManagerDialog; }
+
+ /**
+ * Ask the user for a file to save to, and check that it's
+ * good and that (if it exists) the user agrees to overwrite.
+ * Return a null string if the write should not go ahead.
+ */
+ QString getValidWriteFile(QString extension, QString label);
+
+ /**
+ * Find any non-ASCII strings in a composition that has been
+ * generated by MIDI import or any other procedure that produces
+ * events with unknown text encoding, and ask the user what
+ * encoding to translate them from. This assumes all text strings
+ * in the composition are of the same encoding, and that it is not
+ * (known to be) utf8 (in which case no transcoding would be
+ * necessary).
+ */
+ void fixTextEncodings(Composition *);
+ QTextCodec *guessTextCodec(std::string);
+
+ /**
+ * Set the current document
+ *
+ * Do all the needed housework when the current document changes
+ * (like closing edit views, emitting documentChanged signal, etc...)
+ */
+ void setDocument(RosegardenGUIDoc*);
+
+ /**
+ * Jog a selection of segments by an amount
+ */
+ void jogSelection(timeT amount);
+
+ void createAndSetupTransport();
+
+signals:
+ void startupStatusMessage(QString message);
+
+ /// emitted just before the document is changed
+ void documentAboutToChange();
+
+ /// emitted when the current document changes
+ void documentChanged(RosegardenGUIDoc*);
+
+ /// emitted when the set of selected segments changes (relayed from RosegardenGUIView)
+ void segmentsSelected(const SegmentSelection &);
+
+ /// emitted when the composition state (selected track, solo, etc...) changes
+ void compositionStateUpdate();
+
+ /// emitted when instrument parameters change (relayed from InstrumentParameterBox)
+ void instrumentParametersChanged(InstrumentId);
+
+ /// emitted when a plugin dialog selects a plugin
+ void pluginSelected(InstrumentId, int, int);
+
+ /// emitted when a plugin dialog (un)bypasses a plugin
+ void pluginBypassed(InstrumentId, int, bool);
+
+public slots:
+
+ /**
+ * open a URL - used for Dn'D
+ *
+ * @param url : a string containing a url (protocol://foo/bar/file.rg)
+ */
+ virtual void slotOpenDroppedURL(QString url);
+
+ /**
+ * Open the document properties dialog on the Audio page
+ */
+ virtual void slotOpenAudioPathSettings();
+
+ /**
+ * open a new application window by creating a new instance of
+ * RosegardenGUIApp
+ */
+ void slotFileNewWindow();
+
+ /**
+ * clears the document in the actual view to reuse it as the new
+ * document
+ */
+ void slotFileNew();
+
+ /**
+ * open a file and load it into the document
+ */
+ void slotFileOpen();
+
+ /**
+ * opens a file from the recent files menu
+ */
+ void slotFileOpenRecent(const KURL&);
+
+ /**
+ * save a document
+ */
+ void slotFileSave();
+
+ /**
+ * save a document by a new filename
+ */
+ bool slotFileSaveAs();
+
+ /**
+ * asks for saving if the file is modified, then closes the actual
+ * file and window
+ */
+ void slotFileClose();
+
+ /**
+ * print the actual file
+ */
+ void slotFilePrint();
+
+ /**
+ * print preview
+ */
+ void slotFilePrintPreview();
+
+ /**
+ * Let the user select a Rosegarden Project file for import
+ */
+ void slotImportProject();
+
+ /**
+ * Let the user select a MIDI file for import
+ */
+ void slotImportMIDI();
+
+ /**
+ * Revert to last loaded file
+ */
+ void slotRevertToSaved();
+
+ /**
+ * Let the user select a Rosegarden 2.1 file for import
+ */
+ void slotImportRG21();
+
+ /**
+ * Select a Hydrogen drum machine file for import
+ */
+ void slotImportHydrogen();
+
+ /**
+ * Let the user select a MIDI file for merge
+ */
+ void slotMerge();
+
+ /**
+ * Let the user select a MIDI file for merge
+ */
+ void slotMergeMIDI();
+
+ /**
+ * Let the user select a MIDI file for merge
+ */
+ void slotMergeRG21();
+
+ /**
+ * Select a Hydrogen drum machine file for merge
+ */
+ void slotMergeHydrogen();
+
+ /**
+ * Let the user export a Rosegarden Project file
+ */
+ void slotExportProject();
+
+ /**
+ * Let the user enter a MIDI file to export to
+ */
+ void slotExportMIDI();
+
+ /**
+ * Let the user enter a Csound scorefile to export to
+ */
+ void slotExportCsound();
+
+ /**
+ * Let the user enter a Mup file to export to
+ */
+ void slotExportMup();
+
+ /**
+ * Let the user enter a LilyPond file to export to
+ */
+ void slotExportLilyPond();
+
+ /**
+ * Export to a temporary file and process
+ */
+ void slotPrintLilyPond();
+ void slotPreviewLilyPond();
+ void slotLilyPondViewProcessExited(KProcess *);
+
+ /**
+ * Let the user enter a MusicXml file to export to
+ */
+ void slotExportMusicXml();
+
+ /**
+ * closes all open windows by calling close() on each memberList
+ * item until the list is empty, then quits the application. If
+ * queryClose() returns false because the user canceled the
+ * saveModified() dialog, the closing breaks.
+ */
+ void slotQuit();
+
+ /**
+ * 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 a time range (sections of segments, tempo, and time
+ * signature events within that range).
+ */
+ void slotCutRange();
+
+ /**
+ * Copy a time range.
+ */
+ void slotCopyRange();
+
+ /**
+ * Paste the clipboard at the current pointer position, moving all
+ * subsequent material along to make space.
+ */
+ void slotPasteRange();
+
+ /**
+ * Delete a time range.
+ */
+ void slotDeleteRange();
+
+ /**
+ * Insert a time range (asking the user for a duration).
+ */
+ void slotInsertRange();
+
+ /**
+ * select all segments on all tracks
+ */
+ void slotSelectAll();
+
+ /**
+ * delete selected segments, duh
+ */
+ void slotDeleteSelectedSegments();
+
+ /**
+ * Quantize the selected segments (after asking the user how)
+ */
+ void slotQuantizeSelection();
+
+ /**
+ * Quantize the selected segments by repeating the last iterative quantize
+ */
+ void slotRepeatQuantizeSelection();
+
+ /**
+ * Calculate timing/tempo info based on selected segment
+ */
+ void slotGrooveQuantize();
+
+ /**
+ * Rescale the selected segments by a factor requested from
+ * the user
+ */
+ void slotRescaleSelection();
+
+ /**
+ * Split the selected segments on silences (or new timesig, etc)
+ */
+ void slotAutoSplitSelection();
+
+ /**
+ * Jog a selection left or right by an amount
+ */
+ void slotJogRight();
+ void slotJogLeft();
+
+ /**
+ * Split the selected segments by pitch
+ */
+ void slotSplitSelectionByPitch();
+
+ /**
+ * Split the selected segments by recorded source
+ */
+ void slotSplitSelectionByRecordedSrc();
+
+ /**
+ * Split the selected segments at some time
+ */
+ void slotSplitSelectionAtTime();
+
+ /**
+ * Produce a harmony segment from the selected segments
+ */
+ void slotHarmonizeSelection();
+
+ /**
+ * Set the start times of the selected segments
+ */
+ void slotSetSegmentStartTimes();
+
+ /**
+ * Set the durations of the selected segments
+ */
+ void slotSetSegmentDurations();
+
+ /**
+ * Merge the selected segments
+ */
+ void slotJoinSegments();
+
+ /**
+ * Tempo to Segment length
+ */
+ void slotTempoToSegmentLength();
+ void slotTempoToSegmentLength(QWidget* parent);
+
+ /**
+ * toggle segment labels
+ */
+ void slotToggleSegmentLabels();
+
+ /**
+ * open the default editor for each of the currently-selected segments
+ */
+ void slotEdit();
+
+ /**
+ * open an event list view for each of the currently-selected segments
+ */
+ void slotEditInEventList();
+
+ /**
+ * open a matrix view for each of the currently-selected segments
+ */
+ void slotEditInMatrix();
+
+ /**
+ * open a percussion matrix view for each of the currently-selected segments
+ */
+ void slotEditInPercussionMatrix();
+
+ /**
+ * open a notation view with all currently-selected segments in it
+ */
+ void slotEditAsNotation();
+
+ /**
+ * open a tempo/timesig edit view
+ */
+ void slotEditTempos();
+ void slotEditTempos(timeT openAtTime);
+
+ /**
+ * Edit the tempo - called from a Transport signal
+ */
+ void slotEditTempo();
+ void slotEditTempo(timeT atTime);
+ void slotEditTempo(QWidget *parent);
+ void slotEditTempo(QWidget *parent, timeT atTime);
+
+ /**
+ * Edit the time signature - called from a Transport signal
+ */
+ void slotEditTimeSignature();
+ void slotEditTimeSignature(timeT atTime);
+ void slotEditTimeSignature(QWidget *parent);
+ void slotEditTimeSignature(QWidget *parent, timeT atTime);
+
+ /**
+ * Edit the playback pointer position - called from a Transport signal
+ */
+ void slotEditTransportTime();
+ void slotEditTransportTime(QWidget *parent);
+
+ /**
+ * Change the length of the composition
+ */
+ void slotChangeCompositionLength();
+
+ /**
+ * open a dialog for document properties
+ */
+ void slotEditDocumentProperties();
+
+ /**
+ * Manage MIDI Devices
+ */
+ void slotManageMIDIDevices();
+
+ /**
+ * Manage plugin synths
+ */
+ void slotManageSynths();
+
+ /**
+ * Show the mixers
+ */
+ void slotOpenAudioMixer();
+ void slotOpenMidiMixer();
+
+ /**
+ * Edit Banks/Programs
+ */
+ void slotEditBanks();
+
+ /**
+ * Edit Banks/Programs for a particular device
+ */
+ void slotEditBanks(DeviceId);
+
+ /**
+ * Edit Control Parameters for a particular device
+ */
+ void slotEditControlParameters(DeviceId);
+
+ /**
+ * Edit Document Markers
+ */
+ void slotEditMarkers();
+
+ /**
+ * Not an actual action slot : populates the set_track_instrument sub menu
+ */
+ void slotPopulateTrackInstrumentPopup();
+
+ /**
+ * Remap instruments
+ */
+ void slotRemapInstruments();
+
+ /**
+ * Modify MIDI filters
+ */
+ void slotModifyMIDIFilters();
+
+ /**
+ * Manage Metronome
+ */
+ void slotManageMetronome();
+
+ /**
+ * Save Studio as Default
+ */
+ void slotSaveDefaultStudio();
+
+ /**
+ * Import Studio from File
+ */
+ void slotImportStudio();
+
+ /**
+ * Import Studio from Autoload
+ */
+ void slotImportDefaultStudio();
+
+ /**
+ * Import Studio from File
+ */
+ void slotImportStudioFromFile(const QString &file);
+
+ /**
+ * Send MIDI_RESET to all MIDI devices
+ */
+ void slotResetMidiNetwork();
+
+ /**
+ * toggles the toolbar
+ */
+ void slotToggleToolBar();
+
+ /**
+ * toggles the transport window
+ */
+ void slotToggleTransport();
+
+ /**
+ * hides the transport window
+ */
+ void slotHideTransport();
+
+ /**
+ * toggles the tools toolbar
+ */
+ void slotToggleToolsToolBar();
+
+ /**
+ * toggles the tracks toolbar
+ */
+ void slotToggleTracksToolBar();
+
+ /**
+ * toggles the editors toolbar
+ */
+ void slotToggleEditorsToolBar();
+
+ /**
+ * toggles the transport toolbar
+ */
+ void slotToggleTransportToolBar();
+
+ /**
+ * toggles the zoom toolbar
+ */
+ void slotToggleZoomToolBar();
+
+ /**
+ * toggles the statusbar
+ */
+ void slotToggleStatusBar();
+
+ /**
+ * changes the statusbar contents for the standard label
+ * permanently, used to indicate current actions.
+ *
+ * @param text the text that is displayed in the statusbar
+ */
+ void slotStatusMsg(QString text);
+
+ /**
+ * changes the status message of the whole statusbar for two
+ * seconds, then restores the last status. This is used to display
+ * statusbar messages that give information about actions for
+ * toolbar icons and menuentries.
+ *
+ * @param text the text that is displayed in the statusbar
+ */
+ void slotStatusHelpMsg(QString text);
+
+ /**
+ * enables/disables the transport window
+ */
+ void slotEnableTransport(bool);
+
+ /**
+ * segment select tool
+ */
+ void slotPointerSelected();
+
+ /**
+ * segment eraser tool is selected
+ */
+ void slotEraseSelected();
+
+ /**
+ * segment draw tool is selected
+ */
+ void slotDrawSelected();
+
+ /**
+ * segment move tool is selected
+ */
+ void slotMoveSelected();
+
+ /**
+ * segment resize tool is selected
+ */
+ void slotResizeSelected();
+
+ /*
+ * Segment join tool
+ *
+ */
+ void slotJoinSelected();
+
+ /*
+ * Segment split tool
+ *
+ */
+ void slotSplitSelected();
+
+ /**
+ * Add one new track
+ */
+ void slotAddTrack();
+
+ /**
+ * Add new tracks
+ */
+ void slotAddTracks();
+
+ /*
+ * Delete Tracks
+ */
+ void slotDeleteTrack();
+
+ /*
+ * Modify track position
+ */
+ void slotMoveTrackUp();
+ void slotMoveTrackDown();
+
+ /**
+ * timeT version of the same
+ */
+ void slotSetPointerPosition(timeT t);
+
+ /**
+ * Set the pointer position and start playing (from LoopRuler)
+ */
+ void slotSetPlayPosition(timeT position);
+
+ /**
+ * Set a loop
+ */
+ void slotSetLoop(timeT lhs, timeT rhs);
+
+
+ /**
+ * Update the transport with the bar, beat and unit times for
+ * a given timeT
+ */
+ void slotDisplayBarTime(timeT t);
+
+
+ /**
+ * Transport controls
+ */
+ void slotPlay();
+ void slotStop();
+ void slotRewind();
+ void slotFastforward();
+ void slotRecord();
+ void slotToggleRecord();
+ void slotRewindToBeginning();
+ void slotFastForwardToEnd();
+ void slotJumpToTime(int sec, int usec);
+ void slotStartAtTime(int sec, int usec);
+ void slotRefreshTimeDisplay();
+ void slotToggleTracking();
+
+ /**
+ * Called when the sequencer auxiliary process exits
+ */
+ void slotSequencerExited(KProcess*);
+
+ /// When the transport closes
+ void slotCloseTransport();
+
+ /**
+ * called by RosegardenApplication when session management tells
+ * it to save its state. This is to avoid saving the transport as
+ * a 2nd main window
+ */
+ void slotDeleteTransport();
+
+ /**
+ * Put the GUI into a given Tool edit mode
+ */
+ void slotActivateTool(QString toolName);
+
+ /**
+ * Toggles either the play or record metronome according
+ * to Transport status
+ */
+ void slotToggleMetronome();
+
+ /*
+ * Toggle the solo mode
+ */
+ void slotToggleSolo(bool);
+
+ /**
+ * Set and unset the loop from the transport loop button with
+ * these slots.
+ */
+ void slotSetLoop();
+ void slotUnsetLoop();
+
+ /**
+ * Set and unset the loop start/end time from the transport loop start/stop buttons with
+ * these slots.
+ */
+ void slotSetLoopStart();
+ void slotSetLoopStop();
+
+ /**
+ * Toggle the track labels on the TrackEditor
+ */
+ void slotToggleTrackLabels();
+
+ /**
+ * Toggle the rulers on the TrackEditor
+ * (aka bar buttons)
+ */
+ void slotToggleRulers();
+
+ /**
+ * Toggle the tempo ruler on the TrackEditor
+ */
+ void slotToggleTempoRuler();
+
+ /**
+ * Toggle the chord-name ruler on the TrackEditor
+ */
+ void slotToggleChordNameRuler();
+
+ /**
+ * Toggle the segment canvas previews
+ */
+ void slotTogglePreviews();
+
+ /**
+ * 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);
+
+ /**
+ * Display tip-of-day dialog on demand
+ */
+ void slotShowTip();
+
+ /*
+ * Select Track up or down
+ */
+ void slotTrackUp();
+ void slotTrackDown();
+
+ /**
+ * Mute/Unmute
+ */
+ void slotMuteAllTracks();
+ void slotUnmuteAllTracks();
+ void slotToggleMutedCurrentTrack();
+
+ /**
+ * Toggle arm (record) current track
+ */
+ void slotToggleRecordCurrentTrack();
+
+ /**
+ * save general Options like all bar positions and status as well
+ * as the geometry and the recent file list to the configuration
+ * file
+ */
+ void slotSaveOptions();
+
+ /**
+ * Show the configure dialog
+ */
+ void slotConfigure();
+
+ /**
+ * Show the key mappings
+ *
+ */
+ void slotEditKeys();
+
+ /**
+ * Edit toolbars
+ */
+ void slotEditToolbars();
+
+ /**
+ * Update the toolbars after edition
+ */
+ void slotUpdateToolbars();
+
+ /**
+ * Zoom slider moved
+ */
+ void slotChangeZoom(int index);
+
+ void slotZoomIn();
+ void slotZoomOut();
+
+ /**
+ * Modify tempo
+ */
+ void slotChangeTempo(timeT time,
+ tempoT value,
+ tempoT target,
+ TempoDialog::TempoDialogAction action);
+
+ /**
+ * Move a tempo change
+ */
+ void slotMoveTempo(timeT oldTime,
+ timeT newTime);
+
+ /**
+ * Remove a tempo change
+ */
+ void slotDeleteTempo(timeT time);
+
+ /**
+ * Add marker
+ */
+ void slotAddMarker(timeT time);
+
+ /**
+ * Remove a marker
+ */
+ void slotDeleteMarker(int id,
+ timeT time,
+ QString name,
+ QString description);
+
+ /**
+ * Document modified
+ */
+ void slotDocumentModified(bool modified = true);
+
+
+ /**
+ * This slot is here to be connected to RosegardenGUIView's
+ * stateChange signal. We use a bool for the 2nd arg rather than a
+ * KXMLGUIClient::ReverseStateChange to spare the include of
+ * kxmlguiclient.h just for one typedef.
+ *
+ * Hopefully we'll be able to get rid of this eventually,
+ * I should slip this in KMainWindow for KDE 4.
+ */
+ void slotStateChanged(QString, bool noReverse);
+
+ /**
+ * A command has happened; check the clipboard in case we
+ * need to change state
+ */
+ void slotTestClipboard();
+
+ /**
+ * Show a 'play list' dialog
+ */
+ void slotPlayList();
+
+ /**
+ * Play the requested URL
+ *
+ * Stop current playback, close current document,
+ * open specified document and play it.
+ */
+ void slotPlayListPlay(QString url);
+
+ /**
+ * Call up the online tutorial
+ */
+ void slotTutorial();
+
+ /**
+ * Surf to the bug reporting guidelines
+ */
+ void slotBugGuidelines();
+
+ /**
+ * View the trigger segments manager
+ */
+ void slotManageTriggerSegments();
+
+ /**
+ * View the audio file manager - and some associated actions
+ */
+ void slotAudioManager();
+
+ void slotAddAudioFile(AudioFileId);
+ void slotDeleteAudioFile(AudioFileId);
+ void slotPlayAudioFile(AudioFileId,
+ const RealTime &,
+ const RealTime &);
+ void slotCancelAudioPlayingFile(AudioFileId);
+ void slotDeleteAllAudioFiles();
+
+ /**
+ * Reflect segment deletion from the audio manager
+ */
+ void slotDeleteSegments(const SegmentSelection&);
+
+ void slotRepeatingSegments();
+ void slotRelabelSegments();
+ void slotTransposeSegments();
+
+ /// Panic button pressed
+ void slotPanic();
+
+ // Auto-save
+ //
+ void slotAutoSave();
+
+ // Auto-save update interval changes
+ //
+ void slotUpdateAutoSaveInterval(unsigned int interval);
+
+ // Update the side-bar when the configuration page changes its style.
+ //
+ void slotUpdateSidebarStyle(unsigned int style);
+
+ /**
+ * called when the PlayList is being closed
+ */
+ void slotPlayListClosed();
+
+ /**
+ * called when the BankEditor is being closed
+ */
+ void slotBankEditorClosed();
+
+ /**
+ * called when the Device Manager is being closed
+ */
+ void slotDeviceManagerClosed();
+
+ /**
+ * called when the synth manager is being closed
+ */
+ void slotSynthPluginManagerClosed();
+
+ /**
+ * called when the Mixer is being closed
+ */
+ void slotAudioMixerClosed();
+ void slotMidiMixerClosed();
+
+ /**
+ * when ControlEditor is being closed
+ */
+ void slotControlEditorClosed();
+
+ /**
+ * when MarkerEditor is being closed
+ */
+ void slotMarkerEditorClosed();
+
+ /**
+ * when TempoView is being closed
+ */
+ void slotTempoViewClosed();
+
+ /**
+ * when TriggerManager is being closed
+ */
+ void slotTriggerManagerClosed();
+
+ /**
+ * when AudioManagerDialog is being closed
+ */
+ void slotAudioManagerClosed();
+
+ /**
+ * Update the pointer position from the sequencer mmapped file when playing
+ */
+ void slotUpdatePlaybackPosition();
+
+ /**
+ * Update the CPU level meter
+ */
+ void slotUpdateCPUMeter(bool playing);
+
+ /**
+ * Update the monitor levels from the sequencer mmapped file when not playing
+ * (slotUpdatePlaybackPosition does this among other things when playing)
+ */
+ void slotUpdateMonitoring();
+
+ /**
+ * Create a plugin dialog for a given instrument and slot, or
+ * raise an exising one.
+ */
+ void slotShowPluginDialog(QWidget *parent,
+ InstrumentId instrument,
+ int index);
+
+ void slotPluginSelected(InstrumentId instrument,
+ int index, int plugin);
+
+ /**
+ * An external GUI has requested a port change.
+ */
+ void slotChangePluginPort(InstrumentId instrument,
+ int index, int portIndex, float value);
+
+ /**
+ * Our internal GUI has made a port change -- the
+ * PluginPortInstance already contains the new value, but we need
+ * to inform the sequencer and update external GUIs.
+ */
+ void slotPluginPortChanged(InstrumentId instrument,
+ int index, int portIndex);
+
+ /**
+ * An external GUI has requested a program change.
+ */
+ void slotChangePluginProgram(InstrumentId instrument,
+ int index, QString program);
+
+ /**
+ * Our internal GUI has made a program change -- the
+ * AudioPluginInstance already contains the new program, but we
+ * need to inform the sequencer, update external GUIs, and update
+ * the port values for the new program.
+ */
+ void slotPluginProgramChanged(InstrumentId instrument,
+ int index);
+
+ /**
+ * An external GUI has requested a configure call. (This can only
+ * happen from an external GUI, we have no way to manage these
+ * internally.)
+ */
+ void slotChangePluginConfiguration(InstrumentId, int index,
+ bool global, QString key, QString value);
+ void slotPluginDialogDestroyed(InstrumentId instrument,
+ int index);
+ void slotPluginBypassed(InstrumentId,
+ int index, bool bypassed);
+
+ void slotShowPluginGUI(InstrumentId, int index);
+ void slotStopPluginGUI(InstrumentId, int index);
+ void slotPluginGUIExited(InstrumentId, int index);
+
+ void slotDocumentDevicesResyncd();
+
+ void slotTestStartupTester();
+
+ void slotDebugDump();
+
+ /**
+ * Enable or disable the internal MIDI Thru routing.
+ *
+ * This policy is implemented at the sequencer side, controlled
+ * by this flag and also by the MIDI Thru filters.
+ *
+ * @see ControlBlock::isMidiRoutingEnabled()
+ * @see RosegardenSequencerApp::processAsynchronousEvents()
+ * @see RosegardenSequencerApp::processRecordedEvents()
+ */
+ void slotEnableMIDIThruRouting();
+
+ void slotShowToolHelp(const QString &);
+
+ void slotNewerVersionAvailable(QString);
+
+ void slotSetQuickMarker();
+
+ void slotJumpToQuickMarker();
+
+private:
+
+
+ //--------------- Data members ---------------------------------
+
+ bool m_actionsSetup;
+
+ KRecentFilesAction* m_fileRecent;
+
+ /**
+ * view is the main widget which represents your working area. The
+ * View class should handle all events of the view widget. It is
+ * kept empty so you can create your view according to your
+ * application's needs by changing the view class.
+ */
+ RosegardenGUIView* m_view;
+ RosegardenGUIView* m_swapView;
+
+ KDockWidget* m_mainDockWidget;
+ KDockWidget* m_dockLeft;
+
+ /**
+ * doc represents your actual document and is created only
+ * once. It keeps information such as filename and does the
+ * serialization of your files.
+ */
+ RosegardenGUIDoc* m_doc;
+
+ /**
+ * KAction pointers to enable/disable actions
+ */
+ KRecentFilesAction* m_fileOpenRecent;
+
+ KToggleAction* m_viewToolBar;
+ KToggleAction* m_viewToolsToolBar;
+ KToggleAction* m_viewTracksToolBar;
+ KToggleAction* m_viewEditorsToolBar;
+ KToggleAction* m_viewZoomToolBar;
+ KToggleAction* m_viewStatusBar;
+ KToggleAction* m_viewTransport;
+ KToggleAction* m_viewTransportToolBar;
+ KToggleAction* m_viewTrackLabels;
+ KToggleAction* m_viewRulers;
+ KToggleAction* m_viewTempoRuler;
+ KToggleAction* m_viewChordNameRuler;
+ KToggleAction* m_viewPreviews;
+ KToggleAction* m_viewSegmentLabels;
+ KToggleAction* m_enableMIDIrouting;
+
+ KAction *m_playTransport;
+ KAction *m_stopTransport;
+ KAction *m_rewindTransport;
+ KAction *m_ffwdTransport;
+ KAction *m_recordTransport;
+ KAction *m_rewindEndTransport;
+ KAction *m_ffwdEndTransport;
+
+ KProcess* m_sequencerProcess;
+ bool m_sequencerCheckedIn;
+
+#ifdef HAVE_LIBJACK
+ KProcess* m_jackProcess;
+#endif // HAVE_LIBJACK
+
+ ZoomSlider<double> *m_zoomSlider;
+ QLabel *m_zoomLabel;
+
+ ProgressBar *m_progressBar;
+
+ // SequenceManager
+ //
+ SequenceManager *m_seqManager;
+
+ // Transport dialog pointer
+ //
+ TransportDialog *m_transport;
+
+ // Dialogs which depend on the document
+
+ // Audio file manager
+ //
+ AudioManagerDialog *m_audioManagerDialog;
+
+ bool m_originatingJump;
+
+ // Use these in conjucntion with the loop button to
+ // remember where a loop was if we've ever set one.
+ timeT m_storedLoopStart;
+ timeT m_storedLoopEnd;
+
+ bool m_useSequencer;
+ bool m_dockVisible;
+
+ AudioPluginManager *m_pluginManager;
+
+ QTimer* m_autoSaveTimer;
+
+ Clipboard *m_clipboard;
+
+ SegmentParameterBox *m_segmentParameterBox;
+ InstrumentParameterBox *m_instrumentParameterBox;
+ TrackParameterBox *m_trackParameterBox;
+
+ PlayListDialog *m_playList;
+ DeviceManagerDialog *m_deviceManager;
+ SynthPluginManagerDialog *m_synthManager;
+ AudioMixerWindow *m_audioMixer;
+ MidiMixerWindow *m_midiMixer;
+ BankEditorDialog *m_bankEditor;
+ MarkerEditor *m_markerEditor;
+ TempoView *m_tempoView;
+ TriggerSegmentManager *m_triggerSegmentManager;
+ std::set<ControlEditorDialog *> m_controlEditors;
+ std::map<int, AudioPluginDialog*> m_pluginDialogs;
+#ifdef HAVE_LIBLO
+ AudioPluginOSCGUIManager *m_pluginGUIManager;
+#endif
+
+ static RosegardenGUIApp *m_myself;
+
+ static std::map<KProcess *, KTempFile *> m_lilyTempFileMap;
+
+ // Used to fetch the current sequencer position from the mmapped sequencer information file
+ //
+ QTimer *m_playTimer;
+ QTimer *m_stopTimer;
+
+ StartupTester *m_startupTester;
+
+ bool m_firstRun;
+ bool m_haveAudioImporter;
+
+ RosegardenParameterArea *m_parameterArea;
+
+ KAction *m_setQuickMarkerAction;
+ KAction *m_jumpToQuickMarkerAction;
+
+#ifdef HAVE_LIRC
+ LircClient *m_lircClient;
+ LircCommander *m_lircCommander;
+#endif
+};
+
+
+}
+
+#endif
diff --git a/src/gui/application/RosegardenGUIView.cpp b/src/gui/application/RosegardenGUIView.cpp
new file mode 100644
index 0000000..c61b51e
--- /dev/null
+++ b/src/gui/application/RosegardenGUIView.cpp
@@ -0,0 +1,2041 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "RosegardenGUIView.h"
+#include <kapplication.h>
+
+#include "sound/Midi.h"
+#include "gui/editors/segment/TrackButtons.h"
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "document/ConfigGroups.h"
+#include "gui/application/RosegardenDCOP.h"
+#include "base/AudioLevel.h"
+#include "base/Composition.h"
+#include "base/Instrument.h"
+#include "base/MidiDevice.h"
+#include "base/MidiProgram.h"
+#include "base/NotationTypes.h"
+#include "base/RealTime.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "base/Studio.h"
+#include "base/Track.h"
+#include "commands/segment/AudioSegmentAutoSplitCommand.h"
+#include "commands/segment/AudioSegmentInsertCommand.h"
+#include "commands/segment/SegmentSingleRepeatToCopyCommand.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/RosegardenGUIDoc.h"
+#include "RosegardenApplication.h"
+#include "gui/configuration/GeneralConfigurationPage.h"
+#include "gui/configuration/AudioConfigurationPage.h"
+#include "gui/dialogs/AudioSplitDialog.h"
+#include "gui/dialogs/AudioManagerDialog.h"
+#include "gui/dialogs/DocumentConfigureDialog.h"
+#include "gui/dialogs/TempoDialog.h"
+#include "gui/editors/eventlist/EventView.h"
+#include "gui/editors/matrix/MatrixView.h"
+#include "gui/editors/notation/NotationView.h"
+#include "gui/editors/parameters/InstrumentParameterBox.h"
+#include "gui/editors/parameters/SegmentParameterBox.h"
+#include "gui/editors/parameters/TrackParameterBox.h"
+#include "gui/editors/segment/segmentcanvas/CompositionView.h"
+#include "gui/editors/segment/segmentcanvas/SegmentSelector.h"
+#include "gui/editors/segment/TrackEditor.h"
+#include "gui/seqmanager/SequenceManager.h"
+#include "gui/seqmanager/SequencerMapper.h"
+#include "gui/rulers/ChordNameRuler.h"
+#include "gui/rulers/LoopRuler.h"
+#include "gui/rulers/TempoRuler.h"
+#include "gui/rulers/StandardRuler.h"
+#include "gui/widgets/ProgressDialog.h"
+#include "gui/widgets/CurrentProgressDialog.h"
+#include "RosegardenGUIApp.h"
+#include "SetWaitCursor.h"
+#include "sound/AudioFile.h"
+#include "sound/AudioFileManager.h"
+#include "sound/MappedEvent.h"
+#include <kcommand.h>
+#include <kconfig.h>
+#include <kmessagebox.h>
+#include <kprocess.h>
+#include <qapplication.h>
+#include <qcursor.h>
+#include <qdialog.h>
+#include <qfileinfo.h>
+#include <qobject.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+// Use this to define the basic unit of the main QCanvas size.
+//
+// This apparently arbitrary figure is what we think is an
+// appropriate width in pixels for a 4/4 bar. Beware of making it
+// too narrow, as shorter bars will be proportionally smaller --
+// the visual difference between 2/4 and 4/4 is perhaps greater
+// than it sounds.
+//
+static double barWidth44 = 100.0;
+
+const QWidget *RosegardenGUIView::m_lastActiveMainWindow = 0;
+
+// This is the maximum number of matrix, event view or percussion
+// matrix editors to open in a single operation (not the maximum that
+// can be open at a time -- there isn't one)
+//
+static int maxEditorsToOpen = 8;
+
+RosegardenGUIView::RosegardenGUIView(bool showTrackLabels,
+ SegmentParameterBox* segmentParameterBox,
+ InstrumentParameterBox* instrumentParameterBox,
+ TrackParameterBox* trackParameterBox,
+ QWidget *parent,
+ const char* /*name*/)
+ : QVBox(parent),
+ m_rulerScale(0),
+ m_trackEditor(0),
+ m_segmentParameterBox(segmentParameterBox),
+ m_instrumentParameterBox(instrumentParameterBox),
+ m_trackParameterBox(trackParameterBox)
+{
+ RosegardenGUIDoc* doc = getDocument();
+ Composition *comp = &doc->getComposition();
+
+ double unitsPerPixel =
+ TimeSignature(4, 4).getBarDuration() / barWidth44;
+ m_rulerScale = new SimpleRulerScale(comp, 0, unitsPerPixel);
+
+ // Construct the trackEditor first so we can then
+ // query it for placement information
+ //
+ m_trackEditor = new TrackEditor(doc, this,
+ m_rulerScale, showTrackLabels, unitsPerPixel, this /*hbox*/);
+
+ connect(m_trackEditor->getSegmentCanvas(),
+ SIGNAL(editSegment(Segment*)),
+ SLOT(slotEditSegment(Segment*)));
+
+ connect(m_trackEditor->getSegmentCanvas(),
+ SIGNAL(editSegmentNotation(Segment*)),
+ SLOT(slotEditSegmentNotation(Segment*)));
+
+ connect(m_trackEditor->getSegmentCanvas(),
+ SIGNAL(editSegmentMatrix(Segment*)),
+ SLOT(slotEditSegmentMatrix(Segment*)));
+
+ connect(m_trackEditor->getSegmentCanvas(),
+ SIGNAL(editSegmentAudio(Segment*)),
+ SLOT(slotEditSegmentAudio(Segment*)));
+
+ connect(m_trackEditor->getSegmentCanvas(),
+ SIGNAL(audioSegmentAutoSplit(Segment*)),
+ SLOT(slotSegmentAutoSplit(Segment*)));
+
+ connect(m_trackEditor->getSegmentCanvas(),
+ SIGNAL(editSegmentEventList(Segment*)),
+ SLOT(slotEditSegmentEventList(Segment*)));
+
+ connect(m_trackEditor->getSegmentCanvas(),
+ SIGNAL(editRepeat(Segment*, timeT)),
+ SLOT(slotEditRepeat(Segment*, timeT)));
+
+ connect(m_trackEditor->getSegmentCanvas(),
+ SIGNAL(setPointerPosition(timeT)),
+ doc, SLOT(slotSetPointerPosition(timeT)));
+
+ connect(m_trackEditor,
+ SIGNAL(droppedDocument(QString)),
+ parent,
+ SLOT(slotOpenDroppedURL(QString)));
+
+ connect(m_trackEditor,
+ SIGNAL(droppedAudio(QString)),
+ this,
+ SLOT(slotDroppedAudio(QString)));
+
+ connect(m_trackEditor,
+ SIGNAL(droppedNewAudio(QString)),
+ this,
+ SLOT(slotDroppedNewAudio(QString)));
+
+ connect(m_instrumentParameterBox,
+ SIGNAL(changeInstrumentLabel(InstrumentId, QString)),
+ this,
+ SLOT(slotChangeInstrumentLabel(InstrumentId, QString)));
+
+ connect(m_instrumentParameterBox,
+ SIGNAL(changeInstrumentLabel(InstrumentId, QString)),
+ m_trackParameterBox,
+ SLOT(slotInstrumentLabelChanged(InstrumentId, QString)));
+
+ connect(m_trackEditor->getTrackButtons(),
+ SIGNAL(nameChanged()),
+ m_trackParameterBox,
+ SLOT(slotSelectedTrackNameChanged()));
+
+ connect(m_trackEditor->getTrackButtons(),
+ SIGNAL(instrumentSelected(int)),
+ m_trackParameterBox,
+ SLOT(slotUpdateControls(int)));
+
+ connect(m_trackParameterBox,
+ SIGNAL(instrumentSelected(TrackId, int)),
+ m_trackEditor->getTrackButtons(),
+ SLOT(slotTrackInstrumentSelection(TrackId, int)));
+
+ connect(this, SIGNAL(controllerDeviceEventReceived(MappedEvent *, const void *)),
+ this, SLOT(slotControllerDeviceEventReceived(MappedEvent *, const void *)));
+
+ if (doc) {
+ /* signal no longer exists
+ connect(doc, SIGNAL(recordingSegmentUpdated(Segment *,
+ timeT)),
+ this, SLOT(slotUpdateRecordingSegment(Segment *,
+ timeT)));
+ */
+
+ QObject::connect
+ (getCommandHistory(), SIGNAL(commandExecuted()),
+ m_trackEditor->getSegmentCanvas(), SLOT(slotUpdateSegmentsDrawBuffer()));
+ }
+}
+
+RosegardenGUIView::~RosegardenGUIView()
+{
+ RG_DEBUG << "~RosegardenGUIView()" << endl;
+ delete m_rulerScale;
+}
+
+RosegardenGUIDoc*
+RosegardenGUIView::getDocument() const
+{
+ return RosegardenGUIApp::self()->getDocument();
+}
+
+void RosegardenGUIView::print(Composition* p, bool previewOnly)
+{
+ SetWaitCursor waitCursor;
+
+ std::vector<Segment *> segmentsToEdit;
+
+ for (Composition::iterator i = p->begin(); i != p->end(); ++i) {
+ if ((*i)->getType() != Segment::Audio) {
+ segmentsToEdit.push_back(*i);
+ }
+ }
+
+ if (segmentsToEdit.empty()) {
+ KMessageBox::sorry(this, i18n("No non-audio segments in composition"));
+ return ;
+ }
+
+ NotationView *notationView = new NotationView(getDocument(),
+ segmentsToEdit,
+ this,
+ (NotationView *)0);
+
+ if (!notationView->isOK()) {
+ RG_DEBUG << "RosegardenGUIView::print : operation cancelled" << endl;
+ delete notationView;
+ return ;
+ }
+
+ notationView->print(previewOnly);
+
+ delete notationView;
+}
+
+void RosegardenGUIView::selectTool(const QString toolName)
+{
+ m_trackEditor->getSegmentCanvas()->slotSetTool(toolName);
+}
+
+bool
+RosegardenGUIView::haveSelection()
+{
+ return m_trackEditor->getSegmentCanvas()->haveSelection();
+}
+
+SegmentSelection
+RosegardenGUIView::getSelection()
+{
+ return m_trackEditor->getSegmentCanvas()->getSelectedSegments();
+}
+
+void RosegardenGUIView::updateSelectionContents()
+{
+ m_trackEditor->getSegmentCanvas()->updateSelectionContents();
+}
+
+/* hjj: WHAT DO DO WITH THIS ?
+void
+RosegardenGUIView::slotEditMetadata(QString name)
+{
+ const QWidget *ww = dynamic_cast<const QWidget *>(sender());
+ QWidget *w = const_cast<QWidget *>(ww);
+
+ DocumentConfigureDialog *configDlg =
+ new DocumentConfigureDialog(getDocument(), w ? w : this);
+
+ configDlg->selectMetadata(name);
+
+ configDlg->show();
+}
+*/
+
+void RosegardenGUIView::slotEditSegment(Segment* segment)
+{
+ Segment::SegmentType type = Segment::Internal;
+
+ if (segment) {
+ type = segment->getType();
+ } else {
+ if (haveSelection()) {
+
+ bool haveType = false;
+
+ SegmentSelection selection = getSelection();
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+
+ Segment::SegmentType myType = (*i)->getType();
+ if (haveType) {
+ if (myType != type) {
+ KMessageBox::sorry(this, i18n("Selection must contain only audio or non-audio segments"));
+ return ;
+ }
+ } else {
+ type = myType;
+ haveType = true;
+ segment = *i;
+ }
+ }
+ } else
+ return ;
+ }
+
+ if (type == Segment::Audio) {
+ slotEditSegmentAudio(segment);
+ } else {
+
+ KConfig* config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+ GeneralConfigurationPage::DoubleClickClient
+ client =
+ (GeneralConfigurationPage::DoubleClickClient)
+ (config->readUnsignedNumEntry("doubleclickclient",
+ (unsigned int)GeneralConfigurationPage::NotationView));
+
+ if (client == GeneralConfigurationPage::MatrixView) {
+
+ bool isPercussion = false;
+ Track *track = getDocument()->getComposition().getTrackById
+ (segment->getTrack());
+ if (track) {
+ InstrumentId iid = track->getInstrument();
+ Instrument *instrument =
+ getDocument()->getStudio().getInstrumentById(iid);
+ if (instrument && instrument->isPercussion()) isPercussion = true;
+ }
+
+ if (isPercussion) {
+ slotEditSegmentPercussionMatrix(segment);
+ } else {
+ slotEditSegmentMatrix(segment);
+ }
+
+ } else if (client == GeneralConfigurationPage::EventView) {
+ slotEditSegmentEventList(segment);
+ } else {
+ slotEditSegmentNotation(segment);
+ }
+ }
+}
+
+void RosegardenGUIView::slotEditRepeat(Segment *segment,
+ timeT time)
+{
+ SegmentSingleRepeatToCopyCommand *command =
+ new SegmentSingleRepeatToCopyCommand(segment, time);
+ slotAddCommandToHistory(command);
+}
+
+void RosegardenGUIView::slotEditSegmentNotation(Segment* p)
+{
+ SetWaitCursor waitCursor;
+ std::vector<Segment *> segmentsToEdit;
+
+ RG_DEBUG << "\n\n\n\nRosegardenGUIView::slotEditSegmentNotation: p is " << p << endl;
+
+ // The logic here is: If we're calling for this operation to
+ // happen on a particular segment, then open that segment and if
+ // it's part of a selection open all other selected segments too.
+ // If we're not calling for any particular segment, then open all
+ // selected segments if there are any.
+
+ if (haveSelection()) {
+
+ SegmentSelection selection = getSelection();
+
+ if (!p || (selection.find(p) != selection.end())) {
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+ if ((*i)->getType() != Segment::Audio) {
+ segmentsToEdit.push_back(*i);
+ }
+ }
+ } else {
+ if (p->getType() != Segment::Audio) {
+ segmentsToEdit.push_back(p);
+ }
+ }
+
+ } else if (p) {
+ if (p->getType() != Segment::Audio) {
+ segmentsToEdit.push_back(p);
+ }
+ } else {
+ return ;
+ }
+
+ if (segmentsToEdit.empty()) {
+ KMessageBox::sorry(this, i18n("No non-audio segments selected"));
+ return ;
+ }
+
+ slotEditSegmentsNotation(segmentsToEdit);
+}
+
+void RosegardenGUIView::slotEditSegmentsNotation(std::vector<Segment *> segmentsToEdit)
+{
+ NotationView *view = createNotationView(segmentsToEdit);
+ if (view)
+ view->show();
+}
+
+NotationView *
+RosegardenGUIView::createNotationView(std::vector<Segment *> segmentsToEdit)
+{
+ NotationView *notationView =
+ new NotationView(getDocument(), segmentsToEdit, this, true);
+
+ if (!notationView->isOK()) {
+ RG_DEBUG << "slotEditSegmentNotation : operation cancelled" << endl;
+ delete notationView;
+ return 0;
+ }
+
+ // For tempo changes (ugh -- it'd be nicer to make a tempo change
+ // command that could interpret all this stuff from the dialog)
+ //
+ connect(notationView, SIGNAL(changeTempo(timeT,
+ tempoT,
+ tempoT,
+ TempoDialog::TempoDialogAction)),
+ RosegardenGUIApp::self(), SLOT(slotChangeTempo(timeT,
+ tempoT,
+ tempoT,
+ TempoDialog::TempoDialogAction)));
+
+
+ connect(notationView, SIGNAL(windowActivated()),
+ this, SLOT(slotActiveMainWindowChanged()));
+
+ connect(notationView, SIGNAL(selectTrack(int)),
+ this, SLOT(slotSelectTrackSegments(int)));
+
+ connect(notationView, SIGNAL(play()),
+ RosegardenGUIApp::self(), SLOT(slotPlay()));
+ connect(notationView, SIGNAL(stop()),
+ RosegardenGUIApp::self(), SLOT(slotStop()));
+ connect(notationView, SIGNAL(fastForwardPlayback()),
+ RosegardenGUIApp::self(), SLOT(slotFastforward()));
+ connect(notationView, SIGNAL(rewindPlayback()),
+ RosegardenGUIApp::self(), SLOT(slotRewind()));
+ connect(notationView, SIGNAL(fastForwardPlaybackToEnd()),
+ RosegardenGUIApp::self(), SLOT(slotFastForwardToEnd()));
+ connect(notationView, SIGNAL(rewindPlaybackToBeginning()),
+ RosegardenGUIApp::self(), SLOT(slotRewindToBeginning()));
+ connect(notationView, SIGNAL(panic()),
+ RosegardenGUIApp::self(), SLOT(slotPanic()));
+
+ connect(notationView, SIGNAL(saveFile()),
+ RosegardenGUIApp::self(), SLOT(slotFileSave()));
+ connect(notationView, SIGNAL(jumpPlaybackTo(timeT)),
+ getDocument(), SLOT(slotSetPointerPosition(timeT)));
+ connect(notationView, SIGNAL(openInNotation(std::vector<Segment *>)),
+ this, SLOT(slotEditSegmentsNotation(std::vector<Segment *>)));
+ connect(notationView, SIGNAL(openInMatrix(std::vector<Segment *>)),
+ this, SLOT(slotEditSegmentsMatrix(std::vector<Segment *>)));
+ connect(notationView, SIGNAL(openInPercussionMatrix(std::vector<Segment *>)),
+ this, SLOT(slotEditSegmentsPercussionMatrix(std::vector<Segment *>)));
+ connect(notationView, SIGNAL(openInEventList(std::vector<Segment *>)),
+ this, SLOT(slotEditSegmentsEventList(std::vector<Segment *>)));
+/* hjj: WHAT DO DO WITH THIS ?
+ connect(notationView, SIGNAL(editMetadata(QString)),
+ this, SLOT(slotEditMetadata(QString)));
+*/
+ connect(notationView, SIGNAL(editTriggerSegment(int)),
+ this, SLOT(slotEditTriggerSegment(int)));
+ connect(notationView, SIGNAL(staffLabelChanged(TrackId, QString)),
+ this, SLOT(slotChangeTrackLabel(TrackId, QString)));
+ connect(notationView, SIGNAL(toggleSolo(bool)),
+ RosegardenGUIApp::self(), SLOT(slotToggleSolo(bool)));
+ connect(notationView, SIGNAL(editTimeSignature(timeT)),
+ RosegardenGUIApp::self(), SLOT(slotEditTempos(timeT)));
+
+ SequenceManager *sM = getDocument()->getSequenceManager();
+
+ connect(sM, SIGNAL(insertableNoteOnReceived(int, int)),
+ notationView, SLOT(slotInsertableNoteOnReceived(int, int)));
+ connect(sM, SIGNAL(insertableNoteOffReceived(int, int)),
+ notationView, SLOT(slotInsertableNoteOffReceived(int, int)));
+
+ connect(notationView, SIGNAL(stepByStepTargetRequested(QObject *)),
+ this, SIGNAL(stepByStepTargetRequested(QObject *)));
+ connect(this, SIGNAL(stepByStepTargetRequested(QObject *)),
+ notationView, SLOT(slotStepByStepTargetRequested(QObject *)));
+ connect(RosegardenGUIApp::self(), SIGNAL(compositionStateUpdate()),
+ notationView, SLOT(slotCompositionStateUpdate()));
+ connect(this, SIGNAL(compositionStateUpdate()),
+ notationView, SLOT(slotCompositionStateUpdate()));
+
+ // Encourage the notation view window to open to the same
+ // interval as the current segment view
+ if (m_trackEditor->getSegmentCanvas()->horizontalScrollBar()->value() > 1) { // don't scroll unless we need to
+ // first find the time at the center of the visible segment canvas
+ int centerX = (int)(m_trackEditor->getSegmentCanvas()->contentsX() +
+ m_trackEditor->getSegmentCanvas()->visibleWidth() / 2);
+ timeT centerSegmentView = m_trackEditor->getRulerScale()->getTimeForX(centerX);
+ // then scroll the notation view to that time, "localized" for the current segment
+ notationView->scrollToTime(centerSegmentView);
+ notationView->updateView();
+ }
+
+ return notationView;
+}
+
+void RosegardenGUIView::slotEditSegmentMatrix(Segment* p)
+{
+ SetWaitCursor waitCursor;
+
+ std::vector<Segment *> segmentsToEdit;
+
+ // unlike notation, if we're calling for this on a particular
+ // segment we don't open all the other selected segments as well
+ // (fine in notation because they're in a single window)
+
+ if (p) {
+ if (p->getType() != Segment::Audio) {
+ segmentsToEdit.push_back(p);
+ }
+ } else {
+ int count = 0;
+ SegmentSelection selection = getSelection();
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+ if ((*i)->getType() != Segment::Audio) {
+ slotEditSegmentMatrix(*i);
+ if (++count == maxEditorsToOpen)
+ break;
+ }
+ }
+ return ;
+ }
+
+ if (segmentsToEdit.empty()) {
+ KMessageBox::sorry(this, i18n("No non-audio segments selected"));
+ return ;
+ }
+
+ slotEditSegmentsMatrix(segmentsToEdit);
+}
+
+void RosegardenGUIView::slotEditSegmentPercussionMatrix(Segment* p)
+{
+ SetWaitCursor waitCursor;
+
+ std::vector<Segment *> segmentsToEdit;
+
+ // unlike notation, if we're calling for this on a particular
+ // segment we don't open all the other selected segments as well
+ // (fine in notation because they're in a single window)
+
+ if (p) {
+ if (p->getType() != Segment::Audio) {
+ segmentsToEdit.push_back(p);
+ }
+ } else {
+ int count = 0;
+ SegmentSelection selection = getSelection();
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+ if ((*i)->getType() != Segment::Audio) {
+ slotEditSegmentPercussionMatrix(*i);
+ if (++count == maxEditorsToOpen)
+ break;
+ }
+ }
+ return ;
+ }
+
+ if (segmentsToEdit.empty()) {
+ KMessageBox::sorry(this, i18n("No non-audio segments selected"));
+ return ;
+ }
+
+ slotEditSegmentsPercussionMatrix(segmentsToEdit);
+}
+
+void RosegardenGUIView::slotEditSegmentsMatrix(std::vector<Segment *> segmentsToEdit)
+{
+ int count = 0;
+ for (std::vector<Segment *>::iterator i = segmentsToEdit.begin();
+ i != segmentsToEdit.end(); ++i) {
+ std::vector<Segment *> tmpvec;
+ tmpvec.push_back(*i);
+ MatrixView *view = createMatrixView(tmpvec, false);
+ if (view) {
+ view->show();
+ if (++count == maxEditorsToOpen)
+ break;
+ }
+ }
+}
+
+void RosegardenGUIView::slotEditSegmentsPercussionMatrix(std::vector<Segment *> segmentsToEdit)
+{
+ int count = 0;
+ for (std::vector<Segment *>::iterator i = segmentsToEdit.begin();
+ i != segmentsToEdit.end(); ++i) {
+ std::vector<Segment *> tmpvec;
+ tmpvec.push_back(*i);
+ MatrixView *view = createMatrixView(tmpvec, true);
+ if (view) {
+ view->show();
+ if (++count == maxEditorsToOpen)
+ break;
+ }
+ }
+}
+
+MatrixView *
+RosegardenGUIView::createMatrixView(std::vector<Segment *> segmentsToEdit, bool drumMode)
+{
+ MatrixView *matrixView = new MatrixView(getDocument(),
+ segmentsToEdit,
+ this,
+ drumMode);
+
+ // For tempo changes (ugh -- it'd be nicer to make a tempo change
+ // command that could interpret all this stuff from the dialog)
+ //
+ connect(matrixView, SIGNAL(changeTempo(timeT,
+ tempoT,
+ tempoT,
+ TempoDialog::TempoDialogAction)),
+ RosegardenGUIApp::self(), SLOT(slotChangeTempo(timeT,
+ tempoT,
+ tempoT,
+ TempoDialog::TempoDialogAction)));
+
+ connect(matrixView, SIGNAL(windowActivated()),
+ this, SLOT(slotActiveMainWindowChanged()));
+
+ connect(matrixView, SIGNAL(selectTrack(int)),
+ this, SLOT(slotSelectTrackSegments(int)));
+
+ connect(matrixView, SIGNAL(play()),
+ RosegardenGUIApp::self(), SLOT(slotPlay()));
+ connect(matrixView, SIGNAL(stop()),
+ RosegardenGUIApp::self(), SLOT(slotStop()));
+ connect(matrixView, SIGNAL(fastForwardPlayback()),
+ RosegardenGUIApp::self(), SLOT(slotFastforward()));
+ connect(matrixView, SIGNAL(rewindPlayback()),
+ RosegardenGUIApp::self(), SLOT(slotRewind()));
+ connect(matrixView, SIGNAL(fastForwardPlaybackToEnd()),
+ RosegardenGUIApp::self(), SLOT(slotFastForwardToEnd()));
+ connect(matrixView, SIGNAL(rewindPlaybackToBeginning()),
+ RosegardenGUIApp::self(), SLOT(slotRewindToBeginning()));
+ connect(matrixView, SIGNAL(panic()),
+ RosegardenGUIApp::self(), SLOT(slotPanic()));
+
+ connect(matrixView, SIGNAL(saveFile()),
+ RosegardenGUIApp::self(), SLOT(slotFileSave()));
+ connect(matrixView, SIGNAL(jumpPlaybackTo(timeT)),
+ getDocument(), SLOT(slotSetPointerPosition(timeT)));
+ connect(matrixView, SIGNAL(openInNotation(std::vector<Segment *>)),
+ this, SLOT(slotEditSegmentsNotation(std::vector<Segment *>)));
+ connect(matrixView, SIGNAL(openInMatrix(std::vector<Segment *>)),
+ this, SLOT(slotEditSegmentsMatrix(std::vector<Segment *>)));
+ connect(matrixView, SIGNAL(openInEventList(std::vector<Segment *>)),
+ this, SLOT(slotEditSegmentsEventList(std::vector<Segment *>)));
+ connect(matrixView, SIGNAL(editTriggerSegment(int)),
+ this, SLOT(slotEditTriggerSegment(int)));
+ connect(matrixView, SIGNAL(toggleSolo(bool)),
+ RosegardenGUIApp::self(), SLOT(slotToggleSolo(bool)));
+ connect(matrixView, SIGNAL(editTimeSignature(timeT)),
+ RosegardenGUIApp::self(), SLOT(slotEditTempos(timeT)));
+
+ SequenceManager *sM = getDocument()->getSequenceManager();
+
+ connect(sM, SIGNAL(insertableNoteOnReceived(int, int)),
+ matrixView, SLOT(slotInsertableNoteOnReceived(int, int)));
+ connect(sM, SIGNAL(insertableNoteOffReceived(int, int)),
+ matrixView, SLOT(slotInsertableNoteOffReceived(int, int)));
+
+ connect(matrixView, SIGNAL(stepByStepTargetRequested(QObject *)),
+ this, SIGNAL(stepByStepTargetRequested(QObject *)));
+ connect(this, SIGNAL(stepByStepTargetRequested(QObject *)),
+ matrixView, SLOT(slotStepByStepTargetRequested(QObject *)));
+ connect(RosegardenGUIApp::self(), SIGNAL(compositionStateUpdate()),
+ matrixView, SLOT(slotCompositionStateUpdate()));
+ connect(this, SIGNAL(compositionStateUpdate()),
+ matrixView, SLOT(slotCompositionStateUpdate()));
+ connect(this,
+ SIGNAL(instrumentLevelsChanged(InstrumentId,
+ const LevelInfo &)),
+ matrixView,
+ SLOT(slotInstrumentLevelsChanged(InstrumentId,
+ const LevelInfo &)));
+
+ // Encourage the matrix view window to open to the same
+ // interval as the current segment view
+ if (m_trackEditor->getSegmentCanvas()->horizontalScrollBar()->value() > 1) { // don't scroll unless we need to
+ // first find the time at the center of the visible segment canvas
+ int centerX = (int)(m_trackEditor->getSegmentCanvas()->contentsX());
+ // Seems to work better for matrix view to scroll to left side
+ // + m_trackEditor->getSegmentCanvas()->visibleWidth() / 2);
+ timeT centerSegmentView = m_trackEditor->getRulerScale()->getTimeForX(centerX);
+ // then scroll the notation view to that time, "localized" for the current segment
+ matrixView->scrollToTime(centerSegmentView);
+ matrixView->updateView();
+ }
+
+ return matrixView;
+}
+
+void RosegardenGUIView::slotEditSegmentEventList(Segment *p)
+{
+ SetWaitCursor waitCursor;
+
+ std::vector<Segment *> segmentsToEdit;
+
+ // unlike notation, if we're calling for this on a particular
+ // segment we don't open all the other selected segments as well
+ // (fine in notation because they're in a single window)
+
+ if (p) {
+ if (p->getType() != Segment::Audio) {
+ segmentsToEdit.push_back(p);
+ }
+ } else {
+ int count = 0;
+ SegmentSelection selection = getSelection();
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+ if ((*i)->getType() != Segment::Audio) {
+ slotEditSegmentEventList(*i);
+ if (++count == maxEditorsToOpen)
+ break;
+ }
+ }
+ return ;
+ }
+
+ if (segmentsToEdit.empty()) {
+ KMessageBox::sorry(this, i18n("No non-audio segments selected"));
+ return ;
+ }
+
+ slotEditSegmentsEventList(segmentsToEdit);
+}
+
+void RosegardenGUIView::slotEditSegmentsEventList(std::vector<Segment *> segmentsToEdit)
+{
+ int count = 0;
+ for (std::vector<Segment *>::iterator i = segmentsToEdit.begin();
+ i != segmentsToEdit.end(); ++i) {
+ std::vector<Segment *> tmpvec;
+ tmpvec.push_back(*i);
+ EventView *view = createEventView(tmpvec);
+ if (view) {
+ view->show();
+ if (++count == maxEditorsToOpen)
+ break;
+ }
+ }
+}
+
+void RosegardenGUIView::slotEditTriggerSegment(int id)
+{
+ SetWaitCursor waitCursor;
+
+ std::vector<Segment *> segmentsToEdit;
+
+ Segment *s = getDocument()->getComposition().getTriggerSegment(id);
+
+ if (s) {
+ segmentsToEdit.push_back(s);
+ } else {
+ return ;
+ }
+
+ slotEditSegmentsEventList(segmentsToEdit);
+}
+
+void RosegardenGUIView::slotSegmentAutoSplit(Segment *segment)
+{
+ AudioSplitDialog aSD(this, segment, getDocument());
+
+ if (aSD.exec() == QDialog::Accepted) {
+ KCommand *command =
+ new AudioSegmentAutoSplitCommand(getDocument(),
+ segment, aSD.getThreshold());
+ slotAddCommandToHistory(command);
+ }
+}
+
+void RosegardenGUIView::slotEditSegmentAudio(Segment *segment)
+{
+ std::cout << "RosegardenGUIView::slotEditSegmentAudio() - "
+ << "starting external audio editor" << std::endl;
+
+ KConfig* config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+
+ QString application = config->readEntry("externalaudioeditor", "");
+
+ if (application == "") {
+ application = AudioConfigurationPage::getBestAvailableAudioEditor();
+ }
+
+ QStringList splitCommand = QStringList::split(" ", application);
+
+ if (splitCommand.size() == 0) {
+
+ std::cerr << "RosegardenGUIView::slotEditSegmentAudio() - "
+ << "external editor \"" << application.data()
+ << "\" not found" << std::endl;
+
+ KMessageBox::sorry(this,
+ i18n("You've not yet defined an audio editor for Rosegarden to use.\nSee Settings -> Configure Rosegarden -> Audio."));
+
+ return ;
+ }
+
+ QFileInfo *appInfo = new QFileInfo(splitCommand[0]);
+ if (appInfo->exists() == false || appInfo->isExecutable() == false) {
+ std::cerr << "RosegardenGUIView::slotEditSegmentAudio() - "
+ << "can't execute \"" << splitCommand[0] << "\""
+ << std::endl;
+ return;
+ }
+
+ AudioFile *aF = getDocument()->getAudioFileManager().
+ getAudioFile(segment->getAudioFileId());
+ if (aF == 0) {
+ std::cerr << "RosegardenGUIView::slotEditSegmentAudio() - "
+ << "can't find audio file" << std::endl;
+ return ;
+ }
+
+ // wait cursor
+ QApplication::setOverrideCursor(QCursor(Qt::waitCursor));
+
+ // Prepare the process
+ //
+ KProcess *process = new KProcess();
+ (*process) << splitCommand;
+ (*process) << QString(aF->getFilename().c_str());
+
+ // Start it
+ //
+ if (!process->start()) {
+ std::cerr << "RosegardenGUIView::slotEditSegmentAudio() - "
+ << "can't start external editor" << std::endl;
+ }
+
+ // restore cursor
+ QApplication::restoreOverrideCursor();
+
+}
+
+void RosegardenGUIView::setZoomSize(double size)
+{
+ m_rulerScale->setUnitsPerPixel(size);
+
+ double duration44 = TimeSignature(4, 4).getBarDuration();
+
+ double xScale = duration44 / (size * barWidth44);
+ RG_DEBUG << "RosegardenGUIView::setZoomSize - xScale = " << xScale << endl;
+
+ m_trackEditor->slotSetPointerPosition
+ (getDocument()->getComposition().getPosition());
+
+ m_trackEditor->getSegmentCanvas()->clearSegmentRectsCache(true);
+ m_trackEditor->getSegmentCanvas()->slotUpdateSize();
+ m_trackEditor->getSegmentCanvas()->slotUpdateSegmentsDrawBuffer();
+
+ if (m_trackEditor->getTempoRuler()) {
+ m_trackEditor->getTempoRuler()->repaint();
+ }
+
+ if (m_trackEditor->getChordNameRuler()) {
+ m_trackEditor->getChordNameRuler()->repaint();
+ }
+
+ if (m_trackEditor->getTopStandardRuler()) {
+ m_trackEditor->getTopStandardRuler()->repaint();
+ }
+
+ if (m_trackEditor->getBottomStandardRuler()) {
+ m_trackEditor->getBottomStandardRuler()->repaint();
+ }
+}
+
+void RosegardenGUIView::slotSelectTrackSegments(int trackId)
+{
+ // update the instrument parameter box
+ Composition &comp = getDocument()->getComposition();
+ Track *track = comp.getTrackById(trackId);
+
+ if (track == 0)
+ return ;
+
+ // Show the selection on the track buttons. Find the position.
+ //
+ m_trackEditor->getTrackButtons()->selectLabel(track->getPosition());
+ m_trackEditor->slotScrollToTrack(track->getPosition());
+
+ SegmentSelection segments;
+
+ for (Composition::iterator i =
+ getDocument()->getComposition().begin();
+ i != getDocument()->getComposition().end(); i++) {
+ if (((int)(*i)->getTrack()) == trackId)
+ segments.insert(*i);
+ }
+
+ // Store the selected Track in the Composition
+ //
+ comp.setSelectedTrack(trackId);
+
+ m_trackParameterBox->slotSelectedTrackChanged();
+
+ slotUpdateInstrumentParameterBox(comp.getTrackById(trackId)->
+ getInstrument());
+
+
+ slotPropagateSegmentSelection(segments);
+
+ // inform
+ emit segmentsSelected(segments);
+ emit compositionStateUpdate();
+}
+
+void RosegardenGUIView::slotPropagateSegmentSelection(const SegmentSelection &segments)
+{
+ // Send this signal to the GUI to activate the correct tool
+ // on the toolbar so that we have a SegmentSelector object
+ // to write the Segments into
+ //
+ if (!segments.empty()) {
+ emit activateTool(SegmentSelector::ToolName);
+ }
+
+ // Send the segment list even if it's empty as we
+ // use that to clear any current selection
+ //
+ m_trackEditor->getSegmentCanvas()->slotSelectSegments(segments);
+
+ // update the segment parameter box
+ m_segmentParameterBox->useSegments(segments);
+
+ if (!segments.empty()) {
+ emit stateChange("have_selection", true);
+ if (!segments.hasNonAudioSegment()) {
+ emit stateChange("audio_segment_selected", true);
+ }
+ } else {
+ emit stateChange("have_selection", false);
+ }
+}
+
+void RosegardenGUIView::slotSelectAllSegments()
+{
+ SegmentSelection segments;
+
+ InstrumentId instrument = 0;
+ bool haveInstrument = false;
+ bool multipleInstruments = false;
+
+ Composition &comp = getDocument()->getComposition();
+
+ for (Composition::iterator i = comp.begin(); i != comp.end(); ++i) {
+
+ InstrumentId myInstrument =
+ comp.getTrackById((*i)->getTrack())->getInstrument();
+
+ if (haveInstrument) {
+ if (myInstrument != instrument) {
+ multipleInstruments = true;
+ }
+ } else {
+ instrument = myInstrument;
+ haveInstrument = true;
+ }
+
+ segments.insert(*i);
+ }
+
+ // Send this signal to the GUI to activate the correct tool
+ // on the toolbar so that we have a SegmentSelector object
+ // to write the Segments into
+ //
+ if (!segments.empty()) {
+ emit activateTool(SegmentSelector::ToolName);
+ }
+
+ // Send the segment list even if it's empty as we
+ // use that to clear any current selection
+ //
+ m_trackEditor->getSegmentCanvas()->slotSelectSegments(segments);
+
+ // update the segment parameter box
+ m_segmentParameterBox->useSegments(segments);
+
+ // update the instrument parameter box
+ if (haveInstrument && !multipleInstruments) {
+ slotUpdateInstrumentParameterBox(instrument);
+ } else {
+ m_instrumentParameterBox->useInstrument(0);
+ }
+
+ //!!! similarly, how to set no selected track?
+ //comp.setSelectedTrack(trackId);
+
+ if (!segments.empty()) {
+ emit stateChange("have_selection", true);
+ if (!segments.hasNonAudioSegment()) {
+ emit stateChange("audio_segment_selected", true);
+ }
+ } else {
+ emit stateChange("have_selection", false);
+ }
+
+ // inform
+ //!!! inform what? is this signal actually used?
+ emit segmentsSelected(segments);
+}
+
+void RosegardenGUIView::slotUpdateInstrumentParameterBox(int id)
+{
+ Studio &studio = getDocument()->getStudio();
+ Instrument *instrument = studio.getInstrumentById(id);
+ Composition &comp = getDocument()->getComposition();
+
+ Track *track = comp.getTrackById(comp.getSelectedTrack());
+
+ // Reset the instrument
+ //
+ m_instrumentParameterBox->useInstrument(instrument);
+
+ // Then do this instrument/track fiddling
+ //
+ /*
+ if (track && instrument &&
+ instrument->getType() == Instrument::Audio)
+ {
+ // Set the mute status
+ m_instrumentParameterBox->setMute(track->isMuted());
+
+ // Set the record track
+ m_instrumentParameterBox->setRecord(
+ track->getId() == comp.getRecordTrack());
+
+ // Set solo
+ m_instrumentParameterBox->setSolo(
+ comp.isSolo() && (track->getId() == comp.getSelectedTrack()));
+ }
+ */
+ emit checkTrackAssignments();
+}
+
+void RosegardenGUIView::showVisuals(const MappedEvent *mE)
+{
+ double valueLeft = ((double)mE->getData1()) / 127.0;
+ double valueRight = ((double)mE->getData2()) / 127.0;
+
+ if (mE->getType() == MappedEvent::AudioLevel) {
+
+ // Send to the high sensitivity instrument parameter box
+ // (if any)
+ //
+ if (m_instrumentParameterBox->getSelectedInstrument() &&
+ mE->getInstrument() ==
+ m_instrumentParameterBox->getSelectedInstrument()->getId()) {
+ float dBleft = AudioLevel::fader_to_dB
+ (mE->getData1(), 127, AudioLevel::LongFader);
+ float dBright = AudioLevel::fader_to_dB
+ (mE->getData2(), 127, AudioLevel::LongFader);
+
+ m_instrumentParameterBox->setAudioMeter(dBleft, dBright,
+ AudioLevel::DB_FLOOR,
+ AudioLevel::DB_FLOOR);
+ }
+
+ // Don't always send all audio levels so we don't
+ // get vu meter flickering on track meters
+ //
+ if (valueLeft < 0.05 && valueRight < 0.05)
+ return ;
+
+ } else if (mE->getType() != MappedEvent::MidiNote)
+ return ;
+
+ m_trackEditor->getTrackButtons()->
+ slotSetMetersByInstrument((valueLeft + valueRight) / 2,
+ mE->getInstrument());
+
+}
+
+void
+RosegardenGUIView::updateMeters(SequencerMapper *mapper)
+{
+ const int unknownState = 0, oldState = 1, newState = 2;
+
+ typedef std::map<InstrumentId, int> StateMap;
+ static StateMap states;
+ static StateMap recStates;
+
+ typedef std::map<InstrumentId, LevelInfo> LevelMap;
+ static LevelMap levels;
+ static LevelMap recLevels;
+
+ for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
+ i->second = unknownState;
+ }
+ for (StateMap::iterator i = recStates.begin(); i != recStates.end(); ++i) {
+ i->second = unknownState;
+ }
+
+ for (Composition::trackcontainer::iterator i =
+ getDocument()->getComposition().getTracks().begin();
+ i != getDocument()->getComposition().getTracks().end(); ++i) {
+
+ Track *track = i->second;
+ if (!track)
+ continue;
+
+ InstrumentId instrumentId = track->getInstrument();
+
+ if (states[instrumentId] == unknownState) {
+ bool isNew = mapper->getInstrumentLevel(instrumentId,
+ levels[instrumentId]);
+ states[instrumentId] = (isNew ? newState : oldState);
+ }
+
+ if (recStates[instrumentId] == unknownState) {
+ bool isNew = mapper->getInstrumentRecordLevel(instrumentId,
+ recLevels[instrumentId]);
+ recStates[instrumentId] = (isNew ? newState : oldState);
+ }
+
+ if (states[instrumentId] == oldState &&
+ recStates[instrumentId] == oldState)
+ continue;
+
+ Instrument *instrument =
+ getDocument()->getStudio().getInstrumentById(instrumentId);
+ if (!instrument)
+ continue;
+
+ // This records the level of this instrument, not neccessarily
+ // caused by notes on this particular track.
+ LevelInfo &info = levels[instrumentId];
+ LevelInfo &recInfo = recLevels[instrumentId];
+
+ if (instrument->getType() == Instrument::Audio ||
+ instrument->getType() == Instrument::SoftSynth) {
+
+ float dBleft = AudioLevel::DB_FLOOR;
+ float dBright = AudioLevel::DB_FLOOR;
+ float recDBleft = AudioLevel::DB_FLOOR;
+ float recDBright = AudioLevel::DB_FLOOR;
+
+ bool toSet = false;
+
+ if (states[instrumentId] == newState &&
+ (getDocument()->getSequenceManager()->getTransportStatus()
+ != STOPPED)) {
+
+ if (info.level != 0 || info.levelRight != 0) {
+ dBleft = AudioLevel::fader_to_dB
+ (info.level, 127, AudioLevel::LongFader);
+ dBright = AudioLevel::fader_to_dB
+ (info.levelRight, 127, AudioLevel::LongFader);
+ }
+ toSet = true;
+ m_trackEditor->getTrackButtons()->slotSetTrackMeter
+ ((info.level + info.levelRight) / 254.0, track->getPosition());
+ }
+
+ if (recStates[instrumentId] == newState &&
+ instrument->getType() == Instrument::Audio &&
+ (getDocument()->getSequenceManager()->getTransportStatus()
+ != PLAYING)) {
+
+ if (recInfo.level != 0 || recInfo.levelRight != 0) {
+ recDBleft = AudioLevel::fader_to_dB
+ (recInfo.level, 127, AudioLevel::LongFader);
+ recDBright = AudioLevel::fader_to_dB
+ (recInfo.levelRight, 127, AudioLevel::LongFader);
+ }
+ toSet = true;
+ }
+
+ if (toSet &&
+ m_instrumentParameterBox->getSelectedInstrument() &&
+ instrument->getId() ==
+ m_instrumentParameterBox->getSelectedInstrument()->getId()) {
+
+ m_instrumentParameterBox->setAudioMeter(dBleft, dBright,
+ recDBleft, recDBright);
+ }
+
+ } else {
+ // Not audio or softsynth
+ if (info.level == 0)
+ continue;
+
+ if (getDocument()->getSequenceManager()->getTransportStatus()
+ != STOPPED) {
+
+ // The information in 'info' is specific for this instrument, not
+ // for this track.
+ //m_trackEditor->getTrackButtons()->slotSetTrackMeter
+ // (info.level / 127.0, track->getPosition());
+ m_trackEditor->getTrackButtons()->slotSetMetersByInstrument
+ (info.level / 127.0, instrumentId);
+ }
+ }
+ }
+
+ for (StateMap::iterator i = states.begin(); i != states.end(); ++i) {
+ if (i->second == newState) {
+ emit instrumentLevelsChanged(i->first, levels[i->first]);
+ }
+ }
+}
+
+void
+RosegardenGUIView::updateMonitorMeters(SequencerMapper *mapper)
+{
+ Instrument *instrument =
+ m_instrumentParameterBox->getSelectedInstrument();
+ if (!instrument ||
+ (instrument->getType() != Instrument::Audio))
+ return ;
+
+ LevelInfo level;
+ if (!mapper->getInstrumentRecordLevel(instrument->getId(), level))
+ return ;
+
+ float dBleft = AudioLevel::fader_to_dB
+ (level.level, 127, AudioLevel::LongFader);
+ float dBright = AudioLevel::fader_to_dB
+ (level.levelRight, 127, AudioLevel::LongFader);
+
+ m_instrumentParameterBox->setAudioMeter
+ (AudioLevel::DB_FLOOR, AudioLevel::DB_FLOOR,
+ dBleft, dBright);
+}
+
+void
+RosegardenGUIView::slotSelectedSegments(const SegmentSelection &segments)
+{
+ // update the segment parameter box
+ m_segmentParameterBox->useSegments(segments);
+
+ if (!segments.empty()) {
+ emit stateChange("have_selection", true);
+ if (!segments.hasNonAudioSegment())
+ emit stateChange("audio_segment_selected", true);
+ } else {
+ emit stateChange("have_selection", false);
+ }
+
+ emit segmentsSelected(segments);
+}
+
+void RosegardenGUIView::slotShowRulers(bool v)
+{
+ if (v) {
+ m_trackEditor->getTopStandardRuler()->getLoopRuler()->show();
+ m_trackEditor->getBottomStandardRuler()->getLoopRuler()->show();
+ } else {
+ m_trackEditor->getTopStandardRuler()->getLoopRuler()->hide();
+ m_trackEditor->getBottomStandardRuler()->getLoopRuler()->hide();
+ }
+}
+
+void RosegardenGUIView::slotShowTempoRuler(bool v)
+{
+ if (v) {
+ m_trackEditor->getTempoRuler()->show();
+ } else {
+ m_trackEditor->getTempoRuler()->hide();
+ }
+}
+
+void RosegardenGUIView::slotShowChordNameRuler(bool v)
+{
+ if (v) {
+ m_trackEditor->getChordNameRuler()->setStudio(&getDocument()->getStudio());
+ m_trackEditor->getChordNameRuler()->show();
+ } else {
+ m_trackEditor->getChordNameRuler()->hide();
+ }
+}
+
+void RosegardenGUIView::slotShowPreviews(bool v)
+{
+ m_trackEditor->getSegmentCanvas()->setShowPreviews(v);
+ m_trackEditor->getSegmentCanvas()->slotUpdateSegmentsDrawBuffer();
+}
+
+void RosegardenGUIView::slotShowSegmentLabels(bool v)
+{
+ m_trackEditor->getSegmentCanvas()->setShowSegmentLabels(v);
+ m_trackEditor->getSegmentCanvas()->slotUpdateSegmentsDrawBuffer();
+}
+
+void RosegardenGUIView::slotAddTracks(unsigned int nbTracks,
+ InstrumentId id, int pos)
+{
+ RG_DEBUG << "RosegardenGUIView::slotAddTracks(" << nbTracks << ", " << pos << ")" << endl;
+ m_trackEditor->slotAddTracks(nbTracks, id, pos);
+}
+
+void RosegardenGUIView::slotDeleteTracks(
+ std::vector<TrackId> tracks)
+{
+ RG_DEBUG << "RosegardenGUIView::slotDeleteTracks - "
+ << "deleting " << tracks.size() << " tracks"
+ << endl;
+
+ m_trackEditor->slotDeleteTracks(tracks);
+}
+
+MultiViewCommandHistory*
+RosegardenGUIView::getCommandHistory()
+{
+ return getDocument()->getCommandHistory();
+}
+
+void
+RosegardenGUIView::slotAddCommandToHistory(KCommand *command)
+{
+ getCommandHistory()->addCommand(command);
+}
+
+void
+RosegardenGUIView::slotChangeInstrumentLabel(InstrumentId id,
+ QString label)
+{
+ m_trackEditor->getTrackButtons()->changeInstrumentLabel(id, label);
+}
+
+void
+RosegardenGUIView::slotChangeTrackLabel(TrackId id,
+ QString label)
+{
+ m_trackEditor->getTrackButtons()->changeTrackLabel(id, label);
+}
+
+void
+RosegardenGUIView::slotAddAudioSegment(AudioFileId audioId,
+ TrackId trackId,
+ timeT position,
+ const RealTime &startTime,
+ const RealTime &endTime)
+{
+ AudioSegmentInsertCommand *command =
+ new AudioSegmentInsertCommand(getDocument(),
+ trackId,
+ position,
+ audioId,
+ startTime,
+ endTime);
+ slotAddCommandToHistory(command);
+
+ Segment *newSegment = command->getNewSegment();
+ if (newSegment) {
+ SegmentSelection selection;
+ selection.insert(newSegment);
+ slotPropagateSegmentSelection(selection);
+ emit segmentsSelected(selection);
+ }
+}
+
+void
+RosegardenGUIView::slotAddAudioSegmentCurrentPosition(AudioFileId audioFileId,
+ const RealTime &startTime,
+ const RealTime &endTime)
+{
+ Composition &comp = getDocument()->getComposition();
+
+ AudioSegmentInsertCommand *command =
+ new AudioSegmentInsertCommand(getDocument(),
+ comp.getSelectedTrack(),
+ comp.getPosition(),
+ audioFileId,
+ startTime,
+ endTime);
+ slotAddCommandToHistory(command);
+
+ Segment *newSegment = command->getNewSegment();
+ if (newSegment) {
+ SegmentSelection selection;
+ selection.insert(newSegment);
+ slotPropagateSegmentSelection(selection);
+ emit segmentsSelected(selection);
+ }
+}
+
+void
+RosegardenGUIView::slotAddAudioSegmentDefaultPosition(AudioFileId audioFileId,
+ const RealTime &startTime,
+ const RealTime &endTime)
+{
+ // Add at current track if it's an audio track, otherwise at first
+ // empty audio track if there is one, otherwise at first audio track.
+ // This behaviour should be of no interest to proficient users (who
+ // should have selected the right track already, or be using drag-
+ // and-drop) but it should save beginners from inserting an audio
+ // segment and being quite unable to work out why it won't play
+
+ Composition &comp = getDocument()->getComposition();
+ Studio &studio = getDocument()->getStudio();
+
+ TrackId currentTrackId = comp.getSelectedTrack();
+ Track *track = comp.getTrackById(currentTrackId);
+
+ if (track) {
+ InstrumentId ii = track->getInstrument();
+ Instrument *instrument = studio.getInstrumentById(ii);
+
+ if (instrument &&
+ instrument->getType() == Instrument::Audio) {
+ slotAddAudioSegment(audioFileId, currentTrackId,
+ comp.getPosition(), startTime, endTime);
+ return ;
+ }
+ }
+
+ // current track is not an audio track, find a more suitable one
+
+ TrackId bestSoFar = currentTrackId;
+
+ for (Composition::trackcontainer::const_iterator
+ ti = comp.getTracks().begin();
+ ti != comp.getTracks().end(); ++ti) {
+
+ InstrumentId ii = ti->second->getInstrument();
+ Instrument *instrument = studio.getInstrumentById(ii);
+
+ if (instrument &&
+ instrument->getType() == Instrument::Audio) {
+
+ if (bestSoFar == currentTrackId)
+ bestSoFar = ti->first;
+ bool haveSegment = false;
+
+ for (Composition::segmentcontainer::const_iterator si =
+ comp.getSegments().begin();
+ si != comp.getSegments().end(); ++si) {
+ if ((*si)->getTrack() == ti->first) {
+ // there's a segment on this track
+ haveSegment = true;
+ break;
+ }
+ }
+
+ if (!haveSegment) { // perfect
+ slotAddAudioSegment(audioFileId, ti->first,
+ comp.getPosition(), startTime, endTime);
+ return ;
+ }
+ }
+ }
+
+ slotAddAudioSegment(audioFileId, bestSoFar,
+ comp.getPosition(), startTime, endTime);
+ return ;
+}
+
+void
+RosegardenGUIView::slotDroppedNewAudio(QString audioDesc)
+{
+ QTextIStream s(&audioDesc);
+
+ QString url;
+ int trackId;
+ timeT time;
+ url = s.readLine();
+ s >> trackId;
+ s >> time;
+
+ std::cerr << "RosegardenGUIView::slotDroppedNewAudio: url " << url << ", trackId " << trackId << ", time " << time << std::endl;
+
+ RosegardenGUIApp *app = RosegardenGUIApp::self();
+ AudioFileManager &aFM = getDocument()->getAudioFileManager();
+
+ AudioFileId audioFileId = 0;
+
+ int sampleRate = 0;
+ if (getDocument()->getSequenceManager()) {
+ sampleRate = getDocument()->getSequenceManager()->getSampleRate();
+ }
+
+ KURL kurl(url);
+ if (!kurl.isLocalFile()) {
+ if (!RosegardenGUIApp::self()->testAudioPath("importing a remote audio file")) return;
+ } else if (aFM.fileNeedsConversion(qstrtostr(kurl.path()), sampleRate)) {
+ if (!RosegardenGUIApp::self()->testAudioPath("importing an audio file that needs to be converted or resampled")) return;
+ }
+
+ ProgressDialog progressDlg(i18n("Adding audio file..."),
+ 100,
+ this);
+
+ CurrentProgressDialog::set(&progressDlg);
+ progressDlg.progressBar()->hide();
+ progressDlg.show();
+
+ // Connect the progress dialog
+ //
+ connect(&aFM, SIGNAL(setProgress(int)),
+ progressDlg.progressBar(), SLOT(setValue(int)));
+ connect(&aFM, SIGNAL(setOperationName(QString)),
+ &progressDlg, SLOT(slotSetOperationName(QString)));
+ connect(&progressDlg, SIGNAL(cancelClicked()),
+ &aFM, SLOT(slotStopImport()));
+
+ try {
+ audioFileId = aFM.importURL(kurl, sampleRate);
+ } catch (AudioFileManager::BadAudioPathException e) {
+ CurrentProgressDialog::freeze();
+ QString errorString = i18n("Can't add dropped file. ") + strtoqstr(e.getMessage());
+ KMessageBox::sorry(this, errorString);
+ return ;
+ } catch (SoundFile::BadSoundFileException e) {
+ CurrentProgressDialog::freeze();
+ QString errorString = i18n("Can't add dropped file. ") + strtoqstr(e.getMessage());
+ KMessageBox::sorry(this, errorString);
+ return ;
+ }
+
+ disconnect(&progressDlg, SIGNAL(cancelClicked()),
+ &aFM, SLOT(slotStopImport()));
+ connect(&progressDlg, SIGNAL(cancelClicked()),
+ &aFM, SLOT(slotStopPreview()));
+ progressDlg.progressBar()->show();
+ progressDlg.slotSetOperationName(i18n("Generating audio preview..."));
+
+ try {
+ aFM.generatePreview(audioFileId);
+ } catch (Exception e) {
+ CurrentProgressDialog::freeze();
+ QString message = strtoqstr(e.getMessage()) + "\n\n" +
+ i18n("Try copying this file to a directory where you have write permission and re-add it");
+ KMessageBox::information(this, message);
+ //return false;
+ }
+
+ disconnect(&progressDlg, SIGNAL(cancelClicked()),
+ &aFM, SLOT(slotStopPreview()));
+
+ // add the file at the sequencer
+ emit addAudioFile(audioFileId);
+
+ // Now fetch file details
+ //
+ AudioFile *aF = aFM.getAudioFile(audioFileId);
+
+ if (aF) {
+ slotAddAudioSegment(audioFileId, trackId, time,
+ RealTime(0, 0), aF->getLength());
+
+ RG_DEBUG << "RosegardenGUIView::slotDroppedNewAudio("
+ << "file = " << url
+ << ", trackid = " << trackId
+ << ", time = " << time << endl;
+ }
+}
+
+void
+RosegardenGUIView::slotDroppedAudio(QString audioDesc)
+{
+ QTextIStream s(&audioDesc);
+
+ AudioFileId audioFileId;
+ TrackId trackId;
+ timeT position;
+ RealTime startTime, endTime;
+
+ // read the audio info
+ s >> audioFileId;
+ s >> trackId;
+ s >> position;
+ s >> startTime.sec;
+ s >> startTime.nsec;
+ s >> endTime.sec;
+ s >> endTime.nsec;
+
+ RG_DEBUG << "RosegardenGUIView::slotDroppedAudio("
+ //<< audioDesc
+ << ") : audioFileId = " << audioFileId
+ << " - trackId = " << trackId
+ << " - position = " << position
+ << " - startTime.sec = " << startTime.sec
+ << " - startTime.nsec = " << startTime.nsec
+ << " - endTime.sec = " << endTime.sec
+ << " - endTime.nsec = " << endTime.nsec
+ << endl;
+
+ slotAddAudioSegment(audioFileId, trackId, position, startTime, endTime);
+}
+
+void
+RosegardenGUIView::slotSetMuteButton(TrackId track, bool value)
+{
+ RG_DEBUG << "RosegardenGUIView::slotSetMuteButton - track id = " << track
+ << ", value = " << value << endl;
+
+ m_trackEditor->getTrackButtons()->setMuteButton(track, value);
+ Track *trackObj = getDocument()->
+ getComposition().getTrackById(track);
+ /*
+ // to fix 739544
+ if (m_instrumentParameterBox->getSelectedInstrument() &&
+ m_instrumentParameterBox->getSelectedInstrument()->getId() ==
+ trackObj->getInstrument())
+ {
+ m_instrumentParameterBox->setMute(value);
+ }
+ */
+ // set the value in the composition
+ trackObj->setMuted(value);
+
+ getDocument()->slotDocumentModified(); // set the modification flag
+
+}
+
+void
+RosegardenGUIView::slotSetMute(InstrumentId id, bool value)
+{
+ RG_DEBUG << "RosegardenGUIView::slotSetMute - "
+ << "id = " << id
+ << ",value = " << value << endl;
+
+ Composition &comp = getDocument()->getComposition();
+ Composition::trackcontainer &tracks = comp.getTracks();
+ Composition::trackiterator it;
+
+ for (it = tracks.begin(); it != tracks.end(); ++it) {
+ if ((*it).second->getInstrument() == id)
+ slotSetMuteButton((*it).second->getId(), value);
+ }
+
+}
+
+void
+RosegardenGUIView::slotSetRecord(InstrumentId id, bool value)
+{
+ RG_DEBUG << "RosegardenGUIView::slotSetRecord - "
+ << "id = " << id
+ << ",value = " << value << endl;
+ /*
+ // IPB
+ //
+ m_instrumentParameterBox->setRecord(value);
+ */
+ Composition &comp = getDocument()->getComposition();
+ Composition::trackcontainer &tracks = comp.getTracks();
+ Composition::trackiterator it;
+#ifdef NOT_DEFINED
+
+ for (it = tracks.begin(); it != tracks.end(); ++it) {
+ if (comp.getSelectedTrack() == (*it).second->getId()) {
+ //!!! MTR m_trackEditor->getTrackButtons()->
+ // setRecordTrack((*it).second->getPosition());
+ //!!! MTR is this needed? I think probably not
+ slotUpdateInstrumentParameterBox((*it).second->getInstrument());
+ }
+ }
+#endif
+ Studio &studio = getDocument()->getStudio();
+ Instrument *instr = studio.getInstrumentById(id);
+}
+
+void
+RosegardenGUIView::slotSetSolo(InstrumentId id, bool value)
+{
+ RG_DEBUG << "RosegardenGUIView::slotSetSolo - "
+ << "id = " << id
+ << ",value = " << value << endl;
+
+ emit toggleSolo(value);
+}
+
+void
+RosegardenGUIView::slotUpdateRecordingSegment(Segment *segment,
+ timeT )
+{
+ // We're only interested in this on the first call per recording segment,
+ // when we possibly create a view for it
+ static Segment *lastRecordingSegment = 0;
+
+ if (segment == lastRecordingSegment)
+ return ;
+ lastRecordingSegment = segment;
+
+ KConfig* config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+
+ int tracking = config->readUnsignedNumEntry("recordtracking", 0);
+ if (tracking != 1)
+ return ;
+
+ RG_DEBUG << "RosegardenGUIView::slotUpdateRecordingSegment: segment is " << segment << ", lastRecordingSegment is " << lastRecordingSegment << ", opening a new view" << endl;
+
+ std::vector<Segment *> segments;
+ segments.push_back(segment);
+
+ NotationView *view = createNotationView(segments);
+ if (!view)
+ return ;
+
+ /* signal no longer exists
+ QObject::connect
+ (getDocument(), SIGNAL(recordingSegmentUpdated(Segment *, timeT)),
+ view, SLOT(slotUpdateRecordingSegment(Segment *, timeT)));
+ */
+
+ view->show();
+}
+
+void
+RosegardenGUIView::slotSynchroniseWithComposition()
+{
+ // Track buttons
+ //
+ m_trackEditor->getTrackButtons()->slotSynchroniseWithComposition();
+
+ // Update all IPBs
+ //
+ Composition &comp = getDocument()->getComposition();
+ Track *track = comp.getTrackById(comp.getSelectedTrack());
+ slotUpdateInstrumentParameterBox(track->getInstrument());
+
+ m_instrumentParameterBox->slotUpdateAllBoxes();
+}
+
+void
+RosegardenGUIView::windowActivationChange(bool)
+{
+ if (isActiveWindow()) {
+ slotActiveMainWindowChanged(this);
+ }
+}
+
+void
+RosegardenGUIView::slotActiveMainWindowChanged(const QWidget *w)
+{
+ m_lastActiveMainWindow = w;
+}
+
+void
+RosegardenGUIView::slotActiveMainWindowChanged()
+{
+ const QWidget *w = dynamic_cast<const QWidget *>(sender());
+ if (w)
+ slotActiveMainWindowChanged(w);
+}
+
+void
+RosegardenGUIView::slotControllerDeviceEventReceived(MappedEvent *e)
+{
+ RG_DEBUG << "Controller device event received - send to " << (void *)m_lastActiveMainWindow << " (I am " << this << ")" << endl;
+
+ //!!! So, what _should_ we do with these?
+
+ // -- external controller that sends e.g. volume control for each
+ // of a number of channels -> if mixer present, use control to adjust
+ // tracks on mixer
+
+ // -- external controller that sends e.g. separate controllers on
+ // the same channel for adjusting various parameters -> if IPB
+ // visible, adjust it. Should we use the channel to select the
+ // track? maybe as an option
+
+ // do we actually need the last active main window for either of
+ // these? -- yes, to determine whether to send to mixer or to IPB
+ // in the first place. Send to audio mixer if active, midi mixer
+ // if active, plugin dialog if active, otherwise keep it for
+ // ourselves for the IPB. But, we'll do that by having the edit
+ // views pass it back to us.
+
+ // -- then we need to send back out to device.
+
+ //!!! special cases: controller 81 received by any window ->
+ // select window 0->main, 1->audio mix, 2->midi mix
+
+ //!!! controller 82 received by main window -> select track
+
+ //!!! these obviously should be configurable
+
+ if (e->getType() == MappedEvent::MidiController) {
+
+ if (e->getData1() == 81) {
+
+ // select window
+ int window = e->getData2();
+
+ if (window < 10) { // me
+
+ show();
+ raise();
+ setActiveWindow();
+
+ } else if (window < 20) {
+
+ RosegardenGUIApp::self()->slotOpenAudioMixer();
+
+ } else if (window < 30) {
+
+ RosegardenGUIApp::self()->slotOpenMidiMixer();
+ }
+ }
+ }
+
+ emit controllerDeviceEventReceived(e, m_lastActiveMainWindow);
+}
+
+void
+RosegardenGUIView::slotControllerDeviceEventReceived(MappedEvent *e, const void *preferredCustomer)
+{
+ if (preferredCustomer != this)
+ return ;
+ RG_DEBUG << "RosegardenGUIView::slotControllerDeviceEventReceived: this one's for me" << endl;
+ raise();
+
+ RG_DEBUG << "Event is type: " << int(e->getType()) << ", channel " << int(e->getRecordedChannel()) << ", data1 " << int(e->getData1()) << ", data2 " << int(e->getData2()) << endl;
+
+ Composition &comp = getDocument()->getComposition();
+ Studio &studio = getDocument()->getStudio();
+
+ TrackId currentTrackId = comp.getSelectedTrack();
+ Track *track = comp.getTrackById(currentTrackId);
+
+ // If the event is a control change on channel n, then (if
+ // follow-channel is on) switch to the nth track of the same type
+ // as the current track -- or the first track of the given
+ // channel?, and set the control appropriately. Any controls in
+ // IPB are supported for a MIDI device plus program and bank; only
+ // volume and pan are supported for audio/synth devices.
+ //!!! complete this
+
+ if (e->getType() != MappedEvent::MidiController) {
+
+ if (e->getType() == MappedEvent::MidiProgramChange) {
+ int program = e->getData1();
+ if (!track)
+ return ;
+ InstrumentId ii = track->getInstrument();
+ Instrument *instrument = studio.getInstrumentById(ii);
+ if (!instrument)
+ return ;
+ instrument->setProgramChange(program);
+ emit instrumentParametersChanged(ii);
+ }
+ return ;
+ }
+
+ unsigned int channel = e->getRecordedChannel();
+ MidiByte controller = e->getData1();
+ MidiByte value = e->getData2();
+
+ if (controller == 82) { //!!! magic select-track controller
+ int tracks = comp.getNbTracks();
+ Track *track = comp.getTrackByPosition(value * tracks / 127);
+ if (track) {
+ slotSelectTrackSegments(track->getId());
+ }
+ return ;
+ }
+
+ if (!track)
+ return ;
+
+ InstrumentId ii = track->getInstrument();
+ Instrument *instrument = studio.getInstrumentById(ii);
+
+ if (!instrument)
+ return ;
+
+ switch (instrument->getType()) {
+
+ case Instrument::Midi: {
+ MidiDevice *md = dynamic_cast<MidiDevice *>
+ (instrument->getDevice());
+ if (!md) {
+ std::cerr << "WARNING: MIDI instrument has no MIDI device in slotControllerDeviceEventReceived" << std::endl;
+ return ;
+ }
+
+ //!!! we need a central clearing house for these changes,
+ // for a proper mvc structure. reqd for automation post-1.2.
+ // in the mean time this duplicates much of
+ // MIDIInstrumentParameterPanel::slotControllerChanged etc
+
+ switch (controller) {
+
+ case MIDI_CONTROLLER_VOLUME:
+ RG_DEBUG << "Setting volume for instrument " << instrument->getId() << " to " << value << endl;
+ instrument->setVolume(value);
+ break;
+
+ case MIDI_CONTROLLER_PAN:
+ RG_DEBUG << "Setting pan for instrument " << instrument->getId() << " to " << value << endl;
+ instrument->setPan(value);
+ break;
+
+ default: {
+ ControlList cl = md->getIPBControlParameters();
+ for (ControlList::const_iterator i = cl.begin();
+ i != cl.end(); ++i) {
+ if ((*i).getControllerValue() == controller) {
+ RG_DEBUG << "Setting controller " << controller << " for instrument " << instrument->getId() << " to " << value << endl;
+ instrument->setControllerValue(controller, value);
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ break;
+ }
+
+ case Instrument::SoftSynth:
+ case Instrument::Audio:
+
+ switch (controller) {
+
+ case MIDI_CONTROLLER_VOLUME:
+ RG_DEBUG << "Setting volume for instrument " << instrument->getId() << " to " << value << endl;
+ instrument->setLevel(AudioLevel::fader_to_dB
+ (value, 127, AudioLevel::ShortFader));
+ break;
+
+ case MIDI_CONTROLLER_PAN:
+ RG_DEBUG << "Setting pan for instrument " << instrument->getId() << " to " << value << endl;
+ instrument->setPan(MidiByte((value / 64.0) * 100.0 + 0.01));
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+ }
+
+ emit instrumentParametersChanged(instrument->getId());
+
+ //!!! send out updates via MIDI
+}
+
+void
+RosegardenGUIView::initChordNameRuler()
+{
+ getTrackEditor()->getChordNameRuler()->setReady();
+}
+
+EventView *
+RosegardenGUIView::createEventView(std::vector<Segment *> segmentsToEdit)
+{
+ EventView *eventView = new EventView(getDocument(),
+ segmentsToEdit,
+ this);
+
+ connect(eventView, SIGNAL(windowActivated()),
+ this, SLOT(slotActiveMainWindowChanged()));
+
+ connect(eventView, SIGNAL(selectTrack(int)),
+ this, SLOT(slotSelectTrackSegments(int)));
+
+ connect(eventView, SIGNAL(saveFile()),
+ RosegardenGUIApp::self(), SLOT(slotFileSave()));
+
+ connect(eventView, SIGNAL(openInNotation(std::vector<Segment *>)),
+ this, SLOT(slotEditSegmentsNotation(std::vector<Segment *>)));
+ connect(eventView, SIGNAL(openInMatrix(std::vector<Segment *>)),
+ this, SLOT(slotEditSegmentsMatrix(std::vector<Segment *>)));
+ connect(eventView, SIGNAL(openInPercussionMatrix(std::vector<Segment *>)),
+ this, SLOT(slotEditSegmentsPercussionMatrix(std::vector<Segment *>)));
+ connect(eventView, SIGNAL(openInEventList(std::vector<Segment *>)),
+ this, SLOT(slotEditSegmentsEventList(std::vector<Segment *>)));
+ connect(eventView, SIGNAL(editTriggerSegment(int)),
+ this, SLOT(slotEditTriggerSegment(int)));
+ connect(this, SIGNAL(compositionStateUpdate()),
+ eventView, SLOT(slotCompositionStateUpdate()));
+ connect(RosegardenGUIApp::self(), SIGNAL(compositionStateUpdate()),
+ eventView, SLOT(slotCompositionStateUpdate()));
+ connect(eventView, SIGNAL(toggleSolo(bool)),
+ RosegardenGUIApp::self(), SLOT(slotToggleSolo(bool)));
+
+ // create keyboard accelerators on view
+ //
+ RosegardenGUIApp *par = dynamic_cast<RosegardenGUIApp*>(parent());
+
+ if (par) {
+ par->plugAccelerators(eventView, eventView->getAccelerators());
+ }
+
+ return eventView;
+}
+
+}
+#include "RosegardenGUIView.moc"
diff --git a/src/gui/application/RosegardenGUIView.h b/src/gui/application/RosegardenGUIView.h
new file mode 100644
index 0000000..b3727f3
--- /dev/null
+++ b/src/gui/application/RosegardenGUIView.h
@@ -0,0 +1,347 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ROSEGARDENGUIVIEW_H_
+#define _RG_ROSEGARDENGUIVIEW_H_
+
+#include "base/Event.h"
+#include "base/MidiProgram.h"
+#include "base/Selection.h"
+#include "base/Track.h"
+#include "sound/AudioFile.h"
+#include "gui/editors/segment/TrackEditor.h"
+#include <qstring.h>
+#include <qvbox.h>
+
+
+class QWidget;
+class QObject;
+class LevelInfo;
+class KCommand;
+
+
+namespace Rosegarden
+{
+
+class TrackParameterBox;
+class TrackEditor;
+class SimpleRulerScale;
+class SequencerMapper;
+class SegmentParameterBox;
+class Segment;
+class RosegardenGUIDoc;
+class RealTime;
+class NotationView;
+class MultiViewCommandHistory;
+class MatrixView;
+class MappedEvent;
+class InstrumentParameterBox;
+class EventView;
+class Composition;
+class LevelInfo;
+
+/**
+ * The RosegardenGUIView class provides the view widget for the
+ * RosegardenGUIApp instance. The View instance inherits QWidget as a
+ * base class and represents the view object of a KTMainWindow. As
+ * RosegardenGUIView is part of the docuement-view model, it needs a
+ * reference to the document object connected with it by the
+ * RosegardenGUIApp class to manipulate and display the document
+ * structure provided by the RosegardenGUIDoc class.
+ *
+ * @author Source Framework Automatically Generated by KDevelop, (c) The KDevelop Team.
+ * @version KDevelop version 0.4 code generation
+ */
+class RosegardenGUIView : public QVBox
+{
+ Q_OBJECT
+public:
+
+ /**p
+ * Constructor for the main view
+ */
+ RosegardenGUIView(bool showTrackLabels,
+ SegmentParameterBox*,
+ InstrumentParameterBox*,
+ TrackParameterBox*,
+ QWidget *parent = 0,
+ const char *name=0);
+
+ /**
+ * Destructor for the main view
+ */
+ ~RosegardenGUIView();
+
+ /**
+ * returns a pointer to the document connected to the view
+ * instance. Mind that this method requires a RosegardenGUIApp
+ * instance as a parent widget to get to the window document
+ * pointer by calling the RosegardenGUIApp::getDocument() method.
+ *
+ * @see RosegardenGUIApp#getDocument
+ */
+ RosegardenGUIDoc* getDocument() const;
+
+ /**
+ * Command history
+ */
+ MultiViewCommandHistory* getCommandHistory();
+
+ TrackEditor* getTrackEditor() { return m_trackEditor; }
+
+ /**
+ * contains the implementation for printing functionality
+ */
+ void print(Composition*, bool previewOnly = false);
+
+ // the following aren't slots because they're called from
+ // RosegardenGUIApp
+
+ /**
+ * Select a tool at the SegmentCanvas
+ */
+ void selectTool(QString toolName);
+
+ /**
+ * Show output levels
+ */
+ void showVisuals(const MappedEvent *mE);
+
+ void updateMeters(SequencerMapper *mapper);
+ void updateMonitorMeters(SequencerMapper *mapper);
+
+ /**
+ * Change zoom size -- set the RulerScale's units-per-pixel to size
+ */
+ void setZoomSize(double size);
+
+ void initChordNameRuler();
+
+ bool haveSelection();
+ SegmentSelection getSelection();
+ void updateSelectionContents();
+
+ static bool isMainWindowLastActive(const QWidget *w) {
+ return w == m_lastActiveMainWindow;
+ }
+
+public slots:
+ void slotEditSegment(Segment*);
+ void slotEditSegmentNotation(Segment*);
+ void slotEditSegmentsNotation(std::vector<Segment*>);
+ void slotEditSegmentMatrix(Segment*);
+ void slotEditSegmentsMatrix(std::vector<Segment*>);
+ void slotEditSegmentPercussionMatrix(Segment*);
+ void slotEditSegmentsPercussionMatrix(std::vector<Segment*>);
+ void slotEditSegmentEventList(Segment*);
+ void slotEditSegmentsEventList(std::vector<Segment*>);
+ void slotEditTriggerSegment(int);
+ void slotEditSegmentAudio(Segment*);
+ void slotSegmentAutoSplit(Segment*);
+ void slotEditRepeat(Segment*, timeT);
+/* hjj: WHAT DO DO WITH THIS ?
+ void slotEditMetadata(QString);
+*/
+
+ /**
+ * Highlight all the Segments on a Track because the Track has
+ * been selected * We have to ensure we create a Selector object
+ * before we can highlight * these tracks.
+ *
+ * Called by signal from Track selection routine to highlight
+ * all available Segments on a Track
+ */
+ void slotSelectTrackSegments(int);
+
+ void slotSelectAllSegments();
+
+ void slotUpdateInstrumentParameterBox(int id);
+
+ // This is called from the canvas (actually the selector tool) moving out
+ //
+ void slotSelectedSegments(const SegmentSelection &segments);
+
+ // And this one from the user interface going down
+ //
+ void slotPropagateSegmentSelection(const SegmentSelection &segments);
+
+ void slotShowRulers(bool);
+
+ void slotShowTempoRuler(bool);
+
+ void slotShowChordNameRuler(bool);
+
+ void slotShowPreviews(bool);
+
+ void slotShowSegmentLabels(bool);
+
+ void slotAddTracks(unsigned int count, InstrumentId instrument, int position);
+
+ void slotDeleteTracks(std::vector<TrackId> tracks);
+
+ void slotAddAudioSegmentCurrentPosition(AudioFileId,
+ const RealTime &startTime,
+ const RealTime &endTime);
+
+ void slotAddAudioSegmentDefaultPosition(AudioFileId,
+ const RealTime &startTime,
+ const RealTime &endTime);
+
+ void slotAddAudioSegment(AudioFileId audioId,
+ TrackId trackId,
+ timeT position,
+ const RealTime &startTime,
+ const RealTime &endTime);
+
+ void slotDroppedAudio(QString audioDesc);
+ void slotDroppedNewAudio(QString audioDesc);
+
+ /*
+ * Commands
+ *
+ */
+ void slotAddCommandToHistory(KCommand *command);
+
+ /*
+ * Change the Instrument Label
+ */
+ void slotChangeInstrumentLabel(InstrumentId id, QString label);
+
+ /*
+ * Change the Track Label
+ */
+ void slotChangeTrackLabel(TrackId id, QString label);
+
+ /*
+ * Set the mute button on the track buttons and on the instrument
+ * parameter box
+ */
+ void slotSetMuteButton(TrackId track, bool value);
+
+ /*
+ * Set mute, record and solo by instrument id
+ */
+ void slotSetMute(InstrumentId, bool);
+ void slotSetRecord(InstrumentId, bool);
+ void slotSetSolo(InstrumentId, bool);
+
+ /**
+ * To indicate that we should track the recording segment (despite
+ * no commands being issued on it)
+ */
+ void slotUpdateRecordingSegment(Segment *segment,
+ timeT updatedFrom);
+
+ /**
+ * A manual fudgy way of creating a view update for certain
+ * semi-static data (devices/instrument labels mainly)
+ */
+ void slotSynchroniseWithComposition();
+
+ /**
+ * To indicate that an edit view, mixer, etc (something that might
+ * want to receive MIDI input) has become active. We only send
+ * inputs such as MIDI to a single one of these, in most cases,
+ * and it's whichever was most recently made active. (It doesn't
+ * have to still _be_ active -- we want to allow moving focus to
+ * another application entirely but still receiving MIDI etc in
+ * Rosegarden.)
+ */
+ void slotActiveMainWindowChanged(const QWidget *);
+ void slotActiveMainWindowChanged(); // uses sender()
+
+ /**
+ * An event has been received from a device connected to the
+ * external controller port.
+ */
+ void slotControllerDeviceEventReceived(MappedEvent *);
+ void slotControllerDeviceEventReceived(MappedEvent *, const void *);
+
+signals:
+ void activateTool(QString toolName);
+
+ void stateChange(QString, bool);
+
+ // Inform that we've got a SegmentSelection
+ //
+ void segmentsSelected(const SegmentSelection&);
+
+ void toggleSolo(bool);
+
+ /**
+ * Current used to dispatch things like track select changes, solo, etc...
+ * to edit views
+ */
+ void compositionStateUpdate();
+
+
+ /**
+ * This signal is used to dispatch a notification for a request to
+ * set the step-by-step-editing target window to all candidate targets,
+ * so that they can either know that their request has been granted
+ * (if they match the QObject passed) or else deactivate any step-by-
+ * step editing currently active in their own window (otherwise).
+ */
+ void stepByStepTargetRequested(QObject *);
+
+ /*
+ * Add an audio file at the sequencer - when we drop a new file
+ * on the segment canvas.
+ */
+ void addAudioFile(AudioFileId);
+
+ void checkTrackAssignments();
+
+ void instrumentLevelsChanged(InstrumentId,
+ const LevelInfo &);
+
+ void controllerDeviceEventReceived(MappedEvent *,
+ const void *);
+
+ void instrumentParametersChanged(InstrumentId);
+
+protected:
+ NotationView *createNotationView(std::vector<Segment *>);
+ MatrixView *createMatrixView (std::vector<Segment *>, bool drumMode);
+ EventView *createEventView (std::vector<Segment *>);
+
+ virtual void windowActivationChange(bool);
+
+ //--------------- Data members ---------------------------------
+
+ SimpleRulerScale *m_rulerScale;
+ TrackEditor *m_trackEditor;
+
+ SegmentParameterBox *m_segmentParameterBox;
+ InstrumentParameterBox *m_instrumentParameterBox;
+ TrackParameterBox *m_trackParameterBox;
+
+ static const QWidget *m_lastActiveMainWindow;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/application/RosegardenIface.cpp b/src/gui/application/RosegardenIface.cpp
new file mode 100644
index 0000000..7e07f14
--- /dev/null
+++ b/src/gui/application/RosegardenIface.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Parts of this file are from KDE Konqueror : KonqMainWindowIface
+ Copyright (C) 2000 Simon Hausmann <hausmann@kde.org>
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ 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 "RosegardenIface.h"
+
+#include "sound/MappedComposition.h"
+#include <dcopobject.h>
+#include <dcopref.h>
+#include <kaction.h>
+#include <kdcopactionproxy.h>
+#include <kmainwindow.h>
+#include <qcstring.h>
+#include <qstring.h>
+#include <qvaluelist.h>
+#include <kapplication.h>
+#include <dcopclient.h>
+
+
+namespace Rosegarden
+{
+
+RosegardenIface::RosegardenIface(KMainWindow* mainWindow)
+ : DCOPObject(mainWindow->name()),
+ m_dcopActionProxy(0)
+{}
+
+void RosegardenIface::iFaceDelayedInit(KMainWindow* mainWindow)
+{
+ m_dcopActionProxy = new KDCOPActionProxy(mainWindow->actionCollection(),
+ this);
+}
+
+DCOPRef RosegardenIface::action(const QCString &name)
+{
+ return DCOPRef(kapp->dcopClient()->appId(),
+ m_dcopActionProxy->actionObjectId(name));
+}
+
+QCStringList RosegardenIface::actions()
+{
+ QCStringList res;
+ QValueList<KAction *> lst = m_dcopActionProxy->actions();
+ QValueList<KAction *>::ConstIterator it = lst.begin();
+ QValueList<KAction *>::ConstIterator end = lst.end();
+ for (; it != end; ++it )
+ res.append( (*it)->name() );
+
+ return res;
+}
+
+QMap<QCString,DCOPRef> RosegardenIface::actionMap()
+{
+ return m_dcopActionProxy->actionMap();
+}
+
+}
diff --git a/src/gui/application/RosegardenIface.h b/src/gui/application/RosegardenIface.h
new file mode 100644
index 0000000..baa8b4e
--- /dev/null
+++ b/src/gui/application/RosegardenIface.h
@@ -0,0 +1,130 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Parts of this file are from KDE Konqueror : KonqMainWindowIface
+ Copyright (C) 2000 Simon Hausmann <hausmann@kde.org>
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ 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_ROSEGARDENIFACE_H_
+#define _RG_ROSEGARDENIFACE_H_
+
+#include <dcopobject.h>
+#include <dcopref.h>
+#include <qmap.h>
+#include <qstring.h>
+#include <qvaluevector.h>
+
+#include "base/Instrument.h"
+#include "sound/MappedComposition.h"
+
+class QCString;
+class KMainWindow;
+class KDCOPActionProxy;
+
+
+namespace Rosegarden
+{
+
+
+/**
+ * RosegardenGUI DCOP Interface
+ */
+class RosegardenIface : virtual public DCOPObject
+{
+ K_DCOP
+
+public:
+ RosegardenIface(KMainWindow*);
+ void iFaceDelayedInit(KMainWindow*);
+
+k_dcop:
+ virtual void openFile(QString file) = 0;
+ virtual void openURL(QString url) = 0;
+ virtual void mergeFile(QString file) = 0;
+ virtual void fileNew() = 0;
+ virtual void fileSave() = 0;
+ virtual void fileClose() = 0;
+ virtual void quit() = 0;
+
+ virtual void play() = 0;
+ virtual void stop() = 0;
+ virtual void rewind() = 0;
+ virtual void fastForward() = 0;
+ virtual void record() = 0;
+ virtual void rewindToBeginning() = 0;
+ virtual void fastForwardToEnd() = 0;
+ virtual void jumpToTime(int sec, int usec) = 0;
+ virtual void startAtTime(int sec, int usec) = 0;
+
+ // Extra functions used by Infrared Remotes
+ virtual void trackDown() = 0;
+ virtual void trackUp() = 0;
+ virtual void toggleMutedCurrentTrack() = 0;
+ virtual void toggleRecordCurrentTrack() = 0;
+
+ // Sequencer updates GUI with status
+ //
+ virtual void notifySequencerStatus(int status) = 0;
+
+ // Used to map unexpected (async) MIDI events to the user interface.
+ // We can show these on the Transport or on a MIDI Mixer.
+ //
+ virtual void processAsynchronousMidi(const MappedComposition &mC) = 0;
+
+ // The sequencer tries to call this action until it can - then
+ // we can go on and retrive device information
+ //
+ virtual void alive() = 0;
+
+ // The sequencer requests that a new audio file is created - the
+ // gui does so and returns the path of the new file so that the
+ // sequencer can use it.
+ //
+ virtual QString createNewAudioFile() = 0;
+ virtual QValueVector<QString> createRecordAudioFiles
+ (const QValueVector<InstrumentId> &recordInstruments) = 0;
+ virtual QString getAudioFilePath() = 0;
+
+ virtual QValueVector<InstrumentId> getArmedInstruments() = 0;
+
+ virtual void showError(QString error) = 0;
+
+ // Actions proxy
+ //
+ DCOPRef action( const QCString &name );
+ QCStringList actions();
+ QMap<QCString,DCOPRef> actionMap();
+
+protected:
+ //--------------- Data members ---------------------------------
+
+ KDCOPActionProxy *m_dcopActionProxy;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/application/SetWaitCursor.cpp b/src/gui/application/SetWaitCursor.cpp
new file mode 100644
index 0000000..7fc0053
--- /dev/null
+++ b/src/gui/application/SetWaitCursor.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "SetWaitCursor.h"
+
+#include "gui/editors/segment/TrackEditor.h"
+#include "gui/editors/segment/segmentcanvas/CompositionView.h"
+#include "misc/Debug.h"
+#include "RosegardenGUIApp.h"
+#include "RosegardenGUIView.h"
+#include <kcursor.h>
+#include <qcursor.h>
+#include <qwidget.h>
+#include <kapplication.h>
+
+namespace Rosegarden
+{
+
+SetWaitCursor::SetWaitCursor()
+ : m_guiApp(dynamic_cast<RosegardenGUIApp*>(kapp->mainWidget()))
+{
+ if (m_guiApp) {
+
+ // play it safe, so we can use this class at anytime even very early in the app init
+ if ((m_guiApp->getView() &&
+ m_guiApp->getView()->getTrackEditor() &&
+ m_guiApp->getView()->getTrackEditor()->getSegmentCanvas() &&
+ m_guiApp->getView()->getTrackEditor()->getSegmentCanvas()->viewport())) {
+
+ m_saveSegmentCanvasCursor = m_guiApp->getView()->getTrackEditor()->getSegmentCanvas()->viewport()->cursor();
+
+ }
+
+ RG_DEBUG << "SetWaitCursor::SetWaitCursor() : setting waitCursor\n";
+ m_saveCursor = m_guiApp->cursor();
+
+ m_guiApp->setCursor(KCursor::waitCursor());
+ }
+}
+
+SetWaitCursor::~SetWaitCursor()
+{
+ if (m_guiApp) {
+
+ RG_DEBUG << "SetWaitCursor::SetWaitCursor() : restoring normal cursor\n";
+ QWidget* viewport = 0;
+ QCursor currentSegmentCanvasCursor;
+
+ if ((m_guiApp->getView() &&
+ m_guiApp->getView()->getTrackEditor() &&
+ m_guiApp->getView()->getTrackEditor()->getSegmentCanvas() &&
+ m_guiApp->getView()->getTrackEditor()->getSegmentCanvas()->viewport())) {
+ viewport = m_guiApp->getView()->getTrackEditor()->getSegmentCanvas()->viewport();
+ currentSegmentCanvasCursor = viewport->cursor();
+ }
+
+ m_guiApp->setCursor(m_saveCursor);
+
+ if (viewport) {
+ if (currentSegmentCanvasCursor.shape() == KCursor::waitCursor().shape()) {
+ viewport->setCursor(m_saveSegmentCanvasCursor);
+ } else {
+ viewport->setCursor(currentSegmentCanvasCursor); // because m_guiApp->setCursor() has replaced it
+ }
+ }
+
+ // otherwise, it's been modified elsewhere, so leave it as is
+
+ }
+
+}
+
+}
diff --git a/src/gui/application/SetWaitCursor.h b/src/gui/application/SetWaitCursor.h
new file mode 100644
index 0000000..38687c5
--- /dev/null
+++ b/src/gui/application/SetWaitCursor.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_SETWAITCURSOR_H_
+#define _RG_SETWAITCURSOR_H_
+
+#include <qcursor.h>
+
+
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIApp;
+
+
+/**
+ * Temporarily change the global cursor to waitCursor
+ */
+class SetWaitCursor
+{
+public:
+ SetWaitCursor();
+ ~SetWaitCursor();
+
+protected:
+ RosegardenGUIApp* m_guiApp;
+ QCursor m_saveCursor;
+ QCursor m_saveSegmentCanvasCursor;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/application/StartupTester.cpp b/src/gui/application/StartupTester.cpp
new file mode 100644
index 0000000..15940b6
--- /dev/null
+++ b/src/gui/application/StartupTester.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "StartupTester.h"
+
+#include "misc/Debug.h"
+#include "gui/dialogs/LilyPondOptionsDialog.h"
+
+#include <kprocess.h>
+#include <qmutex.h>
+#include <qthread.h>
+#include <qregexp.h>
+
+
+namespace Rosegarden
+{
+
+StartupTester::StartupTester() :
+ m_ready(false),
+ m_haveProjectPackager(false),
+ m_haveLilyPondView(false),
+ m_haveAudioFileImporter(false)
+{
+ QHttp *http = new QHttp();
+ connect(http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)),
+ this, SLOT(slotHttpResponseHeaderReceived(const QHttpResponseHeader &)));
+ connect(http, SIGNAL(done(bool)),
+ this, SLOT(slotHttpDone(bool)));
+ m_versionHttpFailed = false;
+ http->setHost("www.rosegardenmusic.com");
+ http->get("/latest-version.txt");
+}
+
+StartupTester::~StartupTester()
+{
+}
+
+void
+StartupTester::run()
+{
+ m_projectPackagerMutex.lock();
+ m_lilyPondViewMutex.lock();
+ m_audioFileImporterMutex.lock();
+ m_ready = true;
+
+ KProcess *proc = new KProcess();
+ m_stdoutBuffer = "";
+ QObject::connect(proc, SIGNAL(receivedStdout(KProcess *, char *, int)),
+ this, SLOT(stdoutReceived(KProcess *, char *, int)));
+ *proc << "rosegarden-audiofile-importer";
+ *proc << "--conftest";
+ proc->start(KProcess::Block, KProcess::All);
+ if (!proc->normalExit() || proc->exitStatus()) {
+ RG_DEBUG << "StartupTester - No audio file importer available" << endl;
+ m_haveAudioFileImporter = false;
+ parseStdoutBuffer(m_audioFileImporterMissing);
+ } else {
+ RG_DEBUG << "StartupTester - Audio file importer OK" << endl;
+ m_haveAudioFileImporter = true;
+ }
+ delete proc;
+ m_audioFileImporterMutex.unlock();
+
+ proc = new KProcess;
+ m_stdoutBuffer = "";
+ QObject::connect(proc, SIGNAL(receivedStdout(KProcess *, char *, int)),
+ this, SLOT(stdoutReceived(KProcess *, char *, int)));
+ *proc << "rosegarden-project-package";
+ *proc << "--conftest";
+ proc->start(KProcess::Block, KProcess::All);
+ if (!proc->normalExit() || proc->exitStatus()) {
+ m_haveProjectPackager = false;
+ // rosegarden-project-package ran but exited with an error code
+ RG_DEBUG << "StartupTester - No project packager available" << endl;
+ m_haveProjectPackager = false;
+ parseStdoutBuffer(m_projectPackagerMissing);
+ } else {
+ RG_DEBUG << "StartupTester - Project packager OK" << endl;
+ m_haveProjectPackager = true;
+ }
+ delete proc;
+ m_projectPackagerMutex.unlock();
+
+ proc = new KProcess();
+ m_stdoutBuffer = "";
+ QObject::connect(proc, SIGNAL(receivedStdout(KProcess *, char *, int)),
+ this, SLOT(stdoutReceived(KProcess *, char *, int)));
+ *proc << "rosegarden-lilypondview";
+ *proc << "--conftest";
+ proc->start(KProcess::Block, KProcess::All);
+ if (!proc->normalExit() || proc->exitStatus()) {
+ RG_DEBUG << "StartupTester - No lilypondview available" << endl;
+ m_haveLilyPondView = false;
+ parseStdoutBuffer(m_lilyPondViewMissing);
+ } else {
+ RG_DEBUG << "StartupTester - lilypondview OK" << endl;
+ m_haveLilyPondView = true;
+ QRegExp re("LilyPond version: ([^\n]*)");
+ if (re.search(m_stdoutBuffer) != -1) {
+ LilyPondOptionsDialog::setDefaultLilyPondVersion(re.cap(1));
+ }
+ }
+ delete proc;
+ m_lilyPondViewMutex.unlock();
+}
+
+bool
+StartupTester::isReady()
+{
+ while (!m_ready)
+ usleep(10000);
+ if (m_projectPackagerMutex.tryLock()) {
+ m_projectPackagerMutex.unlock();
+ } else {
+ return false;
+ }
+ if (m_lilyPondViewMutex.tryLock()) {
+ m_lilyPondViewMutex.unlock();
+ } else {
+ return false;
+ }
+ return true;
+}
+
+void
+StartupTester::stdoutReceived(KProcess *, char *buffer, int len)
+{
+ m_stdoutBuffer += QString::fromLatin1(buffer, len);
+}
+
+void
+StartupTester::parseStdoutBuffer(QStringList &target)
+{
+ QRegExp re("Required: ([^\n]*)");
+ if (re.search(m_stdoutBuffer) != -1) {
+ target = QStringList::split(", ", re.cap(1));
+ }
+}
+
+bool
+StartupTester::haveProjectPackager(QStringList *missing)
+{
+ while (!m_ready)
+ usleep(10000);
+ QMutexLocker locker(&m_projectPackagerMutex);
+ if (missing) *missing = m_projectPackagerMissing;
+ return m_haveProjectPackager;
+}
+
+bool
+StartupTester::haveLilyPondView(QStringList *missing)
+{
+ while (!m_ready)
+ usleep(10000);
+ QMutexLocker locker(&m_lilyPondViewMutex);
+ if (missing) *missing = m_lilyPondViewMissing;
+ return m_haveLilyPondView;
+}
+
+bool
+StartupTester::haveAudioFileImporter(QStringList *missing)
+{
+ while (!m_ready)
+ usleep(10000);
+ QMutexLocker locker(&m_audioFileImporterMutex);
+ if (missing) *missing = m_audioFileImporterMissing;
+ return m_haveAudioFileImporter;
+}
+
+bool
+StartupTester::isVersionNewerThan(QString a, QString b)
+{
+ QRegExp re("[._-]");
+ QStringList alist = QStringList::split(re, a);
+ QStringList blist = QStringList::split(re, b);
+ int ae = alist.size();
+ int be = blist.size();
+ int e = std::max(ae, be);
+ for (int i = 0; i < e; ++i) {
+ int an = 0, bn = 0;
+ if (i < ae) {
+ an = alist[i].toInt();
+ if (an == 0) an = -1; // non-numeric field -> "-pre1" etc
+ }
+ if (i < be) {
+ bn = blist[i].toInt();
+ if (bn == 0) bn = -1;
+ }
+ if (an < bn) return false;
+ if (an > bn) return true;
+ }
+ return false;
+}
+
+void
+StartupTester::slotHttpResponseHeaderReceived(const QHttpResponseHeader &h)
+{
+ if (h.statusCode() / 100 != 2) m_versionHttpFailed = true;
+}
+
+void
+StartupTester::slotHttpDone(bool error)
+{
+ QHttp *http = const_cast<QHttp *>(dynamic_cast<const QHttp *>(sender()));
+ if (!http) return;
+ http->deleteLater();
+ if (error) return;
+
+ QByteArray responseData = http->readAll();
+ QString str = QString::fromUtf8(responseData.data());
+ QStringList lines = QStringList::split('\n', str);
+ if (lines.empty()) return;
+
+ QString latestVersion = lines[0];
+ std::cerr << "Comparing current version \"" << VERSION
+ << "\" with latest version \"" << latestVersion << "\""
+ << std::endl;
+ if (isVersionNewerThan(latestVersion, VERSION)) {
+ emit newerVersionAvailable(latestVersion);
+ }
+}
+
+}
+
+#include "StartupTester.moc"
+
diff --git a/src/gui/application/StartupTester.h b/src/gui/application/StartupTester.h
new file mode 100644
index 0000000..9c82e07
--- /dev/null
+++ b/src/gui/application/StartupTester.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_STARTUPTESTER_H_
+#define _RG_STARTUPTESTER_H_
+
+#include <qmutex.h>
+#include <qthread.h>
+#include <qstringlist.h>
+#include <qobject.h>
+#include <qhttp.h>
+
+class KProcess;
+
+namespace Rosegarden
+{
+
+class StartupTester : public QObject, public QThread
+{
+ Q_OBJECT
+
+public:
+ StartupTester();
+ virtual ~StartupTester();
+
+ virtual void run();
+
+ bool isReady();
+
+ // If you call one of these methods before the startup test has
+ // completed in the background, then it will block.
+
+ bool haveProjectPackager(QStringList *missingApplications);
+ bool haveLilyPondView(QStringList *missingApplications);
+ bool haveAudioFileImporter(QStringList *missingApplications);
+
+signals:
+ void newerVersionAvailable(QString);
+
+protected slots:
+ void stdoutReceived(KProcess *, char *, int);
+
+ void slotHttpResponseHeaderReceived(const QHttpResponseHeader &);
+ void slotHttpDone(bool);
+
+protected:
+ bool m_ready;
+ QMutex m_projectPackagerMutex;
+ QMutex m_lilyPondViewMutex;
+ QMutex m_audioFileImporterMutex;
+ bool m_haveProjectPackager;
+ QStringList m_projectPackagerMissing;
+ bool m_haveLilyPondView;
+ QStringList m_lilyPondViewMissing;
+ bool m_haveAudioFileImporter;
+ QStringList m_audioFileImporterMissing;
+ QString m_stdoutBuffer;
+ bool m_versionHttpFailed;
+ void parseStdoutBuffer(QStringList &target);
+ bool isVersionNewerThan(QString, QString);
+};
+
+
+}
+
+#endif
diff --git a/src/gui/application/main.cpp b/src/gui/application/main.cpp
new file mode 100644
index 0000000..d7b5779
--- /dev/null
+++ b/src/gui/application/main.cpp
@@ -0,0 +1,741 @@
+// -*- c-basic-offset: 4 -*-
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <bownie@bownie.com>
+
+ 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 <qtimer.h>
+#include <kapplication.h>
+#include <sys/time.h>
+#include "base/RealTime.h"
+
+#include <kcmdlineargs.h>
+#include <kaboutdata.h>
+#include <klocale.h>
+#include <dcopclient.h>
+#include <kconfig.h>
+#include <kmessagebox.h>
+#include <kstddirs.h>
+#include <ktip.h>
+#include <kprocess.h>
+#include <kglobalsettings.h>
+
+#include <qstringlist.h>
+#include <qregexp.h>
+#include <qvbox.h>
+#include <qlabel.h>
+
+#include "document/ConfigGroups.h"
+#include "misc/Strings.h"
+#include "misc/Debug.h"
+#include "gui/application/RosegardenGUIApp.h"
+#include "gui/widgets/CurrentProgressDialog.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/kdeext/KStartupLogo.h"
+
+#include "gui/application/RosegardenApplication.h"
+#include "gui/application/RosegardenDCOP.h"
+
+#include "gui/kdeext/klearlook.h"
+
+using namespace Rosegarden;
+
+/*! \mainpage Rosegarden global design
+
+Rosegarden is split into 3 main parts:
+
+\section base Base
+
+The base library holds all of the fundamental "music handling"
+structures, of which the primary ones are Event, Segment, Track,
+Instrument and Composition. It also contains a selection of utility
+and helper classes of a kind that is not specific to any particular
+GUI. Everything here is part of the Rosegarden namespace, and there
+are no dependencies on KDE or Qt (although it uses the STL heavily).
+
+The keyword for the basic structures in use is "flexibility". Our
+Event objects can be extended arbitrarily for the convenience of GUI
+or performance code without having to change their declaration or
+modify anything in the base library. And most of our assumptions
+about the use of the container classes can be violated without
+disastrous side-effects.
+
+\subsection musicstructs Music Structures
+
+ - \link Event Event\endlink is the basic musical element. It's more or less a
+ generalization of the MIDI event. Each note or rest, each key
+ change or tempo change, is an event: there's no "note class" or
+ "rest class" as such, they are simply represented by events whose
+ type happens to be "note" or "rest".
+ Each Event has a type code, absolute time (the moment at which the
+ Event starts, relative only to the start of the Composition) and
+ duration (usually non-zero only for notes and rests), together
+ with an arbitrary set of named and typed properties that can be
+ assigned and queried dynamically by other parts of the
+ application. So, for example, a note event is likely to have an
+ integer property called "pitch", and probably a "velocity", as
+ well as potentially many others -- but this is not fixed anywhere,
+ and there's no definition of what exactly a note is: client code
+ is simply expected to ignore any unrecognised events or properties
+ and to cope if properties that should be there are not.
+
+ - \link Segment Segment\endlink is a series of consecutive Events found on the same Track,
+ automatically ordered by their absolute time. It's the usual
+ container for Events. A Segment has a starting time that can be
+ changed, and a duration that is based solely on the end time of
+ the last Event it contains. Note that in order to facilitate
+ musical notation editing, we explicitly store silences as series
+ of rest Events; thus a Segment really should contain no gaps
+ between its Events. (This isn't checked anywhere and nothing will
+ break very badly if there are gaps, but notation won't quite work
+ correctly.)
+
+ - \link Track Track \endlink is much the same thing as on a mixing table, usually
+ assigned to an instrument, a voice, etc. Although a Track is not
+ a container of Events and is not strictly a container of Segments
+ either, it is referred to by a set of Segments that are therefore
+ mutually associated with the same instruments and parameters. In
+ GUI terms, the Track is a horizontal row on the main Rosegarden
+ window, whereas a Segment is a single blue box within that row, of
+ which there may be any number.
+
+ - \link Instrument Instrument \endlink corresponds broadly to a MIDI or Audio channel, and is
+ the destination for a performed Event. Each Track is mapped to a
+ single Instrument (although many Tracks may have the same
+ Instrument), and the Instrument is indicated in the header at the
+ left of the Track's row in the GUI.
+
+ - \link Composition Composition\endlink is the container for the entire piece of music. It
+ consists of a set of Segments, together with a set of Tracks that
+ the Segments may or may not be associated with, a set of
+ Instruments, and some information about time signature and tempo
+ changes. (The latter are not stored in Segments; they are only
+ stored in the top-level Composition. You can't have differing
+ time signatures or tempos in different Segments.) Any code that
+ wants to know about the locations of bar lines, or request
+ real-time calculations based on tempo changes, talks to the
+ Composition.
+
+
+See also docs/data_struct/units.txt for an explanation of the units we
+use for time and pitch values. See docs/discussion/names.txt for some
+name-related discussion. See docs/code/creating_events.txt for an
+explanation of how to create new Events and add properties to them.
+
+The base directory also contains various music-related helper classes:
+
+ - The NotationTypes.[Ch] files contain classes that help with
+ creating and manipulating events. It's very important to realise
+ that these classes are not the events themselves: although there
+ is a Note class in this file, and a TimeSignature class, and Clef
+ and Key classes, instances of these are rarely stored anywhere.
+ Instead they're created on-the-fly in order to do calculation
+ related to note durations or time signatures or whatever, and they
+ contain getAsEvent() methods that may be used when an event for
+ storage is required. But the class of a stored event is always
+ simply Event.
+
+ The NotationTypes classes also define important constants for the
+ names of common properties in Events. For example, the Note class
+ contains Note::EventType, which is the type of a note Event, and
+ Note::EventRestType, the type of a rest Event; and Key contains
+ Key::EventType, the type of a key change Event, KeyPropertyName,
+ the name of the property that defines the key change, and a set
+ of the valid strings for key changes.
+
+ - BaseProperties.[Ch] contains a set of "standard"-ish Event
+ property names that are not basic enough to go in NotationTypes.
+
+ - \link SegmentNotationHelper SegmentNotationHelper\endlink
+ and \link SegmentPerformanceHelper SegmentPerformanceHelper\endlink
+ do tasks that
+ may be useful to notation-type code and performer code
+ respectively. For example, SegmentNotationHelper is used to
+ manage rests when inserting and deleting notes in a score editor,
+ and to create beamed groups and suchlike; SegmentPerformanceHelper
+ generally does calculations involving real performance time of
+ notes (taking into account tied notes, tuplets and tempo changes).
+ These two lightweight helper classes are also usually constructed
+ on-the-fly for use on the events in a given Segment and then
+ discarded after use.
+
+ - \link Quantizer Quantizer\endlink is used to quantize event timings and set quantized
+ timing properties on those events. Note that quantization is
+ non-destructive, as it takes advantage of the ability to set new
+ Event properties to simply assign the quantized values as separate
+ properties from the original absolute time and duration.
+
+
+\section gui GUI
+
+The GUI directory builds into a KDE/Qt application. Like most KDE
+applications, it follows a document/view model. The document (class
+RosegardenGUIDoc, which wraps a Composition) can have several views
+(class RosegardenGUIView), although at the moment only a single one is
+used.
+
+This view is the TrackEditor, which shows all the Composition's
+Segments organized in Tracks. Each Segment can be edited in two ways:
+notation (score) or matrix (piano roll).
+
+All editor views are derived from EditView. An EditView is the class
+dealing with the edition per se of the events. It uses several
+components:
+
+ - Layout classes, horizontal and vertical: these are the classes
+ which determine the x and y coordinates of the graphic items
+ representing the events (notes or piano-roll rectangles). They
+ are derived from the LayoutEngine base-class in the base library.
+
+ - Tools, which implement each editing function at the GUI (such as
+ insert, erase, cut and paste). These are the tools which appear on
+ the EditView's toolbar.
+
+ - Toolbox, which is a simple string => tool map.
+
+ - Commands, which are the fundamental implementations of editing
+ operations (both menu functions and tool operations) subclassed
+ from KDE's Command and used for undo and redo.
+
+ - a canvas view. Although this isn't a part of the EditView's
+ definition, both of the existing edit views (notation and matrix)
+ use one, because they both use a QCanvas to represent data.
+
+ - LinedStaff, a staff with lines. Like the canvas view, this isn't
+ part of the EditView definition, but both views use one.
+
+
+There are currently two editor views:
+
+ - NotationView, with accompanying classes NotationHLayout,
+ NotationVLayout, NotationStaff, and all the classes in the
+ notationtool and notationcommands files. These are also closely
+ associated with the NotePixmapFactory and NoteFont classes, which
+ are used to generate notes from component pixmap files.
+
+ - MatrixView, with accompanying classes MatrixHLayout,
+ MatrixVLayout, MatrixStaff and other classes in the matrixview
+ files.
+
+The editing process works as follows:
+
+[NOTE : in the following, we're talking both about events as UI events
+or user events (mouse button clicks, mouse move, keystrokes, etc...)
+and Events (our basic music element). To help lift the ambiguity,
+"events" is for UI events, Events is for Event.]
+
+ -# The canvas view gets the user events (see
+ NotationCanvasView::contentsMousePressEvent(QMouseEvent*) for an
+ example). It locates where the event occured in terms of musical
+ element: which note or staff line the user clicked on, which pitch
+ and time this corresponds to, that kind of stuff. (In the
+ Notation and Matrix views, the LinedStaff calculates mappings
+ between coordinates and staff lines: the former is especially
+ complicated because of its support for page layout.)\n
+ -# The canvas view transmits this kind of info as a signal, which is
+ connected to a slot in the parent EditView.
+ -# The EditView delegates action to the current tool.\n
+ -# The tool performs the actual job (inserting or deleting a note,
+ etc...).
+
+Since this action is usually complex (merely inserting a note requires
+dealing with the surrounding Events, rests or notes), it does it
+through a SegmentHelper (for instance, base/SegmentNotationHelper)
+which "wraps" the complexity into simple calls and performs all the
+hidden tasks.
+
+The EditView also maintains (obviously) its visual appearance with the
+layout classes, applying them when appropriate.
+
+\section sequencer Sequencer
+
+The sequencer directory also builds into a KDE/Qt application, but one
+which doesn't have a gui. The Sequencer can be started automatically
+by the main Rosegarden GUI or manually if testing - it's sometimes
+more convenient to do the latter as the Sequencer needs to be connected
+up to the underlying sound system every time it is started.
+
+The Sequencer interfaces directly with \link AlsaDriver ALSA\endlink
+and provides MIDI "play" and "record" ports which can be connected to
+other MIDI clients (MIDI IN and OUT hardware ports or ALSA synth devices)
+using any ALSA MIDI Connection Manager. The Sequencer also supports
+playing and recording of Audio sample files using \link JackDriver Jack\endlink
+
+The GUI and Sequencer communicate using the KDE DCOP communication framework.
+Look in:
+ - \link rosegardenguiiface.h gui/rosegardenguiiface.h\endlink
+ - \link rosegardensequenceriface.h sequencer/rosegardensequenceriface.h\endlink
+
+for definitions of the DCOP interfaces pertinent to the Sequencer
+and GUI. The main DCOP operations from the GUI involve starting and
+stopping the Sequencer, playing and recording, fast forwarding and
+rewinding. Once a play or record cycle is enabled it's the Sequencer
+that does most of the hard work. Events are read from (or written to, when recording)
+a set of mmapped files.
+
+The Sequencer makes use of two libraries libRosegardenSequencer
+and libRosegardenSound:
+
+ - libRosegardenSequencer holds everything pertinent to sequencing
+ for Rosegarden including the
+ Sequencer class itself. This library is only linked into the
+ Rosegarden Sequencer.
+
+ - libRosegardenSound holds the MidiFile class (writing and reading
+ MIDI files) and the MappedEvent and MappedComposition classes (the
+ communication class for transferring events back and forth across
+ DCOP). This library is needed by the GUI as well as the Sequencer.
+
+The main Sequencer state machine is a good starting point and clearly
+visible at the bottom of rosegarden/sequencer/main.cpp.
+
+
+*/
+
+static const char *description =
+ I18N_NOOP("Rosegarden - A sequencer and musical notation editor");
+
+static KCmdLineOptions options[] =
+ {
+ { "nosequencer", I18N_NOOP("Don't use the sequencer (support editing only)"), 0 },
+ { "nosplash", I18N_NOOP("Don't show the splash screen"), 0 },
+ { "nofork", I18N_NOOP("Don't automatically run in the background"), 0 },
+ { "existingsequencer", I18N_NOOP("Attach to a running sequencer process, if found"), 0 },
+ { "ignoreversion", I18N_NOOP("Ignore installed version - for devs only"), 0 },
+ { "+[File]", I18N_NOOP("file to open"), 0 },
+ { 0, 0, 0 }
+ };
+
+
+// -----------------------------------------------------------------
+
+#ifdef Q_WS_X11
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#include <X11/SM/SMlib.h>
+
+static int _x_errhandler( Display *dpy, XErrorEvent *err )
+{
+ char errstr[256];
+ XGetErrorText( dpy, err->error_code, errstr, 256 );
+ if ( err->error_code != BadWindow )
+ kdWarning() << "Rosegarden: detected X Error: " << errstr << " " << err->error_code
+ << "\n Major opcode: " << err->request_code << endl;
+ return 0;
+}
+#endif
+
+// NOTE: to get a dump of the stack trace from KDE during program execution:
+// std::cerr << kdBacktrace() << std::endl
+// (see kdebug.h)
+
+void testInstalledVersion()
+{
+ QString versionLocation = locate("appdata", "version.txt");
+ QString installedVersion;
+
+ if (versionLocation) {
+ QFile versionFile(versionLocation);
+ if (versionFile.open(IO_ReadOnly)) {
+ QTextStream text(&versionFile);
+ QString s = text.readLine().stripWhiteSpace();
+ versionFile.close();
+ if (s) {
+ if (s == VERSION) return;
+ installedVersion = s;
+ }
+ }
+ }
+
+ if (installedVersion) {
+
+ KMessageBox::detailedError
+ (0,
+ i18n("Installation contains the wrong version of Rosegarden."),
+ i18n(" The wrong versions of Rosegarden's data files were\n"
+ " found in the standard KDE installation directories.\n"
+ " (I am %1, but the installed files are for version %2.)\n\n"
+ " This may mean one of the following:\n\n"
+ " 1. This is a new upgrade of Rosegarden, and it has not yet been\n"
+ " installed. If you compiled it yourself, check that you have\n"
+ " run \"make install\" and that the procedure completed\n"
+ " successfully.\n\n"
+ " 2. The upgrade was installed in a non-standard directory,\n"
+ " and an old version was found in a standard directory. If so,\n"
+ " you will need to add the correct directory to your KDEDIRS\n"
+ " environment variable before you can run it.").arg(VERSION).arg(installedVersion),
+ i18n("Installation problem"));
+
+ } else {
+
+ KMessageBox::detailedError
+ (0,
+ i18n("Rosegarden does not appear to have been installed."),
+ i18n(" One or more of Rosegarden's data files could not be\n"
+ " found in the standard KDE installation directories.\n\n"
+ " This may mean one of the following:\n\n"
+ " 1. Rosegarden has not been correctly installed. If you compiled\n"
+ " it yourself, check that you have run \"make install\" and that\n"
+ " the procedure completed successfully.\n\n"
+ " 2. Rosegarden has been installed in a non-standard directory,\n"
+ " and you need to add this directory to your KDEDIRS environment\n"
+ " variable before you can run it. This may be the case if you\n"
+ " installed into $HOME or a local third-party package directory\n"
+ " like /usr/local or /opt."),
+ i18n("Installation problem"));
+ }
+
+ exit(1);
+}
+
+
+int main(int argc, char *argv[])
+{
+ setsid(); // acquire shiny new process group
+
+ srandom((unsigned int)time(0) * (unsigned int)getpid());
+
+ KAboutData aboutData( "rosegarden", I18N_NOOP("Rosegarden"),
+ VERSION, description, KAboutData::License_GPL,
+ I18N_NOOP("Copyright 2000 - 2008 Guillaume Laurent, Chris Cannam, Richard Bown\nParts copyright 1994 - 2004 Chris Cannam, Andy Green, Richard Bown, Guillaume Laurent\nLilyPond fonts copyright 1997 - 2005 Han-Wen Nienhuys and Jan Nieuwenhuizen"),
+ 0,
+ "http://www.rosegardenmusic.com/",
+ "rosegarden-devel@lists.sourceforge.net");
+
+ aboutData.addAuthor("Guillaume Laurent (lead)", 0, "glaurent@telegraph-road.org", "http://telegraph-road.org");
+ aboutData.addAuthor("Chris Cannam (lead)", 0, "cannam@all-day-breakfast.com", "http://all-day-breakfast.com");
+ aboutData.addAuthor("Richard Bown (lead)", 0, "richard.bown@ferventsoftware.com");
+ aboutData.addAuthor("D. Michael McIntyre", 0, "dmmcintyr@users.sourceforge.net");
+ aboutData.addAuthor("Pedro Lopez-Cabanillas", 0, "plcl@users.sourceforge.net");
+ aboutData.addAuthor("Heikki Johannes Junes", 0, "hjunes@users.sourceforge.net");
+
+ aboutData.addCredit("Randall Farmer", I18N_NOOP("Chord labelling code"), " rfarme@simons-rock.edu");
+ aboutData.addCredit("Hans Kieserman", I18N_NOOP("LilyPond output\nassorted other patches\ni18n-ization"), "hkieserman@mail.com");
+ aboutData.addCredit("Levi Burton", I18N_NOOP("UI improvements\nbug fixes"), "donburton@sbcglobal.net");
+ aboutData.addCredit("Mark Hymers", I18N_NOOP("Segment colours\nOther UI and bug fixes"), "<markh@linuxfromscratch.org>");
+ aboutData.addCredit("Alexandre Prokoudine", I18N_NOOP("Russian translation\ni18n-ization"), "avp@altlinux.ru");
+ aboutData.addCredit("Jörg Schumann", I18N_NOOP("German translation"), "jrschumann@gmx.de");
+ aboutData.addCredit("Eckhard Jokisch", I18N_NOOP("German translation"), "e.jokisch@u-code.de");
+ aboutData.addCredit("Kevin Donnelly", I18N_NOOP("Welsh translation"));
+ aboutData.addCredit("Didier Burli", I18N_NOOP("French translation"), "didierburli@bluewin.ch");
+ aboutData.addCredit("Yves Guillemot", I18N_NOOP("French translation\nBug fixes"), "yc.guillemot@wanadoo.fr");
+ aboutData.addCredit("Daniele Medri", I18N_NOOP("Italian translation"), "madrid@linuxmeeting.net");
+ aboutData.addCredit("Alessandro Musesti", I18N_NOOP("Italian translation"), "a.musesti@dmf.unicatt.it");
+ aboutData.addCredit("Stefan Asserhäll", I18N_NOOP("Swedish translation"), "stefan.asserhall@comhem.se");
+ aboutData.addCredit("Erik Magnus Johansson", I18N_NOOP("Swedish translation"), "erik.magnus.johansson@telia.com");
+ aboutData.addCredit("Hasso Tepper", I18N_NOOP("Estonian translation"), "hasso@estpak.ee");
+ aboutData.addCredit("Jelmer Vernooij", I18N_NOOP("Dutch translation"), "jelmer@samba.org");
+ aboutData.addCredit("Jasper Stein", I18N_NOOP("Dutch translation"), "jasper.stein@12move.nl");
+ aboutData.addCredit("Kevin Liang", I18N_NOOP("HSpinBox class"), "xkliang@rhpcs.mcmaster.ca");
+ aboutData.addCredit("Arnout Engelen", I18N_NOOP("Transposition by interval"));
+ aboutData.addCredit("Thorsten Wilms", I18N_NOOP("Original designs for rotary controllers"), "t_w_@freenet.de");
+ aboutData.addCredit("Oota Toshiya", I18N_NOOP("Japanese translation"), "ribbon@users.sourceforge.net");
+ aboutData.addCredit("William", I18N_NOOP("Auto-scroll deceleration\nRests outside staves and other bug fixes"), "rosegarden4p AT orthoset.com");
+ aboutData.addCredit("Liu Songhe", I18N_NOOP("Simplified Chinese translation"), "jackliu9999@msn.com");
+ aboutData.addCredit("Toni Arnold", I18N_NOOP("LIRC infrared remote-controller support"), "<toni__arnold@bluewin.ch>");
+ aboutData.addCredit("Vince Negri", I18N_NOOP("MTC slave timing implementation"), "vince.negri@gmail.com");
+ aboutData.addCredit("Jan Bína", I18N_NOOP("Czech translation"), "jbina@sky.cz");
+ aboutData.addCredit("Thomas Nagy", I18N_NOOP("SCons/bksys building system"), "tnagy256@yahoo.fr");
+ aboutData.addCredit("Vladimir Savic", I18N_NOOP("icons, icons, icons"), "vladimir@vladimirsavic.net");
+ aboutData.addCredit("Marcos Germán Guglielmetti", I18N_NOOP("Spanish translation"), "marcospcmusica@yahoo.com.ar");
+ aboutData.addCredit("Lisandro Damián Nicanor Pérez Meyer", I18N_NOOP("Spanish translation"), "perezmeyer@infovia.com.ar");
+ aboutData.addCredit("Javier Castrillo", I18N_NOOP("Spanish translation"), "riverplatense@gmail.com");
+ aboutData.addCredit("Lucas Godoy", I18N_NOOP("Spanish translation"), "godoy.lucas@gmail.com");
+ aboutData.addCredit("Feliu Ferrer", I18N_NOOP("Catalan translation"), "mverge2@pie.xtec.es");
+ aboutData.addCredit("Quim Perez i Noguer", I18N_NOOP("Catalan translation"), "noguer@osona.com");
+ aboutData.addCredit("Carolyn McIntyre", I18N_NOOP("1.2.3 splash screen photo\nGave birth to D. Michael McIntyre, bought him a good flute once\nupon a time, and always humored him when he came over to play her\nsome new instrument, even though she really hated his playing.\nBorn October 19, 1951, died September 21, 2007, R. I. P."), "DECEASED");
+ aboutData.addCredit("Stephen Torri", I18N_NOOP("Initial guitar chord editing code"), "storri@torri.org");
+ aboutData.addCredit("Piotr Sawicki", I18N_NOOP("Polish translation"), "pelle@plusnet.pl");
+ aboutData.addCredit("David García-Abad", I18N_NOOP("Basque translation"), "davidgarciabad@telefonica.net");
+ aboutData.addCredit("Joerg C. Koenig, Craig Drummond, Bernhard Rosenkränzer, Preston Brown, Than Ngo", I18N_NOOP("Klearlook theme"), "jck@gmx.org");
+
+ aboutData.setTranslator(I18N_NOOP("_: NAME OF TRANSLATORS\nYour names") , I18N_NOOP("_: EMAIL OF TRANSLATORS\nYour emails"));
+
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.
+ KUniqueApplication::addCmdLineOptions(); // Add KUniqueApplication options.
+
+ if (!RosegardenApplication::start())
+ return 0;
+
+ RosegardenApplication app;
+
+ //
+ // Ensure quit on last window close
+ // Register main DCOP interface
+ //
+ QObject::connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit()));
+ app.dcopClient()->registerAs(app.name(), false);
+ app.dcopClient()->setDefaultObject(ROSEGARDEN_GUI_IFACE_NAME);
+
+ // Parse cmd line args
+ //
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ if (!args->isSet("ignoreversion")) {
+ // Give up immediately if we haven't been installed or if the
+ // installation is out of date
+ //
+ testInstalledVersion();
+ }
+
+ KConfig *config = kapp->config();
+
+ config->setGroup(GeneralOptionsConfigGroup);
+ QString lastVersion = config->readEntry("lastversion", "");
+ bool newVersion = (lastVersion != VERSION);
+ if (newVersion) {
+ std::cerr << "*** This is the first time running this Rosegarden version" << std::endl;
+ config->writeEntry("lastversion", VERSION);
+ }
+
+ // If there is no config setting for the startup window size, set
+ // one now. But base the default on the appropriate desktop size
+ // (i.e. not the entire desktop, if Xinerama is in use). This is
+ // obtained from KGlobalSettings::desktopGeometry(), but we can't
+ // give it a meaningful point to measure from at this stage so we
+ // always use the "leftmost" display (point 0,0).
+
+ // The config keys are "Height X" and "Width Y" where X and Y are
+ // the sizes of the available desktop (i.e. the whole shebang if
+ // under Xinerama). These are obtained from QDesktopWidget.
+
+ config->setGroup("MainView");
+ int windowWidth = 0, windowHeight = 0;
+
+ QDesktopWidget *desktop = KApplication::desktop();
+ if (desktop) {
+ QRect totalRect(desktop->screenGeometry());
+ QRect desktopRect = KGlobalSettings::desktopGeometry(QPoint(0, 0));
+ QSize startupSize;
+ if (desktopRect.height() <= 800) {
+ startupSize = QSize((desktopRect.width() * 6) / 7,
+ (desktopRect.height() * 6) / 7);
+ } else {
+ startupSize = QSize((desktopRect.width() * 4) / 5,
+ (desktopRect.height() * 4) / 5);
+ }
+ QString widthKey = QString("Width %1").arg(totalRect.width());
+ QString heightKey = QString("Height %1").arg(totalRect.height());
+ windowWidth = config->readUnsignedNumEntry
+ (widthKey, startupSize.width());
+ windowHeight = config->readUnsignedNumEntry
+ (heightKey, startupSize.height());
+ }
+
+ config->setGroup("KDE Action Restrictions");
+ config->writeEntry("action/help_report_bug", false);
+
+ config->setGroup(GeneralOptionsConfigGroup);
+ int install = config->readNumEntry("Install Own Theme", 1);
+ if (install == 2 || (install == 1 && !getenv("KDE_FULL_SESSION"))) {
+ kapp->setStyle(new KlearlookStyle);
+ }
+
+ // Show Startup logo
+ // (this code borrowed from KDevelop 2.0,
+ // (c) The KDevelop Development Team
+ //
+ config->setGroup(GeneralOptionsConfigGroup);
+ KStartupLogo* startLogo = 0L;
+
+ // See if the config wants us to control JACK
+ //
+ if (config->readBoolEntry("Logo", true) && (!kapp->isRestored() && args->isSet("splash")) ) {
+ RG_DEBUG << k_funcinfo << "Showing startup logo\n";
+ startLogo = KStartupLogo::getInstance();
+ startLogo->setShowTip(!newVersion);
+ startLogo->show();
+ }
+
+ struct timeval logoShowTime;
+ gettimeofday(&logoShowTime, 0);
+
+ //
+ // Start application
+ //
+ RosegardenGUIApp *rosegardengui = 0;
+
+ if (app.isRestored()) {
+ RG_DEBUG << "Restoring from session\n";
+
+ // RESTORE(RosegardenGUIApp);
+ int n = 1;
+ while (KMainWindow::canBeRestored(n)) {
+ // memory leak if more than one can be restored?
+ RG_DEBUG << "Restoring from session - restoring app #" << n << endl;
+ (rosegardengui = new RosegardenGUIApp)->restore(n);
+ n++;
+ }
+
+ } else {
+
+#ifndef NO_SOUND
+ app.setNoSequencerMode(!args->isSet("sequencer"));
+#else
+
+ app.setNoSequencerMode(true);
+#endif // NO_SOUND
+
+ rosegardengui = new RosegardenGUIApp(!app.noSequencerMode(),
+ args->isSet("existingsequencer"),
+ startLogo);
+
+ rosegardengui->setIsFirstRun(newVersion);
+
+ app.setMainWidget(rosegardengui);
+
+ if (windowWidth != 0 && windowHeight != 0) {
+ rosegardengui->resize(windowWidth, windowHeight);
+ }
+
+ rosegardengui->show();
+
+ // raise start logo
+ //
+ if (startLogo) {
+ startLogo->raise();
+ startLogo->setHideEnabled(true);
+ QApplication::flushX();
+ }
+
+ if (args->count()) {
+ rosegardengui->openFile(QFile::decodeName(args->arg(0)), RosegardenGUIApp::ImportCheckType);
+ } else {
+ // rosegardengui->openDocumentFile();
+ }
+
+ args->clear();
+
+ }
+
+ QObject::connect(&app, SIGNAL(aboutToSaveState()),
+ rosegardengui, SLOT(slotDeleteTransport()));
+
+ // Now that we've started up, raise start logo
+ //
+ if (startLogo) {
+ startLogo->raise();
+ startLogo->setHideEnabled(true);
+ QApplication::flushX();
+ }
+
+ // Check for sequencer and launch if needed
+ //
+ try {
+ rosegardengui->launchSequencer(args->isSet("existingsequencer"));
+ } catch (std::string e) {
+ RG_DEBUG << "RosegardenGUI - " << e << endl;
+ } catch (QString e) {
+ RG_DEBUG << "RosegardenGUI - " << e << endl;
+ } catch (Exception e) {
+ RG_DEBUG << "RosegardenGUI - " << e.getMessage() << endl;
+ }
+
+
+ config->setGroup(SequencerOptionsConfigGroup);
+
+ // See if the config wants us to load a soundfont
+ //
+ if (config->readBoolEntry("sfxloadenabled", false)) {
+ QString sfxLoadPath = config->readEntry("sfxloadpath", "/bin/sfxload");
+ QString soundFontPath = config->readEntry("soundfontpath", "");
+ QFileInfo sfxLoadInfo(sfxLoadPath), soundFontInfo(soundFontPath);
+ if (sfxLoadInfo.isExecutable() && soundFontInfo.isReadable()) {
+ KProcess* sfxLoadProcess = new KProcess;
+ (*sfxLoadProcess) << sfxLoadPath << soundFontPath;
+ RG_DEBUG << "Starting sfxload : " << sfxLoadPath << " " << soundFontPath << endl;
+
+ QObject::connect(sfxLoadProcess, SIGNAL(processExited(KProcess*)),
+ &app, SLOT(sfxLoadExited(KProcess*)));
+
+ sfxLoadProcess->start();
+ } else {
+ RG_DEBUG << "sfxload not executable or soundfont not readable : "
+ << sfxLoadPath << " " << soundFontPath << endl;
+ }
+
+ } else {
+ RG_DEBUG << "sfxload disabled\n";
+ }
+
+
+#ifdef Q_WS_X11
+ XSetErrorHandler( _x_errhandler );
+#endif
+
+ if (startLogo) {
+
+ // pause to ensure the logo has been visible for a reasonable
+ // length of time, just 'cos it looks a bit silly to show it
+ // and remove it immediately
+
+ struct timeval now;
+ gettimeofday(&now, 0);
+
+ RealTime visibleFor =
+ RealTime(now.tv_sec, now.tv_usec * 1000) -
+ RealTime(logoShowTime.tv_sec, logoShowTime.tv_usec * 1000);
+
+ if (visibleFor < RealTime(2, 0)) {
+ int waitTime = visibleFor.sec * 1000 + visibleFor.msec();
+ QTimer::singleShot(2500 - waitTime, startLogo, SLOT(close()));
+ } else {
+ startLogo->close();
+ }
+
+ } else {
+
+ // if the start logo is there, it's responsible for showing this;
+ // otherwise we have to
+
+ if (!newVersion) {
+ RosegardenGUIApp::self()->awaitDialogClearance();
+ KTipDialog::showTip(locate("data", "rosegarden/tips"));
+ }
+ }
+
+ if (newVersion) {
+ KStartupLogo::hideIfStillThere();
+ CurrentProgressDialog::freeze();
+
+ KDialogBase *dialog = new KDialogBase(rosegardengui, "welcome",
+ true, i18n("Welcome!"),
+ KDialogBase::Ok,
+ KDialogBase::Ok, false);
+ QVBox *mw = dialog->makeVBoxMainWidget();
+ QHBox *hb = new QHBox(mw);
+ QLabel *image = new QLabel(hb);
+ image->setAlignment(Qt::AlignTop);
+ QString iconFile = locate("appdata", "pixmaps/misc/welcome-icon.png");
+ if (iconFile) {
+ image->setPixmap(QPixmap(iconFile));
+ }
+ QLabel *label = new QLabel(hb);
+ label->setText(i18n("<h2>Welcome to Rosegarden!</h2><p>Welcome to the Rosegarden audio and MIDI sequencer and musical notation editor.</p><ul><li>If you have not already done so, you may wish to install some DSSI synth plugins, or a separate synth program such as QSynth. Rosegarden does not synthesize sounds from MIDI on its own, so without these you will hear nothing.</li><br><br><li>Rosegarden uses the JACK audio server for recording and playback of audio, and for playback from DSSI synth plugins. These features will only be available if the JACK server is running.</li><br><br><li>Rosegarden has comprehensive documentation: see the Help menu for the handbook, tutorials, and other information!</li></ul><p>Rosegarden was brought to you by a team of volunteers across the world. To learn more, go to <a href=\"http://www.rosegardenmusic.com/\">http://www.rosegardenmusic.com/</a>.</p>"));
+ dialog->showButtonOK(true);
+ rosegardengui->awaitDialogClearance();
+ dialog->exec();
+
+ CurrentProgressDialog::thaw();
+ }
+
+ return kapp->exec();
+}
+
diff --git a/src/gui/configuration/AudioConfigurationPage.cpp b/src/gui/configuration/AudioConfigurationPage.cpp
new file mode 100644
index 0000000..28aff71
--- /dev/null
+++ b/src/gui/configuration/AudioConfigurationPage.cpp
@@ -0,0 +1,323 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "AudioConfigurationPage.h"
+
+#include "sound/Midi.h"
+#include "sound/SoundDriver.h"
+#include "document/ConfigGroups.h"
+#include "base/MidiProgram.h"
+#include "base/Studio.h"
+#include "ConfigurationPage.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/dialogs/ShowSequencerStatusDialog.h"
+#include "gui/seqmanager/SequenceManager.h"
+#include "gui/application/RosegardenApplication.h"
+#include "gui/studio/StudioControl.h"
+#include "sound/MappedEvent.h"
+#include "TabbedConfigurationPage.h"
+#include <kcombobox.h>
+#include <kconfig.h>
+#include <kfiledialog.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qobject.h>
+#include <qpushbutton.h>
+#include <qlayout.h>
+#include <qslider.h>
+#include <qspinbox.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qtabwidget.h>
+#include <qtooltip.h>
+#include <qwidget.h>
+#include <kmessagebox.h>
+
+
+namespace Rosegarden
+{
+
+AudioConfigurationPage::AudioConfigurationPage(
+ RosegardenGUIDoc *doc,
+ KConfig *cfg,
+ QWidget *parent,
+ const char *name):
+ TabbedConfigurationPage(cfg, parent, name),
+ m_externalAudioEditorPath(0)
+{
+ // set the document in the super class
+ m_doc = doc;
+
+ m_cfg->setGroup(SequencerOptionsConfigGroup);
+
+ QFrame *frame = new QFrame(m_tabWidget);
+ QGridLayout *layout = new QGridLayout(frame, 7, 2, 10, 5);
+
+ QLabel *label = 0;
+
+ int row = 0;
+
+ m_cfg->setGroup(GeneralOptionsConfigGroup);
+
+ layout->setRowSpacing(row, 15);
+ ++row;
+
+ layout->addWidget(new QLabel(i18n("Audio preview scale"),
+ frame), row, 0);
+
+ m_previewStyle = new KComboBox(frame);
+ m_previewStyle->insertItem(i18n("Linear - easier to see loud peaks"));
+ m_previewStyle->insertItem(i18n("Meter scaling - easier to see quiet activity"));
+ m_previewStyle->setCurrentItem(m_cfg->readUnsignedNumEntry("audiopreviewstyle", 1));
+ layout->addMultiCellWidget(m_previewStyle, row, row, 1, 2);
+ ++row;
+
+#ifdef HAVE_LIBJACK
+ m_cfg->setGroup(SequencerOptionsConfigGroup);
+
+ label = new QLabel(i18n("Record audio files as"), frame);
+ m_audioRecFormat = new KComboBox(frame);
+ m_audioRecFormat->insertItem(i18n("16-bit PCM WAV format (smaller files)"));
+ m_audioRecFormat->insertItem(i18n("32-bit float WAV format (higher quality)"));
+ m_audioRecFormat->setCurrentItem(m_cfg->readUnsignedNumEntry("audiorecordfileformat", 1));
+ layout->addWidget(label, row, 0);
+ layout->addMultiCellWidget(m_audioRecFormat, row, row, 1, 2);
+ ++row;
+#endif
+
+ m_cfg->setGroup(GeneralOptionsConfigGroup);
+
+ layout->addWidget(new QLabel(i18n("External audio editor"), frame),
+ row, 0);
+
+ QString defaultAudioEditor = getBestAvailableAudioEditor();
+
+ std::cerr << "defaultAudioEditor = " << defaultAudioEditor << std::endl;
+
+ QString externalAudioEditor = m_cfg->readEntry("externalaudioeditor",
+ defaultAudioEditor);
+
+ if (externalAudioEditor == "") {
+ externalAudioEditor = defaultAudioEditor;
+ m_cfg->writeEntry("externalaudioeditor", externalAudioEditor);
+ }
+
+ m_externalAudioEditorPath = new QLineEdit(externalAudioEditor, frame);
+// m_externalAudioEditorPath->setMinimumWidth(150);
+ layout->addWidget(m_externalAudioEditorPath, row, 1);
+
+ QPushButton *changePathButton =
+ new QPushButton(i18n("Choose..."), frame);
+
+ layout->addWidget(changePathButton, row, 2);
+ connect(changePathButton, SIGNAL(clicked()), SLOT(slotFileDialog()));
+ ++row;
+
+ m_cfg->setGroup(SequencerOptionsConfigGroup);
+
+ layout->addWidget(new QLabel(i18n("Create JACK outputs"), frame),
+ row, 0);
+// ++row;
+
+#ifdef HAVE_LIBJACK
+ m_createFaderOuts = new QCheckBox(i18n("for individual audio instruments"), frame);
+ m_createFaderOuts->setChecked(m_cfg->readBoolEntry("audiofaderouts", false));
+
+// layout->addWidget(label, row, 0, Qt::AlignRight);
+ layout->addWidget(m_createFaderOuts, row, 1);
+ ++row;
+
+ m_createSubmasterOuts = new QCheckBox(i18n("for submasters"), frame);
+ m_createSubmasterOuts->setChecked(m_cfg->readBoolEntry("audiosubmasterouts",
+ false));
+
+// layout->addWidget(label, row, 0, Qt::AlignRight);
+ layout->addWidget(m_createSubmasterOuts, row, 1);
+ ++row;
+#endif
+
+ layout->setRowStretch(row, 10);
+
+ addTab(frame, i18n("General"));
+
+ // --------------------- Startup control ----------------------
+ //
+#ifdef HAVE_LIBJACK
+#define OFFER_JACK_START_OPTION 1
+#ifdef OFFER_JACK_START_OPTION
+
+ frame = new QFrame(m_tabWidget);
+ layout = new QGridLayout(frame, 8, 4, 10, 5);
+
+ row = 0;
+
+ layout->setRowSpacing(row, 15);
+ ++row;
+
+ label = new QLabel(i18n("Rosegarden can start the JACK audio daemon (jackd) for you automatically if it isn't already running when Rosegarden starts.\n\nThis is recommended for beginners and those who use Rosegarden as their main audio application, but it might not be to the liking of advanced users.\n\nIf you want to start JACK automatically, make sure the command includes a full path where necessary as well as any command-line arguments you want to use.\n\nFor example: /usr/local/bin/jackd -d alsa -d hw -r44100 -p 2048 -n 2\n\n"), frame);
+ label->setAlignment(Qt::WordBreak);
+
+ layout->addMultiCellWidget(label, row, row, 0, 3);
+ ++row;
+
+ // JACK control things
+ //
+ bool startJack = m_cfg->readBoolEntry("jackstart", false);
+ m_startJack = new QCheckBox(frame);
+ m_startJack->setChecked(startJack);
+
+ layout->addWidget(new QLabel(i18n("Start JACK when Rosegarden starts"), frame), 2, 0);
+
+ layout->addWidget(m_startJack, row, 1);
+ ++row;
+
+ layout->addWidget(new QLabel(i18n("JACK command"), frame),
+ row, 0);
+
+ QString jackPath = m_cfg->readEntry("jackcommand",
+ // "/usr/local/bin/jackd -d alsa -d hw -r 44100 -p 2048 -n 2");
+ "/usr/bin/qjackctl -s");
+ m_jackPath = new QLineEdit(jackPath, frame);
+
+ layout->addMultiCellWidget(m_jackPath, row, row, 1, 3);
+ ++row;
+
+ layout->setRowStretch(row, 10);
+
+ addTab(frame, i18n("JACK Startup"));
+
+#endif // OFFER_JACK_START_OPTION
+#endif // HAVE_LIBJACK
+
+}
+
+void
+AudioConfigurationPage::slotFileDialog()
+{
+ QString path = KFileDialog::getOpenFileName(QString::null, QString::null, this, i18n("External audio editor path"));
+ m_externalAudioEditorPath->setText(path);
+}
+
+void
+AudioConfigurationPage::apply()
+{
+ m_cfg->setGroup(SequencerOptionsConfigGroup);
+
+#ifdef HAVE_LIBJACK
+#ifdef OFFER_JACK_START_OPTION
+ // Jack control
+ //
+ m_cfg->writeEntry("jackstart", m_startJack->isChecked());
+ m_cfg->writeEntry("jackcommand", m_jackPath->text());
+#endif // OFFER_JACK_START_OPTION
+
+ // Jack audio inputs
+ //
+ m_cfg->writeEntry("audiofaderouts", m_createFaderOuts->isChecked());
+ m_cfg->writeEntry("audiosubmasterouts", m_createSubmasterOuts->isChecked());
+ m_cfg->writeEntry("audiorecordfileformat", m_audioRecFormat->currentItem());
+#endif
+
+ m_cfg->setGroup(GeneralOptionsConfigGroup);
+
+ int previewstyle = m_previewStyle->currentItem();
+ m_cfg->writeEntry("audiopreviewstyle", previewstyle);
+
+ QString externalAudioEditor = getExternalAudioEditor();
+
+ QStringList extlist = QStringList::split(" ", externalAudioEditor);
+ QString extpath = "";
+ if (extlist.size() > 0) extpath = extlist[0];
+
+ if (extpath != "") {
+ QFileInfo info(extpath);
+ if (!info.exists() || !info.isExecutable()) {
+ KMessageBox::error(0, i18n("External audio editor \"%1\" not found or not executable").arg(extpath));
+ m_cfg->writeEntry("externalaudioeditor", "");
+ } else {
+ m_cfg->writeEntry("externalaudioeditor", externalAudioEditor);
+ }
+ } else {
+ m_cfg->writeEntry("externalaudioeditor", "");
+ }
+}
+
+QString
+AudioConfigurationPage::getBestAvailableAudioEditor()
+{
+ static QString result = "";
+ static bool haveResult = false;
+
+ if (haveResult) return result;
+
+ QString path;
+ const char *cpath = getenv("PATH");
+ if (cpath) path = cpath;
+ else path = "/usr/bin:/bin";
+
+ QStringList pathList = QStringList::split(":", path);
+
+ const char *candidates[] = {
+ "mhwaveedit",
+ "rezound",
+ "audacity"
+ };
+
+ for (int i = 0;
+ i < sizeof(candidates)/sizeof(candidates[0]) && result == "";
+ i++) {
+
+ QString n(candidates[i]);
+
+ for (int j = 0;
+ j < pathList.size() && result == "";
+ j++) {
+
+ QDir dir(pathList[j]);
+ QString fp(dir.filePath(n));
+ QFileInfo fi(fp);
+
+ if (fi.exists() && fi.isExecutable()) {
+ if (n == "rezound") {
+ result = QString("%1 --audio-method=jack").arg(fp);
+ } else {
+ result = fp;
+ }
+ }
+ }
+ }
+
+ haveResult = true;
+ return result;
+}
+
+}
+#include "AudioConfigurationPage.moc"
+
diff --git a/src/gui/configuration/AudioConfigurationPage.h b/src/gui/configuration/AudioConfigurationPage.h
new file mode 100644
index 0000000..bd71df6
--- /dev/null
+++ b/src/gui/configuration/AudioConfigurationPage.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_AUDIOCONFIGURATIONPAGE_H_
+#define _RG_AUDIOCONFIGURATIONPAGE_H_
+
+#include "TabbedConfigurationPage.h"
+#include <qstring.h>
+#include <klocale.h>
+#include <qlineedit.h>
+
+class QWidget;
+class QSpinBox;
+class QSlider;
+class QPushButton;
+class QLabel;
+class QComboBox;
+class QCheckBox;
+class KConfig;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+
+
+class AudioConfigurationPage : public TabbedConfigurationPage
+{
+ Q_OBJECT
+public:
+ AudioConfigurationPage(RosegardenGUIDoc *doc,
+ KConfig *cfg,
+ QWidget *parent=0,
+ const char *name=0);
+
+ virtual void apply();
+
+ static QString iconLabel() { return i18n("Audio"); }
+ static QString title() { return i18n("Audio Settings"); }
+ static QString iconName() { return "configure-audio"; }
+
+#ifdef HAVE_LIBJACK
+ QString getJackPath() { return m_jackPath->text(); }
+#endif // HAVE_LIBJACK
+
+ static QString getBestAvailableAudioEditor();
+
+protected slots:
+ void slotFileDialog();
+
+protected:
+ QString getExternalAudioEditor() { return m_externalAudioEditorPath->text(); }
+
+
+ //--------------- Data members ---------------------------------
+
+#ifdef HAVE_LIBJACK
+ QCheckBox *m_startJack;
+ QLineEdit *m_jackPath;
+#endif // HAVE_LIBJACK
+
+
+#ifdef HAVE_LIBJACK
+ // Number of JACK input ports our RG client creates -
+ // this decides how many audio input destinations
+ // we have.
+ //
+ QCheckBox *m_createFaderOuts;
+ QCheckBox *m_createSubmasterOuts;
+
+ QComboBox *m_audioRecFormat;
+
+#endif // HAVE_LIBJACK
+
+ QLineEdit* m_externalAudioEditorPath;
+ QComboBox* m_previewStyle;
+
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/configuration/AudioPropertiesPage.cpp b/src/gui/configuration/AudioPropertiesPage.cpp
new file mode 100644
index 0000000..65d574e
--- /dev/null
+++ b/src/gui/configuration/AudioPropertiesPage.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "AudioPropertiesPage.h"
+
+#include "misc/Strings.h"
+#include "ConfigurationPage.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/application/RosegardenApplication.h"
+#include "gui/studio/AudioPluginManager.h"
+#include "sound/AudioFileManager.h"
+#include "TabbedConfigurationPage.h"
+#include <kconfig.h>
+#include <kdiskfreesp.h>
+#include <kfiledialog.h>
+#include <kfile.h>
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <qdialog.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qstring.h>
+#include <qtabwidget.h>
+#include <qwidget.h>
+#include <qlayout.h>
+
+
+namespace Rosegarden
+{
+
+AudioPropertiesPage::AudioPropertiesPage(RosegardenGUIDoc *doc,
+ QWidget *parent,
+ const char *name)
+ : TabbedConfigurationPage(doc, parent, name)
+{
+ AudioFileManager &afm = doc->getAudioFileManager();
+
+ QFrame *frame = new QFrame(m_tabWidget);
+ QGridLayout *layout = new QGridLayout(frame, 4, 3, 10, 5);
+ layout->addWidget(new QLabel(i18n("Audio file path:"), frame), 0, 0);
+ m_path = new QLabel(QString(afm.getAudioPath().c_str()), frame);
+ layout->addWidget(m_path, 0, 1);
+
+ m_changePathButton =
+ new QPushButton(i18n("Choose..."), frame);
+
+ layout->addWidget(m_changePathButton, 0, 2);
+
+ m_diskSpace = new QLabel(frame);
+ layout->addWidget(new QLabel(i18n("Disk space remaining:"), frame), 1, 0);
+ layout->addWidget(m_diskSpace, 1, 1);
+
+ m_minutesAtStereo = new QLabel(frame);
+ layout->addWidget(
+ new QLabel(i18n("Equivalent minutes of 16-bit stereo:"),
+ frame), 2, 0);
+
+ layout->addWidget(m_minutesAtStereo, 2, 1, AlignCenter);
+
+ layout->setRowStretch(3, 2);
+
+ calculateStats();
+
+ connect(m_changePathButton, SIGNAL(released()),
+ SLOT(slotFileDialog()));
+
+ addTab(frame, i18n("Modify audio path"));
+}
+
+void
+AudioPropertiesPage::calculateStats()
+{
+ // This stolen from KDE libs kfile/kpropertiesdialog.cpp
+ //
+ QString mountPoint = KIO::findPathMountPoint(m_path->text());
+ KDiskFreeSp * job = new KDiskFreeSp;
+ connect(job, SIGNAL(foundMountPoint(const QString&, unsigned long, unsigned long,
+ unsigned long)),
+ this, SLOT(slotFoundMountPoint(const QString&, unsigned long, unsigned long,
+ unsigned long)));
+ job->readDF(mountPoint);
+}
+
+void
+AudioPropertiesPage::slotFoundMountPoint(const QString&,
+ unsigned long kBSize,
+ unsigned long /*kBUsed*/,
+ unsigned long kBAvail )
+{
+ m_diskSpace->setText(i18n("%1 out of %2 (%3% used)")
+ .arg(KIO::convertSizeFromKB(kBAvail))
+ .arg(KIO::convertSizeFromKB(kBSize))
+ .arg( 100 - (int)(100.0 * kBAvail / kBSize) ));
+
+
+ AudioPluginManager *apm = m_doc->getPluginManager();
+
+ int sampleRate = 48000;
+ QCString replyType;
+ QByteArray replyData;
+
+ if (rgapp->sequencerCall("getSampleRate()", replyType, replyData)) {
+
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ unsigned int result;
+ streamIn >> result;
+ sampleRate = result;
+ }
+
+ // Work out total bytes and divide this by the sample rate times the
+ // number of channels (2) times the number of bytes per sample (2)
+ // times 60 seconds.
+ //
+ float stereoMins = ( float(kBAvail) * 1024.0 ) /
+ ( float(sampleRate) * 2.0 * 2.0 * 60.0 );
+ QString minsStr;
+ minsStr.sprintf("%8.1f", stereoMins);
+
+ m_minutesAtStereo->
+ setText(QString("%1 %2 %3Hz").arg(minsStr)
+ .arg(i18n("minutes at"))
+ .arg(sampleRate));
+}
+
+void
+AudioPropertiesPage::slotFileDialog()
+{
+ AudioFileManager &afm = m_doc->getAudioFileManager();
+
+ KFileDialog *fileDialog = new KFileDialog(QString(afm.getAudioPath().c_str()),
+ QString::null,
+ this, "file dialog", true);
+ fileDialog->setMode(KFile::Directory);
+
+ connect(fileDialog, SIGNAL(fileSelected(const QString&)),
+ SLOT(slotFileSelected(const QString&)));
+
+ connect(fileDialog, SIGNAL(destroyed()),
+ SLOT(slotDirectoryDialogClosed()));
+
+ if (fileDialog->exec() == QDialog::Accepted) {
+ m_path->setText(fileDialog->selectedFile());
+ calculateStats();
+ }
+ delete fileDialog;
+}
+
+void
+AudioPropertiesPage::apply()
+{
+ AudioFileManager &afm = m_doc->getAudioFileManager();
+ QString newDir = m_path->text();
+
+ if (!newDir.isNull()) {
+ afm.setAudioPath(qstrtostr(newDir));
+ m_doc->slotDocumentModified();
+ }
+}
+
+}
+#include "AudioPropertiesPage.moc"
diff --git a/src/gui/configuration/AudioPropertiesPage.h b/src/gui/configuration/AudioPropertiesPage.h
new file mode 100644
index 0000000..f21fecc
--- /dev/null
+++ b/src/gui/configuration/AudioPropertiesPage.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_AUDIOPROPERTIESPAGE_H_
+#define _RG_AUDIOPROPERTIESPAGE_H_
+
+#include "TabbedConfigurationPage.h"
+#include <qstring.h>
+#include <klocale.h>
+
+
+class QWidget;
+class QPushButton;
+class QLabel;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+
+
+/**
+ * Audio Properties page
+ *
+ * (document-wide settings)
+ */
+class AudioPropertiesPage : public TabbedConfigurationPage
+{
+ Q_OBJECT
+public:
+ AudioPropertiesPage(RosegardenGUIDoc *doc,
+ QWidget *parent=0, const char *name=0);
+ virtual void apply();
+
+ static QString iconLabel() { return i18n("Audio"); }
+ static QString title() { return i18n("Audio Settings"); }
+ static QString iconName() { return "configure-audio"; }
+
+protected slots:
+ void slotFileDialog();
+
+ // Work out and display remaining disk space and time left
+ // at current path.
+ //
+ void calculateStats();
+
+ void slotFoundMountPoint(const QString&,
+ unsigned long kBSize,
+ unsigned long kBUsed,
+ unsigned long kBAvail);
+
+protected:
+
+ //--------------- Data members ---------------------------------
+
+ QLabel *m_path;
+ QLabel *m_diskSpace;
+ QLabel *m_minutesAtStereo;
+
+ QPushButton *m_changePathButton;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/configuration/ColourConfigurationPage.cpp b/src/gui/configuration/ColourConfigurationPage.cpp
new file mode 100644
index 0000000..f87cf20
--- /dev/null
+++ b/src/gui/configuration/ColourConfigurationPage.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ColourConfigurationPage.h"
+
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/Colour.h"
+#include "base/ColourMap.h"
+#include "commands/segment/SegmentColourMapCommand.h"
+#include "ConfigurationPage.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/MultiViewCommandHistory.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/widgets/ColourTable.h"
+#include "TabbedConfigurationPage.h"
+#include <kcolordialog.h>
+#include <kconfig.h>
+#include <kinputdialog.h>
+#include <qcolor.h>
+#include <qframe.h>
+#include <qpushbutton.h>
+#include <qstring.h>
+#include <qtabwidget.h>
+#include <qwidget.h>
+#include <qlayout.h>
+
+
+namespace Rosegarden
+{
+
+ColourConfigurationPage::ColourConfigurationPage(RosegardenGUIDoc *doc,
+ QWidget *parent,
+ const char *name)
+ : TabbedConfigurationPage(doc, parent, name)
+{
+ QFrame *frame = new QFrame(m_tabWidget);
+ QGridLayout *layout = new QGridLayout(frame, 2, 2,
+ 10, 5);
+
+ m_map = m_doc->getComposition().getSegmentColourMap();
+
+ m_colourtable = new ColourTable(frame, m_map, m_listmap);
+ m_colourtable->setFixedHeight(280);
+
+ layout->addMultiCellWidget(m_colourtable, 0, 0, 0, 1);
+
+ QPushButton* addColourButton = new QPushButton(i18n("Add New Color"),
+ frame);
+ layout->addWidget(addColourButton, 1, 0, Qt::AlignHCenter);
+
+ QPushButton* deleteColourButton = new QPushButton(i18n("Delete Color"),
+ frame);
+ layout->addWidget(deleteColourButton, 1, 1, Qt::AlignHCenter);
+
+ connect(addColourButton, SIGNAL(clicked()),
+ this, SLOT(slotAddNew()));
+
+ connect(deleteColourButton, SIGNAL(clicked()),
+ this, SLOT(slotDelete()));
+
+ connect(this, SIGNAL(docColoursChanged()),
+ m_doc, SLOT(slotDocColoursChanged()));
+
+ connect(m_colourtable, SIGNAL(entryTextChanged(unsigned int, QString)),
+ this, SLOT(slotTextChanged(unsigned int, QString)));
+
+ connect(m_colourtable, SIGNAL(entryColourChanged(unsigned int, QColor)),
+ this, SLOT(slotColourChanged(unsigned int, QColor)));
+
+ addTab(frame, i18n("Color Map"));
+
+}
+
+void
+ColourConfigurationPage::slotTextChanged(unsigned int index, QString string)
+{
+ m_map.modifyNameByIndex(m_listmap[index], string.ascii());
+ m_colourtable->populate_table(m_map, m_listmap);
+}
+
+void
+ColourConfigurationPage::slotColourChanged(unsigned int index, QColor color)
+{
+ m_map.modifyColourByIndex(m_listmap[index], GUIPalette::convertColour(color));
+ m_colourtable->populate_table(m_map, m_listmap);
+}
+
+void
+ColourConfigurationPage::apply()
+{
+ SegmentColourMapCommand *command = new SegmentColourMapCommand(m_doc, m_map);
+ m_doc->getCommandHistory()->addCommand(command);
+
+ RG_DEBUG << "ColourConfigurationPage::apply() emitting docColoursChanged()" << endl;
+ emit docColoursChanged();
+}
+
+void
+ColourConfigurationPage::slotAddNew()
+{
+ QColor temp;
+
+ bool ok = false;
+
+ QString newName = KInputDialog::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( temp );
+
+ if (result == KColorDialog::Accepted) {
+ Colour temp2 = GUIPalette::convertColour(temp);
+ m_map.addItem(temp2, qstrtostr(newName));
+ m_colourtable->populate_table(m_map, m_listmap);
+ }
+ // Else we don't do anything as they either didn't give a name
+ // or didn't give a colour
+ }
+
+}
+
+void
+ColourConfigurationPage::slotDelete()
+{
+ QTableSelection temp = m_colourtable->selection(0);
+
+ if ((!temp.isActive()) || (temp.topRow() == 0))
+ return ;
+
+ unsigned int toDel = temp.topRow();
+
+ m_map.deleteItemByIndex(m_listmap[toDel]);
+ m_colourtable->populate_table(m_map, m_listmap);
+
+}
+
+}
+#include "ColourConfigurationPage.moc"
diff --git a/src/gui/configuration/ColourConfigurationPage.h b/src/gui/configuration/ColourConfigurationPage.h
new file mode 100644
index 0000000..9ef4ae0
--- /dev/null
+++ b/src/gui/configuration/ColourConfigurationPage.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_COLOURCONFIGURATIONPAGE_H_
+#define _RG_COLOURCONFIGURATIONPAGE_H_
+
+#include "base/ColourMap.h"
+#include "gui/widgets/ColourTable.h"
+#include "TabbedConfigurationPage.h"
+#include <qstring.h>
+#include <klocale.h>
+
+
+class QWidget;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+
+
+/**
+ * Colour Configuration Page
+ *
+ * (document-wide settings)
+ */
+class ColourConfigurationPage : public TabbedConfigurationPage
+{
+ Q_OBJECT
+public:
+ ColourConfigurationPage(RosegardenGUIDoc *doc,
+ QWidget *parent=0, const char *name=0);
+ virtual void apply();
+
+ void populate_table();
+
+ static QString iconLabel() { return i18n("Color"); }
+ static QString title() { return i18n("Color Settings"); }
+ static QString iconName() { return "colorize"; }
+
+signals:
+ void docColoursChanged();
+
+protected slots:
+ void slotAddNew();
+ void slotDelete();
+ void slotTextChanged(unsigned int, QString);
+ void slotColourChanged(unsigned int, QColor);
+
+protected:
+ ColourTable *m_colourtable;
+
+ ColourMap m_map;
+ ColourTable::ColourList m_listmap;
+
+};
+
+// ----------- SequencerConfigurationage -----------------
+//
+
+
+}
+
+#endif
diff --git a/src/gui/configuration/ConfigurationPage.cpp b/src/gui/configuration/ConfigurationPage.cpp
new file mode 100644
index 0000000..3f3730b
--- /dev/null
+++ b/src/gui/configuration/ConfigurationPage.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ConfigurationPage.h"
+
+#include "document/RosegardenGUIDoc.h"
+#include <kconfig.h>
+#include <qwidget.h>
+#include <algorithm>
+
+
+namespace Rosegarden
+{
+}
+#include "ConfigurationPage.moc"
diff --git a/src/gui/configuration/ConfigurationPage.h b/src/gui/configuration/ConfigurationPage.h
new file mode 100644
index 0000000..4a93195
--- /dev/null
+++ b/src/gui/configuration/ConfigurationPage.h
@@ -0,0 +1,104 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Parts of the configuration classes are taken from KMail.
+ Copyright (C) 2000 The KMail Development Team.
+
+ 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_CONFIGURATIONPAGE_H_
+#define _RG_CONFIGURATIONPAGE_H_
+
+#include <qwidget.h>
+
+
+class KConfig;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+
+
+/**
+ * This class borrowed from KMail
+ * (c) 2000 The KMail Development Team
+ */
+class ConfigurationPage : public QWidget
+{
+ Q_OBJECT
+
+public:
+ ConfigurationPage(RosegardenGUIDoc *doc,
+ QWidget *parent=0, const char *name=0)
+ : QWidget(parent, name), m_doc(doc), m_cfg(0), m_pageIndex(0) {}
+
+ ConfigurationPage(KConfig *cfg,
+ QWidget *parent=0, const char *name=0)
+ : QWidget(parent, name), m_doc(0), m_cfg(cfg), m_pageIndex(0) {}
+
+ ConfigurationPage(RosegardenGUIDoc *doc, KConfig *cfg,
+ QWidget *parent=0, const char *name=0)
+ : QWidget(parent, name), m_doc(doc), m_cfg(cfg), m_pageIndex(0) {}
+
+ virtual ~ConfigurationPage() {};
+
+ /**
+ * Should set the page up (ie. read the setting from the @ref
+ * KConfig object into the widgets) after creating it in the
+ * constructor. Called from @ref ConfigureDialog.
+ */
+// virtual void setup() = 0;
+
+ /**
+ * Should apply the changed settings (ie. read the settings from
+ * the widgets into the @ref KConfig object). Called from @ref
+ * ConfigureDialog.
+ */
+ virtual void apply() = 0;
+
+ /**
+ * Should cleanup any temporaries after cancel. The default
+ * implementation does nothing. Called from @ref
+ * ConfigureDialog.
+ */
+ virtual void dismiss() {}
+
+ void setPageIndex( int aPageIndex ) { m_pageIndex = aPageIndex; }
+ int pageIndex() const { return m_pageIndex; }
+
+protected:
+
+ //--------------- Data members ---------------------------------
+
+ RosegardenGUIDoc* m_doc;
+ KConfig* m_cfg;
+
+ int m_pageIndex;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/configuration/DocumentMetaConfigurationPage.cpp b/src/gui/configuration/DocumentMetaConfigurationPage.cpp
new file mode 100644
index 0000000..9f5064b
--- /dev/null
+++ b/src/gui/configuration/DocumentMetaConfigurationPage.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "DocumentMetaConfigurationPage.h"
+
+#include "base/Event.h"
+#include "base/BaseProperties.h"
+#include "misc/Strings.h"
+#include "base/Colour.h"
+#include "base/Composition.h"
+#include "base/Configuration.h"
+#include "base/NotationTypes.h"
+#include "base/PropertyName.h"
+#include "base/BasicQuantizer.h"
+#include "base/RealTime.h"
+#include "base/Segment.h"
+#include "ConfigurationPage.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/editors/notation/NotationStrings.h"
+#include "gui/configuration/HeadersConfigurationPage.h"
+#include "gui/general/GUIPalette.h"
+#include "TabbedConfigurationPage.h"
+#include <kconfig.h>
+#include <klistview.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qlistview.h>
+#include <qpixmap.h>
+#include <qpushbutton.h>
+#include <qstring.h>
+#include <qtable.h>
+#include <qtabwidget.h>
+#include <qwidget.h>
+#include <qlayout.h>
+
+
+namespace Rosegarden
+{
+
+static QString durationToString(Rosegarden::Composition &comp,
+ Rosegarden::timeT absTime,
+ Rosegarden::timeT duration,
+ Rosegarden::RealTime rt)
+{
+ return i18n("%1 minutes %2.%3%4 seconds (%5 units, %6 measures)") // TODO - PLURAL
+ .arg(rt.sec / 60).arg(rt.sec % 60)
+ .arg(rt.msec() / 100).arg((rt.msec() / 10) % 10)
+ .arg(duration).arg(comp.getBarNumber(absTime + duration) -
+ comp.getBarNumber(absTime));
+}
+
+class SegmentDataItem : public QTableItem
+{
+public:
+ SegmentDataItem(QTable *t, QString s) :
+ QTableItem(t, QTableItem::Never, s) { }
+ virtual int alignment() const { return Qt::AlignCenter; }
+
+ virtual QString key() const {
+
+ // It doesn't seem to be possible to specify a comparator so
+ // as to get the right sorting for numeric items (what am I
+ // missing here?), only to override this function to return a
+ // string for comparison. So for integer items we'll return a
+ // string that starts with a single digit corresponding to the
+ // number of digits in the integer, which should ensure that
+ // dictionary sorting works correctly.
+ //
+ // This relies on the assumption that any item whose text
+ // starts with a digit will contain nothing other than a
+ // single non-negative integer of no more than 9 digits. That
+ // assumption should hold for all current uses of this class,
+ // but may need checking for future uses...
+
+ QString s(text());
+ if (s[0].digitValue() >= 0) {
+ return QString("%1%2").arg(s.length()).arg(s);
+ } else {
+ return s;
+ }
+ }
+};
+
+DocumentMetaConfigurationPage::DocumentMetaConfigurationPage(RosegardenGUIDoc *doc,
+ QWidget *parent,
+ const char *name) :
+ TabbedConfigurationPage(doc, parent, name)
+{
+ m_headersPage = new HeadersConfigurationPage(this, doc);
+ addTab(m_headersPage, i18n("Headers"));
+
+ Composition &comp = doc->getComposition();
+ std::set
+ <TrackId> usedTracks;
+
+ int audioSegments = 0, internalSegments = 0;
+ for (Composition::iterator ci = comp.begin();
+ ci != comp.end(); ++ci) {
+ usedTracks.insert((*ci)->getTrack());
+ if ((*ci)->getType() == Segment::Audio)
+ ++audioSegments;
+ else
+ ++internalSegments;
+ }
+
+ QFrame *frame = new QFrame(m_tabWidget);
+ QGridLayout *layout = new QGridLayout(frame,
+ 6, 2,
+ 10, 5);
+
+ layout->addWidget(new QLabel(i18n("Filename:"), frame), 0, 0);
+ layout->addWidget(new QLabel(doc->getTitle(), frame), 0, 1);
+
+ layout->addWidget(new QLabel(i18n("Formal duration (to end marker):"), frame), 1, 0);
+ timeT d = comp.getEndMarker();
+ RealTime rtd = comp.getElapsedRealTime(d);
+ layout->addWidget(new QLabel(durationToString(comp, 0, d, rtd), frame), 1, 1);
+
+ layout->addWidget(new QLabel(i18n("Playing duration:"), frame), 2, 0);
+ d = comp.getDuration();
+ rtd = comp.getElapsedRealTime(d);
+ layout->addWidget(new QLabel(durationToString(comp, 0, d, rtd), frame), 2, 1);
+
+ layout->addWidget(new QLabel(i18n("Tracks:"), frame), 3, 0);
+ layout->addWidget(new QLabel(i18n("%1 used, %2 total")
+ .arg(usedTracks.size())
+ .arg(comp.getNbTracks()),
+ frame), 3, 1);
+
+ layout->addWidget(new QLabel(i18n("Segments:"), frame), 4, 0);
+ layout->addWidget(new QLabel(i18n("%1 MIDI, %2 audio, %3 total")
+ .arg(internalSegments)
+ .arg(audioSegments)
+ .arg(internalSegments + audioSegments),
+ frame), 4, 1);
+
+ layout->setRowStretch(5, 2);
+
+ addTab(frame, i18n("Statistics"));
+
+ frame = new QFrame(m_tabWidget);
+ layout = new QGridLayout(frame, 1, 1, 10, 5);
+
+ QTable *table = new QTable(1, 11, frame, "Segment Table");
+ table->setSelectionMode(QTable::NoSelection);
+ table->setSorting(true);
+ table->horizontalHeader()->setLabel(0, i18n("Type"));
+ table->horizontalHeader()->setLabel(1, i18n("Track"));
+ table->horizontalHeader()->setLabel(2, i18n("Label"));
+ table->horizontalHeader()->setLabel(3, i18n("Time"));
+ table->horizontalHeader()->setLabel(4, i18n("Duration"));
+ table->horizontalHeader()->setLabel(5, i18n("Events"));
+ table->horizontalHeader()->setLabel(6, i18n("Polyphony"));
+ table->horizontalHeader()->setLabel(7, i18n("Repeat"));
+ table->horizontalHeader()->setLabel(8, i18n("Quantize"));
+ table->horizontalHeader()->setLabel(9, i18n("Transpose"));
+ table->horizontalHeader()->setLabel(10, i18n("Delay"));
+ table->setNumRows(audioSegments + internalSegments);
+
+ table->setColumnWidth(0, 50);
+ table->setColumnWidth(1, 50);
+ table->setColumnWidth(2, 150);
+ table->setColumnWidth(3, 80);
+ table->setColumnWidth(4, 80);
+ table->setColumnWidth(5, 80);
+ table->setColumnWidth(6, 80);
+ table->setColumnWidth(7, 80);
+ table->setColumnWidth(8, 80);
+ table->setColumnWidth(9, 80);
+ table->setColumnWidth(10, 80);
+
+ int i = 0;
+
+ for (Composition::iterator ci = comp.begin();
+ ci != comp.end(); ++ci) {
+
+ Segment *s = *ci;
+
+ table->setItem(i, 0, new SegmentDataItem
+ (table,
+ s->getType() == Segment::Audio ?
+ i18n("Audio") : i18n("MIDI")));
+
+ table->setItem(i, 1, new SegmentDataItem
+ (table,
+ QString("%1").arg(s->getTrack() + 1)));
+
+ QPixmap colourPixmap(16, 16);
+ Colour colour =
+ comp.getSegmentColourMap().getColourByIndex(s->getColourIndex());
+ colourPixmap.fill(GUIPalette::convertColour(colour));
+
+ table->setItem(i, 2,
+ new QTableItem(table, QTableItem::Never,
+ strtoqstr(s->getLabel()),
+ colourPixmap));
+
+ table->setItem(i, 3, new SegmentDataItem
+ (table,
+ QString("%1").arg(s->getStartTime())));
+
+ table->setItem(i, 4, new SegmentDataItem
+ (table,
+ QString("%1").arg(s->getEndMarkerTime() -
+ s->getStartTime())));
+
+ std::set<long> notesOn;
+ std::multimap<timeT, long> noteOffs;
+ int events = 0, notes = 0, poly = 0, maxPoly = 0;
+
+ for (Segment::iterator si = s->begin();
+ s->isBeforeEndMarker(si); ++si) {
+ ++events;
+ if ((*si)->isa(Note::EventType)) {
+ ++notes;
+ timeT startTime = (*si)->getAbsoluteTime();
+ timeT endTime = startTime + (*si)->getDuration();
+ if (endTime == startTime) continue;
+ while (!noteOffs.empty() &&
+ (startTime >= noteOffs.begin()->first)) {
+ notesOn.erase(noteOffs.begin()->second);
+ noteOffs.erase(noteOffs.begin());
+ }
+ long pitch = 0;
+ (*si)->get<Int>(BaseProperties::PITCH, pitch);
+ notesOn.insert(pitch);
+ noteOffs.insert(std::multimap<timeT, long>::value_type(endTime, pitch));
+ poly = notesOn.size();
+ if (poly > maxPoly) maxPoly = poly;
+ }
+ }
+
+ table->setItem(i, 5, new SegmentDataItem
+ (table,
+ QString("%1").arg(events)));
+
+ table->setItem(i, 6, new SegmentDataItem
+ (table,
+ QString("%1").arg(maxPoly)));
+
+ table->setItem(i, 7, new SegmentDataItem
+ (table,
+ s->isRepeating() ? i18n("Yes") : i18n("No")));
+
+ timeT discard;
+
+ if (s->getQuantizer() && s->hasQuantization()) {
+ timeT unit = s->getQuantizer()->getUnit();
+ table->setItem(i, 8, new SegmentDataItem
+ (table,
+ NotationStrings::makeNoteMenuLabel
+ (unit, true, discard, false)));
+ } else {
+ table->setItem(i, 8, new SegmentDataItem
+ (table,
+ i18n("Off")));
+ }
+
+ table->setItem(i, 9, new SegmentDataItem
+ (table,
+ QString("%1").arg(s->getTranspose())));
+
+ if (s->getDelay() != 0) {
+ if (s->getRealTimeDelay() != RealTime::zeroTime) {
+ table->setItem(i, 10, new SegmentDataItem
+ (table,
+ QString("%1 + %2 ms")
+ .arg(NotationStrings::makeNoteMenuLabel
+ (s->getDelay(), true, discard, false))
+ .arg(s->getRealTimeDelay().sec * 1000 +
+ s->getRealTimeDelay().msec())));
+ } else {
+ table->setItem(i, 10, new SegmentDataItem
+ (table,
+ NotationStrings::makeNoteMenuLabel
+ (s->getDelay(), true, discard, false)));
+ }
+ } else if (s->getRealTimeDelay() != RealTime::zeroTime) {
+ table->setItem(i, 10, new SegmentDataItem
+ (table,
+ QString("%2 ms")
+ .arg(s->getRealTimeDelay().sec * 1000 +
+ s->getRealTimeDelay().msec())));
+ } else {
+ table->setItem(i, 10, new SegmentDataItem
+ (table,
+ i18n("None")));
+ }
+
+ ++i;
+ }
+
+ layout->addWidget(table, 0, 0);
+
+ addTab(frame, i18n("Segment Summary"));
+
+}
+
+void
+DocumentMetaConfigurationPage::apply()
+{
+ m_headersPage->apply();
+
+ m_doc->slotDocumentModified();
+}
+
+/* hjj: WHAT TO DO WITH THIS ?
+void
+DocumentMetaConfigurationPage::selectMetadata(QString name)
+{
+ std::vector<PropertyName> fixedKeys =
+ CompositionMetadataKeys::getFixedKeys();
+ std::vector<PropertyName>::iterator i = fixedKeys.begin();
+
+ for (QListViewItem *item = m_fixed->firstChild();
+ item != 0; item = item->nextSibling()) {
+
+ if (i == fixedKeys.end())
+ break;
+
+ if (name == strtoqstr(i->getName())) {
+ m_fixed->setSelected(item, true);
+ m_fixed->setCurrentItem(item);
+ return ;
+ }
+
+ ++i;
+ }
+
+ for (QListViewItem *item = m_metadata->firstChild();
+ item != 0; item = item->nextSibling()) {
+
+ if (item->text(0).lower() != name)
+ continue;
+
+ m_metadata->setSelected(item, true);
+ m_metadata->setCurrentItem(item);
+ return ;
+ }
+}
+*/
+
+}
+#include "DocumentMetaConfigurationPage.moc"
diff --git a/src/gui/configuration/DocumentMetaConfigurationPage.h b/src/gui/configuration/DocumentMetaConfigurationPage.h
new file mode 100644
index 0000000..db26f54
--- /dev/null
+++ b/src/gui/configuration/DocumentMetaConfigurationPage.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_DOCUMENTMETACONFIGURATIONPAGE_H_
+#define _RG_DOCUMENTMETACONFIGURATIONPAGE_H_
+
+#include "TabbedConfigurationPage.h"
+#include <qstring.h>
+#include <klocale.h>
+
+
+class QWidget;
+class KListView;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class HeadersConfigurationPage;
+
+/**
+ * Document Meta-information page
+ *
+ * (document-wide settings)
+ */
+class DocumentMetaConfigurationPage : public TabbedConfigurationPage
+{
+ Q_OBJECT
+public:
+ DocumentMetaConfigurationPage(RosegardenGUIDoc *doc,
+ QWidget *parent = 0, const char *name = 0);
+ virtual void apply();
+
+ static QString iconLabel() { return i18n("About"); }
+ static QString title() { return i18n("About"); }
+ static QString iconName() { return "contents"; }
+
+/* hjj: WHAT TO DO WITH THIS ?
+ void selectMetadata(QString name);
+*/
+
+protected:
+
+ //--------------- Data members ---------------------------------
+
+ HeadersConfigurationPage *m_headersPage;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/configuration/GeneralConfigurationPage.cpp b/src/gui/configuration/GeneralConfigurationPage.cpp
new file mode 100644
index 0000000..22915ed
--- /dev/null
+++ b/src/gui/configuration/GeneralConfigurationPage.cpp
@@ -0,0 +1,429 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "GeneralConfigurationPage.h"
+
+#include "document/ConfigGroups.h"
+#include "ConfigurationPage.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/editors/eventlist/EventView.h"
+#include "gui/editors/parameters/RosegardenParameterArea.h"
+#include "gui/studio/StudioControl.h"
+#include "gui/dialogs/ShowSequencerStatusDialog.h"
+#include "gui/seqmanager/SequenceManager.h"
+#include "sound/SoundDriver.h"
+#include "TabbedConfigurationPage.h"
+#include <kcombobox.h>
+#include <kconfig.h>
+#include <kfiledialog.h>
+#include <kmessagebox.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qfileinfo.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qpushbutton.h>
+#include <qspinbox.h>
+#include <qstring.h>
+#include <qtabwidget.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <qlayout.h>
+
+
+namespace Rosegarden
+{
+
+GeneralConfigurationPage::GeneralConfigurationPage(RosegardenGUIDoc *doc,
+ KConfig *cfg,
+ QWidget *parent, const char *name)
+ : TabbedConfigurationPage(cfg, parent, name),
+ m_doc(doc),
+ m_client(0),
+ m_countIn(0),
+ m_nameStyle(0)
+{
+ m_cfg->setGroup(GeneralOptionsConfigGroup);
+
+ QFrame *frame;
+ QGridLayout *layout;
+ QLabel *label = 0;
+ int row = 0;
+
+ //
+ // "Behavior" tab
+ //
+ frame = new QFrame(m_tabWidget);
+ layout = new QGridLayout(frame,
+ 6, 2, // nbrow, nbcol
+ 10, 5);
+
+ layout->setRowSpacing(row, 15);
+ ++row;
+
+ layout->addWidget(new QLabel(i18n("Double-click opens segment in"),
+ frame), row, 0);
+
+ m_client = new KComboBox(frame);
+ m_client->insertItem(i18n("Notation editor"));
+ m_client->insertItem(i18n("Matrix editor"));
+ m_client->insertItem(i18n("Event List editor"));
+ m_client->setCurrentItem(m_cfg->readUnsignedNumEntry("doubleclickclient", NotationView));
+
+ layout->addMultiCellWidget(m_client, row, row, 1, 2);
+ ++row;
+
+ layout->addWidget(new QLabel(i18n("Number of count-in measures when recording"),
+ frame), row, 0);
+
+ m_countIn = new QSpinBox(frame);
+ m_countIn->setValue(m_cfg->readUnsignedNumEntry("countinbars", 0));
+ m_countIn->setMaxValue(10);
+ m_countIn->setMinValue(0);
+ layout->addMultiCellWidget(m_countIn, row, row, 1, 2);
+ ++row;
+
+ layout->addWidget(new QLabel(i18n("Auto-save interval"), frame), row, 0);
+
+ m_autoSave = new KComboBox(frame);
+ m_autoSave->insertItem(i18n("Every 30 seconds"));
+ m_autoSave->insertItem(i18n("Every minute"));
+ m_autoSave->insertItem(i18n("Every five minutes"));
+ m_autoSave->insertItem(i18n("Every half an hour"));
+ m_autoSave->insertItem(i18n("Never"));
+
+ bool doAutoSave = m_cfg->readBoolEntry("autosave", true);
+ int autoSaveInterval = m_cfg->readUnsignedNumEntry("autosaveinterval", 300);
+ if (!doAutoSave || autoSaveInterval == 0) {
+ m_autoSave->setCurrentItem(4); // off
+ } else if (autoSaveInterval < 45) {
+ m_autoSave->setCurrentItem(0);
+ } else if (autoSaveInterval < 150) {
+ m_autoSave->setCurrentItem(1);
+ } else if (autoSaveInterval < 900) {
+ m_autoSave->setCurrentItem(2);
+ } else {
+ m_autoSave->setCurrentItem(3);
+ }
+
+ layout->addMultiCellWidget(m_autoSave, row, row, 1, 2);
+ ++row;
+
+ // JACK Transport
+ //
+#ifdef HAVE_LIBJACK
+ m_cfg->setGroup(SequencerOptionsConfigGroup);
+
+ label = new QLabel(i18n("Use JACK transport"), frame);
+ layout->addWidget(label, row, 0);
+
+ m_jackTransport = new QCheckBox(frame);
+ layout->addMultiCellWidget(m_jackTransport, row, row, 1, 2);
+
+// m_jackTransport->insertItem(i18n("Ignore JACK transport"));
+// m_jackTransport->insertItem(i18n("Sync"));
+
+ /*!!! Removed as not yet implemented
+ m_jackTransport->insertItem(i18n("Sync, and offer timebase master"));
+ */
+
+ bool jackMaster = m_cfg->readBoolEntry("jackmaster", false);
+ bool jackTransport = m_cfg->readBoolEntry("jacktransport", false);
+/*
+ if (jackTransport)
+ m_jackTransport->setCurrentItem(1);
+ else
+ m_jackTransport->setCurrentItem(0);
+*/
+ m_jackTransport->setChecked(jackTransport);
+
+ ++row;
+
+ m_cfg->setGroup(GeneralOptionsConfigGroup);
+#endif
+
+ layout->setRowSpacing(row, 20);
+ ++row;
+
+ layout->addWidget(new QLabel(i18n("Sequencer status"), frame), row, 0);
+
+ QString status(i18n("Unknown"));
+ SequenceManager *mgr = doc->getSequenceManager();
+ if (mgr) {
+ int driverStatus = mgr->getSoundDriverStatus() & (AUDIO_OK | MIDI_OK);
+ switch (driverStatus) {
+ case AUDIO_OK:
+ status = i18n("No MIDI, audio OK");
+ break;
+ case MIDI_OK:
+ status = i18n("MIDI OK, no audio");
+ break;
+ case AUDIO_OK | MIDI_OK:
+ status = i18n("MIDI OK, audio OK");
+ break;
+ default:
+ status = i18n("No driver");
+ break;
+ }
+ }
+
+ layout->addWidget(new QLabel(status, frame), row, 1);
+
+ QPushButton *showStatusButton = new QPushButton(i18n("Details..."),
+ frame);
+ QObject::connect(showStatusButton, SIGNAL(clicked()),
+ this, SLOT(slotShowStatus()));
+ layout->addWidget(showStatusButton, row, 2, Qt::AlignRight);
+ ++row;
+
+ layout->setRowStretch(row, 10);
+
+ addTab(frame, i18n("Behavior"));
+
+ //
+ // "Appearance" tab
+ //
+ frame = new QFrame(m_tabWidget);
+ layout = new QGridLayout(frame,
+ 7, 4, // nbrow, nbcol -- one extra row improves layout
+ 10, 5);
+
+ row = 0;
+
+ layout->setRowSpacing(row, 15);
+ ++row;
+
+ layout->addWidget(new QLabel(i18n("Side-bar parameter box layout"),
+ frame), row, 0);
+
+ m_sidebarStyle = new KComboBox(frame);
+ m_sidebarStyle->insertItem(i18n("Vertically stacked"),
+ RosegardenParameterArea::CLASSIC_STYLE);
+ m_sidebarStyle->insertItem(i18n("Tabbed"),
+ RosegardenParameterArea::TAB_BOX_STYLE);
+
+ m_sidebarStyle->setCurrentItem(m_cfg->readUnsignedNumEntry("sidebarstyle",
+ 0));
+ layout->addMultiCellWidget(m_sidebarStyle, row, row, 1, 3);
+ ++row;
+
+ layout->addWidget(new QLabel(i18n("Note name style"),
+ frame), row, 0);
+
+ m_nameStyle = new KComboBox(frame);
+ m_nameStyle->insertItem(i18n("Always use US names (e.g. quarter, 8th)"));
+ m_nameStyle->insertItem(i18n("Localized (where available)"));
+ m_nameStyle->setCurrentItem(m_cfg->readUnsignedNumEntry("notenamestyle", Local));
+ layout->addMultiCellWidget(m_nameStyle, row, row, 1, 3);
+ ++row;
+/*
+ layout->addWidget(new QLabel(i18n("Show tool context help in status bar"), frame), row, 0);
+
+ m_toolContextHelp = new QCheckBox(frame);
+ layout->addWidget(m_toolContextHelp, row, 1);
+ m_toolContextHelp->setChecked(m_cfg->readBoolEntry
+ ("toolcontexthelp", true));
+ ++row;
+*/
+
+ layout->addWidget(new QLabel(i18n("Show textured background on"), frame), row, 0);
+
+ m_backgroundTextures = new QCheckBox(i18n("Main window"), frame);
+ layout->addWidget(m_backgroundTextures, row, 1);
+
+ m_matrixBackgroundTextures = new QCheckBox(i18n("Matrix"), frame);
+ layout->addWidget(m_matrixBackgroundTextures, row, 2);
+
+ m_notationBackgroundTextures = new QCheckBox(i18n("Notation"), frame);
+ layout->addWidget(m_notationBackgroundTextures, row, 3);
+
+ m_backgroundTextures->setChecked(m_cfg->readBoolEntry
+ ("backgroundtextures", true));
+
+ m_cfg->setGroup(MatrixViewConfigGroup);
+ m_matrixBackgroundTextures->setChecked(m_cfg->readBoolEntry
+ ("backgroundtextures-1.6-plus", true));
+ m_cfg->setGroup(NotationViewConfigGroup);
+ m_notationBackgroundTextures->setChecked(m_cfg->readBoolEntry
+ ("backgroundtextures", true));
+ m_cfg->setGroup(GeneralOptionsConfigGroup);
+ ++row;
+
+ layout->addWidget(new QLabel(i18n("Use bundled Klearlook theme"), frame), row, 0);
+ m_globalStyle = new KComboBox(frame);
+ m_globalStyle->insertItem(i18n("Never"));
+ m_globalStyle->insertItem(i18n("When not running under KDE"));
+ m_globalStyle->insertItem(i18n("Always"));
+ m_globalStyle->setCurrentItem(m_cfg->readUnsignedNumEntry("Install Own Theme", 1));
+ layout->addMultiCellWidget(m_globalStyle, row, row, 1, 3);
+
+ ++row;
+
+ layout->setRowStretch(row, 10);
+
+ addTab(frame, i18n("Presentation"));
+
+}
+
+void
+GeneralConfigurationPage::slotShowStatus()
+{
+ ShowSequencerStatusDialog dialog(this);
+ dialog.exec();
+}
+
+void GeneralConfigurationPage::apply()
+{
+ m_cfg->setGroup(GeneralOptionsConfigGroup);
+
+ int countIn = getCountInSpin();
+ m_cfg->writeEntry("countinbars", countIn);
+
+ int client = getDblClickClient();
+ m_cfg->writeEntry("doubleclickclient", client);
+
+ int globalstyle = m_globalStyle->currentItem();
+ m_cfg->writeEntry("Install Own Theme", globalstyle);
+
+ int namestyle = getNoteNameStyle();
+ m_cfg->writeEntry("notenamestyle", namestyle);
+/*
+ m_cfg->writeEntry("toolcontexthelp", m_toolContextHelp->isChecked());
+*/
+ bool texturesChanged = false;
+ bool mainTextureChanged = false;
+ m_cfg->setGroup(GeneralOptionsConfigGroup);
+
+ if (m_cfg->readBoolEntry("backgroundtextures", true) !=
+ m_backgroundTextures->isChecked()) {
+ texturesChanged = true;
+ mainTextureChanged = true;
+ } else {
+ m_cfg->setGroup(MatrixViewConfigGroup);
+ if (m_cfg->readBoolEntry("backgroundtextures-1.6-plus", false) !=
+ m_matrixBackgroundTextures->isChecked()) {
+ texturesChanged = true;
+ } else {
+ m_cfg->setGroup(NotationViewConfigGroup);
+ if (m_cfg->readBoolEntry("backgroundtextures", true) !=
+ m_notationBackgroundTextures->isChecked()) {
+ texturesChanged = true;
+ }
+ }
+ }
+
+ m_cfg->setGroup(GeneralOptionsConfigGroup);
+ m_cfg->writeEntry("backgroundtextures", m_backgroundTextures->isChecked());
+
+ m_cfg->setGroup(MatrixViewConfigGroup);
+ m_cfg->writeEntry("backgroundtextures-1.6-plus", m_matrixBackgroundTextures->isChecked());
+
+ m_cfg->setGroup(NotationViewConfigGroup);
+ m_cfg->writeEntry("backgroundtextures", m_notationBackgroundTextures->isChecked());
+
+ m_cfg->setGroup(GeneralOptionsConfigGroup);
+
+ int sidebarStyle = m_sidebarStyle->currentItem();
+ m_cfg->writeEntry("sidebarstyle", sidebarStyle);
+ emit updateSidebarStyle(sidebarStyle);
+
+ unsigned int interval = 0;
+
+ if (m_autoSave->currentItem() == 4) {
+ m_cfg->writeEntry("autosave", false);
+ } else {
+ m_cfg->writeEntry("autosave", true);
+ if (m_autoSave->currentItem() == 0) {
+ interval = 30;
+ } else if (m_autoSave->currentItem() == 1) {
+ interval = 60;
+ } else if (m_autoSave->currentItem() == 2) {
+ interval = 300;
+ } else {
+ interval = 1800;
+ }
+ m_cfg->writeEntry("autosaveinterval", interval);
+ emit updateAutoSaveInterval(interval);
+ }
+
+#ifdef HAVE_LIBJACK
+ m_cfg->setGroup(SequencerOptionsConfigGroup);
+
+ // Write the JACK entry
+ //
+/*
+ int jackValue = m_jackTransport->currentItem();
+ bool jackTransport, jackMaster;
+
+ switch (jackValue) {
+ case 2:
+ jackTransport = true;
+ jackMaster = true;
+ break;
+
+ case 1:
+ jackTransport = true;
+ jackMaster = false;
+ break;
+
+ default:
+ jackValue = 0;
+
+ case 0:
+ jackTransport = false;
+ jackMaster = false;
+ break;
+ }
+*/
+
+ bool jackTransport = m_jackTransport->isChecked();
+ bool jackMaster = false;
+
+ int jackValue = 0; // 0 -> nothing, 1 -> sync, 2 -> master
+ if (jackTransport) jackValue = 1;
+
+ // Write the items
+ //
+ m_cfg->writeEntry("jacktransport", jackTransport);
+ m_cfg->writeEntry("jackmaster", jackMaster);
+
+ // Now send it
+ //
+ MappedEvent mEjackValue(MidiInstrumentBase, // InstrumentId
+ MappedEvent::SystemJackTransport,
+ MidiByte(jackValue));
+
+ StudioControl::sendMappedEvent(mEjackValue);
+#endif // HAVE_LIBJACK
+
+ if (mainTextureChanged) {
+ KMessageBox::information(this, i18n("Changes to the textured background in the main window will not take effect until you restart Rosegarden."));
+ }
+
+}
+
+}
+#include "GeneralConfigurationPage.moc"
diff --git a/src/gui/configuration/GeneralConfigurationPage.h b/src/gui/configuration/GeneralConfigurationPage.h
new file mode 100644
index 0000000..7d3203d
--- /dev/null
+++ b/src/gui/configuration/GeneralConfigurationPage.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_GENERALCONFIGURATIONPAGE_H_
+#define _RG_GENERALCONFIGURATIONPAGE_H_
+
+#include "TabbedConfigurationPage.h"
+#include "gui/editors/eventlist/EventView.h"
+#include <qstring.h>
+#include <qcheckbox.h>
+#include <qspinbox.h>
+#include <qcombobox.h>
+#include <qlineedit.h>
+#include <klocale.h>
+#include <kiconloader.h>
+
+class QWidget;
+class KConfig;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+
+
+/**
+ * General Rosegarden Configuration page
+ *
+ * (application-wide settings)
+ */
+class GeneralConfigurationPage : public TabbedConfigurationPage
+{
+ Q_OBJECT
+
+public:
+ enum DoubleClickClient
+ {
+ NotationView,
+ MatrixView,
+ EventView
+ };
+
+ enum NoteNameStyle
+ {
+ American,
+ Local
+ };
+
+ GeneralConfigurationPage(RosegardenGUIDoc *doc,
+ KConfig *cfg,
+ QWidget *parent=0, const char *name=0);
+
+ virtual void apply();
+
+ static QString iconLabel() { return i18n("General"); }
+ static QString title() { return i18n("General Configuration"); }
+ static QString iconName() { return "configure-general"; }
+
+signals:
+ void updateAutoSaveInterval(unsigned int);
+ void updateSidebarStyle(unsigned int);
+
+protected slots:
+ void slotShowStatus();
+
+protected:
+ int getCountInSpin() { return m_countIn->value(); }
+ int getDblClickClient() { return m_client->currentItem(); }
+ int getNoteNameStyle() { return m_nameStyle->currentItem(); }
+
+ //--------------- Data members ---------------------------------
+ RosegardenGUIDoc* m_doc;
+
+ QComboBox* m_client;
+ QSpinBox* m_countIn;
+ QCheckBox* m_toolContextHelp;
+ QCheckBox* m_backgroundTextures;
+ QCheckBox* m_notationBackgroundTextures;
+ QCheckBox* m_matrixBackgroundTextures;
+ QComboBox *m_autoSave;
+ QComboBox* m_nameStyle;
+ QComboBox* m_sidebarStyle;
+ QComboBox* m_globalStyle;
+ QCheckBox *m_jackTransport;
+
+};
+
+
+
+
+}
+
+#endif
diff --git a/src/gui/configuration/HeadersConfigurationPage.cpp b/src/gui/configuration/HeadersConfigurationPage.cpp
new file mode 100644
index 0000000..0571fb5
--- /dev/null
+++ b/src/gui/configuration/HeadersConfigurationPage.cpp
@@ -0,0 +1,294 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "HeadersConfigurationPage.h"
+
+#include "document/ConfigGroups.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/io/LilyPondExporter.h"
+#include "gui/widgets/CollapsingFrame.h"
+#include "misc/Strings.h"
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <klistview.h>
+#include <klocale.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qpushbutton.h>
+#include <qstring.h>
+#include <qtabwidget.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <qfont.h>
+
+namespace Rosegarden
+{
+
+HeadersConfigurationPage::HeadersConfigurationPage(QWidget *parent,
+ RosegardenGUIDoc *doc) :
+ QVBox(parent),
+ m_doc(doc)
+{
+ //
+ // LilyPond export: Printable headers
+ //
+
+ QGroupBox *headersBox = new QGroupBox
+ (1, Horizontal,
+ i18n("Printable headers"), this);
+ QFrame *frameHeaders = new QFrame(headersBox);
+ QGridLayout *layoutHeaders = new QGridLayout(frameHeaders, 10, 6, 10, 5);
+
+ // grab user headers from metadata
+ Configuration metadata = (&m_doc->getComposition())->getMetadata();
+ std::vector<std::string> propertyNames = metadata.getPropertyNames();
+ std::vector<PropertyName> fixedKeys =
+ CompositionMetadataKeys::getFixedKeys();
+
+ std::set<std::string> shown;
+
+ for (unsigned int index = 0; index < fixedKeys.size(); index++) {
+ std::string key = fixedKeys[index].getName();
+ std::string header = "";
+ for (unsigned int i = 0; i < propertyNames.size(); ++i) {
+ std::string property = propertyNames [i];
+ if (property == key) {
+ header = metadata.get<String>(property);
+ }
+ }
+
+ unsigned int row = 0, col = 0, width = 1;
+ QLineEdit *editHeader = new QLineEdit(strtoqstr( header ), frameHeaders);
+ QString trName;
+ if (key == headerDedication) {
+ m_editDedication = editHeader;
+ row = 0; col = 2; width = 2;
+ trName = i18n("Dedication");
+ } else if (key == headerTitle) {
+ m_editTitle = editHeader;
+ row = 1; col = 1; width = 4;
+ trName = i18n("Title");
+ } else if (key == headerSubtitle) {
+ m_editSubtitle = editHeader;
+ row = 2; col = 1; width = 4;
+ trName = i18n("Subtitle");
+ } else if (key == headerSubsubtitle) {
+ m_editSubsubtitle = editHeader;
+ row = 3; col = 2; width = 2;
+ trName = i18n("Subsubtitle");
+ } else if (key == headerPoet) {
+ m_editPoet = editHeader;
+ row = 4; col = 0; width = 2;
+ trName = i18n("Poet");
+ } else if (key == headerInstrument) {
+ m_editInstrument = editHeader;
+ row = 4; col = 2; width = 2;
+ trName = i18n("Instrument");
+ } else if (key == headerComposer) {
+ m_editComposer = editHeader;
+ row = 4; col = 4; width = 2;
+ trName = i18n("Composer");
+ } else if (key == headerMeter) {
+ m_editMeter = editHeader;
+ row = 5; col = 0; width = 3;
+ trName = i18n("Meter");
+ } else if (key == headerArranger) {
+ m_editArranger = editHeader;
+ row = 5; col = 3; width = 3;
+ trName = i18n("Arranger");
+ } else if (key == headerPiece) {
+ m_editPiece = editHeader;
+ row = 6; col = 0; width = 3;
+ trName = i18n("Piece");
+ } else if (key == headerOpus) {
+ m_editOpus = editHeader;
+ row = 6; col = 3; width = 3;
+ trName = i18n("Opus");
+ } else if (key == headerCopyright) {
+ m_editCopyright = editHeader;
+ row = 8; col = 1; width = 4;
+ trName = i18n("Copyright");
+ } else if (key == headerTagline) {
+ m_editTagline = editHeader;
+ row = 9; col = 1; width = 4;
+ trName = i18n("Tagline");
+ }
+
+ // editHeader->setReadOnly( true );
+ editHeader->setAlignment( (col == 0 ? Qt::AlignLeft : (col >= 3 ? Qt::AlignRight : Qt::AlignCenter) ));
+
+ layoutHeaders->addMultiCellWidget(editHeader, row, row, col, col+(width-1) );
+
+ //
+ // ToolTips
+ //
+ QToolTip::add( editHeader, trName );
+
+ shown.insert(key);
+ }
+ QLabel *separator = new QLabel(i18n("The composition comes here."), frameHeaders);
+ separator->setAlignment( Qt::AlignCenter );
+ layoutHeaders->addMultiCellWidget(separator, 7, 7, 1, 4 );
+
+ //
+ // LilyPond export: Non-printable headers
+ //
+
+ // set default expansion to false for this group -- what a faff
+ KConfig *config = kapp->config();
+ QString groupTemp = config->group();
+ config->setGroup("CollapsingFrame");
+ bool expanded = config->readBoolEntry("nonprintableheaders", false);
+ config->writeEntry("nonprintableheaders", expanded);
+ config->setGroup(groupTemp);
+
+ CollapsingFrame *otherHeadersBox = new CollapsingFrame
+ (i18n("Non-printable headers"), this, "nonprintableheaders");
+ QFrame *frameOtherHeaders = new QFrame(otherHeadersBox);
+ otherHeadersBox->setWidgetFill(true);
+ QFont font(otherHeadersBox->font());
+ font.setBold(false);
+ otherHeadersBox->setFont(font);
+ otherHeadersBox->setWidget(frameOtherHeaders);
+
+ QGridLayout *layoutOtherHeaders = new QGridLayout(frameOtherHeaders, 2, 2, 10, 5);
+
+ m_metadata = new KListView(frameOtherHeaders);
+ m_metadata->addColumn(i18n("Name"));
+ m_metadata->addColumn(i18n("Value"));
+ m_metadata->setFullWidth(true);
+ m_metadata->setItemsRenameable(true);
+ m_metadata->setRenameable(0);
+ m_metadata->setRenameable(1);
+ m_metadata->setItemMargin(5);
+ m_metadata->setDefaultRenameAction(QListView::Accept);
+ m_metadata->setShowSortIndicator(true);
+
+ std::vector<std::string> names(metadata.getPropertyNames());
+
+ for (unsigned int i = 0; i < names.size(); ++i) {
+
+ if (shown.find(names[i]) != shown.end())
+ continue;
+
+ QString name(strtoqstr(names[i]));
+
+ // property names stored in lower case
+ name = name.left(1).upper() + name.right(name.length() - 1);
+
+ new KListViewItem(m_metadata, name,
+ strtoqstr(metadata.get<String>(names[i])));
+
+ shown.insert(names[i]);
+ }
+
+ layoutOtherHeaders->addMultiCellWidget(m_metadata, 0, 0, 0, 1);
+
+ QPushButton* addPropButton = new QPushButton(i18n("Add New Property"),
+ frameOtherHeaders);
+ layoutOtherHeaders->addWidget(addPropButton, 1, 0, Qt::AlignHCenter);
+
+ QPushButton* deletePropButton = new QPushButton(i18n("Delete Property"),
+ frameOtherHeaders);
+ layoutOtherHeaders->addWidget(deletePropButton, 1, 1, Qt::AlignHCenter);
+
+ connect(addPropButton, SIGNAL(clicked()),
+ this, SLOT(slotAddNewProperty()));
+
+ connect(deletePropButton, SIGNAL(clicked()),
+ this, SLOT(slotDeleteProperty()));
+}
+
+void
+HeadersConfigurationPage::slotAddNewProperty()
+{
+ QString propertyName;
+ int i = 0;
+
+ while (1) {
+ propertyName =
+ (i > 0 ? i18n("{new property %1}").arg(i) : i18n("{new property}"));
+ if (!m_doc->getComposition().getMetadata().has(qstrtostr(propertyName)) &&
+ m_metadata->findItem(qstrtostr(propertyName),0) == 0)
+ break;
+ ++i;
+ }
+
+ new KListViewItem(m_metadata, propertyName, i18n("{undefined}"));
+}
+
+void
+HeadersConfigurationPage::slotDeleteProperty()
+{
+ delete m_metadata->currentItem();
+}
+
+void HeadersConfigurationPage::apply()
+{
+ KConfig *config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+
+ // If one of the items still has focus, it won't remember edits.
+ // Switch between two fields in order to lose the current focus.
+ m_editTitle->setFocus();
+ m_metadata->setFocus();
+
+ //
+ // Update header fields
+ //
+
+ Configuration &metadata = (&m_doc->getComposition())->getMetadata();
+ metadata.clear();
+
+ metadata.set<String>(CompositionMetadataKeys::Dedication, qstrtostr(m_editDedication->text()));
+ metadata.set<String>(CompositionMetadataKeys::Title, qstrtostr(m_editTitle->text()));
+ metadata.set<String>(CompositionMetadataKeys::Subtitle, qstrtostr(m_editSubtitle->text()));
+ metadata.set<String>(CompositionMetadataKeys::Subsubtitle, qstrtostr(m_editSubsubtitle->text()));
+ metadata.set<String>(CompositionMetadataKeys::Poet, qstrtostr(m_editPoet->text()));
+ metadata.set<String>(CompositionMetadataKeys::Composer, qstrtostr(m_editComposer->text()));
+ metadata.set<String>(CompositionMetadataKeys::Meter, qstrtostr(m_editMeter->text()));
+ metadata.set<String>(CompositionMetadataKeys::Opus, qstrtostr(m_editOpus->text()));
+ metadata.set<String>(CompositionMetadataKeys::Arranger, qstrtostr(m_editArranger->text()));
+ metadata.set<String>(CompositionMetadataKeys::Instrument, qstrtostr(m_editInstrument->text()));
+ metadata.set<String>(CompositionMetadataKeys::Piece, qstrtostr(m_editPiece->text()));
+ metadata.set<String>(CompositionMetadataKeys::Copyright, qstrtostr(m_editCopyright->text()));
+ metadata.set<String>(CompositionMetadataKeys::Tagline, qstrtostr(m_editTagline->text()));
+
+ for (QListViewItem *item = m_metadata->firstChild();
+ item != 0; item = item->nextSibling()) {
+
+ metadata.set<String>(qstrtostr(item->text(0).lower()),
+ qstrtostr(item->text(1)));
+ }
+
+ m_doc->slotDocumentModified();
+}
+
+}
+#include "HeadersConfigurationPage.moc"
diff --git a/src/gui/configuration/HeadersConfigurationPage.h b/src/gui/configuration/HeadersConfigurationPage.h
new file mode 100644
index 0000000..403d412
--- /dev/null
+++ b/src/gui/configuration/HeadersConfigurationPage.h
@@ -0,0 +1,80 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_HEADERSCONFIGURATIONPAGE_H_
+#define _RG_HEADERSCONFIGURATIONPAGE_H_
+
+#include <qvbox.h>
+
+class QVBox;
+class QWidget;
+class QLineEdit;
+class KListView;
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+
+class HeadersConfigurationPage : public QVBox
+{
+ Q_OBJECT
+
+public:
+ HeadersConfigurationPage(QWidget *parent = 0,
+ RosegardenGUIDoc *doc = 0);
+
+public slots:
+ void apply();
+
+protected slots:
+ void slotAddNewProperty();
+ void slotDeleteProperty();
+
+protected:
+ RosegardenGUIDoc *m_doc;
+
+ // Header fields
+ QLineEdit *m_editDedication;
+ QLineEdit *m_editTitle;
+ QLineEdit *m_editSubtitle;
+ QLineEdit *m_editSubsubtitle;
+ QLineEdit *m_editPoet;
+ QLineEdit *m_editComposer;
+ QLineEdit *m_editMeter;
+ QLineEdit *m_editOpus;
+ QLineEdit *m_editArranger;
+ QLineEdit *m_editInstrument;
+ QLineEdit *m_editPiece;
+ QLineEdit *m_editCopyright;
+ QLineEdit *m_editTagline;
+
+ KListView *m_metadata;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/configuration/LatencyConfigurationPage.cpp b/src/gui/configuration/LatencyConfigurationPage.cpp
new file mode 100644
index 0000000..ff89edb
--- /dev/null
+++ b/src/gui/configuration/LatencyConfigurationPage.cpp
@@ -0,0 +1,157 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "LatencyConfigurationPage.h"
+#include <qlayout.h>
+
+#include "document/ConfigGroups.h"
+#include "ConfigurationPage.h"
+#include "document/RosegardenGUIDoc.h"
+#include "TabbedConfigurationPage.h"
+#include <kconfig.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qslider.h>
+#include <qstring.h>
+#include <qtabwidget.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+LatencyConfigurationPage::LatencyConfigurationPage(RosegardenGUIDoc *doc,
+ KConfig *cfg,
+ QWidget *parent,
+ const char *name)
+ : TabbedConfigurationPage(doc, cfg, parent, name)
+{
+ // Configuration &config = doc->getConfiguration();
+ m_cfg->setGroup(LatencyOptionsConfigGroup);
+
+#ifdef NOT_DEFINED
+#ifdef HAVE_LIBJACK
+
+ frame = new QFrame(m_tabWidget, i18n("JACK latency"));
+ layout = new QGridLayout(frame, 6, 5, 10, 10);
+
+ layout->addMultiCellWidget(new QLabel(i18n("Use the \"Fetch JACK latencies\" button to discover the latency values set at\nthe sequencer. It's recommended that you use the returned values but it's also\npossible to override them manually using the sliders. Note that if you change\nyour JACK server parameters you should always fetch the latency values again.\nThe latency values will be stored by Rosegarden for use next time."), frame),
+ 0, 0,
+ 0, 3);
+
+ layout->addWidget(new QLabel(i18n("JACK playback latency (in ms)"), frame), 1, 0);
+ layout->addWidget(new QLabel(i18n("JACK record latency (in ms)"), frame), 3, 0);
+
+ m_fetchLatencyValues = new QPushButton(i18n("Fetch JACK latencies"),
+ frame);
+
+ layout->addWidget(m_fetchLatencyValues, 1, 3);
+
+ connect(m_fetchLatencyValues, SIGNAL(released()),
+ SLOT(slotFetchLatencyValues()));
+
+ int jackPlaybackValue = (m_cfg->readLongNumEntry(
+ "jackplaybacklatencyusec", 0) / 1000) +
+ (m_cfg->readLongNumEntry(
+ "jackplaybacklatencysec", 0) * 1000);
+
+ m_jackPlayback = new QSlider(Horizontal, frame);
+ m_jackPlayback->setTickmarks(QSlider::Below);
+ layout->addMultiCellWidget(m_jackPlayback, 3, 3, 2, 3);
+
+ QLabel *jackPlaybackLabel = new QLabel(QString("%1").arg(jackPlaybackValue),
+ frame);
+ layout->addWidget(jackPlaybackLabel, 2, 2, Qt::AlignHCenter);
+ connect(m_jackPlayback, SIGNAL(valueChanged(int)),
+ jackPlaybackLabel, SLOT(setNum(int)));
+
+ m_jackPlayback->setMinValue(0);
+ layout->addWidget(new QLabel("0", frame), 3, 1, Qt::AlignRight);
+
+ m_jackPlayback->setMaxValue(500);
+ layout->addWidget(new QLabel("500", frame), 3, 4, Qt::AlignLeft);
+
+ m_jackPlayback->setValue(jackPlaybackValue);
+
+ int jackRecordValue = (m_cfg->readLongNumEntry(
+ "jackrecordlatencyusec", 0) / 1000) +
+ (m_cfg->readLongNumEntry(
+ "jackrecordlatencysec", 0) * 1000);
+
+ m_jackRecord = new QSlider(Horizontal, frame);
+ m_jackRecord->setTickmarks(QSlider::Below);
+ layout->addMultiCellWidget(m_jackRecord, 5, 5, 2, 3);
+
+ QLabel *jackRecordLabel = new QLabel(QString("%1").arg(jackRecordValue),
+ frame);
+ layout->addWidget(jackRecordLabel, 4, 2, Qt::AlignHCenter);
+ connect(m_jackRecord, SIGNAL(valueChanged(int)),
+ jackRecordLabel, SLOT(setNum(int)));
+
+ m_jackRecord->setMinValue(0);
+ layout->addWidget(new QLabel("0", frame), 5, 1, Qt::AlignRight);
+
+ m_jackRecord->setMaxValue(500);
+ m_jackRecord->setValue(jackRecordValue);
+ layout->addWidget(new QLabel("500", frame), 5, 4, Qt::AlignLeft);
+
+ addTab(frame, i18n("JACK Latency"));
+#endif // HAVE_LIBJACK
+#endif // NOT_DEFINED
+
+}
+
+void LatencyConfigurationPage::apply()
+{
+ m_cfg->setGroup(LatencyOptionsConfigGroup);
+
+#ifdef HAVE_LIBJACK
+
+ int jackPlayback = getJACKPlaybackValue();
+ m_cfg->writeEntry("jackplaybacklatencysec", jackPlayback / 1000);
+ m_cfg->writeEntry("jackplaybacklatencyusec", jackPlayback * 1000);
+
+ int jackRecord = getJACKRecordValue();
+ m_cfg->writeEntry("jackrecordlatencysec", jackRecord / 1000);
+ m_cfg->writeEntry("jackrecordlatencyusec", jackRecord * 1000);
+
+#endif // HAVE_LIBJACK
+}
+
+void LatencyConfigurationPage::slotFetchLatencyValues()
+{
+ int jackPlaybackValue = m_doc->getAudioPlayLatency().msec() +
+ m_doc->getAudioPlayLatency().sec * 1000;
+
+ m_jackPlayback->setValue(jackPlaybackValue);
+
+ int jackRecordValue = m_doc->getAudioRecordLatency().msec() +
+ m_doc->getAudioRecordLatency().sec * 1000;
+ m_jackRecord->setValue(jackRecordValue);
+}
+
+}
+#include "LatencyConfigurationPage.moc"
diff --git a/src/gui/configuration/LatencyConfigurationPage.h b/src/gui/configuration/LatencyConfigurationPage.h
new file mode 100644
index 0000000..6caba88
--- /dev/null
+++ b/src/gui/configuration/LatencyConfigurationPage.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_LATENCYCONFIGURATIONPAGE_H_
+#define _RG_LATENCYCONFIGURATIONPAGE_H_
+
+#include "TabbedConfigurationPage.h"
+#include <qstring.h>
+#include <klocale.h>
+#include <qslider.h>
+
+
+class QWidget;
+class QPushButton;
+class KConfig;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+
+
+/**
+ * Latency Configuration page
+ *
+ * (application-wide settings)
+ */
+class LatencyConfigurationPage : public TabbedConfigurationPage
+{
+ Q_OBJECT
+
+public:
+ LatencyConfigurationPage(RosegardenGUIDoc *doc,
+ KConfig *cfg,
+ QWidget *parent=0, const char *name=0);
+
+ virtual void apply();
+
+ static QString iconLabel() { return i18n("Latency"); }
+ static QString title() { return i18n("Sequencer Latency"); }
+
+ int getJACKPlaybackValue() { return m_jackPlayback->value(); }
+ int getJACKRecordValue() { return m_jackRecord->value(); }
+
+protected slots:
+ // Get the latest latency values from the sequencer
+ //
+ void slotFetchLatencyValues();
+
+protected:
+
+ //--------------- Data members ---------------------------------
+
+ QSlider* m_jackPlayback;
+ QSlider* m_jackRecord;
+
+ QPushButton* m_fetchLatencyValues;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/configuration/MIDIConfigurationPage.cpp b/src/gui/configuration/MIDIConfigurationPage.cpp
new file mode 100644
index 0000000..3d46841
--- /dev/null
+++ b/src/gui/configuration/MIDIConfigurationPage.cpp
@@ -0,0 +1,400 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "MIDIConfigurationPage.h"
+
+#include "sound/Midi.h"
+#include "sound/SoundDriver.h"
+#include "document/ConfigGroups.h"
+#include "base/MidiProgram.h"
+#include "base/Studio.h"
+#include "ConfigurationPage.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/dialogs/ShowSequencerStatusDialog.h"
+#include "gui/seqmanager/SequenceManager.h"
+#include "gui/application/RosegardenApplication.h"
+#include "gui/studio/StudioControl.h"
+#include "sound/MappedEvent.h"
+#include "TabbedConfigurationPage.h"
+#include <kcombobox.h>
+#include <kconfig.h>
+#include <kfiledialog.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qobject.h>
+#include <qpushbutton.h>
+#include <qlayout.h>
+#include <qslider.h>
+#include <qspinbox.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qtabwidget.h>
+#include <qtooltip.h>
+#include <qwidget.h>
+#include <qcheckbox.h>
+#include <qhbox.h>
+
+namespace Rosegarden
+{
+
+MIDIConfigurationPage::MIDIConfigurationPage(
+ RosegardenGUIDoc *doc,
+ KConfig *cfg,
+ QWidget *parent,
+ const char *name):
+ TabbedConfigurationPage(cfg, parent, name),
+ m_midiPitchOctave(0)
+{
+ // set the document in the super class
+ m_doc = doc;
+
+ // ---------------- General tab ------------------
+ //
+ QFrame *frame = new QFrame(m_tabWidget);
+ QGridLayout *layout = new QGridLayout(frame, 9, 4, 10, 5);
+
+ int row = 0;
+
+ layout->setRowSpacing(row, 15);
+ ++row;
+
+ QLabel *label = 0;
+
+ m_cfg->setGroup(GeneralOptionsConfigGroup);
+
+ layout->addMultiCellWidget(new QLabel(i18n("Base octave number for MIDI pitch display"),
+ frame), row, row, 0, 1);
+
+ m_midiPitchOctave = new QSpinBox(frame);
+ m_midiPitchOctave->setMaxValue(10);
+ m_midiPitchOctave->setMinValue( -10);
+ m_midiPitchOctave->setValue(m_cfg->readNumEntry("midipitchoctave", -2));
+ layout->addMultiCellWidget(m_midiPitchOctave, row, row, 2, 3);
+ ++row;
+
+ layout->setRowSpacing(row, 20);
+ ++row;
+
+ m_cfg->setGroup(GeneralOptionsConfigGroup);
+
+ layout->addMultiCellWidget(new QLabel(i18n("Always use default studio when loading files"),
+ frame), row, row, 0, 1);
+
+ m_studio = new QCheckBox(frame);
+ m_studio->setChecked(m_cfg->readBoolEntry("alwaysusedefaultstudio", false));
+ layout->addWidget(m_studio, row, 2);
+ ++row;
+
+ // Send Controllers
+ //
+ m_cfg->setGroup(SequencerOptionsConfigGroup);
+
+ label = new QLabel(i18n("Send all MIDI Controllers at start of each playback"), frame);
+
+ QString controllerTip = i18n("Rosegarden can send all MIDI Controllers (Pan, Reverb etc) to all MIDI devices every\ntime you hit play if you so wish. Please note that this option will usually incur a\ndelay at the start of playback due to the amount of data being transmitted.");
+ QToolTip::add
+ (label, controllerTip);
+ layout->addMultiCellWidget(label, row, row, 0, 1);
+
+ m_sendControllersAtPlay = new QCheckBox(frame);
+ bool sendControllers = m_cfg->readBoolEntry("alwayssendcontrollers", false);
+ m_sendControllersAtPlay->setChecked(sendControllers);
+ QToolTip::add
+ (m_sendControllersAtPlay, controllerTip);
+ layout->addWidget(m_sendControllersAtPlay, row, 2);
+ ++row;
+
+ // Timer selection
+ //
+ m_cfg->setGroup(SequencerOptionsConfigGroup);
+
+ label = new QLabel(i18n("Sequencer timing source"), frame);
+ layout->addMultiCellWidget(label, row, row, 0, 1);
+
+ m_timer = new KComboBox(frame);
+ layout->addMultiCellWidget(m_timer, row, row, 2, 3);
+
+ QStringList timers = m_doc->getTimers();
+ m_origTimer = m_doc->getCurrentTimer();
+ QString currentTimer = m_cfg->readEntry("timer", m_origTimer);
+
+ for (unsigned int i = 0; i < timers.size(); ++i) {
+ m_timer->insertItem(timers[i]);
+ if (timers[i] == currentTimer)
+ m_timer->setCurrentItem(i);
+ }
+
+ ++row;
+
+ layout->setRowSpacing(row, 20);
+ ++row;
+
+ m_cfg->setGroup(SequencerOptionsConfigGroup);
+
+ // SoundFont loading
+ //
+ QLabel* lbl = new QLabel(i18n("Load SoundFont to SoundBlaster card at startup"), frame);
+ QString tooltip = i18n("Check this box to enable soundfont loading on EMU10K-based cards when Rosegarden is launched");
+ QToolTip::add(lbl, tooltip);
+ layout->addMultiCellWidget(lbl, row, row, 0, 1);
+
+ m_sfxLoadEnabled = new QCheckBox(frame);
+ layout->addWidget(m_sfxLoadEnabled, row, 2);
+ QToolTip::add(m_sfxLoadEnabled, tooltip);
+ ++row;
+
+ layout->addWidget(new QLabel(i18n("Path to 'asfxload' or 'sfxload' command"), frame), row, 0);
+ m_sfxLoadPath = new QLineEdit(m_cfg->readEntry("sfxloadpath", "/bin/sfxload"), frame);
+ layout->addMultiCellWidget(m_sfxLoadPath, row, row, 1, 2);
+ m_sfxLoadChoose = new QPushButton("Choose...", frame);
+ layout->addWidget(m_sfxLoadChoose, row, 3);
+ ++row;
+
+ layout->addWidget(new QLabel(i18n("SoundFont"), frame), row, 0);
+ m_soundFontPath = new QLineEdit(m_cfg->readEntry("soundfontpath", ""), frame);
+ layout->addMultiCellWidget(m_soundFontPath, row, row, 1, 2);
+ m_soundFontChoose = new QPushButton("Choose...", frame);
+ layout->addWidget(m_soundFontChoose, row, 3);
+ ++row;
+
+ bool sfxLoadEnabled = m_cfg->readBoolEntry("sfxloadenabled", false);
+ m_sfxLoadEnabled->setChecked(sfxLoadEnabled);
+ if (!sfxLoadEnabled) {
+ m_sfxLoadPath->setEnabled(false);
+ m_sfxLoadChoose->setEnabled(false);
+ m_soundFontPath->setEnabled(false);
+ m_soundFontChoose->setEnabled(false);
+ }
+
+ connect(m_sfxLoadEnabled, SIGNAL(toggled(bool)),
+ this, SLOT(slotSoundFontToggled(bool)));
+
+ connect(m_sfxLoadChoose, SIGNAL(clicked()),
+ this, SLOT(slotSfxLoadPathChoose()));
+
+ connect(m_soundFontChoose, SIGNAL(clicked()),
+ this, SLOT(slotSoundFontChoose()));
+
+ layout->setRowStretch(row, 10);
+
+ addTab(frame, i18n("General"));
+
+ m_cfg->setGroup(SequencerOptionsConfigGroup);
+
+ // -------------- Synchronisation tab -----------------
+ //
+ frame = new QFrame(m_tabWidget);
+ layout = new QGridLayout(frame, 7, 2, 10, 5);
+
+ row = 0;
+
+ layout->setRowSpacing(row, 15);
+ ++row;
+
+ // MIDI Clock and System Realtime Messages
+ //
+ label = new QLabel(i18n("MIDI Clock and System messages"), frame);
+ layout->addWidget(label, row, 0);
+ m_midiSync = new KComboBox(frame);
+ layout->addWidget(m_midiSync, row, 1);
+
+ m_midiSync->insertItem(i18n("Off"));
+ m_midiSync->insertItem(i18n("Send MIDI Clock, Start and Stop"));
+ m_midiSync->insertItem(i18n("Accept Start, Stop and Continue"));
+
+ int midiClock = m_cfg->readNumEntry("midiclock", 0);
+ if (midiClock < 0 || midiClock > 2)
+ midiClock = 0;
+ m_midiSync->setCurrentItem(midiClock);
+
+ ++row;
+
+ // MMC Transport
+ //
+ label = new QLabel(i18n("MIDI Machine Control mode"), frame);
+ layout->addWidget(label, row, 0);
+
+ m_mmcTransport = new KComboBox(frame);
+ layout->addWidget(m_mmcTransport, row, 1); //, Qt::AlignHCenter);
+
+ m_mmcTransport->insertItem(i18n("Off"));
+ m_mmcTransport->insertItem(i18n("MMC Master"));
+ m_mmcTransport->insertItem(i18n("MMC Slave"));
+
+ int mmcMode = m_cfg->readNumEntry("mmcmode", 0);
+ if (mmcMode < 0 || mmcMode > 2)
+ mmcMode = 0;
+ m_mmcTransport->setCurrentItem(mmcMode);
+
+ ++row;
+
+ // MTC transport
+ //
+ label = new QLabel(i18n("MIDI Time Code mode"), frame);
+ layout->addWidget(label, row, 0);
+
+ m_mtcTransport = new KComboBox(frame);
+ layout->addWidget(m_mtcTransport, row, 1);
+
+ m_mtcTransport->insertItem(i18n("Off"));
+ m_mtcTransport->insertItem(i18n("MTC Master"));
+ m_mtcTransport->insertItem(i18n("MTC Slave"));
+
+ int mtcMode = m_cfg->readNumEntry("mtcmode", 0);
+ if (mtcMode < 0 || mtcMode > 2)
+ mtcMode = 0;
+ m_mtcTransport->setCurrentItem(mtcMode);
+
+ ++row;
+
+ QHBox *hbox = new QHBox(frame);
+ hbox->setSpacing(5);
+ layout->addMultiCellWidget(hbox, row, row, 0, 1);
+
+ label = new QLabel(i18n("Automatically connect sync output to all devices in use"), hbox);
+// layout->addWidget(label, row, 0);
+ m_midiSyncAuto = new QCheckBox(hbox);
+// layout->addWidget(m_midiSyncAuto, row, 1);
+
+ m_midiSyncAuto->setChecked(m_cfg->readBoolEntry("midisyncautoconnect", false));
+
+ ++row;
+
+ layout->setRowStretch(row, 10);
+
+ addTab(frame, i18n("MIDI Sync"));
+}
+
+
+void
+MIDIConfigurationPage::slotSoundFontToggled(bool isChecked)
+{
+ m_sfxLoadPath->setEnabled(isChecked);
+ m_sfxLoadChoose->setEnabled(isChecked);
+ m_soundFontPath->setEnabled(isChecked);
+ m_soundFontChoose->setEnabled(isChecked);
+}
+
+void
+MIDIConfigurationPage::slotSfxLoadPathChoose()
+{
+ QString path = KFileDialog::getOpenFileName(":SFXLOAD", QString::null, this, i18n("sfxload path"));
+ m_sfxLoadPath->setText(path);
+}
+
+void
+MIDIConfigurationPage::slotSoundFontChoose()
+{
+ QString path = KFileDialog::getOpenFileName(":SOUNDFONTS", "*.sb *.sf2 *.SF2 *.SB", this, i18n("Soundfont path"));
+ m_soundFontPath->setText(path);
+}
+
+void
+MIDIConfigurationPage::apply()
+{
+ m_cfg->setGroup(SequencerOptionsConfigGroup);
+
+ m_cfg->writeEntry("alwayssendcontrollers",
+ m_sendControllersAtPlay->isChecked());
+
+ m_cfg->writeEntry("sfxloadenabled", m_sfxLoadEnabled->isChecked());
+ m_cfg->writeEntry("sfxloadpath", m_sfxLoadPath->text());
+ m_cfg->writeEntry("soundfontpath", m_soundFontPath->text());
+
+ m_cfg->writeEntry("timer", m_timer->currentText());
+ if (m_timer->currentText() != m_origTimer) {
+ m_doc->setCurrentTimer(m_timer->currentText());
+ }
+
+ // Write the entries
+ //
+ m_cfg->writeEntry("mmcmode", m_mmcTransport->currentItem());
+ m_cfg->writeEntry("mtcmode", m_mtcTransport->currentItem());
+ m_cfg->writeEntry("midisyncautoconnect", m_midiSyncAuto->isChecked());
+
+ // Now send
+ //
+ MappedEvent mEmccValue(MidiInstrumentBase, // InstrumentId
+ MappedEvent::SystemMMCTransport,
+ MidiByte(m_mmcTransport->currentItem()));
+
+ StudioControl::sendMappedEvent(mEmccValue);
+
+ MappedEvent mEmtcValue(MidiInstrumentBase, // InstrumentId
+ MappedEvent::SystemMTCTransport,
+ MidiByte(m_mtcTransport->currentItem()));
+
+ StudioControl::sendMappedEvent(mEmtcValue);
+
+ MappedEvent mEmsaValue(MidiInstrumentBase, // InstrumentId
+ MappedEvent::SystemMIDISyncAuto,
+ MidiByte(m_midiSyncAuto->isChecked() ? 1 : 0));
+
+ StudioControl::sendMappedEvent(mEmsaValue);
+
+
+ // ------------- MIDI Clock and System messages ------------
+ //
+ int midiClock = m_midiSync->currentItem();
+ m_cfg->writeEntry("midiclock", midiClock);
+
+ // Now send it (OLD METHOD - to be removed)
+ //!!! No, don't remove -- this controls SPP as well doesn't it?
+ //
+ MappedEvent mEMIDIClock(MidiInstrumentBase, // InstrumentId
+ MappedEvent::SystemMIDIClock,
+ MidiByte(midiClock));
+
+ StudioControl::sendMappedEvent(mEMIDIClock);
+
+
+ // Now update the metronome mapped segment with new clock ticks
+ // if needed.
+ //
+ Studio &studio = m_doc->getStudio();
+ const MidiMetronome *metronome = studio.
+ getMetronomeFromDevice(studio.getMetronomeDevice());
+
+ if (metronome) {
+ InstrumentId instrument = metronome->getInstrument();
+ m_doc->getSequenceManager()->metronomeChanged(instrument, true);
+ }
+
+ m_cfg->setGroup(GeneralOptionsConfigGroup);
+
+ bool deftstudio = getUseDefaultStudio();
+ m_cfg->writeEntry("alwaysusedefaultstudio", deftstudio);
+
+ int octave = m_midiPitchOctave->value();
+ m_cfg->writeEntry("midipitchoctave", octave);
+}
+
+}
+#include "MIDIConfigurationPage.moc"
diff --git a/src/gui/configuration/MIDIConfigurationPage.h b/src/gui/configuration/MIDIConfigurationPage.h
new file mode 100644
index 0000000..243f041
--- /dev/null
+++ b/src/gui/configuration/MIDIConfigurationPage.h
@@ -0,0 +1,104 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_MIDICONFIGURATIONPAGE_H_
+#define _RG_MIDICONFIGURATIONPAGE_H_
+
+#include "TabbedConfigurationPage.h"
+#include <qstring.h>
+#include <klocale.h>
+#include <qlineedit.h>
+#include <qcheckbox.h>
+
+
+class QWidget;
+class QSpinBox;
+class QSlider;
+class QPushButton;
+class QLabel;
+class QComboBox;
+class KConfig;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+
+
+class MIDIConfigurationPage : public TabbedConfigurationPage
+{
+ Q_OBJECT
+public:
+ MIDIConfigurationPage(RosegardenGUIDoc *doc,
+ KConfig *cfg,
+ QWidget *parent=0,
+ const char *name=0);
+
+ virtual void apply();
+
+ static QString iconLabel() { return i18n("MIDI"); }
+ static QString title() { return i18n("MIDI Settings"); }
+ static QString iconName() { return "configure-midi"; }
+
+protected slots:
+ void slotSoundFontToggled(bool);
+ void slotSfxLoadPathChoose();
+ void slotSoundFontChoose();
+
+protected:
+ bool getUseDefaultStudio() { return m_studio->isChecked(); }
+
+ //--------------- Data members ---------------------------------
+
+ // General
+ QCheckBox *m_sendControllersAtPlay;
+
+ QCheckBox *m_sfxLoadEnabled;
+ QLineEdit *m_sfxLoadPath;
+ QPushButton *m_sfxLoadChoose;
+ QLineEdit *m_soundFontPath;
+ QPushButton *m_soundFontChoose;
+
+ // Sync and timing
+ //
+ //QCheckBox *m_midiClockEnabled;
+ QComboBox *m_midiSync;
+ QString m_origTimer;
+ QComboBox *m_timer;
+ QComboBox *m_mmcTransport;
+ QComboBox *m_mtcTransport;
+ QCheckBox *m_midiSyncAuto;
+
+ QCheckBox* m_studio;
+ QSpinBox* m_midiPitchOctave;
+
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/configuration/MatrixConfigurationPage.cpp b/src/gui/configuration/MatrixConfigurationPage.cpp
new file mode 100644
index 0000000..e21f3fd
--- /dev/null
+++ b/src/gui/configuration/MatrixConfigurationPage.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "MatrixConfigurationPage.h"
+
+#include "document/ConfigGroups.h"
+#include "ConfigurationPage.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "gui/editors/matrix/MatrixView.h"
+#include "TabbedConfigurationPage.h"
+#include <kconfig.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qstring.h>
+#include <qtabwidget.h>
+#include <qwidget.h>
+#include <qlayout.h>
+
+namespace Rosegarden
+{
+
+MatrixConfigurationPage::MatrixConfigurationPage(KConfig *cfg,
+ QWidget *parent,
+ const char *name) :
+ TabbedConfigurationPage(cfg, parent, name)
+{
+ m_cfg->setGroup(MatrixViewConfigGroup);
+
+ QFrame *frame = new QFrame(m_tabWidget);
+ QGridLayout *layout = new QGridLayout(frame,
+ 4, 2, // nbrow, nbcol
+ 10, 5);
+
+ layout->addWidget(new QLabel("Nothing here yet", frame), 0, 0);
+
+ addTab(frame, i18n("General"));
+}
+
+void MatrixConfigurationPage::apply()
+{
+ m_cfg->setGroup(MatrixViewConfigGroup);
+}
+
+}
+#include "MatrixConfigurationPage.moc"
diff --git a/src/gui/configuration/MatrixConfigurationPage.h b/src/gui/configuration/MatrixConfigurationPage.h
new file mode 100644
index 0000000..9c4b3fc
--- /dev/null
+++ b/src/gui/configuration/MatrixConfigurationPage.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_MATRIXCONFIGURATIONPAGE_H_
+#define _RG_MATRIXCONFIGURATIONPAGE_H_
+
+#include "TabbedConfigurationPage.h"
+#include <qstring.h>
+#include <klocale.h>
+
+
+class QWidget;
+class KConfig;
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * Notation Configuration page
+ */
+class MatrixConfigurationPage : public TabbedConfigurationPage
+{
+ Q_OBJECT
+
+public:
+ MatrixConfigurationPage(KConfig *cfg,
+ QWidget *parent = 0, const char *name=0);
+
+ virtual void apply();
+
+ static QString iconLabel() { return i18n("Matrix"); }
+ static QString title() { return i18n("Matrix"); }
+
+protected slots:
+
+protected:
+
+ //--------------- Data members ---------------------------------
+};
+
+
+}
+
+#endif
diff --git a/src/gui/configuration/NotationConfigurationPage.cpp b/src/gui/configuration/NotationConfigurationPage.cpp
new file mode 100644
index 0000000..a828fe7
--- /dev/null
+++ b/src/gui/configuration/NotationConfigurationPage.cpp
@@ -0,0 +1,741 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "NotationConfigurationPage.h"
+#include <qlayout.h>
+
+#include "misc/Strings.h"
+#include "document/ConfigGroups.h"
+#include "base/Exception.h"
+#include "base/NotationTypes.h"
+#include "commands/edit/PasteEventsCommand.h"
+#include "ConfigurationPage.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/editors/notation/HeadersGroup.h"
+#include "gui/editors/notation/NotationHLayout.h"
+#include "gui/editors/notation/NoteFontFactory.h"
+#include "gui/editors/notation/NoteFont.h"
+#include "gui/editors/notation/NoteFontMap.h"
+#include "gui/editors/notation/NoteFontViewer.h"
+#include "gui/editors/notation/NotePixmapFactory.h"
+#include "gui/editors/notation/NoteStyleFactory.h"
+#include "gui/widgets/QuantizeParameters.h"
+#include "TabbedConfigurationPage.h"
+#include <kcombobox.h>
+#include <kconfig.h>
+#include <kfontrequester.h>
+#include <kmessagebox.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qfont.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qobject.h>
+#include <qpushbutton.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qtabwidget.h>
+#include <qwidget.h>
+#include <qtooltip.h>
+#include <algorithm>
+
+namespace Rosegarden
+{
+
+NotationConfigurationPage::NotationConfigurationPage(KConfig *cfg,
+ QWidget *parent,
+ const char *name) :
+ TabbedConfigurationPage(cfg, parent, name)
+{
+ m_cfg->setGroup(NotationViewConfigGroup);
+
+ QFrame *frame;
+ QGridLayout *layout;
+
+ frame = new QFrame(m_tabWidget);
+ layout = new QGridLayout(frame, 9, 3, 10, 5);
+
+ int row = 0;
+
+ layout->setRowSpacing(row, 15);
+ ++row;
+
+ layout->addWidget(new QLabel(i18n("Default layout mode"), frame), row, 0);
+
+ m_layoutMode = new KComboBox(frame);
+ m_layoutMode->setEditable(false);
+ m_layoutMode->insertItem(i18n("Linear layout"));
+ m_layoutMode->insertItem(i18n("Continuous page layout"));
+ m_layoutMode->insertItem(i18n("Multiple page layout"));
+ int defaultLayoutMode = m_cfg->readNumEntry("layoutmode", 0);
+ if (defaultLayoutMode >= 0 && defaultLayoutMode <= 2) {
+ m_layoutMode->setCurrentItem(defaultLayoutMode);
+ }
+ layout->addMultiCellWidget(m_layoutMode, row, row, 1, 2);
+ ++row;
+
+ layout->addWidget(new QLabel(i18n("Default spacing"), frame), row, 0);
+
+ m_spacing = new KComboBox(frame);
+ m_spacing->setEditable(false);
+
+ std::vector<int> s = NotationHLayout::getAvailableSpacings();
+ int defaultSpacing = m_cfg->readNumEntry("spacing", 100);
+
+ for (std::vector<int>::iterator i = s.begin(); i != s.end(); ++i) {
+
+ QString text("%1 %");
+ if (*i == 100)
+ text = "%1 % (normal)";
+ m_spacing->insertItem(text.arg(*i));
+
+ if (*i == defaultSpacing) {
+ m_spacing->setCurrentItem(m_spacing->count() - 1);
+ }
+ }
+
+ layout->addMultiCellWidget(m_spacing, row, row, 1, 2);
+
+ ++row;
+
+ layout->addWidget(new QLabel(i18n("Default duration factor"), frame), row, 0);
+
+ m_proportion = new KComboBox(frame);
+ m_proportion->setEditable(false);
+
+ s = NotationHLayout::getAvailableProportions();
+ int defaultProportion = m_cfg->readNumEntry("proportion", 60);
+
+ for (std::vector<int>::iterator i = s.begin(); i != s.end(); ++i) {
+
+ QString text = QString("%1 %").arg(*i);
+ if (*i == 40)
+ text = "40 % (normal)";
+ else if (*i == 0)
+ text = i18n("None");
+ else if (*i == 100)
+ text = i18n("Full");
+ m_proportion->insertItem(text);
+
+ if (*i == defaultProportion) {
+ m_proportion->setCurrentItem(m_proportion->count() - 1);
+ }
+ }
+
+ layout->addMultiCellWidget(m_proportion, row, row, 1, 2);
+ ++row;
+
+ layout->addWidget(new QLabel(i18n("Show track headers (linear layout only)"),
+ frame), row, 0);
+
+ m_showTrackHeaders = new KComboBox(frame);
+ m_showTrackHeaders->setEditable(false);
+ m_showTrackHeaders->insertItem(i18n("Never"), HeadersGroup::ShowNever);
+ m_showTrackHeaders->insertItem(i18n("When needed"), HeadersGroup::ShowWhenNeeded);
+ m_showTrackHeaders->insertItem(i18n("Always"), HeadersGroup::ShowAlways);
+ int defaultShowTrackHeaders = m_cfg->readNumEntry("shownotationheader",
+ HeadersGroup::DefaultShowMode);
+ if (HeadersGroup::isValidShowMode(defaultShowTrackHeaders)) {
+ m_showTrackHeaders->setCurrentItem(defaultShowTrackHeaders);
+ }
+ QToolTip::add(m_showTrackHeaders, QString(i18n(
+ "\"Always\" and \"Never\" mean what they usually mean\n"
+ "\"When needed\" means \"when staves are too many to all fit"
+ " in the current window\"")));
+
+ layout->addMultiCellWidget(m_showTrackHeaders, row, row, 1, 2);
+ ++row;
+
+ layout->setRowSpacing(row, 20);
+ ++row;
+
+ layout->addMultiCellWidget
+ (new QLabel
+ (i18n("Show non-notation events as question marks"), frame),
+ row, row, 0, 1);
+ m_showUnknowns = new QCheckBox(frame);
+ bool defaultShowUnknowns = m_cfg->readBoolEntry("showunknowns", false);
+ m_showUnknowns->setChecked(defaultShowUnknowns);
+ layout->addWidget(m_showUnknowns, row, 2);
+ ++row;
+
+ layout->addMultiCellWidget
+ (new QLabel
+ (i18n("Show notation-quantized notes in a different color"), frame),
+ row, row, 0, 1);
+ m_colourQuantize = new QCheckBox(frame);
+ bool defaultColourQuantize = m_cfg->readBoolEntry("colourquantize", false);
+ m_colourQuantize->setChecked(defaultColourQuantize);
+ layout->addWidget(m_colourQuantize, row, 2);
+ ++row;
+
+ layout->addMultiCellWidget
+ (new QLabel
+ (i18n("Show \"invisible\" events in grey"), frame),
+ row, row, 0, 1);
+ m_showInvisibles = new QCheckBox(frame);
+ bool defaultShowInvisibles = m_cfg->readBoolEntry("showinvisibles", true);
+ m_showInvisibles->setChecked(defaultShowInvisibles);
+ layout->addWidget(m_showInvisibles, row, 2);
+ ++row;
+
+ layout->addMultiCellWidget
+ (new QLabel
+ (i18n("Show notes outside suggested playable range in red"), frame),
+ row, row, 0, 1);
+ m_showRanges = new QCheckBox(frame);
+ bool defaultShowRanges = m_cfg->readBoolEntry("showranges", true);
+ m_showRanges->setChecked(defaultShowRanges);
+ layout->addWidget(m_showRanges, row, 2);
+ ++row;
+
+ layout->addMultiCellWidget
+ (new QLabel
+ (i18n("Highlight superimposed notes with a halo effect"), frame),
+ row, row, 0, 1);
+ m_showCollisions = new QCheckBox(frame);
+ bool defaultShowCollisions = m_cfg->readBoolEntry("showcollisions", true);
+ m_showCollisions->setChecked(defaultShowCollisions);
+ layout->addWidget(m_showCollisions, row, 2);
+ ++row;
+
+ layout->setRowSpacing(row, 20);
+ ++row;
+
+ layout->addMultiCellWidget
+ (new QLabel
+ (i18n("When recording MIDI, split-and-tie long notes at barlines"), frame),
+ row, row, 0, 1);
+ m_splitAndTie = new QCheckBox(frame);
+ bool defaultSplitAndTie = m_cfg->readBoolEntry("quantizemakeviable", false);
+ m_splitAndTie->setChecked(defaultSplitAndTie);
+ layout->addWidget(m_splitAndTie, row, 2);
+ ++row;
+
+ layout->setRowStretch(row, 10);
+
+
+ addTab(frame, i18n("Layout"));
+
+
+
+ frame = new QFrame(m_tabWidget);
+ layout = new QGridLayout(frame, 6, 3, 10, 5);
+
+ row = 0;
+
+ layout->setRowSpacing(row, 15);
+ ++row;
+
+ layout->addMultiCellWidget
+ (new QLabel(i18n("Default note style for new notes"), frame),
+ row, row, 0, 1);
+
+ layout->setColStretch(2, 10);
+
+ m_noteStyle = new KComboBox(frame);
+ m_noteStyle->setEditable(false);
+ m_untranslatedNoteStyle.clear();
+
+ QString defaultStyle =
+ m_cfg->readEntry("style", strtoqstr(NoteStyleFactory::DefaultStyle));
+ std::vector<NoteStyleName> styles
+ (NoteStyleFactory::getAvailableStyleNames());
+
+ for (std::vector<NoteStyleName>::iterator i = styles.begin();
+ i != styles.end(); ++i) {
+
+ QString styleQName(strtoqstr(*i));
+ m_untranslatedNoteStyle.append(styleQName);
+ m_noteStyle->insertItem(i18n(styleQName.utf8()));
+ if (styleQName == defaultStyle) {
+ m_noteStyle->setCurrentItem(m_noteStyle->count() - 1);
+ }
+ }
+
+ layout->addWidget(m_noteStyle, row, 2);
+ ++row;
+
+ layout->setRowSpacing(row, 20);
+ ++row;
+
+ layout->addWidget
+ (new QLabel(i18n("When inserting notes..."), frame), row, 0);
+
+ int defaultInsertType = m_cfg->readNumEntry("inserttype", 0);
+
+ m_insertType = new KComboBox(frame);
+ m_insertType->setEditable(false);
+ m_insertType->insertItem
+ (i18n("Split notes into ties to make durations match"));
+ m_insertType->insertItem(i18n("Ignore existing durations"));
+ m_insertType->setCurrentItem(defaultInsertType);
+
+ layout->addMultiCellWidget(m_insertType, row, row, 1, 2);
+ ++row;
+
+ bool autoBeam = m_cfg->readBoolEntry("autobeam", true);
+
+ layout->addMultiCellWidget
+ (new QLabel
+ (i18n("Auto-beam on insert when appropriate"), frame),
+ row, row, 0, 1);
+ m_autoBeam = new QCheckBox(frame);
+ m_autoBeam->setChecked(autoBeam);
+ layout->addMultiCellWidget(m_autoBeam, row, row, 2, 2);
+
+ ++row;
+
+ bool collapse = m_cfg->readBoolEntry("collapse", false);
+
+ layout->addMultiCellWidget
+ (new QLabel
+ (i18n("Collapse rests after erase"), frame),
+ row, row, 0, 1);
+ m_collapseRests = new QCheckBox(frame);
+ m_collapseRests->setChecked(collapse);
+ layout->addMultiCellWidget(m_collapseRests, row, row, 2, 2);
+ ++row;
+
+ layout->setRowSpacing(row, 20);
+ ++row;
+
+ layout->addWidget
+ (new QLabel(i18n("Default paste type"), frame), row, 0);
+
+ m_pasteType = new KComboBox(frame);
+ m_pasteType->setEditable(false);
+
+ unsigned int defaultPasteType = m_cfg->readUnsignedNumEntry
+ ("pastetype", PasteEventsCommand::Restricted);
+
+ PasteEventsCommand::PasteTypeMap pasteTypes =
+ PasteEventsCommand::getPasteTypes();
+
+ for (PasteEventsCommand::PasteTypeMap::iterator i = pasteTypes.begin();
+ i != pasteTypes.end(); ++i) {
+ m_pasteType->insertItem(i->second);
+ }
+
+ m_pasteType->setCurrentItem(defaultPasteType);
+ layout->addMultiCellWidget(m_pasteType, row, row, 1, 2);
+ ++row;
+
+ layout->setRowStretch(row, 10);
+
+ addTab(frame, i18n("Editing"));
+
+
+
+ frame = new QFrame(m_tabWidget);
+ layout = new QGridLayout(frame, 4, 2, 10, 5);
+
+ row = 0;
+
+ layout->setRowSpacing(row, 15);
+ ++row;
+
+ layout->addWidget(new QLabel(i18n("Accidentals in one octave..."), frame), row, 0);
+ m_accOctavePolicy = new KComboBox(frame);
+ m_accOctavePolicy->insertItem(i18n("Affect only that octave"));
+ m_accOctavePolicy->insertItem(i18n("Require cautionaries in other octaves"));
+ m_accOctavePolicy->insertItem(i18n("Affect all subsequent octaves"));
+ int accOctaveMode = m_cfg->readNumEntry("accidentaloctavemode", 1);
+ if (accOctaveMode >= 0 && accOctaveMode < 3) {
+ m_accOctavePolicy->setCurrentItem(accOctaveMode);
+ }
+ layout->addWidget(m_accOctavePolicy, row, 1);
+ ++row;
+
+ layout->addWidget(new QLabel(i18n("Accidentals in one bar..."), frame), row, 0);
+ m_accBarPolicy = new KComboBox(frame);
+ m_accBarPolicy->insertItem(i18n("Affect only that bar"));
+ m_accBarPolicy->insertItem(i18n("Require cautionary resets in following bar"));
+ m_accBarPolicy->insertItem(i18n("Require explicit resets in following bar"));
+ int accBarMode = m_cfg->readNumEntry("accidentalbarmode", 0);
+ if (accBarMode >= 0 && accBarMode < 3) {
+ m_accBarPolicy->setCurrentItem(accBarMode);
+ }
+ layout->addWidget(m_accBarPolicy, row, 1);
+ ++row;
+
+ layout->addWidget(new QLabel(i18n("Key signature cancellation style"), frame), row, 0);
+ m_keySigCancelMode = new KComboBox(frame);
+ m_keySigCancelMode->insertItem(i18n("Cancel only when entering C major or A minor"));
+ m_keySigCancelMode->insertItem(i18n("Cancel whenever removing sharps or flats"));
+ m_keySigCancelMode->insertItem(i18n("Cancel always"));
+ int cancelMode = m_cfg->readNumEntry("keysigcancelmode", 1);
+ if (cancelMode >= 0 && cancelMode < 3) {
+ m_keySigCancelMode->setCurrentItem(cancelMode);
+ }
+ layout->addWidget(m_keySigCancelMode, row, 1);
+ ++row;
+
+ layout->setRowStretch(row, 10);
+
+ addTab(frame, i18n("Accidentals"));
+
+/*
+ QString preamble =
+ (i18n("Rosegarden can apply automatic quantization to recorded "
+ "or imported MIDI data for notation purposes only. "
+ "This does not affect playback, and does not affect "
+ "editing in any of the views except notation."));
+
+ // force to default of 2 if not used before
+ int quantizeType = m_cfg->readNumEntry("quantizetype", 2);
+ m_cfg->writeEntry("quantizetype", quantizeType);
+ m_cfg->writeEntry("quantizenotationonly", true);
+
+ m_quantizeFrame = new QuantizeParameters
+ (m_tabWidget, QuantizeParameters::Notation,
+ false, false, "Notation Options", preamble);
+
+ addTab(m_quantizeFrame, i18n("Quantize"));
+*/
+ row = 0;
+
+// QFrame *mainFrame = new QFrame(m_tabWidget);
+// QGridLayout *mainLayout = new QGridLayout(mainFrame, 2, 4, 10, 5);
+
+// QGroupBox *noteFontBox = new QGroupBox(1, Horizontal, i18n("Notation font"), mainFrame);
+// QGroupBox *otherFontBox = new QGroupBox(1, Horizontal, i18n("Other fonts"), mainFrame);
+// QGroupBox *descriptionBox = new QGroupBox(1, Horizontal, i18n("Description"), mainFrame);
+
+// mainLayout->addWidget(noteFontBox, 0, 0);
+// mainLayout->addWidget(otherFontBox, 1, 0);
+
+// QFrame *mainFrame = new QFrame(m_tabWidget);
+ frame = new QFrame(m_tabWidget);
+ layout = new QGridLayout(frame, 2, 4, 10, 5);
+
+// frame = new QFrame(noteFontBox);
+// layout = new QGridLayout(frame, 5, 2, 10, 5);
+
+ m_viewButton = 0;
+
+ layout->addWidget(new QLabel(i18n("Notation font"), frame), 0, 0);
+
+ m_font = new KComboBox(frame);
+
+#ifdef HAVE_XFT
+ m_viewButton = new QPushButton(i18n("View"), frame);
+ layout->addMultiCellWidget(m_font, row, row, 1, 2);
+ layout->addWidget(m_viewButton, row, 3);
+ QObject::connect(m_viewButton, SIGNAL(clicked()),
+ this, SLOT(slotViewButtonPressed()));
+#else
+ layout->addMultiCellWidget(m_font, row, row, 1, 3);
+#endif
+ m_font->setEditable(false);
+ QObject::connect(m_font, SIGNAL(activated(int)),
+ this, SLOT(slotFontComboChanged(int)));
+ ++row;
+
+ QFrame *subFrame = new QFrame(frame);
+ QGridLayout *subLayout = new QGridLayout(subFrame,
+ 4, 2, // nbrow, nbcol
+ 12, 2);
+
+ QFont font = m_font->font();
+ font.setPointSize((font.pointSize() * 9) / 10);
+
+ QLabel *originLabel = new QLabel(i18n("Origin:"), subFrame);
+ originLabel->setFont(font);
+ subLayout->addWidget(originLabel, 0, 0);
+
+ QLabel *copyrightLabel = new QLabel(i18n("Copyright:"), subFrame);
+ copyrightLabel->setFont(font);
+ subLayout->addWidget(copyrightLabel, 1, 0);
+
+ QLabel *mappedLabel = new QLabel(i18n("Mapped by:"), subFrame);
+ mappedLabel->setFont(font);
+ subLayout->addWidget(mappedLabel, 2, 0);
+
+ QLabel *typeLabel = new QLabel(i18n("Type:"), subFrame);
+ typeLabel->setFont(font);
+ subLayout->addWidget(typeLabel, 3, 0);
+
+ m_fontOriginLabel = new QLabel(subFrame);
+ m_fontOriginLabel->setAlignment(Qt::WordBreak);
+ m_fontOriginLabel->setFont(font);
+// m_fontOriginLabel->setFixedWidth(250);
+ m_fontCopyrightLabel = new QLabel(subFrame);
+ m_fontCopyrightLabel->setAlignment(Qt::WordBreak);
+ m_fontCopyrightLabel->setFont(font);
+// m_fontCopyrightLabel->setFixedWidth(250);
+ m_fontMappedByLabel = new QLabel(subFrame);
+ m_fontMappedByLabel->setFont(font);
+ m_fontTypeLabel = new QLabel(subFrame);
+ m_fontTypeLabel->setFont(font);
+ subLayout->addWidget(m_fontOriginLabel, 0, 1);
+ subLayout->addWidget(m_fontCopyrightLabel, 1, 1);
+ subLayout->addWidget(m_fontMappedByLabel, 2, 1);
+ subLayout->addWidget(m_fontTypeLabel, 3, 1);
+
+ subLayout->setColStretch(1, 10);
+
+ layout->addMultiCellWidget(subFrame,
+ row, row,
+ 0, 3);
+ ++row;
+
+ layout->addMultiCellWidget
+ (new QLabel(i18n("Font size for single-staff views"), frame),
+ row, row, 0, 1);
+ m_singleStaffSize = new KComboBox(frame);
+ m_singleStaffSize->setEditable(false);
+ layout->addMultiCellWidget(m_singleStaffSize, row, row, 2, 2);
+ ++row;
+
+ layout->addMultiCellWidget
+ (new QLabel(i18n("Font size for multi-staff views"), frame),
+ row, row, 0, 1);
+ m_multiStaffSize = new KComboBox(frame);
+ m_multiStaffSize->setEditable(false);
+ layout->addMultiCellWidget(m_multiStaffSize, row, row, 2, 2);
+ ++row;
+
+ layout->addMultiCellWidget
+ (new QLabel(i18n("Font size for printing (pt)"), frame),
+ row, row, 0, 1);
+ m_printingSize = new KComboBox(frame);
+ m_printingSize->setEditable(false);
+ layout->addMultiCellWidget(m_printingSize, row, row, 2, 2);
+ ++row;
+
+ slotPopulateFontCombo(false);
+
+ layout->setRowSpacing(row, 15);
+ ++row;
+
+ QFont defaultTextFont(NotePixmapFactory::defaultSerifFontFamily),
+ defaultSansFont(NotePixmapFactory::defaultSansSerifFontFamily),
+ defaultTimeSigFont(NotePixmapFactory::defaultTimeSigFontFamily);
+
+ layout->addWidget
+ (new QLabel(i18n("Text font"), frame), row, 0);
+ m_textFont = new KFontRequester(frame);
+ QFont textFont = m_cfg->readFontEntry("textfont", &defaultTextFont);
+ m_textFont->setFont(textFont);
+ layout->addMultiCellWidget(m_textFont, row, row, 1, 3);
+ ++row;
+
+ layout->addWidget
+ (new QLabel(i18n("Sans-serif font"), frame), row, 0);
+ m_sansFont = new KFontRequester(frame);
+ QFont sansFont = m_cfg->readFontEntry("sansfont", &defaultSansFont);
+ m_sansFont->setFont(sansFont);
+ layout->addMultiCellWidget(m_sansFont, row, row, 1, 3);
+ ++row;
+
+/*!!! No -- not much point in having the time sig font here: it's only
+ * used if the time sig characters are not found in the notation font,
+ * and our default notation font has all the characters we need
+
+ layout->addWidget
+ (new QLabel(i18n("Time Signature font"), frame), row, 0);
+ m_timeSigFont = new KFontRequester(frame);
+ QFont timeSigFont = m_cfg->readFontEntry("timesigfont", &defaultTimeSigFont);
+ m_timeSigFont->setFont(timeSigFont);
+ layout->addMultiCellWidget(m_timeSigFont, row, row, 1, 3);
+ ++row;
+*/
+
+// addTab(mainFrame, i18n("Font"));
+ addTab(frame, i18n("Font"));
+
+
+}
+
+void
+NotationConfigurationPage::slotViewButtonPressed()
+{
+#ifdef HAVE_XFT
+ std::string fontName = qstrtostr(m_untranslatedFont[m_font->currentItem()]);
+
+ try {
+ NoteFont *noteFont = NoteFontFactory::getFont
+ (fontName, NoteFontFactory::getDefaultSize(fontName));
+ const NoteFontMap &map(noteFont->getNoteFontMap());
+ QStringList systemFontNames(map.getSystemFontNames());
+ if (systemFontNames.count() == 0) {
+ m_viewButton->setEnabled(false); // oops
+ } else {
+ NoteFontViewer *viewer =
+ new NoteFontViewer(0, m_untranslatedFont[m_font->currentItem()],
+ systemFontNames, 24);
+ (void)viewer->exec(); // no return value
+ }
+ } catch (Exception f) {
+ KMessageBox::error(0, i18n(strtoqstr(f.getMessage())));
+ }
+#endif
+}
+
+void
+NotationConfigurationPage::slotPopulateFontCombo(bool rescan)
+{
+ QString defaultFont = m_cfg->readEntry
+ ("notefont", strtoqstr(NoteFontFactory::getDefaultFontName()));
+
+ try {
+ (void)NoteFontFactory::getFont
+ (qstrtostr(defaultFont),
+ NoteFontFactory::getDefaultSize(qstrtostr(defaultFont)));
+ } catch (Exception e) {
+ defaultFont = strtoqstr(NoteFontFactory::getDefaultFontName());
+ }
+
+ std::set
+ <std::string> fs(NoteFontFactory::getFontNames(rescan));
+ std::vector<std::string> f(fs.begin(), fs.end());
+ std::sort(f.begin(), f.end());
+
+ m_untranslatedFont.clear();
+ m_font->clear();
+
+ for (std::vector<std::string>::iterator i = f.begin(); i != f.end(); ++i) {
+ QString s(strtoqstr(*i));
+ m_untranslatedFont.append(s);
+ m_font->insertItem(i18n(s.utf8()));
+ if (s == defaultFont)
+ m_font->setCurrentItem(m_font->count() - 1);
+ }
+
+ slotFontComboChanged(m_font->currentItem());
+}
+
+void
+NotationConfigurationPage::slotFontComboChanged(int index)
+{
+ std::string fontStr = qstrtostr(m_untranslatedFont[index]);
+
+ populateSizeCombo(m_singleStaffSize, fontStr,
+ m_cfg->readUnsignedNumEntry
+ ("singlestaffnotesize",
+ NoteFontFactory::getDefaultSize(fontStr)));
+ populateSizeCombo(m_multiStaffSize, fontStr,
+ m_cfg->readUnsignedNumEntry
+ ("multistaffnotesize",
+ NoteFontFactory::getDefaultSize(fontStr)));
+
+ int printpt = m_cfg->readUnsignedNumEntry("printingnotesize", 5);
+ for (int i = 2; i < 16; ++i) {
+ m_printingSize->insertItem(QString("%1").arg(i));
+ if (i == printpt) {
+ m_printingSize->setCurrentItem(m_printingSize->count() - 1);
+ }
+ }
+
+ try {
+ NoteFont *noteFont = NoteFontFactory::getFont
+ (fontStr, NoteFontFactory::getDefaultSize(fontStr));
+ const NoteFontMap &map(noteFont->getNoteFontMap());
+ m_fontOriginLabel->setText(i18n(strtoqstr(map.getOrigin())));
+ m_fontCopyrightLabel->setText(i18n(strtoqstr(map.getCopyright())));
+ m_fontMappedByLabel->setText(i18n(strtoqstr(map.getMappedBy())));
+ if (map.isSmooth()) {
+ m_fontTypeLabel->setText(
+ i18n("%1 (smooth)").arg(i18n(strtoqstr(map.getType()))));
+ } else {
+ m_fontTypeLabel->setText(
+ i18n("%1 (jaggy)").arg(i18n(strtoqstr(map.getType()))));
+ }
+ if (m_viewButton) {
+ m_viewButton->setEnabled(map.getSystemFontNames().count() > 0);
+ }
+ } catch (Exception f) {
+ KMessageBox::error(0, i18n(strtoqstr(f.getMessage())));
+ }
+}
+
+void
+NotationConfigurationPage::populateSizeCombo(QComboBox *combo,
+ std::string font,
+ int defaultSize)
+{
+ std::vector<int> sizes = NoteFontFactory::getScreenSizes(font);
+ combo->clear();
+
+ for (std::vector<int>::iterator i = sizes.begin(); i != sizes.end(); ++i) {
+ combo->insertItem(QString("%1").arg(*i));
+ if (*i == defaultSize)
+ combo->setCurrentItem(combo->count() - 1);
+ }
+}
+
+void
+NotationConfigurationPage::apply()
+{
+ m_cfg->setGroup(NotationViewConfigGroup);
+
+ m_cfg->writeEntry("notefont", m_untranslatedFont[m_font->currentItem()]);
+ m_cfg->writeEntry("singlestaffnotesize",
+ m_singleStaffSize->currentText().toUInt());
+ m_cfg->writeEntry("multistaffnotesize",
+ m_multiStaffSize->currentText().toUInt());
+ m_cfg->writeEntry("printingnotesize",
+ m_printingSize->currentText().toUInt());
+ m_cfg->writeEntry("textfont",
+ m_textFont->font());
+ m_cfg->writeEntry("sansfont",
+ m_sansFont->font());
+/*!!!
+ m_cfg->writeEntry("timesigfont",
+ m_timeSigFont->font());
+*/
+ std::vector<int> s = NotationHLayout::getAvailableSpacings();
+ m_cfg->writeEntry("spacing", s[m_spacing->currentItem()]);
+
+ s = NotationHLayout::getAvailableProportions();
+ m_cfg->writeEntry("proportion", s[m_proportion->currentItem()]);
+
+ m_cfg->writeEntry("layoutmode", m_layoutMode->currentItem());
+ m_cfg->writeEntry("colourquantize", m_colourQuantize->isChecked());
+ m_cfg->writeEntry("showunknowns", m_showUnknowns->isChecked());
+ m_cfg->writeEntry("showinvisibles", m_showInvisibles->isChecked());
+ m_cfg->writeEntry("showranges", m_showRanges->isChecked());
+ m_cfg->writeEntry("showcollisions", m_showCollisions->isChecked());
+ m_cfg->writeEntry("shownotationheader",
+ m_showTrackHeaders->currentItem());
+ m_cfg->writeEntry("style", m_untranslatedNoteStyle[m_noteStyle->currentItem()]);
+ m_cfg->writeEntry("inserttype", m_insertType->currentItem());
+ m_cfg->writeEntry("autobeam", m_autoBeam->isChecked());
+ m_cfg->writeEntry("collapse", m_collapseRests->isChecked());
+ m_cfg->writeEntry("pastetype", m_pasteType->currentItem());
+ m_cfg->writeEntry("accidentaloctavemode", m_accOctavePolicy->currentItem());
+ m_cfg->writeEntry("accidentalbarmode", m_accBarPolicy->currentItem());
+ m_cfg->writeEntry("keysigcancelmode", m_keySigCancelMode->currentItem());
+
+ m_cfg->writeEntry("quantizemakeviable", m_splitAndTie->isChecked());
+
+// (void)m_quantizeFrame->getQuantizer(); // this also writes to the config
+}
+
+}
+#include "NotationConfigurationPage.moc"
diff --git a/src/gui/configuration/NotationConfigurationPage.h b/src/gui/configuration/NotationConfigurationPage.h
new file mode 100644
index 0000000..a3d3dc5
--- /dev/null
+++ b/src/gui/configuration/NotationConfigurationPage.h
@@ -0,0 +1,117 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_NOTATIONCONFIGURATIONPAGE_H_
+#define _RG_NOTATIONCONFIGURATIONPAGE_H_
+
+#include <string>
+#include "TabbedConfigurationPage.h"
+#include <qstring.h>
+#include <qstringlist.h>
+#include <klocale.h>
+
+
+class QWidget;
+class QPushButton;
+class QLabel;
+class QComboBox;
+class QCheckBox;
+class KFontRequester;
+class KConfig;
+
+
+namespace Rosegarden
+{
+
+class QuantizeParameters;
+
+
+/**
+ * Notation Configuration page
+ */
+class NotationConfigurationPage : public TabbedConfigurationPage
+{
+ Q_OBJECT
+
+public:
+ NotationConfigurationPage(KConfig *cfg,
+ QWidget *parent = 0, const char *name=0);
+
+ virtual void apply();
+
+ static QString iconLabel() { return i18n("Notation"); }
+ static QString title() { return i18n("Notation"); }
+ static QString iconName() { return "configure-notation"; }
+
+protected slots:
+ void slotFontComboChanged(int);
+ void slotPopulateFontCombo(bool rescan);
+ void slotViewButtonPressed();
+
+protected:
+
+ //--------------- Data members ---------------------------------
+
+ QComboBox *m_font;
+ QComboBox *m_singleStaffSize;
+ QComboBox *m_multiStaffSize;
+ QComboBox *m_printingSize;
+ KFontRequester* m_textFont;
+ KFontRequester* m_sansFont;
+ KFontRequester* m_timeSigFont;
+ QPushButton *m_viewButton;
+ QLabel *m_fontOriginLabel;
+ QLabel *m_fontCopyrightLabel;
+ QLabel *m_fontMappedByLabel;
+ QLabel *m_fontTypeLabel;
+ QComboBox *m_layoutMode;
+ QComboBox *m_spacing;
+ QComboBox *m_proportion;
+ QCheckBox *m_colourQuantize;
+ QCheckBox *m_showUnknowns;
+ QCheckBox *m_showInvisibles;
+ QCheckBox *m_showRanges;
+ QCheckBox *m_showCollisions;
+ QComboBox *m_showTrackHeaders;
+ QComboBox *m_noteStyle;
+ QComboBox *m_insertType;
+ QCheckBox *m_autoBeam;
+ QCheckBox *m_collapseRests;
+ QComboBox *m_pasteType;
+ QComboBox *m_accOctavePolicy;
+ QComboBox *m_accBarPolicy;
+ QComboBox *m_keySigCancelMode;
+ QCheckBox *m_splitAndTie;
+ QuantizeParameters *m_quantizeFrame;
+ QStringList m_untranslatedFont;
+ QStringList m_untranslatedNoteStyle;
+
+ void populateSizeCombo(QComboBox *combo, std::string font, int dfltSize);
+};
+
+
+}
+
+#endif
diff --git a/src/gui/configuration/TabbedConfigurationPage.cpp b/src/gui/configuration/TabbedConfigurationPage.cpp
new file mode 100644
index 0000000..cc808a9
--- /dev/null
+++ b/src/gui/configuration/TabbedConfigurationPage.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "TabbedConfigurationPage.h"
+
+#include "ConfigurationPage.h"
+#include "document/RosegardenGUIDoc.h"
+#include <kconfig.h>
+#include <kdialog.h>
+#include <qstring.h>
+#include <qtabwidget.h>
+#include <qwidget.h>
+#include <qlayout.h>
+
+
+namespace Rosegarden
+{
+
+TabbedConfigurationPage::TabbedConfigurationPage(RosegardenGUIDoc *doc,
+ QWidget *parent,
+ const char *name)
+ : ConfigurationPage(doc, parent, name)
+{
+ init();
+}
+
+TabbedConfigurationPage::TabbedConfigurationPage(KConfig *cfg,
+ QWidget *parent,
+ const char *name)
+ : ConfigurationPage(cfg, parent, name)
+{
+ init();
+}
+
+TabbedConfigurationPage::TabbedConfigurationPage(RosegardenGUIDoc *doc,
+ KConfig *cfg,
+ QWidget *parent,
+ const char *name)
+ : ConfigurationPage(doc, cfg, parent, name)
+{
+ init();
+}
+
+void TabbedConfigurationPage::init()
+{
+ QVBoxLayout *vlay = new QVBoxLayout(this, 0, KDialog::spacingHint());
+ m_tabWidget = new QTabWidget(this);
+ vlay->addWidget(m_tabWidget);
+}
+
+void TabbedConfigurationPage::addTab(QWidget *tab, const QString &title)
+{
+ m_tabWidget->addTab(tab, title);
+}
+
+}
+#include "TabbedConfigurationPage.moc"
diff --git a/src/gui/configuration/TabbedConfigurationPage.h b/src/gui/configuration/TabbedConfigurationPage.h
new file mode 100644
index 0000000..8c370d5
--- /dev/null
+++ b/src/gui/configuration/TabbedConfigurationPage.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_TABBEDCONFIGURATIONPAGE_H_
+#define _RG_TABBEDCONFIGURATIONPAGE_H_
+
+#include "ConfigurationPage.h"
+#include <qstring.h>
+
+
+class QWidget;
+class QTabWidget;
+class KConfig;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+
+
+/**
+ * This class borrowed from KMail
+ * (c) 2000 The KMail Development Team
+ */
+class TabbedConfigurationPage : public ConfigurationPage
+{
+ Q_OBJECT
+
+public:
+ TabbedConfigurationPage(RosegardenGUIDoc *doc,
+ QWidget *parent=0, const char *name=0);
+
+ TabbedConfigurationPage(KConfig *cfg,
+ QWidget *parent=0, const char *name=0);
+
+ TabbedConfigurationPage(RosegardenGUIDoc *doc,
+ KConfig *cfg,
+ QWidget *parent=0, const char *name=0);
+
+ static QString iconName() { return "misc"; }
+
+protected:
+ void init();
+ void addTab(QWidget *tab, const QString &title);
+
+ //--------------- Data members ---------------------------------
+
+ QTabWidget *m_tabWidget;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/AddTracksDialog.cpp b/src/gui/dialogs/AddTracksDialog.cpp
new file mode 100644
index 0000000..67e5412
--- /dev/null
+++ b/src/gui/dialogs/AddTracksDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "AddTracksDialog.h"
+
+#include <klocale.h>
+#include <kdialogbase.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qspinbox.h>
+#include <kcombobox.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <kapp.h>
+#include <kconfig.h>
+
+#include "document/ConfigGroups.h"
+
+
+namespace Rosegarden
+{
+
+AddTracksDialog::AddTracksDialog(QWidget *parent, int currentTrack) :
+ KDialogBase(parent, 0, true, i18n("Add Tracks"),
+ Ok | Cancel),
+ m_currentTrack(currentTrack)
+{
+ QVBox *vBox = makeVBoxMainWidget();
+
+ QHBox *countBox = new QHBox(vBox);
+ countBox->setSpacing(4);
+ new QLabel(i18n("How many tracks do you want to add?"), countBox);
+ m_count = new QSpinBox(countBox);
+ m_count->setMinValue(1);
+ m_count->setMaxValue(32);
+ m_count->setValue(1);
+
+ QHBox *posBox = new QHBox(vBox);
+ posBox->setSpacing(4);
+ new QLabel(i18n("Add tracks"), posBox);
+ m_position = new KComboBox(posBox);
+ m_position->insertItem(i18n("At the top"));
+ m_position->insertItem(i18n("Above the current selected track"));
+ m_position->insertItem(i18n("Below the current selected track"));
+ m_position->insertItem(i18n("At the bottom"));
+
+ KConfig *config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+ m_position->setCurrentItem(config->readUnsignedNumEntry("lastaddtracksposition", 2));
+}
+
+int
+AddTracksDialog::getTracks()
+{
+ return m_count->value();
+}
+
+int
+AddTracksDialog::getInsertPosition()
+{
+ int opt = m_position->currentItem();
+
+ KConfig *config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+ config->writeEntry("lastaddtracksposition", opt);
+
+ int pos = 0;
+
+ switch (opt) {
+ case 0: // at top
+ pos = 0;
+ break;
+ case 1: // above current track
+ pos = m_currentTrack;
+ break;
+ case 2: // below current track
+ pos = m_currentTrack + 1;
+ break;
+ case 3: // at bottom
+ pos = -1;
+ break;
+ }
+
+ return pos;
+}
+
+}
+#include "AddTracksDialog.moc"
diff --git a/src/gui/dialogs/AddTracksDialog.h b/src/gui/dialogs/AddTracksDialog.h
new file mode 100644
index 0000000..9930e46
--- /dev/null
+++ b/src/gui/dialogs/AddTracksDialog.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ADDTRACKSDIALOG_H_
+#define _RG_ADDTRACKSDIALOG_H_
+
+#include <kdialogbase.h>
+
+class QWidget;
+class QSpinBox;
+class QComboBox;
+
+namespace Rosegarden
+{
+
+class AddTracksDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ AddTracksDialog(QWidget *parent, int currentTrack = -1);
+
+ int getTracks();
+ int getInsertPosition();
+
+protected:
+ int m_currentTrack;
+ QSpinBox *m_count;
+ QComboBox *m_position;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/AudioManagerDialog.cpp b/src/gui/dialogs/AudioManagerDialog.cpp
new file mode 100644
index 0000000..5982632
--- /dev/null
+++ b/src/gui/dialogs/AudioManagerDialog.cpp
@@ -0,0 +1,1257 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "AudioManagerDialog.h"
+#include <kapplication.h>
+
+#include "base/Event.h"
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "AudioPlayingDialog.h"
+#include "base/Composition.h"
+#include "base/Exception.h"
+#include "base/Instrument.h"
+#include "base/MidiProgram.h"
+#include "base/NotationTypes.h"
+#include "base/RealTime.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "base/Studio.h"
+#include "base/Track.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "gui/application/RosegardenGUIView.h"
+#include "gui/application/RosegardenApplication.h"
+#include "gui/widgets/AudioListItem.h"
+#include "gui/widgets/AudioListView.h"
+#include "gui/widgets/CurrentProgressDialog.h"
+#include "gui/widgets/ProgressDialog.h"
+#include "sound/AudioFile.h"
+#include "sound/AudioFileManager.h"
+#include "sound/WAVAudioFile.h"
+#include "UnusedAudioSelectionDialog.h"
+#include <klocale.h>
+#include <kstddirs.h>
+#include <kaction.h>
+#include <kcommand.h>
+#include <kfiledialog.h>
+#include <kglobal.h>
+#include <klineeditdlg.h>
+#include <klistview.h>
+#include <kmainwindow.h>
+#include <kmessagebox.h>
+#include <kstdaction.h>
+#include <kurl.h>
+#include <kxmlguiclient.h>
+#include <kio/netaccess.h>
+#include <qaccel.h>
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <qdialog.h>
+#include <qdragobject.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qiconset.h>
+#include <qlabel.h>
+#include <qlistview.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qstring.h>
+#include <qstrlist.h>
+#include <qtimer.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+const int AudioManagerDialog::m_maxPreviewWidth = 100;
+const int AudioManagerDialog::m_previewHeight = 30;
+const char* const AudioManagerDialog::m_listViewLayoutName = "AudioManagerDialog Layout";
+
+AudioManagerDialog::AudioManagerDialog(QWidget *parent,
+ RosegardenGUIDoc *doc):
+ KMainWindow(parent, "audioManagerDialog"),
+ m_doc(doc),
+ m_playingAudioFile(0),
+ m_audioPlayingDialog(0),
+ m_playTimer(new QTimer(this)),
+ m_audiblePreview(true)
+{
+ setCaption(i18n("Audio File Manager"));
+ setWFlags(WDestructiveClose);
+
+ QVBox *box = new QVBox(this);
+ setCentralWidget(box);
+ box->setMargin(10);
+ box->setSpacing(5);
+
+ m_sampleRate = 0;
+
+ QCString replyType;
+ QByteArray replyData;
+ if (rgapp->sequencerCall("getSampleRate()", replyType, replyData)) {
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ unsigned int result;
+ streamIn >> result;
+ m_sampleRate = result;
+ }
+
+ m_fileList = new AudioListView(box);
+
+ m_wrongSampleRates = new QLabel(i18n("* Some audio files are encoded at a sample rate different from that of the JACK audio server.\nRosegarden will play them at the correct speed, but they will sound terrible.\nPlease consider resampling such files externally, or adjusting the sample rate of the JACK server."), box);
+ m_wrongSampleRates->hide();
+
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QIconSet icon(QPixmap(pixmapDir + "/toolbar/transport-play.xpm"));
+
+ new KAction(i18n("&Add Audio File..."), "fileopen", 0, this,
+ SLOT(slotAdd()), actionCollection(), "add_audio");
+
+ new KAction(i18n("&Unload Audio File"), "editdelete", 0, this,
+ SLOT(slotRemove()),
+ actionCollection(), "remove_audio");
+
+ icon = QIconSet(QPixmap(pixmapDir + "/toolbar/transport-play.xpm"));
+ new KAction(i18n("&Play Preview"), icon, 0, this,
+ SLOT(slotPlayPreview()),
+ actionCollection(), "preview_audio");
+
+ /*!!! Not actually implemented -- this never worked right!
+ new KAction(i18n("Re&label"), 0, 0, this,
+ SLOT(slotRename()),
+ actionCollection(), "rename_audio");
+ */
+
+ icon = QIconSet(QPixmap(pixmapDir + "/toolbar/insert_audio_into_track.xpm"));
+ new KAction(i18n("&Insert into Selected Audio Track"),
+ icon, 0, this, SLOT(slotInsert()),
+ actionCollection(), "insert_audio");
+
+ new KAction(i18n("Unload &all Audio Files"), 0, 0, this,
+ SLOT(slotRemoveAll()),
+ actionCollection(), "remove_all_audio");
+
+ new KAction(i18n("Unload all &Unused Audio Files"), 0, 0, this,
+ SLOT(slotRemoveAllUnused()),
+ actionCollection(), "remove_all_unused_audio");
+
+ new KAction(i18n("&Delete Unused Audio Files..."), 0, 0, this,
+ SLOT(slotDeleteUnused()),
+ actionCollection(), "delete_unused_audio");
+
+ new KAction(i18n("&Export Audio File..."), "fileexport", 0, this,
+ SLOT(slotExportAudio()),
+ actionCollection(), "export_audio");
+/*
+ new KAction(i18n("Distribute Audio on &MIDI"),
+ 0, 0, this,
+ SLOT(slotDistributeOnMidiSegment()),
+ actionCollection(),
+ "distribute_audio");
+*/
+ // Set the column names
+ //
+ m_fileList->addColumn(i18n("Name")); // 0
+ m_fileList->addColumn(i18n("Duration")); // 1
+ m_fileList->addColumn(i18n("Envelope")); // 2
+ m_fileList->addColumn(i18n("Sample rate")); // 3
+ m_fileList->addColumn(i18n("Channels")); // 4
+ m_fileList->addColumn(i18n("Resolution")); // 5
+ m_fileList->addColumn(i18n("File")); // 6
+
+ m_fileList->setColumnAlignment(1, Qt::AlignHCenter);
+ m_fileList->setColumnAlignment(2, Qt::AlignHCenter);
+ m_fileList->setColumnAlignment(3, Qt::AlignHCenter);
+ m_fileList->setColumnAlignment(4, Qt::AlignHCenter);
+ m_fileList->setColumnAlignment(5, Qt::AlignHCenter);
+
+ m_fileList->restoreLayout(kapp->config(), m_listViewLayoutName);
+
+ // a minimum width for the list box
+ //m_fileList->setMinimumWidth(300);
+
+ // show focus across all columns
+ m_fileList->setAllColumnsShowFocus(true);
+
+ // show tooltips when columns are partially hidden
+ m_fileList->setShowToolTips(true);
+
+ // connect selection mechanism
+ connect(m_fileList, SIGNAL(selectionChanged(QListViewItem*)),
+ SLOT(slotSelectionChanged(QListViewItem*)));
+
+ connect(m_fileList, SIGNAL(dropped(QDropEvent*, QListViewItem*)),
+ SLOT(slotDropped(QDropEvent*, QListViewItem*)));
+
+ // setup local accelerators
+ //
+ m_accelerators = new QAccel(this);
+
+ // delete
+ //
+ m_accelerators->connectItem(m_accelerators->insertItem(Key_Delete),
+ this,
+ SLOT(slotRemove()));
+
+ slotPopulateFileList();
+
+ // Connect command history for updates
+ //
+ connect(getCommandHistory(), SIGNAL(commandExecuted(KCommand *)),
+ this, SLOT(slotCommandExecuted(KCommand *)));
+
+ //setInitialSize(configDialogSize(AudioManagerDialogConfigGroup));
+
+ connect(m_playTimer, SIGNAL(timeout()),
+ this, SLOT(slotCancelPlayingAudio()));
+
+ KStdAction::close(this,
+ SLOT(slotClose()),
+ actionCollection());
+
+ createGUI("audiomanager.rc");
+
+ updateActionState(false);
+}
+
+AudioManagerDialog::~AudioManagerDialog()
+{
+ RG_DEBUG << "\n*** AudioManagerDialog::~AudioManagerDialog\n" << endl;
+ m_fileList->saveLayout(kapp->config(), m_listViewLayoutName);
+ //saveDialogSize(AudioManagerDialogConfigGroup);
+}
+
+void
+AudioManagerDialog::slotPopulateFileList()
+{
+ // create pixmap of given size
+ QPixmap *audioPixmap = new QPixmap(m_maxPreviewWidth, m_previewHeight);
+
+ // Store last selected item if we have one
+ //
+ AudioListItem *selectedItem =
+ dynamic_cast<AudioListItem*>(m_fileList->selectedItem());
+ AudioFileId lastId = 0;
+ Segment *lastSegment = 0;
+ bool findSelection = false;
+ bool foundSelection = false;
+
+ if (selectedItem) {
+ lastId = selectedItem->getId();
+ lastSegment = selectedItem->getSegment();
+ findSelection = true;
+ }
+
+ // We don't want the selection changes to be propagated
+ // to the main view
+ //
+ m_fileList->blockSignals(true);
+
+ // clear file list and disable associated action buttons
+ m_fileList->clear();
+
+ if (m_doc->getAudioFileManager().begin() ==
+ m_doc->getAudioFileManager().end()) {
+ // Turn off selection and report empty list
+ //
+ new AudioListItem(m_fileList, i18n("<no audio files>"), 0);
+ m_fileList->setSelectionMode(QListView::NoSelection);
+ m_fileList->setRootIsDecorated(false);
+
+ m_fileList->blockSignals(false);
+ updateActionState(false);
+ return ;
+ }
+
+ // show tree hierarchy
+ m_fileList->setRootIsDecorated(true);
+
+ // enable selection
+ m_fileList->setSelectionMode(QListView::Single);
+
+ // for the sample file length
+ QString msecs, sRate;
+ RealTime length;
+
+ // Create a vector of audio Segments only
+ //
+ std::vector<Segment*> segments;
+ std::vector<Segment*>::const_iterator iit;
+
+ for (Composition::iterator it = m_doc->getComposition().begin();
+ it != m_doc->getComposition().end(); ++it) {
+ if ((*it)->getType() == Segment::Audio)
+ segments.push_back(*it);
+ }
+
+ // duration
+ RealTime segmentDuration;
+ bool wrongSampleRates = false;
+
+ for (std::vector<AudioFile*>::const_iterator
+ it = m_doc->getAudioFileManager().begin();
+ it != m_doc->getAudioFileManager().end();
+ ++it) {
+ try {
+ m_doc->getAudioFileManager().
+ drawPreview((*it)->getId(),
+ RealTime::zeroTime,
+ (*it)->getLength(),
+ audioPixmap);
+ } catch (Exception e) {
+ audioPixmap->fill(); // white
+ QPainter p(audioPixmap);
+ p.setPen(Qt::black);
+ p.drawText(10, m_previewHeight / 2, QString("<no preview>"));
+ }
+
+ //!!! Why isn't the label the label the user assigned to the file?
+ // Why do we allow the user to assign a label at all, then?
+
+ QString label = QString((*it)->getShortFilename().c_str());
+
+ // Set the label, duration, envelope pixmap and filename
+ //
+ AudioListItem *item = new AudioListItem(m_fileList, label,
+ (*it)->getId());
+ // Duration
+ //
+ length = (*it)->getLength();
+ msecs.sprintf("%03d", length.nsec / 1000000);
+ item->setText(1, QString("%1.%2s").arg(length.sec).arg(msecs));
+
+ // set start time and duration
+ item->setStartTime(RealTime::zeroTime);
+ item->setDuration(length);
+
+ // Envelope pixmap
+ //
+ item->setPixmap(2, *audioPixmap);
+
+ // File location
+ //
+ item->setText(6, QString(
+ m_doc->getAudioFileManager().
+ substituteHomeForTilde((*it)->getFilename()).c_str()));
+
+ // Resolution
+ //
+ item->setText(5, QString("%1 bits").arg((*it)->getBitsPerSample()));
+
+ // Channels
+ //
+ item->setText(4, QString("%1").arg((*it)->getChannels()));
+
+ // Sample rate
+ //
+ if (m_sampleRate != 0 && (*it)->getSampleRate() != m_sampleRate) {
+ sRate.sprintf("%.1f KHz *", float((*it)->getSampleRate()) / 1000.0);
+ wrongSampleRates = true;
+ } else {
+ sRate.sprintf("%.1f KHz", float((*it)->getSampleRate()) / 1000.0);
+ }
+ item->setText(3, sRate);
+
+ // Test audio file element for selection criteria
+ //
+ if (findSelection && lastSegment == 0 && lastId == (*it)->getId()) {
+ m_fileList->setSelected(item, true);
+ findSelection = false;
+ }
+
+ // Add children
+ //
+ for (iit = segments.begin(); iit != segments.end(); iit++) {
+ if ((*iit)->getAudioFileId() == (*it)->getId()) {
+ AudioListItem *childItem =
+ new AudioListItem(item,
+ QString((*iit)->getLabel().c_str()),
+ (*it)->getId());
+ segmentDuration = (*iit)->getAudioEndTime() -
+ (*iit)->getAudioStartTime();
+
+ // store the start time
+ //
+ childItem->setStartTime((*iit)->getAudioStartTime());
+ childItem->setDuration(segmentDuration);
+
+ // Write segment duration
+ //
+ msecs.sprintf("%03d", segmentDuration.nsec / 1000000);
+ childItem->setText(1, QString("%1.%2s")
+ .arg(segmentDuration.sec)
+ .arg(msecs));
+
+ try {
+ m_doc->getAudioFileManager().
+ drawHighlightedPreview((*it)->getId(),
+ RealTime::zeroTime,
+ (*it)->getLength(),
+ (*iit)->getAudioStartTime(),
+ (*iit)->getAudioEndTime(),
+ audioPixmap);
+ } catch (Exception e) {
+ // should already be set to "no file"
+ }
+
+ // set pixmap
+ //
+ childItem->setPixmap(2, *audioPixmap);
+
+ // set segment
+ //
+ childItem->setSegment(*iit);
+
+ if (findSelection && lastSegment == (*iit)) {
+ m_fileList->setSelected(childItem, true);
+ findSelection = false;
+ foundSelection = true;
+ }
+
+ // Add children
+ }
+ }
+ }
+
+ updateActionState(foundSelection);
+
+ if (wrongSampleRates) {
+ m_wrongSampleRates->show();
+ } else {
+ m_wrongSampleRates->hide();
+ }
+
+ m_fileList->blockSignals(false);
+}
+
+AudioFile*
+AudioManagerDialog::getCurrentSelection()
+{
+ // try and get the selected item
+ AudioListItem *item =
+ dynamic_cast<AudioListItem*>(m_fileList->selectedItem());
+ if (item == 0)
+ return 0;
+
+ std::vector<AudioFile*>::const_iterator it;
+
+ for (it = m_doc->getAudioFileManager().begin();
+ it != m_doc->getAudioFileManager().end();
+ ++it) {
+ // If we match then return the valid AudioFile
+ //
+ if (item->getId() == (*it)->getId())
+ return (*it);
+ }
+
+ return 0;
+}
+
+void
+AudioManagerDialog::slotExportAudio()
+{
+ WAVAudioFile *sourceFile
+ = dynamic_cast<WAVAudioFile*>(getCurrentSelection());
+
+ AudioListItem *item =
+ dynamic_cast<AudioListItem*>(m_fileList->selectedItem());
+
+ Segment *segment = item->getSegment();
+
+ QString saveFile =
+ KFileDialog::getSaveFileName(":WAVS",
+ i18n("*.wav|WAV files (*.wav)"),
+ this, i18n("Choose a name to save this file as"));
+
+ if (sourceFile == 0 || item == 0 || saveFile.isEmpty())
+ return ;
+
+ // Check for a dot extension and append ".wav" if not found
+ //
+ if (saveFile.contains(".") == 0)
+ saveFile += ".wav";
+
+ ProgressDialog progressDlg(i18n("Exporting audio file..."),
+ 100,
+ this);
+
+ progressDlg.progressBar()->setProgress(0);
+
+ RealTime clipStartTime = RealTime::zeroTime;
+ RealTime clipDuration = sourceFile->getLength();
+
+ if (segment) {
+ clipStartTime = segment->getAudioStartTime();
+ clipDuration = segment->getAudioEndTime() - clipStartTime;
+ }
+
+ WAVAudioFile *destFile
+ = new WAVAudioFile(qstrtostr(saveFile),
+ sourceFile->getChannels(),
+ sourceFile->getSampleRate(),
+ sourceFile->getBytesPerSecond(),
+ sourceFile->getBytesPerFrame(),
+ sourceFile->getBitsPerSample());
+
+ if (sourceFile->open() == false) {
+ delete destFile;
+ return ;
+ }
+
+ destFile->write();
+
+ sourceFile->scanTo(clipStartTime);
+ destFile->appendSamples(sourceFile->getSampleFrameSlice(clipDuration));
+
+ destFile->close();
+ sourceFile->close();
+ delete destFile;
+
+ progressDlg.progressBar()->setProgress(100);
+}
+
+void
+AudioManagerDialog::slotRemove()
+{
+ AudioFile *audioFile = getCurrentSelection();
+ AudioListItem *item =
+ dynamic_cast<AudioListItem*>(m_fileList->selectedItem());
+
+ if (audioFile == 0 || item == 0)
+ return ;
+
+ // If we're on a Segment then delete it at the Composition
+ // and refresh the list.
+ //
+ if (item->getSegment()) {
+ // Get the next item to highlight
+ //
+ QListViewItem *newItem = item->itemBelow();
+
+ // Or try above
+ //
+ if (newItem == 0)
+ newItem = item->itemAbove();
+
+ // Or the parent
+ //
+ if (newItem == 0)
+ newItem = item->parent();
+
+ // Get the id and segment of the next item so that we can
+ // match against it
+ //
+ AudioFileId id = 0;
+ Segment *segment = 0;
+ AudioListItem *aItem = dynamic_cast<AudioListItem*>(newItem);
+
+ if (aItem) {
+ segment = aItem->getSegment();
+ id = aItem->getId();
+ }
+
+ // Jump to new selection
+ //
+ if (newItem)
+ setSelected(id, segment, true); // propagate
+
+ // Do it - will force update
+ //
+ SegmentSelection selection;
+ selection.insert(item->getSegment());
+ emit deleteSegments(selection);
+
+ return ;
+ }
+
+ // remove segments along with audio file
+ //
+ AudioFileId id = audioFile->getId();
+ SegmentSelection selection;
+ Composition &comp = m_doc->getComposition();
+
+ bool haveSegments = false;
+ for (Composition::iterator it = comp.begin(); it != comp.end(); ++it) {
+ if ((*it)->getType() == Segment::Audio &&
+ (*it)->getAudioFileId() == id) {
+ haveSegments = true;
+ break;
+ }
+ }
+
+ if (haveSegments) {
+
+ QString question = i18n("This will unload audio file \"%1\" and remove all associated segments. Are you sure?")
+ .arg(QString(audioFile->getFilename().c_str()));
+
+ // Ask the question
+ int reply = KMessageBox::warningContinueCancel(this, question);
+
+ if (reply != KMessageBox::Continue)
+ return ;
+ }
+
+ for (Composition::iterator it = comp.begin(); it != comp.end(); ++it) {
+ if ((*it)->getType() == Segment::Audio &&
+ (*it)->getAudioFileId() == id)
+ selection.insert(*it);
+ }
+ emit deleteSegments(selection);
+
+ m_doc->notifyAudioFileRemoval(id);
+
+ m_doc->getAudioFileManager().removeFile(id);
+
+ // tell the sequencer
+ emit deleteAudioFile(id);
+
+ // repopulate
+ slotPopulateFileList();
+}
+
+void
+AudioManagerDialog::slotPlayPreview()
+{
+ AudioFile *audioFile = getCurrentSelection();
+ AudioListItem *item =
+ dynamic_cast<AudioListItem*>(m_fileList->selectedItem());
+
+ if (item == 0 || audioFile == 0)
+ return ;
+
+ // store the audio file we're playing
+ m_playingAudioFile = audioFile->getId();
+
+ // tell the sequencer
+ emit playAudioFile(audioFile->getId(),
+ item->getStartTime(),
+ item->getDuration());
+
+ // now open up the playing dialog
+ //
+ m_audioPlayingDialog =
+ new AudioPlayingDialog(this, QString(audioFile->getFilename().c_str()));
+
+ // Setup timer to pop down dialog after file has completed
+ //
+ int msecs = item->getDuration().sec * 1000 +
+ item->getDuration().nsec / 1000000;
+ m_playTimer->start(msecs, true); // single shot
+
+ // just execute
+ //
+ if (m_audioPlayingDialog->exec() == QDialog::Rejected)
+ emit cancelPlayingAudioFile(m_playingAudioFile);
+
+ delete m_audioPlayingDialog;
+ m_audioPlayingDialog = 0;
+
+ m_playTimer->stop();
+
+}
+
+void
+AudioManagerDialog::slotCancelPlayingAudio()
+{
+ //std::cout << "AudioManagerDialog::slotCancelPlayingAudio" << std::endl;
+ if (m_audioPlayingDialog) {
+ m_playTimer->stop();
+ delete m_audioPlayingDialog;
+ m_audioPlayingDialog = 0;
+ }
+}
+
+void
+AudioManagerDialog::slotAdd()
+{
+ QString extensionList = i18n("*.wav|WAV files (*.wav)\n*.*|All files");
+
+ if (RosegardenGUIApp::self()->haveAudioImporter()) {
+ //!!! This list really needs to come from the importer helper program
+ // (which has an option to supply it -- we just haven't recorded it)
+ extensionList = i18n("*.wav *.flac *.ogg *.mp3|Audio files (*.wav *.flac *.ogg *.mp3)\n*.wav|WAV files (*.wav)\n*.flac|FLAC files (*.flac)\n*.ogg|Ogg files (*.ogg)\n*.mp3|MP3 files (*.mp3)\n*.*|All files");
+ }
+
+ KURL::List kurlList =
+ KFileDialog::getOpenURLs(":WAVS",
+ extensionList,
+ // i18n("*.wav|WAV files (*.wav)\n*.mp3|MP3 files (*.mp3)"),
+ this, i18n("Select one or more audio files"));
+
+ KURL::List::iterator it;
+
+ for (it = kurlList.begin(); it != kurlList.end(); ++it)
+ addFile(*it);
+}
+
+void
+AudioManagerDialog::updateActionState(bool haveSelection)
+{
+ if (m_doc->getAudioFileManager().begin() ==
+ m_doc->getAudioFileManager().end()) {
+ stateChanged("have_audio_files", KXMLGUIClient::StateReverse);
+ } else {
+ stateChanged("have_audio_files", KXMLGUIClient::StateNoReverse);
+ }
+
+ if (haveSelection) {
+
+ stateChanged("have_audio_selected", KXMLGUIClient::StateNoReverse);
+
+ if (m_audiblePreview) {
+ stateChanged("have_audible_preview", KXMLGUIClient::StateNoReverse);
+ } else {
+ stateChanged("have_audible_preview", KXMLGUIClient::StateReverse);
+ }
+
+ if (isSelectedTrackAudio()) {
+ stateChanged("have_audio_insertable", KXMLGUIClient::StateNoReverse);
+ } else {
+ stateChanged("have_audio_insertable", KXMLGUIClient::StateReverse);
+ }
+
+ } else {
+ stateChanged("have_audio_selected", KXMLGUIClient::StateReverse);
+ stateChanged("have_audio_insertable", KXMLGUIClient::StateReverse);
+ stateChanged("have_audible_preview", KXMLGUIClient::StateReverse);
+ }
+}
+
+void
+AudioManagerDialog::slotInsert()
+{
+ AudioFile *audioFile = getCurrentSelection();
+ if (audioFile == 0)
+ return ;
+
+ RG_DEBUG << "AudioManagerDialog::slotInsert\n";
+
+ emit insertAudioSegment(audioFile->getId(),
+ RealTime::zeroTime,
+ audioFile->getLength());
+}
+
+void
+AudioManagerDialog::slotRemoveAll()
+{
+ QString question =
+ i18n("This will unload all audio files and remove their associated segments.\nThis action cannot be undone, and associations with these files will be lost.\nFiles will not be removed from your disk.\nAre you sure?");
+
+ int reply = KMessageBox::warningContinueCancel(this, question);
+
+ if (reply != KMessageBox::Continue)
+ return ;
+
+ SegmentSelection selection;
+ Composition &comp = m_doc->getComposition();
+
+ for (Composition::iterator it = comp.begin(); it != comp.end(); ++it) {
+ if ((*it)->getType() == Segment::Audio)
+ selection.insert(*it);
+ }
+ // delete segments
+ emit deleteSegments(selection);
+
+ for (std::vector<AudioFile*>::const_iterator
+ aIt = m_doc->getAudioFileManager().begin();
+ aIt != m_doc->getAudioFileManager().end(); ++aIt) {
+ m_doc->notifyAudioFileRemoval((*aIt)->getId());
+ }
+
+ m_doc->getAudioFileManager().clear();
+
+ // and now the audio files
+ emit deleteAllAudioFiles();
+
+ // clear the file list
+ m_fileList->clear();
+ slotPopulateFileList();
+}
+
+void
+AudioManagerDialog::slotRemoveAllUnused()
+{
+ QString question =
+ i18n("This will unload all audio files that are not associated with any segments in this composition.\nThis action cannot be undone, and associations with these files will be lost.\nFiles will not be removed from your disk.\nAre you sure?");
+
+ int reply = KMessageBox::warningContinueCancel(this, question);
+
+ if (reply != KMessageBox::Continue)
+ return ;
+
+ std::set
+ <AudioFileId> audioFiles;
+ Composition &comp = m_doc->getComposition();
+
+ for (Composition::iterator it = comp.begin(); it != comp.end(); ++it) {
+ if ((*it)->getType() == Segment::Audio)
+ audioFiles.insert((*it)->getAudioFileId());
+ }
+
+ std::vector<AudioFileId> toDelete;
+ for (std::vector<AudioFile*>::const_iterator
+ aIt = m_doc->getAudioFileManager().begin();
+ aIt != m_doc->getAudioFileManager().end(); ++aIt) {
+ if (audioFiles.find((*aIt)->getId()) == audioFiles.end())
+ toDelete.push_back((*aIt)->getId());
+ }
+
+ // Delete the audio files from the AFM
+ //
+ for (std::vector<AudioFileId>::iterator dIt = toDelete.begin();
+ dIt != toDelete.end(); ++dIt) {
+
+ m_doc->notifyAudioFileRemoval(*dIt);
+ m_doc->getAudioFileManager().removeFile(*dIt);
+ emit deleteAudioFile(*dIt);
+ }
+
+ // clear the file list
+ m_fileList->clear();
+ slotPopulateFileList();
+}
+
+void
+AudioManagerDialog::slotDeleteUnused()
+{
+ std::set
+ <AudioFileId> audioFiles;
+ Composition &comp = m_doc->getComposition();
+
+ for (Composition::iterator it = comp.begin(); it != comp.end(); ++it) {
+ if ((*it)->getType() == Segment::Audio)
+ audioFiles.insert((*it)->getAudioFileId());
+ }
+
+ std::vector<QString> toDelete;
+ std::map<QString, AudioFileId> nameMap;
+
+ for (std::vector<AudioFile*>::const_iterator
+ aIt = m_doc->getAudioFileManager().begin();
+ aIt != m_doc->getAudioFileManager().end(); ++aIt) {
+ if (audioFiles.find((*aIt)->getId()) == audioFiles.end()) {
+ toDelete.push_back(strtoqstr((*aIt)->getFilename()));
+ nameMap[strtoqstr((*aIt)->getFilename())] = (*aIt)->getId();
+ }
+ }
+
+ UnusedAudioSelectionDialog *dialog = new UnusedAudioSelectionDialog
+ (this,
+ i18n("The following audio files are not used in the current composition.\n\nPlease select the ones you wish to delete permanently from the hard disk.\n"),
+ toDelete);
+
+ if (dialog->exec() == QDialog::Accepted) {
+
+ std::vector<QString> names = dialog->getSelectedAudioFileNames();
+
+ if (names.size() > 0) {
+
+ QString question =
+ i18n("<qt>About to delete 1 audio file permanently from the hard disk.<br>This action cannot be undone, and there will be no way to recover this file.<br>Are you sure?</qt>\n", "<qt>About to delete %n audio files permanently from the hard disk.<br>This action cannot be undone, and there will be no way to recover these files.<br>Are you sure?</qt>", names.size());
+
+ int reply = KMessageBox::warningContinueCancel(this, question);
+
+ if (reply != KMessageBox::Continue) {
+ delete dialog;
+ return ;
+ }
+
+ for (int i = 0; i < names.size(); ++i) {
+ std::cerr << i << ": " << names[i] << std::endl;
+ QFile file(names[i]);
+ if (!file.remove()) {
+ KMessageBox::error(this, i18n("File %1 could not be deleted.").arg(names[i]));
+ } else {
+ if (nameMap.find(names[i]) != nameMap.end()) {
+ m_doc->getAudioFileManager().removeFile(nameMap[names[i]]);
+ emit deleteAudioFile(nameMap[names[i]]);
+ } else {
+ std::cerr << "WARNING: Audio file name " << names[i] << " not in name map" << std::endl;
+ }
+
+ QFile peakFile(QString("%1.pk").arg(names[i]));
+ peakFile.remove();
+ }
+ }
+ }
+ }
+
+ m_fileList->clear();
+ slotPopulateFileList();
+
+ delete dialog;
+}
+
+void
+AudioManagerDialog::slotRename()
+{
+ AudioFile *audioFile = getCurrentSelection();
+
+ if (audioFile == 0)
+ return ;
+
+ bool ok = false;
+
+ QString newText = KLineEditDlg::getText(
+ i18n("Change Audio File label"),
+ i18n("Enter new label"),
+ QString(audioFile->getName().c_str()),
+ &ok,
+ this);
+
+ if ( ok && !newText.isEmpty() )
+ audioFile->setName(qstrtostr(newText));
+
+ slotPopulateFileList();
+}
+
+void
+AudioManagerDialog::slotSelectionChanged(QListViewItem *item)
+{
+ AudioListItem *aItem = dynamic_cast<AudioListItem*>(item);
+
+ // If we're on a segment then send a "select" signal
+ // and enable appropriate buttons.
+ //
+ if (aItem && aItem->getSegment()) {
+ SegmentSelection selection;
+ selection.insert(aItem->getSegment());
+ emit segmentsSelected(selection);
+ }
+
+ updateActionState(aItem != 0);
+}
+
+void
+AudioManagerDialog::setSelected(AudioFileId id,
+ const Segment *segment,
+ bool propagate)
+{
+ QListViewItem *it = m_fileList->firstChild();
+ QListViewItem *chIt = 0;
+ AudioListItem *aItem;
+
+ while (it) {
+ // If we're looking for a top level audio file
+ if (segment == 0) {
+ aItem = dynamic_cast<AudioListItem*>(it);
+
+ if (aItem->getId() == id) {
+ selectFileListItemNoSignal(it);
+ return ;
+ }
+ } else // look for a child
+ {
+ if (it->childCount() > 0)
+ chIt = it->firstChild();
+
+ while (chIt) {
+ aItem = dynamic_cast<AudioListItem*>(chIt);
+
+ if (aItem) {
+ if (aItem->getId() == id && aItem->getSegment() == segment) {
+ selectFileListItemNoSignal(chIt);
+
+ // Only propagate to segmentcanvas if asked to
+ if (propagate) {
+ SegmentSelection selection;
+ selection.insert(aItem->getSegment());
+ emit segmentsSelected(selection);
+ }
+
+ return ;
+ }
+ }
+ chIt = chIt->nextSibling();
+ }
+ }
+
+ it = it->nextSibling();
+ }
+
+}
+
+void
+AudioManagerDialog::selectFileListItemNoSignal(QListViewItem* it)
+{
+ m_fileList->blockSignals(true);
+
+ if (it) {
+ m_fileList->ensureItemVisible(it);
+ m_fileList->setSelected(it, true);
+ } else {
+ m_fileList->clearSelection();
+ }
+
+ m_fileList->blockSignals(false);
+}
+
+MultiViewCommandHistory*
+AudioManagerDialog::getCommandHistory()
+{
+ return m_doc->getCommandHistory();
+}
+
+void
+AudioManagerDialog::slotCommandExecuted(KCommand*)
+{
+ slotPopulateFileList();
+}
+
+void
+AudioManagerDialog::slotSegmentSelection(
+ const SegmentSelection &segments)
+{
+ const Segment *segment = 0;
+
+ for (SegmentSelection::const_iterator it = segments.begin();
+ it != segments.end(); ++it) {
+ if ((*it)->getType() == Segment::Audio) {
+ // Only get one audio segment
+ if (segment == 0)
+ segment = *it;
+ else
+ segment = 0;
+ }
+
+ }
+
+ if (segment) {
+ // We don't propagate this segment setting to the canvas
+ // as we probably got called from there.
+ //
+ setSelected(segment->getAudioFileId(), segment, false);
+ } else {
+ selectFileListItemNoSignal(0);
+ }
+
+}
+
+void
+AudioManagerDialog::slotCancelPlayingAudioFile()
+{
+ emit cancelPlayingAudioFile(m_playingAudioFile);
+}
+
+void
+AudioManagerDialog::closePlayingDialog(AudioFileId id)
+{
+ //std::cout << "AudioManagerDialog::closePlayingDialog" << std::endl;
+ if (m_audioPlayingDialog && id == m_playingAudioFile) {
+ m_playTimer->stop();
+ delete m_audioPlayingDialog;
+ m_audioPlayingDialog = 0;
+ }
+
+}
+
+bool
+AudioManagerDialog::addFile(const KURL& kurl)
+{
+ AudioFileId id = 0;
+
+ AudioFileManager &aFM = m_doc->getAudioFileManager();
+
+ if (!kurl.isLocalFile()) {
+ if (!RosegardenGUIApp::self()->testAudioPath("importing a remote audio file")) return false;
+ } else if (aFM.fileNeedsConversion(qstrtostr(kurl.path()), m_sampleRate)) {
+ if (!RosegardenGUIApp::self()->testAudioPath("importing an audio file that needs to be converted or resampled")) return false;
+ }
+
+ ProgressDialog progressDlg(i18n("Adding audio file..."),
+ 100,
+ this);
+
+ CurrentProgressDialog::set(&progressDlg);
+ progressDlg.progressBar()->hide();
+ progressDlg.show();
+
+ // Connect the progress dialog
+ //
+ connect(&aFM, SIGNAL(setProgress(int)),
+ progressDlg.progressBar(), SLOT(setValue(int)));
+ connect(&aFM, SIGNAL(setOperationName(QString)),
+ &progressDlg, SLOT(slotSetOperationName(QString)));
+ connect(&progressDlg, SIGNAL(cancelClicked()),
+ &aFM, SLOT(slotStopImport()));
+
+ try {
+ id = aFM.importURL(kurl, m_sampleRate);
+ } catch (AudioFileManager::BadAudioPathException e) {
+ CurrentProgressDialog::freeze();
+ QString errorString = i18n("Failed to add audio file. ") + strtoqstr(e.getMessage());
+ KMessageBox::sorry(this, errorString);
+ return false;
+ } catch (SoundFile::BadSoundFileException e) {
+ CurrentProgressDialog::freeze();
+ QString errorString = i18n("Failed to add audio file. ") + strtoqstr(e.getMessage());
+ KMessageBox::sorry(this, errorString);
+ return false;
+ }
+
+ disconnect(&progressDlg, SIGNAL(cancelClicked()),
+ &aFM, SLOT(slotStopImport()));
+ connect(&progressDlg, SIGNAL(cancelClicked()),
+ &aFM, SLOT(slotStopPreview()));
+ progressDlg.progressBar()->show();
+ progressDlg.slotSetOperationName(i18n("Generating audio preview..."));
+
+ try {
+ aFM.generatePreview(id);
+ } catch (Exception e) {
+ CurrentProgressDialog::freeze();
+
+ QString message = strtoqstr(e.getMessage()) + "\n\n" +
+ i18n("Try copying this file to a directory where you have write permission and re-add it");
+ KMessageBox::information(this, message);
+ }
+
+ disconnect(&progressDlg, SIGNAL(cancelClicked()),
+ &aFM, SLOT(slotStopPreview()));
+
+ slotPopulateFileList();
+
+ // tell the sequencer
+ emit addAudioFile(id);
+
+ return true;
+}
+
+void
+AudioManagerDialog::slotDropped(QDropEvent *event, QListViewItem*)
+{
+ 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
+ for (QString url = uri.first(); url; url = uri.next()) {
+
+ RG_DEBUG << "AudioManagerDialog::dropEvent() : got "
+ << url << endl;
+
+ addFile(KURL(url));
+ }
+
+ }
+}
+
+void
+AudioManagerDialog::closeEvent(QCloseEvent *e)
+{
+ RG_DEBUG << "AudioManagerDialog::closeEvent()\n";
+ emit closing();
+ KMainWindow::closeEvent(e);
+}
+
+void
+AudioManagerDialog::slotClose()
+{
+ emit closing();
+ close();
+ //KDockMainWindow::slotClose();
+ // delete this;
+}
+
+void
+AudioManagerDialog::setAudioSubsystemStatus(bool ok)
+{
+ // We can do something more fancy in the future but for the moment
+ // this will suffice.
+ //
+ m_audiblePreview = ok;
+}
+
+bool
+AudioManagerDialog::addAudioFile(const QString &filePath)
+{
+ return addFile(QFileInfo(filePath).absFilePath());
+}
+
+bool
+AudioManagerDialog::isSelectedTrackAudio()
+{
+ Composition &comp = m_doc->getComposition();
+ Studio &studio = m_doc->getStudio();
+
+ TrackId currentTrackId = comp.getSelectedTrack();
+ Track *track = comp.getTrackById(currentTrackId);
+
+ if (track) {
+ InstrumentId ii = track->getInstrument();
+ Instrument *instrument = studio.getInstrumentById(ii);
+
+ if (instrument &&
+ instrument->getType() == Instrument::Audio)
+ return true;
+ }
+
+ return false;
+
+}
+
+void
+AudioManagerDialog::slotDistributeOnMidiSegment()
+{
+ RG_DEBUG << "AudioManagerDialog::slotDistributeOnMidiSegment" << endl;
+
+ //Composition &comp = m_doc->getComposition();
+
+ QList<RosegardenGUIView>& viewList = m_doc->getViewList();
+ RosegardenGUIView *w = 0;
+ SegmentSelection selection;
+
+ for (w = viewList.first(); w != 0; w = viewList.next()) {
+ selection = w->getSelection();
+ }
+
+ // Store the insert times in a local vector
+ //
+ std::vector<timeT> insertTimes;
+
+ for (SegmentSelection::iterator i = selection.begin();
+ i != selection.end(); ++i) {
+ // For MIDI (Internal) Segments only of course
+ //
+ if ((*i)->getType() == Segment::Internal) {
+ for (Segment::iterator it = (*i)->begin(); it != (*i)->end(); ++it) {
+ if ((*it)->isa(Note::EventType))
+ insertTimes.push_back((*it)->getAbsoluteTime());
+ }
+ }
+ }
+
+ for (unsigned int i = 0; i < insertTimes.size(); ++i) {
+ RG_DEBUG << "AudioManagerDialog::slotDistributeOnMidiSegment - "
+ << "insert audio segment at " << insertTimes[i]
+ << endl;
+ }
+}
+
+}
+#include "AudioManagerDialog.moc"
diff --git a/src/gui/dialogs/AudioManagerDialog.h b/src/gui/dialogs/AudioManagerDialog.h
new file mode 100644
index 0000000..728b700
--- /dev/null
+++ b/src/gui/dialogs/AudioManagerDialog.h
@@ -0,0 +1,206 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_AUDIOMANAGERDIALOG_H_
+#define _RG_AUDIOMANAGERDIALOG_H_
+
+#include "sound/AudioFile.h"
+#include <kmainwindow.h>
+#include "document/ConfigGroups.h"
+
+
+class QWidget;
+class QTimer;
+class QString;
+class QListViewItem;
+class QLabel;
+class QDropEvent;
+class QCloseEvent;
+class QAccel;
+class KURL;
+class KListView;
+class KCommand;
+
+
+namespace Rosegarden
+{
+
+class SegmentSelection;
+class Segment;
+class RosegardenGUIDoc;
+class RealTime;
+class MultiViewCommandHistory;
+class AudioPlayingDialog;
+class AudioFile;
+
+
+class AudioManagerDialog : public KMainWindow
+{
+ Q_OBJECT
+
+public:
+ AudioManagerDialog(QWidget *parent,
+ RosegardenGUIDoc *doc);
+ ~AudioManagerDialog();
+
+ // Populate the file list from the AudioFileManager
+ //
+
+ // Return a pointer to the currently selected AudioFile -
+ // returns 0 if nothing is selected
+ //
+ AudioFile* getCurrentSelection();
+
+ // Scroll and expand to show this selected item
+ //
+ void setSelected(AudioFileId id,
+ const Segment *segment,
+ bool propagate); // if true then we tell the segmentcanvas
+
+ MultiViewCommandHistory *getCommandHistory();
+
+ // Pop down playing dialog if it's currently up
+ //
+ void closePlayingDialog(AudioFileId id);
+
+ // Can we playback audio currently?
+ //
+ void setAudioSubsystemStatus(bool ok);
+
+ // Return the accelerator object
+ //
+ QAccel* getAccelerators() { return m_accelerators; }
+
+ // Add a new file to the audio file manager
+ //
+ bool addAudioFile(const QString &filePath);
+
+
+public slots:
+ void slotAdd();
+ void slotPlayPreview();
+ void slotRename();
+ void slotInsert();
+ void slotRemove();
+ void slotRemoveAll();
+ void slotRemoveAllUnused();
+ void slotDeleteUnused();
+ void slotExportAudio();
+
+ // get selection
+ void slotSelectionChanged(QListViewItem *);
+
+ // Repopulate
+ //
+ void slotPopulateFileList();
+
+ // Commands
+ //
+ void slotCommandExecuted(KCommand *);
+
+ /**
+ * Accept a list of Segments and highlight accordingly
+ * Used to reflect a selection on the main view
+ * (when the user selects an audio track, the corresponding item
+ * in the audio manager should be selected in turn)
+ *
+ * We check for embedded audio segments and if we find exactly one
+ * we highlight it. If we don't we unselect everything.
+ *
+ */
+ void slotSegmentSelection(const SegmentSelection &);
+
+ /**
+ * Cancel the currently playing audio file
+ */
+ void slotCancelPlayingAudioFile();
+
+ void slotClose();
+
+ /**
+ * Turn a MIDI segment into a set of audio segments triggered
+ * by the MIDI Note Ons
+ */
+ void slotDistributeOnMidiSegment();
+
+signals:
+
+ // Control signals so we can tell the sequencer about our changes
+ // or actions.
+ //
+ void addAudioFile(AudioFileId);
+ void deleteAudioFile(AudioFileId);
+ void playAudioFile(AudioFileId,
+ const RealTime &,
+ const RealTime &);
+ void cancelPlayingAudioFile(AudioFileId);
+ void deleteAllAudioFiles();
+
+ // We've selected a segment here, make the canvas select it too
+ //
+ void segmentsSelected(const SegmentSelection&);
+ void deleteSegments(const SegmentSelection&);
+ void insertAudioSegment(AudioFileId,
+ const RealTime &,
+ const RealTime &);
+
+ void closing();
+protected slots:
+ void slotDropped(QDropEvent*, QListViewItem*);
+ void slotCancelPlayingAudio();
+
+protected:
+ bool addFile(const KURL& kurl);
+ bool isSelectedTrackAudio();
+ void selectFileListItemNoSignal(QListViewItem*);
+ void updateActionState(bool haveSelection);
+
+ virtual void closeEvent(QCloseEvent *);
+
+ //--------------- Data members ---------------------------------
+
+ KListView *m_fileList;
+ QLabel *m_wrongSampleRates;
+ RosegardenGUIDoc *m_doc;
+
+ QAccel *m_accelerators;
+
+ AudioFileId m_playingAudioFile;
+ AudioPlayingDialog *m_audioPlayingDialog;
+ QTimer *m_playTimer;
+
+ static const char* const m_listViewLayoutName;
+ static const int m_maxPreviewWidth;
+ static const int m_previewHeight;
+
+ bool m_audiblePreview;
+ int m_sampleRate;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/AudioPlayingDialog.cpp b/src/gui/dialogs/AudioPlayingDialog.cpp
new file mode 100644
index 0000000..0915ef2
--- /dev/null
+++ b/src/gui/dialogs/AudioPlayingDialog.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "AudioPlayingDialog.h"
+
+#include <klocale.h>
+#include <kdialogbase.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+AudioPlayingDialog::AudioPlayingDialog(QWidget *parent,
+ const QString &name):
+ KDialogBase(parent, 0, true,
+ i18n("Playing audio file"),
+ Cancel)
+{
+ QHBox *w = makeHBoxMainWidget();
+ QLabel *label = new
+ QLabel(i18n("Playing audio file \"%1\"").arg(name), w);
+
+ label->setMinimumHeight(80);
+
+
+}
+
+}
+#include "AudioPlayingDialog.moc"
diff --git a/src/gui/dialogs/AudioPlayingDialog.h b/src/gui/dialogs/AudioPlayingDialog.h
new file mode 100644
index 0000000..880d0bd
--- /dev/null
+++ b/src/gui/dialogs/AudioPlayingDialog.h
@@ -0,0 +1,56 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_AUDIOPLAYINGDIALOG_H_
+#define _RG_AUDIOPLAYINGDIALOG_H_
+
+#include <kdialogbase.h>
+#include "gui/application/RosegardenDCOP.h"
+
+
+class QWidget;
+class QString;
+
+
+namespace Rosegarden
+{
+
+
+
+class AudioPlayingDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ AudioPlayingDialog(QWidget *parent, const QString &label);
+
+signals:
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/AudioPluginDialog.cpp b/src/gui/dialogs/AudioPluginDialog.cpp
new file mode 100644
index 0000000..7f54f71
--- /dev/null
+++ b/src/gui/dialogs/AudioPluginDialog.cpp
@@ -0,0 +1,916 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "AudioPluginDialog.h"
+#include <qlayout.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 "gui/studio/AudioPluginClipboard.h"
+#include "gui/studio/AudioPlugin.h"
+#include "gui/studio/AudioPluginManager.h"
+#include "gui/studio/AudioPluginOSCGUIManager.h"
+#include "gui/studio/StudioControl.h"
+#include "gui/widgets/PluginControl.h"
+#include "sound/MappedStudio.h"
+#include "sound/PluginIdentifier.h"
+#include <kcombobox.h>
+#include <kdialogbase.h>
+#include <qaccel.h>
+#include <qcheckbox.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qsizepolicy.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <set>
+
+namespace Rosegarden
+{
+
+AudioPluginDialog::AudioPluginDialog(QWidget *parent,
+ AudioPluginManager *aPM,
+#ifdef HAVE_LIBLO
+ AudioPluginOSCGUIManager *aGM,
+#endif
+ PluginContainer *pluginContainer,
+ int index):
+ KDialogBase(parent, "", false, i18n("Audio Plugin"),
+#ifdef HAVE_LIBLO
+ Close | Details | Help),
+#else
+ Close | Help),
+#endif
+ m_pluginManager(aPM),
+#ifdef HAVE_LIBLO
+ m_pluginGUIManager(aGM),
+#endif
+ m_pluginContainer(pluginContainer),
+ m_containerId(pluginContainer->getId()),
+ m_programLabel(0),
+ m_index(index),
+ m_generating(true),
+ m_guiShown(false)
+{
+ setHelp("studio-plugins");
+
+ setSizePolicy(QSizePolicy(QSizePolicy::Preferred,
+ QSizePolicy::Fixed));
+
+#ifdef HAVE_LIBLO
+
+ setButtonText(Details, i18n("Editor"));
+#endif
+
+ QVBox *vbox = makeVBoxMainWidget();
+
+ QGroupBox *pluginSelectionBox = new QGroupBox
+ (1, Horizontal, i18n("Plugin"), vbox);
+
+ makePluginParamsBox(vbox, 0, 10);
+
+ m_pluginCategoryBox = new QHBox(pluginSelectionBox);
+ new QLabel(i18n("Category:"), m_pluginCategoryBox);
+ m_pluginCategoryList = new KComboBox(m_pluginCategoryBox);
+ m_pluginCategoryList->setSizeLimit(20);
+
+ QHBox *hbox = new QHBox(pluginSelectionBox);
+ m_pluginLabel = new QLabel(i18n("Plugin:"), hbox);
+ m_pluginList = new KComboBox(hbox);
+ m_pluginList->setSizeLimit(20);
+ QToolTip::add
+ (m_pluginList, i18n("Select a plugin from this list."));
+
+ QHBox *h = new QHBox(pluginSelectionBox);
+
+// top line
+ m_bypass = new QCheckBox(i18n("Bypass"), h);
+ QToolTip::add
+ (m_bypass, i18n("Bypass this plugin."));
+
+ connect(m_bypass, SIGNAL(toggled(bool)),
+ this, SLOT(slotBypassChanged(bool)));
+
+
+ m_insOuts = new QLabel(i18n("<ports>"), h);
+ m_insOuts->setAlignment(AlignRight);
+ QToolTip::add
+ (m_insOuts, i18n("Input and output port counts."));
+
+ m_pluginId = new QLabel(i18n("<id>"), h);
+ m_pluginId->setAlignment(AlignRight);
+ QToolTip::add
+ (m_pluginId, i18n("Unique ID of plugin."));
+
+ connect(m_pluginList, SIGNAL(activated(int)),
+ this, SLOT(slotPluginSelected(int)));
+
+ connect(m_pluginCategoryList, SIGNAL(activated(int)),
+ this, SLOT(slotCategorySelected(int)));
+
+// new line
+ h = new QHBox(pluginSelectionBox);
+ m_copyButton = new QPushButton(i18n("Copy"), h);
+ connect(m_copyButton, SIGNAL(clicked()),
+ this, SLOT(slotCopy()));
+ QToolTip::add
+ (m_copyButton, i18n("Copy plugin parameters"));
+
+ m_pasteButton = new QPushButton(i18n("Paste"), h);
+ connect(m_pasteButton, SIGNAL(clicked()),
+ this, SLOT(slotPaste()));
+ QToolTip::add
+ (m_pasteButton, i18n("Paste plugin parameters"));
+
+ m_defaultButton = new QPushButton(i18n("Default"), h);
+ connect(m_defaultButton, SIGNAL(clicked()),
+ this, SLOT(slotDefault()));
+ QToolTip::add
+ (m_defaultButton, i18n("Set to defaults"));
+
+ populatePluginCategoryList();
+ populatePluginList();
+
+ m_generating = false;
+
+ m_accelerators = new QAccel(this);
+}
+
+#ifdef HAVE_LIBLO
+
+void
+AudioPluginDialog::slotDetails()
+{
+ slotShowGUI();
+}
+#endif
+
+void
+AudioPluginDialog::slotShowGUI()
+{
+ emit showPluginGUI(m_containerId, m_index);
+ m_guiShown = true;
+
+ //!!! need to get notification of when a plugin gui exits from the
+ //gui manager
+}
+
+void
+AudioPluginDialog::populatePluginCategoryList()
+{
+ AudioPluginInstance *inst = m_pluginContainer->getPlugin(m_index);
+ std::set
+ <QString> categories;
+ QString currentCategory;
+
+ for (PluginIterator i = m_pluginManager->begin();
+ i != m_pluginManager->end(); ++i) {
+
+ if (( isSynth() && (*i)->isSynth()) ||
+ (!isSynth() && (*i)->isEffect())) {
+
+ if ((*i)->getCategory() != "") {
+ categories.insert((*i)->getCategory());
+ }
+
+ if (inst && inst->isAssigned() &&
+ ((*i)->getIdentifier() == inst->getIdentifier().c_str())) {
+ currentCategory = (*i)->getCategory();
+ }
+ }
+ }
+
+ if (inst) {
+ RG_DEBUG << "AudioPluginDialog::populatePluginCategoryList: inst id " << inst->getIdentifier() << ", cat " << currentCategory << endl;
+ }
+
+ if (categories.empty()) {
+ m_pluginCategoryBox->hide();
+ m_pluginLabel->hide();
+ }
+
+ m_pluginCategoryList->clear();
+ m_pluginCategoryList->insertItem(i18n("(any)"));
+ m_pluginCategoryList->insertItem(i18n("(unclassified)"));
+ m_pluginCategoryList->setCurrentItem(0);
+
+ for (std::set
+ <QString>::iterator i = categories.begin();
+ i != categories.end(); ++i) {
+
+ m_pluginCategoryList->insertItem(*i);
+
+ if (*i == currentCategory) {
+ m_pluginCategoryList->setCurrentItem(m_pluginCategoryList->count() - 1);
+ }
+ }
+}
+
+void
+AudioPluginDialog::populatePluginList()
+{
+ m_pluginList->clear();
+ m_pluginsInList.clear();
+
+ m_pluginList->insertItem(i18n("(none)"));
+ m_pluginsInList.push_back(0);
+
+ QString category;
+ bool needCategory = false;
+
+ if (m_pluginCategoryList->currentItem() > 0) {
+ needCategory = true;
+ if (m_pluginCategoryList->currentItem() == 1) {
+ category = "";
+ } else {
+ category = m_pluginCategoryList->currentText();
+ }
+ }
+
+ // Check for plugin and setup as required
+ AudioPluginInstance *inst = m_pluginContainer->getPlugin(m_index);
+ if (inst)
+ m_bypass->setChecked(inst->isBypassed());
+
+ // Use this temporary map to ensure that the plugins are sorted
+ // by name when they go into the combobox
+ typedef std::pair<int, AudioPlugin *> PluginPair;
+ typedef std::map<QString, PluginPair> PluginMap;
+ PluginMap plugins;
+ int count = 0;
+
+ for (PluginIterator i = m_pluginManager->begin();
+ i != m_pluginManager->end(); ++i) {
+
+ ++count;
+
+ if (( isSynth() && (*i)->isSynth()) ||
+ (!isSynth() && (*i)->isEffect())) {
+
+ if (needCategory) {
+ QString cat = "";
+ if ((*i)->getCategory())
+ cat = (*i)->getCategory();
+ if (cat != category)
+ continue;
+ }
+
+ QString name = (*i)->getName();
+ bool store = true;
+
+ if (plugins.find(name) != plugins.end()) {
+ // We already have a plugin of this name. If it's a
+ // LADSPA plugin, replace it (this one might be
+ // something better); otherwise leave it alone.
+ QString id = plugins[name].second->getIdentifier();
+ QString type, soname, label;
+ PluginIdentifier::parseIdentifier(id, type, soname, label);
+ if (type != "ladspa") {
+ store = false;
+ }
+ }
+
+ if (store) {
+ plugins[(*i)->getName()] = PluginPair(count, *i);
+ }
+ }
+ }
+
+ const char *currentId = 0;
+ if (inst && inst->isAssigned())
+ currentId = inst->getIdentifier().c_str();
+
+ for (PluginMap::iterator i = plugins.begin(); i != plugins.end(); ++i) {
+
+ QString name = i->first;
+ if (name.endsWith(" VST"))
+ name = name.left(name.length() - 4);
+
+ m_pluginList->insertItem(name);
+ m_pluginsInList.push_back(i->second.first);
+
+ if (currentId && currentId == i->second.second->getIdentifier()) {
+ m_pluginList->setCurrentItem(m_pluginList->count() - 1);
+ }
+ }
+
+ slotPluginSelected(m_pluginList->currentItem());
+}
+
+void
+AudioPluginDialog::makePluginParamsBox(QWidget *parent, int portCount,
+ int tooManyPorts)
+{
+ m_pluginParamsBox = new QFrame(parent);
+
+ int columns = 2;
+ if (portCount > tooManyPorts) {
+ columns = 2;
+ } else if (portCount > 24) {
+ if (portCount > 60) {
+ columns = (portCount - 1) / 16 + 1;
+ } else {
+ columns = (portCount - 1) / 12 + 1;
+ }
+ }
+
+ int perColumn = 4;
+ if (portCount > 48) { // no bounds will be shown
+ perColumn = 2;
+ }
+
+ m_gridLayout = new QGridLayout(m_pluginParamsBox,
+ 1, // rows (will expand)
+ columns * perColumn,
+ 5); // margin
+
+ m_gridLayout->setColStretch(3, 2);
+ m_gridLayout->setColStretch(7, 2);
+}
+
+void
+AudioPluginDialog::slotCategorySelected(int)
+{
+ populatePluginList();
+}
+
+void
+AudioPluginDialog::slotPluginSelected(int i)
+{
+ bool guiWasShown = m_guiShown;
+
+ if (m_guiShown) {
+ emit stopPluginGUI(m_containerId, m_index);
+ m_guiShown = false;
+ }
+
+ int number = m_pluginsInList[i];
+
+ RG_DEBUG << "AudioPluginDialog::::slotPluginSelected - "
+ << "setting up plugin from position " << number << " at menu item " << i << endl;
+
+ QString caption =
+ strtoqstr(m_pluginContainer->getName()) +
+ QString(" [ %1 ] - ").arg(m_index + 1);
+
+ if (number == 0) {
+ setCaption(caption + i18n("<no plugin>"));
+ m_insOuts->setText(i18n("<ports>"));
+ m_pluginId->setText(i18n("<id>"));
+
+ QToolTip::hide();
+ QToolTip::remove
+ (m_pluginList);
+
+ QToolTip::add
+ (m_pluginList, i18n("Select a plugin from this list."));
+ }
+
+ AudioPlugin *plugin = m_pluginManager->getPlugin(number - 1);
+
+ // Destroy old param widgets
+ //
+ QWidget* parent = dynamic_cast<QWidget*>(m_pluginParamsBox->parent());
+
+ delete m_pluginParamsBox;
+ m_pluginWidgets.clear(); // The widgets are deleted with the parameter box
+ m_programCombo = 0;
+
+ int portCount = 0;
+ if (plugin) {
+ for (AudioPlugin::PortIterator it = plugin->begin(); it != plugin->end(); ++it) {
+ if (((*it)->getType() & PluginPort::Control) &&
+ ((*it)->getType() & PluginPort::Input))
+ ++portCount;
+ }
+ }
+
+ int tooManyPorts = 96;
+ makePluginParamsBox(parent, portCount, tooManyPorts);
+ bool showBounds = (portCount <= 48);
+
+ if (portCount > tooManyPorts) {
+
+ m_gridLayout->addMultiCellWidget(
+ new QLabel(i18n("This plugin has too many controls to edit here."),
+ m_pluginParamsBox),
+ 1, 1, 0, m_gridLayout->numCols() - 1, Qt::AlignCenter);
+ }
+
+ AudioPluginInstance *inst = m_pluginContainer->getPlugin(m_index);
+ if (!inst)
+ return ;
+
+ if (plugin) {
+ setCaption(caption + plugin->getName());
+ m_pluginId->setText(i18n("Id: %1").arg(plugin->getUniqueId()));
+
+ QString pluginInfo = plugin->getAuthor() + QString("\n") +
+ plugin->getCopyright();
+
+ QToolTip::hide();
+ QToolTip::remove
+ (m_pluginList);
+ QToolTip::add
+ (m_pluginList, pluginInfo);
+
+ std::string identifier = plugin->getIdentifier().data();
+
+ // Only clear ports &c if this method is accessed by user
+ // action (after the constructor)
+ //
+ if (m_generating == false) {
+ inst->clearPorts();
+ if (inst->getIdentifier() != identifier) {
+ inst->clearConfiguration();
+ }
+ }
+
+ inst->setIdentifier(identifier);
+
+ AudioPlugin::PortIterator it = plugin->begin();
+ int count = 0;
+ int ins = 0, outs = 0;
+
+ for (; it != plugin->end(); ++it) {
+ if (((*it)->getType() & PluginPort::Control) &&
+ ((*it)->getType() & PluginPort::Input)) {
+ // Check for port existence and create with default value
+ // if it doesn't exist. Modification occurs through the
+ // slotPluginPortChanged signal.
+ //
+ if (inst->getPort(count) == 0) {
+ inst->addPort(count, (float)(*it)->getDefaultValue());
+// std::cerr << "Plugin port name " << (*it)->getName() << ", default: " << (*it)->getDefaultValue() << std::endl;
+ }
+
+ } else if ((*it)->getType() & PluginPort::Audio) {
+ if ((*it)->getType() & PluginPort::Input)
+ ++ins;
+ else if ((*it)->getType() & PluginPort::Output)
+ ++outs;
+ }
+
+ ++count;
+ }
+
+ if (ins == 1 && outs == 1)
+ m_insOuts->setText(i18n("mono"));
+ else if (ins == 2 && outs == 2)
+ m_insOuts->setText(i18n("stereo"));
+ else
+ m_insOuts->setText(i18n("%1 in, %2 out").arg(ins).arg(outs));
+
+ QString shortName(plugin->getName());
+ int parenIdx = shortName.find(" (");
+ if (parenIdx > 0) {
+ shortName = shortName.left(parenIdx);
+ if (shortName == "Null")
+ shortName = "Plugin";
+ }
+ }
+
+ adjustSize();
+ setFixedSize(minimumSizeHint());
+
+ // tell the sequencer
+ emit pluginSelected(m_containerId, m_index, number - 1);
+
+ if (plugin) {
+
+ int current = -1;
+ QStringList programs = getProgramsForInstance(inst, current);
+
+ if (programs.count() > 0) {
+
+ m_programLabel = new QLabel(i18n("Program: "), m_pluginParamsBox);
+
+ m_programCombo = new KComboBox(m_pluginParamsBox);
+ m_programCombo->setSizeLimit(20);
+ m_programCombo->insertItem(i18n("<none selected>"));
+ m_gridLayout->addMultiCellWidget(m_programLabel,
+ 0, 0, 0, 0, Qt::AlignRight);
+ m_gridLayout->addMultiCellWidget(m_programCombo,
+ 0, 0, 1, m_gridLayout->numCols() - 1,
+ Qt::AlignLeft);
+ connect(m_programCombo, SIGNAL(activated(const QString &)),
+ this, SLOT(slotPluginProgramChanged(const QString &)));
+
+ m_programCombo->clear();
+ m_programCombo->insertItem(i18n("<none selected>"));
+ m_programCombo->insertStringList(programs);
+ m_programCombo->setCurrentItem(current + 1);
+ m_programCombo->adjustSize();
+
+ m_programLabel->show();
+ m_programCombo->show();
+ }
+
+ AudioPlugin::PortIterator it = plugin->begin();
+ int count = 0;
+
+ for (; it != plugin->end(); ++it) {
+ if (((*it)->getType() & PluginPort::Control) &&
+ ((*it)->getType() & PluginPort::Input)) {
+ PluginControl *control =
+ new PluginControl(m_pluginParamsBox,
+ m_gridLayout,
+ PluginControl::Rotary,
+ *it,
+ m_pluginManager,
+ count,
+ inst->getPort(count)->value,
+ showBounds,
+ portCount > tooManyPorts);
+
+ connect(control, SIGNAL(valueChanged(float)),
+ this, SLOT(slotPluginPortChanged(float)));
+
+ m_pluginWidgets.push_back(control);
+ }
+
+ ++count;
+ }
+
+ m_pluginParamsBox->show();
+ }
+
+ if (guiWasShown) {
+ emit showPluginGUI(m_containerId, m_index);
+ m_guiShown = true;
+ }
+
+#ifdef HAVE_LIBLO
+ bool gui = m_pluginGUIManager->hasGUI(m_containerId, m_index);
+ actionButton(Details)->setEnabled(gui);
+#endif
+
+}
+
+QStringList
+AudioPluginDialog::getProgramsForInstance(AudioPluginInstance *inst, int &current)
+{
+ QStringList list;
+ int mappedId = inst->getMappedId();
+ QString currentProgram = strtoqstr(inst->getProgram());
+
+ MappedObjectPropertyList propertyList = StudioControl::getStudioObjectProperty
+ (mappedId, MappedPluginSlot::Programs);
+
+ current = -1;
+
+ for (MappedObjectPropertyList::iterator i = propertyList.begin();
+ i != propertyList.end(); ++i) {
+ if (*i == currentProgram)
+ current = list.count();
+ list.append(*i);
+ }
+
+ return list;
+}
+
+void
+AudioPluginDialog::slotPluginPortChanged(float value)
+{
+ const QObject* object = sender();
+
+ const PluginControl* control = dynamic_cast<const PluginControl*>(object);
+
+ if (!control)
+ return ;
+
+ // store the new value
+ AudioPluginInstance *inst = m_pluginContainer->getPlugin(m_index);
+ inst->getPort(control->getIndex())->setValue(value);
+
+ emit pluginPortChanged(m_containerId, m_index, control->getIndex());
+}
+
+void
+AudioPluginDialog::slotPluginProgramChanged(const QString &value)
+{
+ // store the new value
+ AudioPluginInstance *inst = m_pluginContainer->getPlugin(m_index);
+
+ if (m_programCombo && value == m_programCombo->text(0)) { // "<none set>"
+ inst->setProgram("");
+ } else {
+ inst->setProgram(qstrtostr(value));
+ emit pluginProgramChanged(m_containerId, m_index);
+ }
+}
+
+void
+AudioPluginDialog::updatePlugin(int number)
+{
+ for (unsigned int i = 0; i < m_pluginsInList.size(); ++i) {
+ if (m_pluginsInList[i] == number + 1) {
+ blockSignals(true);
+ m_pluginList->setCurrentItem(i);
+ blockSignals(false);
+ return ;
+ }
+ }
+}
+
+void
+AudioPluginDialog::updatePluginPortControl(int port)
+{
+ AudioPluginInstance *inst = m_pluginContainer->getPlugin(m_index);
+ if (inst) {
+ PluginPortInstance *pti = inst->getPort(port);
+ if (pti) {
+ for (std::vector<PluginControl *>::iterator i = m_pluginWidgets.begin();
+ i != m_pluginWidgets.end(); ++i) {
+ if ((*i)->getIndex() == port) {
+ (*i)->setValue(pti->value, false); // don't emit
+ return ;
+ }
+ }
+ }
+ }
+}
+
+void
+AudioPluginDialog::updatePluginProgramControl()
+{
+ AudioPluginInstance *inst = m_pluginContainer->getPlugin(m_index);
+ if (inst) {
+ std::string program = inst->getProgram();
+ if (m_programCombo) {
+ m_programCombo->blockSignals(true);
+ m_programCombo->setCurrentText(strtoqstr(program));
+ m_programCombo->blockSignals(false);
+ }
+ for (std::vector<PluginControl *>::iterator i = m_pluginWidgets.begin();
+ i != m_pluginWidgets.end(); ++i) {
+ PluginPortInstance *pti = inst->getPort((*i)->getIndex());
+ if (pti) {
+ (*i)->setValue(pti->value, false); // don't emit
+ }
+ }
+ }
+}
+
+void
+AudioPluginDialog::updatePluginProgramList()
+{
+ if (!m_programLabel)
+ return ;
+
+ AudioPluginInstance *inst = m_pluginContainer->getPlugin(m_index);
+ if (!inst)
+ return ;
+
+ if (!m_programCombo) {
+
+ int current = -1;
+ QStringList programs = getProgramsForInstance(inst, current);
+
+ if (programs.count() > 0) {
+
+ m_programLabel = new QLabel(i18n("Program: "), m_pluginParamsBox);
+
+ m_programCombo = new KComboBox(m_pluginParamsBox);
+ m_programCombo->setSizeLimit(20);
+ m_programCombo->insertItem(i18n("<none selected>"));
+ m_gridLayout->addMultiCellWidget(m_programLabel,
+ 0, 0, 0, 0, Qt::AlignRight);
+ m_gridLayout->addMultiCellWidget(m_programCombo,
+ 0, 0, 1, m_gridLayout->numCols() - 1,
+ Qt::AlignLeft);
+
+ m_programCombo->clear();
+ m_programCombo->insertItem(i18n("<none selected>"));
+ m_programCombo->insertStringList(programs);
+ m_programCombo->setCurrentItem(current + 1);
+ m_programCombo->adjustSize();
+
+ m_programLabel->show();
+ m_programCombo->show();
+
+ m_programCombo->blockSignals(true);
+ connect(m_programCombo, SIGNAL(activated(const QString &)),
+ this, SLOT(slotPluginProgramChanged(const QString &)));
+
+ } else {
+ return ;
+ }
+ } else {
+ }
+
+ while (m_programCombo->count() > 0) {
+ m_programCombo->removeItem(0);
+ }
+
+ int current = -1;
+ QStringList programs = getProgramsForInstance(inst, current);
+
+ if (programs.count() > 0) {
+ m_programCombo->show();
+ m_programLabel->show();
+ m_programCombo->clear();
+ m_programCombo->insertItem(i18n("<none selected>"));
+ m_programCombo->insertStringList(programs);
+ m_programCombo->setCurrentItem(current + 1);
+ } else {
+ m_programLabel->hide();
+ m_programCombo->hide();
+ }
+
+ m_programCombo->blockSignals(false);
+}
+
+void
+AudioPluginDialog::slotBypassChanged(bool bp)
+{
+ AudioPluginInstance *inst = m_pluginContainer->getPlugin(m_index);
+
+ if (inst)
+ inst->setBypass(bp);
+
+ emit bypassed(m_containerId, m_index, bp);
+}
+
+void
+AudioPluginDialog::windowActivationChange(bool oldState)
+{
+ if (isActiveWindow()) {
+ emit windowActivated();
+ }
+}
+
+void
+AudioPluginDialog::closeEvent(QCloseEvent *e)
+{
+ e->accept();
+ emit destroyed(m_containerId, m_index);
+}
+
+void
+AudioPluginDialog::slotClose()
+{
+ emit destroyed(m_containerId, m_index);
+ reject();
+}
+
+void
+AudioPluginDialog::slotCopy()
+{
+ int item = m_pluginList->currentItem();
+ int number = m_pluginsInList[item] - 1;
+
+ if (number >= 0) {
+ AudioPluginClipboard *clipboard =
+ m_pluginManager->getPluginClipboard();
+
+ clipboard->m_pluginNumber = number;
+
+ AudioPluginInstance *inst = m_pluginContainer->getPlugin(m_index);
+ if (inst) {
+ clipboard->m_configuration = inst->getConfiguration();
+ } else {
+ clipboard->m_configuration.clear();
+ }
+
+ std::cout << "AudioPluginDialog::slotCopy - plugin number = " << number
+ << std::endl;
+
+ if (m_programCombo && m_programCombo->currentItem() > 0) {
+ clipboard->m_program = qstrtostr(m_programCombo->currentText());
+ } else {
+ clipboard->m_program = "";
+ }
+
+ clipboard->m_controlValues.clear();
+ std::vector<PluginControl*>::iterator it;
+ for (it = m_pluginWidgets.begin(); it != m_pluginWidgets.end(); ++it) {
+ std::cout << "AudioPluginDialog::slotCopy - "
+ << "value = " << (*it)->getValue() << std::endl;
+
+ clipboard->m_controlValues.push_back((*it)->getValue());
+ }
+ }
+}
+
+void
+AudioPluginDialog::slotPaste()
+{
+ AudioPluginClipboard *clipboard = m_pluginManager->getPluginClipboard();
+
+ std::cout << "AudioPluginDialog::slotPaste - paste plugin id "
+ << clipboard->m_pluginNumber << std::endl;
+
+ if (clipboard->m_pluginNumber != -1) {
+ int count = 0;
+ for (std::vector<int>::iterator it = m_pluginsInList.begin();
+ it != m_pluginsInList.end(); ++it) {
+ if ((*it) == clipboard->m_pluginNumber + 1)
+ break;
+ count++;
+ }
+
+ if (count >= int(m_pluginsInList.size()))
+ return ;
+
+ // now select the plugin
+ //
+ m_pluginList->setCurrentItem(count);
+ slotPluginSelected(count);
+
+ // set configuration data
+ //
+ for (std::map<std::string, std::string>::const_iterator i =
+ clipboard->m_configuration.begin();
+ i != clipboard->m_configuration.end(); ++i) {
+ emit changePluginConfiguration(m_containerId,
+ m_index,
+ false,
+ strtoqstr(i->first),
+ strtoqstr(i->second));
+ }
+
+ // and set the program
+ //
+ if (m_programCombo && clipboard->m_program != "") {
+ m_programCombo->setCurrentText(strtoqstr(clipboard->m_program));
+ slotPluginProgramChanged(strtoqstr(clipboard->m_program));
+ }
+
+ // and ports
+ //
+ count = 0;
+
+ for (std::vector<PluginControl *>::iterator i = m_pluginWidgets.begin();
+ i != m_pluginWidgets.end(); ++i) {
+
+ if (count < clipboard->m_controlValues.size()) {
+ (*i)->setValue(clipboard->m_controlValues[count], true);
+ }
+ ++count;
+ }
+ }
+}
+
+void
+AudioPluginDialog::slotDefault()
+{
+ AudioPluginInstance *inst = m_pluginContainer->getPlugin(m_index);
+ if (!inst)
+ return ;
+
+ int i = m_pluginList->currentItem();
+ int n = m_pluginsInList[i];
+ if (n == 0)
+ return ;
+
+ AudioPlugin *plugin = m_pluginManager->getPlugin(n - 1);
+ if (!plugin)
+ return ;
+
+ for (std::vector<PluginControl *>::iterator i = m_pluginWidgets.begin();
+ i != m_pluginWidgets.end(); ++i) {
+
+ for (AudioPlugin::PortIterator pi = plugin->begin(); pi != plugin->end(); ++pi) {
+ if ((*pi)->getNumber() == (*i)->getIndex()) {
+ (*i)->setValue((*pi)->getDefaultValue(), true); // and emit
+ break;
+ }
+ }
+ }
+}
+
+}
+#include "AudioPluginDialog.moc"
diff --git a/src/gui/dialogs/AudioPluginDialog.h b/src/gui/dialogs/AudioPluginDialog.h
new file mode 100644
index 0000000..bc8a38b
--- /dev/null
+++ b/src/gui/dialogs/AudioPluginDialog.h
@@ -0,0 +1,167 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_AUDIOPLUGINDIALOG_H_
+#define _RG_AUDIOPLUGINDIALOG_H_
+
+#include "base/Instrument.h"
+#include "base/MidiProgram.h"
+#include <kdialogbase.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <vector>
+
+
+class QWidget;
+class QPushButton;
+class QLabel;
+class QGridLayout;
+class QFrame;
+class QCloseEvent;
+class QCheckBox;
+class QAccel;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class PluginControl;
+class PluginContainer;
+class AudioPluginOSCGUIManager;
+class AudioPluginManager;
+class AudioPluginInstance;
+
+
+class AudioPluginDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ AudioPluginDialog(QWidget *parent,
+ AudioPluginManager *aPM,
+#ifdef HAVE_LIBLO
+ AudioPluginOSCGUIManager *aGM,
+#endif
+ PluginContainer *instrument,
+ int index);
+
+ PluginContainer* getPluginContainer() const { return m_pluginContainer; }
+
+ QAccel* getAccelerators() { return m_accelerators; }
+
+ bool isSynth() { return m_index == int(Instrument::SYNTH_PLUGIN_POSITION); }
+
+ void updatePlugin(int number);
+ void updatePluginPortControl(int port);
+ void updatePluginProgramControl();
+ void updatePluginProgramList();
+ void guiExited() { m_guiShown = false; }
+
+public slots:
+ void slotCategorySelected(int);
+ void slotPluginSelected(int index);
+ void slotPluginPortChanged(float value);
+ void slotPluginProgramChanged(const QString &value);
+ void slotBypassChanged(bool);
+ void slotCopy();
+ void slotPaste();
+ void slotDefault();
+ void slotShowGUI();
+
+#ifdef HAVE_LIBLO
+ virtual void slotDetails();
+#endif
+
+signals:
+ void pluginSelected(InstrumentId, int pluginIndex, int plugin);
+ void pluginPortChanged(InstrumentId, int pluginIndex, int portIndex);
+ void pluginProgramChanged(InstrumentId, int pluginIndex);
+ void changePluginConfiguration(InstrumentId, int pluginIndex,
+ bool global, QString key, QString value);
+ void showPluginGUI(InstrumentId, int pluginIndex);
+ void stopPluginGUI(InstrumentId, int pluginIndex);
+
+ // is the plugin being bypassed
+ void bypassed(InstrumentId, int pluginIndex, bool bp);
+ void destroyed(InstrumentId, int index);
+
+ void windowActivated();
+
+protected slots:
+ virtual void slotClose();
+
+protected:
+ virtual void closeEvent(QCloseEvent *e);
+ virtual void windowActivationChange(bool);
+
+ void makePluginParamsBox(QWidget*, int portCount, int tooManyPorts);
+ QStringList getProgramsForInstance(AudioPluginInstance *inst, int &current);
+
+ //--------------- Data members ---------------------------------
+
+ AudioPluginManager *m_pluginManager;
+#ifdef HAVE_LIBLO
+ AudioPluginOSCGUIManager *m_pluginGUIManager;
+#endif
+ PluginContainer *m_pluginContainer;
+ InstrumentId m_containerId;
+
+ QFrame *m_pluginParamsBox;
+ QWidget *m_pluginCategoryBox;
+ KComboBox *m_pluginCategoryList;
+ QLabel *m_pluginLabel;
+ KComboBox *m_pluginList;
+ std::vector<int> m_pluginsInList;
+ QLabel *m_insOuts;
+ QLabel *m_pluginId;
+ QCheckBox *m_bypass;
+ QPushButton *m_copyButton;
+ QPushButton *m_pasteButton;
+ QPushButton *m_defaultButton;
+ QPushButton *m_guiButton;
+
+ QLabel *m_programLabel;
+ KComboBox *m_programCombo;
+ std::vector<PluginControl*> m_pluginWidgets;
+ QGridLayout *m_gridLayout;
+
+ int m_index;
+
+ bool m_generating;
+ bool m_guiShown;
+
+ QAccel *m_accelerators;
+
+ void populatePluginCategoryList();
+ void populatePluginList();
+};
+
+
+} // end of namespace
+
+
+
+#endif
diff --git a/src/gui/dialogs/AudioSplitDialog.cpp b/src/gui/dialogs/AudioSplitDialog.cpp
new file mode 100644
index 0000000..42290b3
--- /dev/null
+++ b/src/gui/dialogs/AudioSplitDialog.cpp
@@ -0,0 +1,339 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "AudioSplitDialog.h"
+#include <kapplication.h>
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/Exception.h"
+#include "base/RealTime.h"
+#include "base/Segment.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/application/RosegardenApplication.h"
+#include "sound/AudioFileManager.h"
+#include <kdialogbase.h>
+#include <qcanvas.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qpalette.h>
+#include <qscrollview.h>
+#include <qspinbox.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+AudioSplitDialog::AudioSplitDialog(QWidget *parent,
+ Segment *segment,
+ RosegardenGUIDoc *doc):
+ KDialogBase(parent, 0, true,
+ i18n("Autosplit Audio Segment"), Ok | Cancel),
+ m_doc(doc),
+ m_segment(segment),
+ m_canvasWidth(500),
+ m_canvasHeight(200),
+ m_previewWidth(400),
+ m_previewHeight(100)
+{
+ if (!segment || segment->getType() != Segment::Audio)
+ reject();
+
+ QVBox *w = makeVBoxMainWidget();
+
+ new QLabel(i18n("AutoSplit Segment \"") +
+ strtoqstr(m_segment->getLabel()) + QString("\""), w);
+
+ m_canvas = new QCanvas(w);
+ m_canvas->resize(m_canvasWidth, m_canvasHeight);
+ m_canvasView = new QCanvasView(m_canvas, w);
+ m_canvasView->setFixedWidth(m_canvasWidth);
+ m_canvasView->setFixedHeight(m_canvasHeight);
+
+ m_canvasView->setHScrollBarMode(QScrollView::AlwaysOff);
+ m_canvasView->setVScrollBarMode(QScrollView::AlwaysOff);
+ m_canvasView->setDragAutoScroll(false);
+
+ QHBox *hbox = new QHBox(w);
+ new QLabel(i18n("Threshold"), hbox);
+ m_thresholdSpin = new QSpinBox(hbox);
+ m_thresholdSpin->setSuffix(" %");
+ connect(m_thresholdSpin, SIGNAL(valueChanged(int)),
+ SLOT(slotThresholdChanged(int)));
+
+ // ensure this is cleared
+ m_previewBoxes.clear();
+
+ // Set thresholds
+ //
+ int threshold = 1;
+ m_thresholdSpin->setValue(threshold);
+ drawPreview();
+ drawSplits(1);
+}
+
+void
+AudioSplitDialog::drawPreview()
+{
+ // Delete everything on the canvas
+ //
+ QCanvasItemList list = m_canvas->allItems();
+ for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); it++)
+ delete *it;
+
+ // empty the preview boxes
+ m_previewBoxes.erase(m_previewBoxes.begin(), m_previewBoxes.end());
+
+ // Draw a bounding box
+ //
+ int border = 5;
+ QCanvasRectangle *rect = new QCanvasRectangle(m_canvas);
+ rect->setSize(m_canvasWidth - border * 2, m_canvasHeight - border * 2);
+ rect->setX(border);
+ rect->setY(border);
+ rect->setZ(1);
+ rect->setPen(kapp->palette().color(QPalette::Active, QColorGroup::Dark));
+ rect->setBrush(kapp->palette().color(QPalette::Active, QColorGroup::Base));
+ rect->setVisible(true);
+
+ // Get preview in vector form
+ //
+ AudioFileManager &aFM = m_doc->getAudioFileManager();
+ int channels = aFM.getAudioFile(m_segment->getAudioFileId())->getChannels();
+
+ std::vector<float> values;
+
+ try {
+ values = aFM.getPreview(m_segment->getAudioFileId(),
+ m_segment->getAudioStartTime(),
+ m_segment->getAudioEndTime(),
+ m_previewWidth,
+ false);
+ } catch (Exception e) {
+ QCanvasText *text = new QCanvasText(m_canvas);
+ text->setColor(kapp->palette().
+ color(QPalette::Active, QColorGroup::Shadow));
+ text->setText(i18n("<no preview generated for this audio file>"));
+ text->setX(30);
+ text->setY(30);
+ text->setZ(4);
+ text->setVisible(true);
+ m_canvas->update();
+ return ;
+ }
+
+ int startX = (m_canvasWidth - m_previewWidth) / 2;
+ int halfHeight = m_canvasHeight / 2;
+ float h1, h2;
+ std::vector<float>::iterator it = values.begin();
+
+ // Draw preview
+ //
+ for (int i = 0; i < m_previewWidth; i++) {
+ if (channels == 1) {
+ h1 = *(it++);
+ h2 = h1;
+ } else {
+ h1 = *(it++);
+ h2 = *(it++);
+ }
+
+
+ int startY = halfHeight + int(h1 * float(m_previewHeight / 2));
+ int endY = halfHeight - int(h2 * float(m_previewHeight / 2));
+
+ if ( startY < 0 ) {
+ RG_DEBUG << "AudioSplitDialog::AudioSplitDialog - "
+ << "startY - out of negative range"
+ << endl;
+ startY = 0;
+ }
+
+ if (endY < 0) {
+ RG_DEBUG << "AudioSplitDialog::AudioSplitDialog - "
+ << "endY - out of negative range"
+ << endl;
+ endY = 0;
+ }
+
+ QCanvasLine *line = new QCanvasLine(m_canvas);
+ line->setPoints(startX + i,
+ startY,
+ startX + i,
+ endY);
+ line->setZ(3);
+ line->setPen(kapp->
+ palette().color(QPalette::Active, QColorGroup::Shadow));
+ line->setBrush(kapp->
+ palette().color(QPalette::Active, QColorGroup::Shadow));
+ line->setVisible(true);
+
+ }
+
+ // Draw zero dc line
+ //
+ rect = new QCanvasRectangle(m_canvas);
+ rect->setX(startX);
+ rect->setY(halfHeight - 1);
+ rect->setSize(m_previewWidth, 2);
+ rect->setPen(kapp->palette().color(QPalette::Active, QColorGroup::Shadow));
+ rect->setBrush(kapp->palette().color(QPalette::Active, QColorGroup::Shadow));
+ rect->setZ(4);
+ rect->setVisible(true);
+
+ // Start time
+ //
+ char msecs[100];
+ sprintf(msecs, "%03d", m_segment->getAudioStartTime().msec());
+ QString startText = QString("%1.%2s")
+ .arg(m_segment->getAudioStartTime().sec)
+ .arg(msecs);
+ QCanvasText *text = new QCanvasText(m_canvas);
+ text->setColor(
+ kapp->palette().color(QPalette::Active, QColorGroup::Shadow));
+ text->setText(startText);
+ text->setX(startX - 20);
+ text->setY(m_canvasHeight / 2 - m_previewHeight / 2 - 35);
+ text->setZ(3);
+ text->setVisible(true);
+
+ rect = new QCanvasRectangle(m_canvas);
+ rect->setX(startX - 1);
+ rect->setY(m_canvasHeight / 2 - m_previewHeight / 2 - 14);
+ rect->setSize(1, m_previewHeight + 28);
+ rect->setPen(kapp->palette().color(QPalette::Active, QColorGroup::Shadow));
+ rect->setZ(3);
+ rect->setVisible(true);
+
+ // End time
+ //
+ sprintf(msecs, "%03d", m_segment->getAudioEndTime().msec());
+ QString endText = QString("%1.%2s")
+ .arg(m_segment->getAudioEndTime().sec)
+ .arg(msecs);
+ text = new QCanvasText(m_canvas);
+ text->setColor(
+ kapp->palette().color(QPalette::Active, QColorGroup::Shadow));
+ text->setText(endText);
+ text->setX(startX + m_previewWidth - 20);
+ text->setY(m_canvasHeight / 2 - m_previewHeight / 2 - 35);
+ text->setZ(3);
+ text->setVisible(true);
+
+ rect = new QCanvasRectangle(m_canvas);
+ rect->setX(startX + m_previewWidth - 1);
+ rect->setY(m_canvasHeight / 2 - m_previewHeight / 2 - 14);
+ rect->setSize(1, m_previewHeight + 28);
+ rect->setPen(kapp->palette().color(QPalette::Active, QColorGroup::Shadow));
+ rect->setZ(3);
+ rect->setVisible(true);
+
+ m_canvas->update();
+}
+
+void
+AudioSplitDialog::drawSplits(int threshold)
+{
+ // Now get the current split points and paint them
+ //
+ RealTime startTime = m_segment->getAudioStartTime();
+ RealTime endTime = m_segment->getAudioEndTime();
+
+ AudioFileManager &aFM = m_doc->getAudioFileManager();
+ std::vector<SplitPointPair> splitPoints =
+ aFM.getSplitPoints(m_segment->getAudioFileId(),
+ startTime,
+ endTime,
+ threshold);
+
+ std::vector<SplitPointPair>::iterator it;
+ std::vector<QCanvasRectangle*> tempRects;
+
+ RealTime length = endTime - startTime;
+ double ticksPerUsec = double(m_previewWidth) /
+ double((length.sec * 1000000.0) + length.usec());
+
+ int startX = (m_canvasWidth - m_previewWidth) / 2;
+ int halfHeight = m_canvasHeight / 2;
+ int x1, x2;
+ int overlapHeight = 10;
+
+ for (it = splitPoints.begin(); it != splitPoints.end(); it++) {
+ RealTime splitStart = it->first - startTime;
+ RealTime splitEnd = it->second - startTime;
+
+ x1 = int(ticksPerUsec * double(double(splitStart.sec) *
+ 1000000.0 + (double)splitStart.usec()));
+
+ x2 = int(ticksPerUsec * double(double(splitEnd.sec) *
+ 1000000.0 + double(splitEnd.usec())));
+
+ QCanvasRectangle *rect = new QCanvasRectangle(m_canvas);
+ rect->setX(startX + x1);
+ rect->setY(halfHeight - m_previewHeight / 2 - overlapHeight / 2);
+ rect->setZ(2);
+ rect->setSize(x2 - x1, m_previewHeight + overlapHeight);
+ rect->setPen(kapp->
+ palette().color(QPalette::Active, QColorGroup::Mid));
+ rect->setBrush(kapp->
+ palette().color(QPalette::Active, QColorGroup::Mid));
+ rect->setVisible(true);
+ tempRects.push_back(rect);
+ }
+
+ std::vector<QCanvasRectangle*>::iterator pIt;
+
+ // We've written the new Rects, now delete the old ones
+ //
+ if (m_previewBoxes.size()) {
+ // clear any previous preview boxes
+ //
+ for (pIt = m_previewBoxes.begin(); pIt != m_previewBoxes.end(); pIt++) {
+ //(*pIt)->setVisible(false);
+ delete (*pIt);
+ }
+ m_previewBoxes.erase(m_previewBoxes.begin(), m_previewBoxes.end());
+ m_canvas->update();
+ }
+ m_canvas->update();
+
+ // Now store the new ones
+ //
+ for (pIt = tempRects.begin(); pIt != tempRects.end(); pIt++)
+ m_previewBoxes.push_back(*pIt);
+}
+
+void
+AudioSplitDialog::slotThresholdChanged(int threshold)
+{
+ drawSplits(threshold);
+}
+
+}
+#include "AudioSplitDialog.moc"
diff --git a/src/gui/dialogs/AudioSplitDialog.h b/src/gui/dialogs/AudioSplitDialog.h
new file mode 100644
index 0000000..7dc52c0
--- /dev/null
+++ b/src/gui/dialogs/AudioSplitDialog.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_AUDIOSPLITDIALOG_H_
+#define _RG_AUDIOSPLITDIALOG_H_
+
+#include <kdialogbase.h>
+#include <vector>
+#include <qspinbox.h>
+
+
+class QWidget;
+class QCanvasView;
+class QCanvasRectangle;
+class QCanvas;
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class RosegardenGUIDoc;
+
+
+class AudioSplitDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ AudioSplitDialog(QWidget *parent,
+ Segment *segment,
+ RosegardenGUIDoc *doc);
+
+ // Draw an audio preview over the segment and draw
+ // the potential splits along it.
+ //
+ void drawPreview();
+ void drawSplits(int threshold);
+
+ // Get the threshold
+ //
+ int getThreshold() { return m_thresholdSpin->value(); }
+
+public slots:
+ void slotThresholdChanged(int);
+
+protected:
+ RosegardenGUIDoc *m_doc;
+ Segment *m_segment;
+ QCanvas *m_canvas;
+ QCanvasView *m_canvasView;
+ QSpinBox *m_thresholdSpin;
+
+ int m_canvasWidth;
+ int m_canvasHeight;
+ int m_previewWidth;
+ int m_previewHeight;
+
+ std::vector<QCanvasRectangle*> m_previewBoxes;
+
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/BeatsBarsDialog.cpp b/src/gui/dialogs/BeatsBarsDialog.cpp
new file mode 100644
index 0000000..774ddb9
--- /dev/null
+++ b/src/gui/dialogs/BeatsBarsDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "BeatsBarsDialog.h"
+#include <qlayout.h>
+
+#include <klocale.h>
+#include "base/Segment.h"
+#include <kcombobox.h>
+#include <kdialogbase.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qspinbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+BeatsBarsDialog::BeatsBarsDialog(QWidget* parent) :
+ KDialogBase(parent, 0, true, i18n("Audio Segment Duration"),
+ Ok | Cancel, Ok)
+{
+ QHBox *hbox = makeHBoxMainWidget();
+
+ QGroupBox *gbox = new QGroupBox(1, Horizontal,
+ i18n("The selected audio segment contains:"), hbox);
+
+ QFrame *frame = new QFrame(gbox);
+ QGridLayout *layout = new QGridLayout(frame, 1, 2, 5, 5);
+
+ m_spinBox = new QSpinBox(1, INT_MAX, 1, frame, "glee");
+ layout->addWidget(m_spinBox, 0, 0);
+
+ m_comboBox = new KComboBox(false, frame);
+ m_comboBox->insertItem(i18n("beat(s)"));
+ m_comboBox->insertItem(i18n("bar(s)"));
+ m_comboBox->setCurrentItem(0);
+ layout->addWidget(m_comboBox, 0, 1);
+}
+
+}
+#include "BeatsBarsDialog.moc"
diff --git a/src/gui/dialogs/BeatsBarsDialog.h b/src/gui/dialogs/BeatsBarsDialog.h
new file mode 100644
index 0000000..6546f01
--- /dev/null
+++ b/src/gui/dialogs/BeatsBarsDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_BEATSBARSDIALOG_H_
+#define _RG_BEATSBARSDIALOG_H_
+
+#include <kdialogbase.h>
+#include <qspinbox.h>
+#include <kcombobox.h>
+
+class QWidget;
+
+
+namespace Rosegarden
+{
+
+/**
+ * ask the user to give us information about the selected audio segment for
+ * Tempo calculations
+ */
+class BeatsBarsDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ BeatsBarsDialog();
+ BeatsBarsDialog(QWidget *parent);
+
+ int getQuantity() { return m_spinBox->value(); }
+ int getMode() { return m_comboBox->currentItem(); }
+
+protected:
+ QSpinBox *m_spinBox;
+ KComboBox *m_comboBox;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/ClefDialog.cpp b/src/gui/dialogs/ClefDialog.cpp
new file mode 100644
index 0000000..8f802b0
--- /dev/null
+++ b/src/gui/dialogs/ClefDialog.cpp
@@ -0,0 +1,273 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ClefDialog.h"
+
+#include "base/NotationTypes.h"
+#include "gui/editors/notation/NotePixmapFactory.h"
+#include "gui/widgets/BigArrowButton.h"
+#include <klocale.h>
+#include <kdialogbase.h>
+#include <qbuttongroup.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qobject.h>
+#include <qpixmap.h>
+#include <qradiobutton.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+ClefDialog::ClefDialog(QWidget *parent,
+ NotePixmapFactory *npf,
+ Clef defaultClef,
+ bool showConversionOptions) :
+ KDialogBase(parent, 0, true, i18n("Clef"), Ok | Cancel | Help),
+ m_notePixmapFactory(npf),
+ m_clef(defaultClef)
+{
+ setHelp("nv-signatures-clef");
+
+ QVBox *vbox = makeVBoxMainWidget();
+
+ QGroupBox *clefFrame = new QGroupBox
+ (1, Horizontal, i18n("Clef"), vbox);
+
+ QButtonGroup *conversionFrame = new QButtonGroup
+ (1, Horizontal, i18n("Existing notes following clef change"), vbox);
+
+ QHBox *clefBox = new QHBox(clefFrame);
+
+ BigArrowButton *clefDown = new BigArrowButton(clefBox, Qt::LeftArrow);
+ QToolTip::add
+ (clefDown, i18n("Lower clef"));
+
+ QHBox *clefLabelBox = new QVBox(clefBox);
+
+ m_octaveUp = new BigArrowButton(clefLabelBox, Qt::UpArrow);
+ QToolTip::add
+ (m_octaveUp, i18n("Up an Octave"));
+
+ m_clefLabel = new QLabel(i18n("Clef"), clefLabelBox);
+ m_clefLabel->setAlignment(AlignVCenter | AlignHCenter);
+
+ m_octaveDown = new BigArrowButton(clefLabelBox, Qt::DownArrow);
+ QToolTip::add
+ (m_octaveDown, i18n("Down an Octave"));
+
+ BigArrowButton *clefUp = new BigArrowButton(clefBox, Qt::RightArrow);
+ QToolTip::add
+ (clefUp, i18n("Higher clef"));
+
+ m_clefNameLabel = new QLabel(i18n("Clef"), clefLabelBox);
+ m_clefNameLabel->setAlignment(AlignVCenter | AlignHCenter);
+
+ if (showConversionOptions) {
+ m_noConversionButton =
+ new QRadioButton
+ (i18n("Maintain current pitches"), conversionFrame);
+ m_changeOctaveButton =
+ new QRadioButton
+ (i18n("Transpose into appropriate octave"), conversionFrame);
+ m_transposeButton = 0;
+
+ //!!! why aren't we offering this option? does it not work? too difficult to describe?
+ // m_transposeButton =
+ // new QRadioButton
+ // (i18n("Maintain current positions on the staff"), conversionFrame);
+ m_changeOctaveButton->setChecked(true);
+ } else {
+ m_noConversionButton = 0;
+ m_changeOctaveButton = 0;
+ m_transposeButton = 0;
+ conversionFrame->hide();
+ }
+
+ QObject::connect(clefUp, SIGNAL(clicked()), this, SLOT(slotClefUp()));
+ QObject::connect(clefDown, SIGNAL(clicked()), this, SLOT(slotClefDown()));
+ QObject::connect(m_octaveUp, SIGNAL(clicked()), this, SLOT(slotOctaveUp()));
+ QObject::connect(m_octaveDown, SIGNAL(clicked()), this, SLOT(slotOctaveDown()));
+
+ redrawClefPixmap();
+}
+
+Clef
+ClefDialog::getClef() const
+{
+ return m_clef;
+}
+
+ClefDialog::ConversionType
+
+ClefDialog::getConversionType() const
+{
+ if (m_noConversionButton && m_noConversionButton->isChecked()) {
+ return NoConversion;
+ } else if (m_changeOctaveButton && m_changeOctaveButton->isChecked()) {
+ return ChangeOctave;
+ } else if (m_transposeButton && m_transposeButton->isChecked()) {
+ return Transpose;
+ }
+ return NoConversion;
+}
+
+void
+ClefDialog::slotClefUp()
+{
+ int octaveOffset = m_clef.getOctaveOffset();
+ Clef::ClefList clefs(Clef::getClefs());
+
+ for (Clef::ClefList::iterator i = clefs.begin();
+ i != clefs.end(); ++i) {
+
+ if (m_clef.getClefType() == i->getClefType()) {
+ if (++i == clefs.end())
+ i = clefs.begin();
+ m_clef = Clef(i->getClefType(), octaveOffset);
+ break;
+ }
+ }
+
+ redrawClefPixmap();
+}
+
+void
+ClefDialog::slotClefDown()
+{
+ int octaveOffset = m_clef.getOctaveOffset();
+ Clef::ClefList clefs(Clef::getClefs());
+
+ for (Clef::ClefList::iterator i = clefs.begin();
+ i != clefs.end(); ++i) {
+
+ if (m_clef.getClefType() == i->getClefType()) {
+ if (i == clefs.begin())
+ i = clefs.end();
+ --i;
+ m_clef = Clef(i->getClefType(), octaveOffset);
+ break;
+ }
+ }
+
+ redrawClefPixmap();
+}
+
+void
+ClefDialog::slotOctaveUp()
+{
+ int octaveOffset = m_clef.getOctaveOffset();
+ if (octaveOffset == 2)
+ return ;
+
+ ++octaveOffset;
+
+ m_octaveDown->setEnabled(true);
+ if (octaveOffset == 2) {
+ m_octaveUp->setEnabled(false);
+ }
+
+ m_clef = Clef(m_clef.getClefType(), octaveOffset);
+ redrawClefPixmap();
+}
+
+void
+ClefDialog::slotOctaveDown()
+{
+ int octaveOffset = m_clef.getOctaveOffset();
+ if (octaveOffset == -2)
+ return ;
+
+ --octaveOffset;
+
+ m_octaveUp->setEnabled(true);
+ if (octaveOffset == 2) {
+ m_octaveDown->setEnabled(false);
+ }
+
+ m_clef = Clef(m_clef.getClefType(), octaveOffset);
+ redrawClefPixmap();
+}
+
+void
+ClefDialog::redrawClefPixmap()
+{
+ QPixmap pmap = NotePixmapFactory::toQPixmap
+ (m_notePixmapFactory->makeClefDisplayPixmap(m_clef));
+ m_clefLabel->setPixmap(pmap);
+
+ QString name;
+ int octave = m_clef.getOctaveOffset();
+
+ switch (octave) {
+ case - 1:
+ name = i18n("%1 down an octave");
+ break;
+ case - 2:
+ name = i18n("%1 down two octaves");
+ break;
+ case 1:
+ name = i18n("%1 up an octave");
+ break;
+ case 2:
+ name = i18n("%1 up two octaves");
+ break;
+ default:
+ name = "%1";
+ break;
+ }
+
+ std::string type = m_clef.getClefType();
+ if (type == Clef::Treble)
+ name = name.arg(i18n("Treble"));
+ else if (type == Clef::French)
+ name = name.arg(i18n("French violin"));
+ else if (type == Clef::Soprano)
+ name = name.arg(i18n("Soprano"));
+ else if (type == Clef::Mezzosoprano)
+ name = name.arg(i18n("Mezzo-soprano"));
+ else if (type == Clef::Alto)
+ name = name.arg(i18n("Alto"));
+ else if (type == Clef::Tenor)
+ name = name.arg(i18n("Tenor"));
+ else if (type == Clef::Baritone)
+ name = name.arg(i18n("C-baritone"));
+ else if (type == Clef::Varbaritone)
+ name = name.arg(i18n("F-baritone"));
+ else if (type == Clef::Bass)
+ name = name.arg(i18n("Bass"));
+ else if (type == Clef::Subbass)
+ name = name.arg(i18n("Sub-bass"));
+
+ m_clefNameLabel->setText(name);
+}
+
+}
+#include "ClefDialog.moc"
diff --git a/src/gui/dialogs/ClefDialog.h b/src/gui/dialogs/ClefDialog.h
new file mode 100644
index 0000000..771cd4a
--- /dev/null
+++ b/src/gui/dialogs/ClefDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_CLEFDIALOG_H_
+#define _RG_CLEFDIALOG_H_
+
+#include "base/NotationTypes.h"
+#include <kdialogbase.h>
+
+
+class QWidget;
+class QRadioButton;
+class QLabel;
+
+
+namespace Rosegarden
+{
+
+class BigArrowButton;
+class NotePixmapFactory;
+
+
+class ClefDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ enum ConversionType {
+ NoConversion,
+ ChangeOctave,
+ Transpose,
+ };
+
+ ClefDialog(QWidget *parent,
+ NotePixmapFactory *npf,
+ Clef defaultClef,
+ bool showConversionOptions = true);
+
+ Clef getClef() const;
+ ConversionType getConversionType() const;
+
+public slots:
+ void slotClefUp();
+ void slotClefDown();
+ void slotOctaveUp();
+ void slotOctaveDown();
+
+protected:
+ void redrawClefPixmap();
+
+ //--------------- Data members ---------------------------------
+
+ NotePixmapFactory *m_notePixmapFactory;
+ Clef m_clef;
+
+ QLabel *m_clefLabel;
+ QLabel *m_clefNameLabel;
+
+ BigArrowButton *m_octaveUp;
+ BigArrowButton *m_octaveDown;
+
+ QRadioButton *m_noConversionButton;
+ QRadioButton *m_changeOctaveButton;
+ QRadioButton *m_transposeButton;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/CompositionLengthDialog.cpp b/src/gui/dialogs/CompositionLengthDialog.cpp
new file mode 100644
index 0000000..24a3107
--- /dev/null
+++ b/src/gui/dialogs/CompositionLengthDialog.cpp
@@ -0,0 +1,84 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "CompositionLengthDialog.h"
+
+#include <klocale.h>
+#include "base/Composition.h"
+#include <kdialogbase.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qspinbox.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+CompositionLengthDialog::CompositionLengthDialog(
+ QWidget *parent,
+ Composition *composition):
+ KDialogBase(parent, 0, true, i18n("Change Composition Length"),
+ Ok | Cancel),
+ m_composition(composition)
+{
+ QVBox *vBox = makeVBoxMainWidget();
+
+ new QLabel(i18n("Set the Start and End bar markers for this Composition"),
+ vBox);
+
+ QHBox *startBox = new QHBox(vBox);
+ new QLabel(i18n("Start Bar"), startBox);
+ m_startMarkerSpinBox = new QSpinBox(startBox);
+ m_startMarkerSpinBox->setMinValue( -10);
+ m_startMarkerSpinBox->setMaxValue(10000);
+ m_startMarkerSpinBox->setValue(
+ m_composition->getBarNumber(m_composition->getStartMarker()) + 1);
+
+ QHBox *endBox = new QHBox(vBox);
+ new QLabel(i18n("End Bar"), endBox);
+ m_endMarkerSpinBox = new QSpinBox(endBox);
+ m_endMarkerSpinBox->setMinValue( -10);
+ m_endMarkerSpinBox->setMaxValue(10000);
+ m_endMarkerSpinBox->setValue(
+ m_composition->getBarNumber(m_composition->getEndMarker()));
+
+}
+
+timeT
+CompositionLengthDialog::getStartMarker()
+{
+ return m_composition->getBarStart(m_startMarkerSpinBox->value() - 1);
+}
+
+timeT
+CompositionLengthDialog::getEndMarker()
+{
+ return m_composition->getBarStart(m_endMarkerSpinBox->value());
+}
+
+}
+#include "CompositionLengthDialog.moc"
diff --git a/src/gui/dialogs/CompositionLengthDialog.h b/src/gui/dialogs/CompositionLengthDialog.h
new file mode 100644
index 0000000..e6d688c
--- /dev/null
+++ b/src/gui/dialogs/CompositionLengthDialog.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_COMPOSITIONLENGTHDIALOG_H_
+#define _RG_COMPOSITIONLENGTHDIALOG_H_
+
+#include <kdialogbase.h>
+#include "base/Event.h"
+
+
+class QWidget;
+class QSpinBox;
+
+
+namespace Rosegarden
+{
+
+class Composition;
+
+
+class CompositionLengthDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ CompositionLengthDialog(QWidget *parent,
+ Composition *composition);
+
+ timeT getStartMarker();
+ timeT getEndMarker();
+
+protected:
+
+ QSpinBox *m_startMarkerSpinBox;
+ QSpinBox *m_endMarkerSpinBox;
+ Composition *m_composition;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/ConfigureDialog.cpp b/src/gui/dialogs/ConfigureDialog.cpp
new file mode 100644
index 0000000..1bdd3b4
--- /dev/null
+++ b/src/gui/dialogs/ConfigureDialog.cpp
@@ -0,0 +1,118 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ConfigureDialog.h"
+#include <qlayout.h>
+
+#include <klocale.h>
+#include "ConfigureDialogBase.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/configuration/GeneralConfigurationPage.h"
+#include "gui/configuration/NotationConfigurationPage.h"
+#include "gui/configuration/AudioConfigurationPage.h"
+#include "gui/configuration/MIDIConfigurationPage.h"
+#include <kconfig.h>
+#include <kdialogbase.h>
+#include <kstddirs.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+static QPixmap loadIcon(const char *name)
+{
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QString fileBase = pixmapDir + "/misc/";
+ fileBase += name;
+ if (QFile(fileBase + ".png").exists()) {
+ return QPixmap(fileBase + ".png");
+ } else if (QFile(fileBase + ".xpm").exists()) {
+ return QPixmap(fileBase + ".xpm");
+ }
+ QPixmap pmap = KGlobal::instance()->iconLoader()
+ ->loadIcon(QString::fromLatin1(name), KIcon::NoGroup, KIcon::SizeMedium);
+ return pmap;
+}
+
+
+ConfigureDialog::ConfigureDialog(RosegardenGUIDoc *doc,
+ KConfig* cfg,
+ QWidget *parent,
+ const char *name)
+ : ConfigureDialogBase(parent, i18n("Configure Rosegarden"), name)
+{
+ QWidget *pageWidget = 0;
+ QVBoxLayout *vlay = 0;
+ ConfigurationPage* page = 0;
+
+ // General Page
+ //
+ pageWidget = addPage(GeneralConfigurationPage::iconLabel(),
+ GeneralConfigurationPage::title(),
+ loadIcon(GeneralConfigurationPage::iconName()));
+ vlay = new QVBoxLayout(pageWidget, 0, spacingHint());
+ page = new GeneralConfigurationPage(doc, cfg, pageWidget);
+ vlay->addWidget(page);
+ page->setPageIndex(pageIndex(pageWidget));
+ m_configurationPages.push_back(page);
+
+ connect(page, SIGNAL(updateAutoSaveInterval(unsigned int)),
+ this, SIGNAL(updateAutoSaveInterval(unsigned int)));
+ connect(page, SIGNAL(updateSidebarStyle(unsigned int)),
+ this, SIGNAL(updateSidebarStyle(unsigned int)));
+
+ pageWidget = addPage(MIDIConfigurationPage::iconLabel(),
+ MIDIConfigurationPage::title(),
+ loadIcon(MIDIConfigurationPage::iconName()));
+ vlay = new QVBoxLayout(pageWidget, 0, spacingHint());
+ page = new MIDIConfigurationPage(doc, cfg, pageWidget);
+ vlay->addWidget(page);
+ page->setPageIndex(pageIndex(pageWidget));
+ m_configurationPages.push_back(page);
+
+ pageWidget = addPage(AudioConfigurationPage::iconLabel(),
+ AudioConfigurationPage::title(),
+ loadIcon(AudioConfigurationPage::iconName()));
+ vlay = new QVBoxLayout(pageWidget, 0, spacingHint());
+ page = new AudioConfigurationPage(doc, cfg, pageWidget);
+ vlay->addWidget(page);
+ page->setPageIndex(pageIndex(pageWidget));
+ m_configurationPages.push_back(page);
+
+ // Notation Page
+ pageWidget = addPage(NotationConfigurationPage::iconLabel(),
+ NotationConfigurationPage::title(),
+ loadIcon(NotationConfigurationPage::iconName()));
+ vlay = new QVBoxLayout(pageWidget, 0, spacingHint());
+ page = new NotationConfigurationPage(cfg, pageWidget);
+ vlay->addWidget(page);
+ page->setPageIndex(pageIndex(pageWidget));
+ m_configurationPages.push_back(page);
+}
+
+}
+#include "ConfigureDialog.moc"
diff --git a/src/gui/dialogs/ConfigureDialog.h b/src/gui/dialogs/ConfigureDialog.h
new file mode 100644
index 0000000..4dd6fff
--- /dev/null
+++ b/src/gui/dialogs/ConfigureDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_CONFIGUREDIALOG_H_
+#define _RG_CONFIGUREDIALOG_H_
+
+#include "ConfigureDialogBase.h"
+
+
+class QWidget;
+class KConfig;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+
+
+class ConfigureDialog : public ConfigureDialogBase
+{
+ Q_OBJECT
+public:
+ ConfigureDialog(RosegardenGUIDoc *doc,
+ KConfig* cfg,
+ QWidget *parent=0,
+ const char *name=0);
+signals:
+ void updateAutoSaveInterval(unsigned int);
+ void updateSidebarStyle(unsigned int);
+};
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/ConfigureDialogBase.cpp b/src/gui/dialogs/ConfigureDialogBase.cpp
new file mode 100644
index 0000000..7d5555a
--- /dev/null
+++ b/src/gui/dialogs/ConfigureDialogBase.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ConfigureDialogBase.h"
+
+#include <klocale.h>
+#include "gui/configuration/ConfigurationPage.h"
+#include <kdialogbase.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+ConfigureDialogBase::ConfigureDialogBase(QWidget *parent,
+ QString label,
+ const char *name):
+ KDialogBase(IconList, label ? label : i18n("Configure"), Help | Apply | Ok | Cancel,
+ Ok, parent, name, true) // modal
+{
+ setWFlags(WDestructiveClose);
+}
+
+ConfigureDialogBase::~ConfigureDialogBase()
+{}
+
+void
+ConfigureDialogBase::slotApply()
+{
+ for (configurationpages::iterator i = m_configurationPages.begin();
+ i != m_configurationPages.end(); ++i)
+ (*i)->apply();
+}
+
+void
+ConfigureDialogBase::slotActivateApply()
+{
+ // ApplyButton->setDisabled(false);
+}
+
+void
+ConfigureDialogBase::slotOk()
+{
+ slotApply();
+ accept();
+}
+
+void
+ConfigureDialogBase::slotCancelOrClose()
+{}
+
+}
+#include "ConfigureDialogBase.moc"
diff --git a/src/gui/dialogs/ConfigureDialogBase.h b/src/gui/dialogs/ConfigureDialogBase.h
new file mode 100644
index 0000000..fe05ebe
--- /dev/null
+++ b/src/gui/dialogs/ConfigureDialogBase.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_CONFIGUREDIALOGBASE_H_
+#define _RG_CONFIGUREDIALOGBASE_H_
+
+#include <kdialogbase.h>
+#include <qstring.h>
+#include <vector>
+
+
+class QWidget;
+
+
+namespace Rosegarden
+{
+
+class ConfigurationPage;
+
+
+class ConfigureDialogBase : public KDialogBase
+{
+ Q_OBJECT
+public:
+ ConfigureDialogBase(QWidget *parent=0,
+ QString label = 0,
+ const char *name=0);
+ virtual ~ConfigureDialogBase();
+
+ typedef std::vector<ConfigurationPage*> configurationpages;
+
+protected slots:
+ virtual void slotOk();
+ virtual void slotApply();
+ virtual void slotCancelOrClose();
+
+ virtual void slotActivateApply();
+
+protected:
+
+ configurationpages m_configurationPages;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/CountdownBar.cpp b/src/gui/dialogs/CountdownBar.cpp
new file mode 100644
index 0000000..cfad3d8
--- /dev/null
+++ b/src/gui/dialogs/CountdownBar.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "CountdownBar.h"
+
+#include "gui/general/GUIPalette.h"
+#include <qframe.h>
+#include <qpainter.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+CountdownBar::CountdownBar(QWidget *parent, int width, int height):
+ QFrame(parent), m_width(width), m_height(height), m_position(0)
+{
+ resize(m_width, m_height);
+ repaint();
+}
+
+void
+CountdownBar::paintEvent(QPaintEvent *e)
+{
+ QPainter p(this);
+
+ p.setClipRegion(e->region());
+ p.setClipRect(e->rect().normalize());
+
+ p.setPen(GUIPalette::getColour(GUIPalette::AudioCountdownBackground));
+ p.setBrush(GUIPalette::getColour(GUIPalette::AudioCountdownBackground));
+ p.drawRect(0, 0, m_position, m_height);
+ p.setPen(GUIPalette::getColour(GUIPalette::AudioCountdownForeground));
+ p.setBrush(GUIPalette::getColour(GUIPalette::AudioCountdownForeground));
+ p.drawRect(m_position, 0, m_width, m_height);
+}
+
+void
+CountdownBar::setPosition(int position)
+{
+ m_position = position;
+ repaint();
+}
+
+}
+#include "CountdownBar.moc"
diff --git a/src/gui/dialogs/CountdownBar.h b/src/gui/dialogs/CountdownBar.h
new file mode 100644
index 0000000..364d0cf
--- /dev/null
+++ b/src/gui/dialogs/CountdownBar.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_COUNTDOWNBAR_H_
+#define _RG_COUNTDOWNBAR_H_
+
+#include <qframe.h>
+
+
+class QWidget;
+class QPaintEvent;
+
+
+namespace Rosegarden
+{
+
+
+
+class CountdownBar : public QFrame
+{
+ Q_OBJECT
+public:
+ CountdownBar(QWidget *parent, int width, int height);
+ void setPosition(int position);
+
+protected:
+ virtual void paintEvent(QPaintEvent *e);
+
+ int m_width;
+ int m_height;
+ int m_position;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/CountdownDialog.cpp b/src/gui/dialogs/CountdownDialog.cpp
new file mode 100644
index 0000000..f624aba
--- /dev/null
+++ b/src/gui/dialogs/CountdownDialog.cpp
@@ -0,0 +1,159 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "CountdownDialog.h"
+#include <qlayout.h>
+
+#include <klocale.h>
+#include "CountdownBar.h"
+#include <qaccel.h>
+#include <qdialog.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qstring.h>
+#include <qwidget.h>
+#include "misc/Debug.h"
+
+
+namespace Rosegarden
+{
+
+CountdownDialog::CountdownDialog(QWidget *parent, int seconds):
+ QDialog(parent, "", false, WStyle_StaysOnTop | WStyle_DialogBorder),
+ m_pastEndMode(false),
+ m_totalTime(seconds),
+ m_progressBarWidth(150),
+ m_progressBarHeight(15)
+{
+ QBoxLayout *layout = new QBoxLayout(this, QBoxLayout::TopToBottom, 10, 14);
+ setCaption(i18n("Recording..."));
+
+ QHBox *hBox = new QHBox(this);
+ m_label = new QLabel(hBox);
+ m_time = new QLabel(hBox);
+
+ layout->addWidget(hBox, 0, AlignCenter);
+
+ m_label->setText(i18n("Recording time remaining: "));
+ m_progressBar =
+ new CountdownBar(this, m_progressBarWidth, m_progressBarHeight);
+
+ m_progressBar->setFixedSize(m_progressBarWidth, m_progressBarHeight);
+
+ // Simply re-emit from Stop button
+ //
+ m_stopButton = new QPushButton(i18n("Stop"), this);
+ m_stopButton->setFixedWidth(60);
+
+ layout->addWidget(m_progressBar, 0, AlignCenter);
+ layout->addWidget(m_stopButton, 0, AlignRight);
+
+ connect (m_stopButton, SIGNAL(released()), this, SIGNAL(stopped()));
+
+ // Set the total time to show the bar in initial position
+ //
+ setElapsedTime(0);
+
+ m_accelerators = new QAccel(this);
+
+}
+
+void
+CountdownDialog::setLabel(const QString &label)
+{
+ m_label->setText(label);
+}
+
+void
+CountdownDialog::setTotalTime(int seconds)
+{
+ m_totalTime = seconds;
+ setElapsedTime(0); // clear
+}
+
+void
+CountdownDialog::setElapsedTime(int elapsedSeconds)
+{
+ int seconds = m_totalTime - elapsedSeconds;
+
+ if (seconds < 0) {
+ seconds = - seconds;
+ if (!m_pastEndMode)
+ setPastEndMode();
+ }
+
+ QString h, m, s;
+ h.sprintf("%02d", seconds / 3600);
+ m.sprintf("%02d", seconds / 60);
+ s.sprintf("%02d", seconds % 60);
+
+ if (seconds < 3600) // less than an hour
+ {
+ m_time->setText(QString("%1:%2").arg(m).arg(s));
+ } else if (seconds < 86400) // less than a day
+ {
+ m_time->setText(QString("%1:%2:%3").arg(h).arg(m).arg(s));
+ } else {
+ m_time->setText(i18n("Just how big is your hard disk?"));
+ }
+
+ // Draw the progress bar
+ //
+ if (m_pastEndMode) {
+ m_progressBar->setPosition(m_progressBarWidth);
+ } else {
+ // Attempt a simplistic fix for #1838190. In the context of an isolated
+ // test example, I'm fairly sure m_totalTime was 0, causing a divide by
+ // zero error, though the trace just listed it as an "Arithmetic
+ // exception."
+ if (m_totalTime == 0) {
+ RG_DEBUG << "CountdownDialog::setElapsedTime: FAILSAFE CODE FIRED, see bug #1838190 for details" << endl;
+ m_totalTime = 1;
+ }
+ int barPosition = m_progressBarWidth -
+ (elapsedSeconds * m_progressBarWidth) / m_totalTime;
+ m_progressBar->setPosition(barPosition);
+ }
+
+ // Dialog complete if the display time is zero
+ if (seconds == 0)
+ emit completed();
+
+}
+
+void
+CountdownDialog::setPastEndMode()
+{
+ if (m_pastEndMode) // already called
+ return ;
+
+ m_pastEndMode = true;
+ m_label->setText(i18n("Recording beyond end of composition: "));
+
+}
+
+}
+#include "CountdownDialog.moc"
diff --git a/src/gui/dialogs/CountdownDialog.h b/src/gui/dialogs/CountdownDialog.h
new file mode 100644
index 0000000..00aa6e0
--- /dev/null
+++ b/src/gui/dialogs/CountdownDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_COUNTDOWNDIALOG_H_
+#define _RG_COUNTDOWNDIALOG_H_
+
+#include <kdialogbase.h>
+#include <qdialog.h>
+
+
+class QWidget;
+class QString;
+class QPushButton;
+class QLabel;
+class QAccel;
+
+
+namespace Rosegarden
+{
+
+class CountdownBar;
+
+
+class CountdownDialog : public QDialog // KDialogBase
+{
+ Q_OBJECT
+
+public:
+ CountdownDialog(QWidget *parent, int seconds = 300);
+
+ void setLabel(const QString &label);
+ void setElapsedTime(int seconds);
+
+ int getTotalTime() const { return m_totalTime; }
+ void setTotalTime(int seconds);
+
+ QAccel* getAccelerators() { return m_accelerators; }
+
+signals:
+ void completed(); // m_totalTime has elapsed
+ void stopped(); // someone pushed the stop button
+
+protected:
+ void setPastEndMode();
+
+ bool m_pastEndMode;
+
+ int m_totalTime;
+
+ QLabel *m_label;
+ QLabel *m_time;
+ CountdownBar *m_progressBar;
+
+ QPushButton *m_stopButton;
+
+ int m_progressBarWidth;
+ int m_progressBarHeight;
+
+ QAccel *m_accelerators;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/DocumentConfigureDialog.cpp b/src/gui/dialogs/DocumentConfigureDialog.cpp
new file mode 100644
index 0000000..5f79f33
--- /dev/null
+++ b/src/gui/dialogs/DocumentConfigureDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "DocumentConfigureDialog.h"
+#include <qlayout.h>
+
+#include <klocale.h>
+#include "ConfigureDialogBase.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/configuration/AudioPropertiesPage.h"
+#include "gui/configuration/ColourConfigurationPage.h"
+#include "gui/configuration/DocumentMetaConfigurationPage.h"
+#include "gui/configuration/GeneralConfigurationPage.h"
+#include <kdialogbase.h>
+#include <qstring.h>
+#include <qwidget.h>
+#include <kstddirs.h>
+
+
+namespace Rosegarden
+{
+static QPixmap loadIcon(const char *name)
+{
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QString fileBase = pixmapDir + "/misc/";
+ fileBase += name;
+ if (QFile(fileBase + ".png").exists()) {
+ return QPixmap(fileBase + ".png");
+ } else if (QFile(fileBase + ".xpm").exists()) {
+ return QPixmap(fileBase + ".xpm");
+ }
+
+ QPixmap pmap = KGlobal::instance()->iconLoader()
+ ->loadIcon(QString::fromLatin1(name), KIcon::NoGroup, KIcon::SizeMedium);
+ return pmap;
+}
+
+
+DocumentConfigureDialog::DocumentConfigureDialog(RosegardenGUIDoc *doc,
+ QWidget *parent,
+ const char *name)
+ : ConfigureDialogBase(parent, i18n("Document Properties"), name)
+{
+ QWidget *pageWidget = 0;
+ QVBoxLayout *vlay = 0;
+ ConfigurationPage* page = 0;
+
+ // Document Meta Page
+ //
+ pageWidget = addPage(DocumentMetaConfigurationPage::iconLabel(),
+ DocumentMetaConfigurationPage::title(),
+ loadIcon(DocumentMetaConfigurationPage::iconName()));
+ vlay = new QVBoxLayout(pageWidget, 0, spacingHint());
+ page = new DocumentMetaConfigurationPage(doc, pageWidget);
+ vlay->addWidget(page);
+ page->setPageIndex(pageIndex(pageWidget));
+ m_configurationPages.push_back(page);
+
+ // Audio Page
+ //
+ pageWidget = addPage(AudioPropertiesPage::iconLabel(),
+ AudioPropertiesPage::title(),
+ loadIcon(AudioPropertiesPage::iconName()));
+ vlay = new QVBoxLayout(pageWidget, 0, spacingHint());
+ page = new AudioPropertiesPage(doc, pageWidget);
+ vlay->addWidget(page);
+ page->setPageIndex(pageIndex(pageWidget));
+ m_configurationPages.push_back(page);
+
+ // Colour Page
+ pageWidget = addPage(ColourConfigurationPage::iconLabel(),
+ ColourConfigurationPage::title(),
+ loadIcon(ColourConfigurationPage::iconName()));
+
+ vlay = new QVBoxLayout(pageWidget, 0, spacingHint());
+ page = new ColourConfigurationPage(doc, pageWidget);
+ vlay->addWidget(page);
+ page->setPageIndex(pageIndex(pageWidget));
+ m_configurationPages.push_back(page);
+
+ resize(minimumSize());
+}
+
+void
+DocumentConfigureDialog::showAudioPage()
+{
+ int index = 0;
+
+ for (configurationpages::iterator i = m_configurationPages.begin();
+ i != m_configurationPages.end(); ++i) {
+
+ AudioPropertiesPage *page =
+ dynamic_cast<AudioPropertiesPage *>(*i);
+
+ if (!page) {
+ ++index;
+ continue;
+ }
+
+ showPage(index);
+ return ;
+ }
+}
+
+/* hjj: WHAT TO DO WITH THIS ?
+void
+DocumentConfigureDialog::selectMetadata(QString name)
+{
+ int index = 0;
+
+ for (configurationpages::iterator i = m_configurationPages.begin();
+ i != m_configurationPages.end(); ++i) {
+
+ DocumentMetaConfigurationPage *page =
+ dynamic_cast<DocumentMetaConfigurationPage *>(*i);
+
+ if (!page) {
+ ++index;
+ continue;
+ }
+
+ page->selectMetadata(name);
+ showPage(index);
+ return ;
+ }
+}
+*/
+
+}
diff --git a/src/gui/dialogs/DocumentConfigureDialog.h b/src/gui/dialogs/DocumentConfigureDialog.h
new file mode 100644
index 0000000..6713047
--- /dev/null
+++ b/src/gui/dialogs/DocumentConfigureDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_DOCUMENTCONFIGUREDIALOG_H_
+#define _RG_DOCUMENTCONFIGUREDIALOG_H_
+
+#include "ConfigureDialogBase.h"
+#include <qstring.h>
+
+
+class QWidget;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+
+
+class DocumentConfigureDialog : public ConfigureDialogBase
+{
+public:
+ DocumentConfigureDialog(RosegardenGUIDoc *doc,
+ QWidget *parent=0,
+ const char *name=0);
+
+ void showAudioPage();
+
+/* hjj: WHAT TO DO WITH THIS ?
+ void selectMetadata(QString name);
+*/
+};
+
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/EventEditDialog.cpp b/src/gui/dialogs/EventEditDialog.cpp
new file mode 100644
index 0000000..c9991f1
--- /dev/null
+++ b/src/gui/dialogs/EventEditDialog.cpp
@@ -0,0 +1,528 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "EventEditDialog.h"
+
+#include <klocale.h>
+#include "misc/Strings.h"
+#include "base/Event.h"
+#include "base/MidiTypes.h"
+#include "base/NotationTypes.h"
+#include "base/PropertyName.h"
+#include "base/RealTime.h"
+#include "gui/editors/notation/NotePixmapFactory.h"
+#include <kdialogbase.h>
+#include <kmessagebox.h>
+#include <qcheckbox.h>
+#include <qfont.h>
+#include <qgrid.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qobject.h>
+#include <qobjectlist.h>
+#include <qpushbutton.h>
+#include <qscrollview.h>
+#include <qsize.h>
+#include <qspinbox.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+EventEditDialog::EventEditDialog(QWidget *parent,
+ const Event &event,
+ bool editable) :
+ KDialogBase(parent, 0, true, i18n(editable ? "Advanced Event Edit" : "Advanced Event Viewer"),
+ (editable ? (Ok | Cancel) : Ok)),
+ m_durationDisplay(0),
+ m_durationDisplayAux(0),
+ m_persistentGrid(0),
+ m_nonPersistentGrid(0),
+ m_nonPersistentView(0),
+ m_originalEvent(event),
+ m_event(event),
+ m_type(event.getType()),
+ m_absoluteTime(event.getAbsoluteTime()),
+ m_duration(event.getDuration()),
+ m_subOrdering(event.getSubOrdering()),
+ m_modified(false)
+{
+ QVBox *vbox = makeVBoxMainWidget();
+
+ QGroupBox *intrinsicBox = new QGroupBox
+ (1, Horizontal, i18n("Intrinsics"), vbox);
+
+ QGrid *intrinsicGrid = new QGrid(4, QGrid::Horizontal, intrinsicBox);
+
+ new QLabel(i18n("Event type: "), intrinsicGrid);
+ new QLabel("", intrinsicGrid);
+ new QLabel("", intrinsicGrid);
+ QLineEdit *lineEdit = new QLineEdit(intrinsicGrid);
+ lineEdit->setText(strtoqstr(event.getType()));
+
+ new QLabel(i18n("Absolute time: "), intrinsicGrid);
+ new QLabel("", intrinsicGrid);
+ new QLabel("", intrinsicGrid);
+ QSpinBox *absoluteTime = new QSpinBox
+ (INT_MIN, INT_MAX, Note(Note::Shortest).getDuration(), intrinsicGrid);
+ absoluteTime->setValue(event.getAbsoluteTime());
+ QObject::connect(absoluteTime, SIGNAL(valueChanged(int)),
+ this, SLOT(slotAbsoluteTimeChanged(int)));
+ slotAbsoluteTimeChanged(event.getAbsoluteTime());
+
+ new QLabel(i18n("Duration: "), intrinsicGrid);
+ m_durationDisplay = new QLabel("(note)", intrinsicGrid);
+ m_durationDisplay->setMinimumWidth(20);
+ m_durationDisplayAux = new QLabel("(note)", intrinsicGrid);
+ m_durationDisplayAux->setMinimumWidth(20);
+
+ QSpinBox *duration = new QSpinBox
+ (0, INT_MAX, Note(Note::Shortest).getDuration(), intrinsicGrid);
+ duration->setValue(event.getDuration());
+ QObject::connect(duration, SIGNAL(valueChanged(int)),
+ this, SLOT(slotDurationChanged(int)));
+ slotDurationChanged(event.getDuration());
+
+ new QLabel(i18n("Sub-ordering: "), intrinsicGrid);
+ new QLabel("", intrinsicGrid);
+ new QLabel("", intrinsicGrid);
+
+ QSpinBox *subOrdering = new QSpinBox( -100, 100, 1, intrinsicGrid);
+ subOrdering->setValue(event.getSubOrdering());
+ QObject::connect(subOrdering, SIGNAL(valueChanged(int)),
+ this, SLOT(slotSubOrderingChanged(int)));
+ slotSubOrderingChanged(event.getSubOrdering());
+
+ QGroupBox *persistentBox = new QGroupBox
+ (1, Horizontal, i18n("Persistent properties"), vbox);
+ m_persistentGrid = new QGrid(4, QGrid::Horizontal, persistentBox);
+
+ QLabel *label = new QLabel(i18n("Name"), m_persistentGrid);
+ QFont font(label->font());
+ font.setItalic(true);
+ label->setFont(font);
+
+ label = new QLabel(i18n("Type"), m_persistentGrid);
+ label->setFont(font);
+ label = new QLabel(i18n("Value"), m_persistentGrid);
+ label->setFont(font);
+ label = new QLabel("", m_persistentGrid);
+ label->setFont(font);
+
+ Event::PropertyNames p = event.getPersistentPropertyNames();
+
+ for (Event::PropertyNames::iterator i = p.begin();
+ i != p.end(); ++i) {
+ addPersistentProperty(*i);
+ }
+
+ p = event.getNonPersistentPropertyNames();
+
+ if (p.begin() == p.end()) {
+ m_nonPersistentView = 0;
+ m_nonPersistentGrid = 0;
+ } else {
+
+ QGroupBox *nonPersistentBox = new QGroupBox
+ (1, Horizontal, i18n("Non-persistent properties"), vbox);
+ new QLabel(i18n("These are cached values, lost if the event is modified."),
+ nonPersistentBox);
+
+ m_nonPersistentView = new QScrollView(nonPersistentBox);
+ //m_nonPersistentView->setHScrollBarMode(QScrollView::AlwaysOff);
+ m_nonPersistentView->setResizePolicy(QScrollView::AutoOneFit);
+
+ m_nonPersistentGrid = new QGrid
+ (4, QGrid::Horizontal, m_nonPersistentView->viewport());
+ m_nonPersistentView->addChild(m_nonPersistentGrid);
+
+ m_nonPersistentGrid->setSpacing(4);
+ m_nonPersistentGrid->setMargin(5);
+
+ label = new QLabel(i18n("Name "), m_nonPersistentGrid);
+ label->setFont(font);
+ label = new QLabel(i18n("Type "), m_nonPersistentGrid);
+ label->setFont(font);
+ label = new QLabel(i18n("Value "), m_nonPersistentGrid);
+ label->setFont(font);
+ label = new QLabel("", m_nonPersistentGrid);
+ label->setFont(font);
+
+ for (Event::PropertyNames::iterator i = p.begin();
+ i != p.end(); ++i) {
+
+ new QLabel(strtoqstr(*i), m_nonPersistentGrid, strtoqstr(*i));
+ new QLabel(strtoqstr(event.getPropertyTypeAsString(*i)), m_nonPersistentGrid, strtoqstr(*i));
+ new QLabel(strtoqstr(event.getAsString(*i)), m_nonPersistentGrid, strtoqstr(*i));
+ QPushButton *button = new QPushButton("P", m_nonPersistentGrid, strtoqstr(*i));
+ button->setFixedSize(QSize(24, 24));
+ QToolTip::add
+ (button, i18n("Make persistent"));
+ QObject::connect(button, SIGNAL(clicked()),
+ this, SLOT(slotPropertyMadePersistent()));
+ }
+ }
+}
+
+void
+EventEditDialog::addPersistentProperty(const PropertyName &name)
+{
+ QLabel *label = new QLabel(strtoqstr(name), m_persistentGrid, strtoqstr(name));
+ label->show();
+ label = new QLabel(strtoqstr(m_originalEvent.getPropertyTypeAsString(name)),
+ m_persistentGrid, strtoqstr(name));
+ label->show();
+
+ PropertyType type(m_originalEvent.getPropertyType(name));
+ switch (type) {
+
+ case Int: {
+ int min = INT_MIN, max = INT_MAX;
+ // DMM - constrain program changes to a useful range of values
+ // Might other types have a similar need for such limits?
+ if (m_originalEvent.isa(ProgramChange::EventType)) {
+ min = 0;
+ max = 127;
+ }
+ QSpinBox *spinBox = new QSpinBox
+ (min, max, 1, m_persistentGrid, strtoqstr(name));
+ spinBox->setValue(m_originalEvent.get<Int>(name));
+ QObject::connect(spinBox, SIGNAL(valueChanged(int)),
+ this, SLOT(slotIntPropertyChanged(int)));
+ spinBox->show();
+ break;
+ }
+case UInt: {
+ int min = 0;
+ int max = UINT_MAX;
+ if (m_originalEvent.isa(ProgramChange::EventType)) {
+ min = 0;
+ max = 65535;
+ }
+ QSpinBox *spinBox = new QSpinBox
+ (min, max, 1, m_persistentGrid, strtoqstr(name));
+ spinBox->setValue(m_originalEvent.get<UInt>(name));
+ QObject::connect(spinBox, SIGNAL(valueChanged(int)),
+ this, SLOT(slotIntPropertyChanged(int)));
+ spinBox->show();
+ break;
+ }
+ case RealTimeT: {
+ RealTime realTime = m_originalEvent.get<RealTimeT>(name);
+
+ QHBox* hbox = new QHBox(m_persistentGrid);
+
+ // seconds
+ //
+ QSpinBox *spinBox = new QSpinBox
+ (INT_MIN, INT_MAX, 1,
+ hbox, strtoqstr(name) + "%sec");
+ spinBox->setValue(realTime.sec);
+
+ QObject::connect(spinBox, SIGNAL(valueChanged(int)),
+ this, SLOT(slotRealTimePropertyChanged(int)));
+
+ // nseconds
+ //
+ spinBox = new QSpinBox
+ (INT_MIN, INT_MAX, 1,
+ hbox, strtoqstr(name) + "%nsec");
+ spinBox->setValue(realTime.nsec);
+
+ QObject::connect(spinBox, SIGNAL(valueChanged(int)),
+ this, SLOT(slotRealTimePropertyChanged(int)));
+ spinBox->show();
+ break;
+ }
+
+ case Bool: {
+ QCheckBox *checkBox = new QCheckBox
+ ("", m_persistentGrid, strtoqstr(name));
+ checkBox->setChecked(m_originalEvent.get<Bool>(name));
+ QObject::connect(checkBox, SIGNAL(activated()),
+ this, SLOT(slotBoolPropertyChanged()));
+ checkBox->show();
+ break;
+ }
+
+ case String: {
+ QLineEdit *lineEdit = new QLineEdit
+ (strtoqstr(m_originalEvent.get<String>(name)),
+ m_persistentGrid,
+ strtoqstr(name));
+ QObject::connect(lineEdit, SIGNAL(textChanged(const QString &)),
+ this, SLOT(slotStringPropertyChanged(const QString &)));
+ lineEdit->show();
+ break;
+ }
+ }
+
+ QPushButton *button = new QPushButton("X", m_persistentGrid,
+ strtoqstr(name));
+ button->setFixedSize(QSize(24, 24));
+ QToolTip::add
+ (button, i18n("Delete this property"));
+ QObject::connect(button, SIGNAL(clicked()),
+ this, SLOT(slotPropertyDeleted()));
+ button->show();
+}
+
+Event
+EventEditDialog::getEvent() const
+{
+ return Event(m_event, m_absoluteTime, m_duration, m_subOrdering);
+}
+
+void
+EventEditDialog::slotEventTypeChanged(const QString &type)
+{
+ std::string t(qstrtostr(type));
+ if (t != m_type) {
+ m_modified = true;
+ m_type = t;
+ }
+}
+
+void
+EventEditDialog::slotAbsoluteTimeChanged(int value)
+{
+ if (value == m_absoluteTime)
+ return ;
+ m_modified = true;
+ m_absoluteTime = value;
+}
+
+void
+EventEditDialog::slotDurationChanged(int value)
+{
+ timeT error = 0;
+ m_durationDisplay->setPixmap
+ (NotePixmapFactory::toQPixmap(m_notePixmapFactory.makeNoteMenuPixmap(timeT(value), error)));
+
+ if (error >= value / 2) {
+ m_durationDisplayAux->setText("++ ");
+ } else if (error > 0) {
+ m_durationDisplayAux->setText("+ ");
+ } else if (error < 0) {
+ m_durationDisplayAux->setText("- ");
+ } else {
+ m_durationDisplayAux->setText(" ");
+ }
+
+ if (timeT(value) == m_duration)
+ return ;
+
+ m_modified = true;
+ m_duration = value;
+}
+
+void
+EventEditDialog::slotSubOrderingChanged(int value)
+{
+ if (value == m_subOrdering)
+ return ;
+ m_modified = true;
+ m_subOrdering = value;
+}
+
+void
+EventEditDialog::slotIntPropertyChanged(int value)
+{
+ const QObject *s = sender();
+ const QSpinBox *spinBox = dynamic_cast<const QSpinBox *>(s);
+ if (!spinBox)
+ return ;
+
+ m_modified = true;
+ QString propertyName = spinBox->name();
+ m_event.set<Int>(qstrtostr(propertyName), value);
+}
+
+void
+EventEditDialog::slotRealTimePropertyChanged(int value)
+{
+ const QObject *s = sender();
+ const QSpinBox *spinBox = dynamic_cast<const QSpinBox *>(s);
+ if (!spinBox)
+ return ;
+
+ m_modified = true;
+ QString propertyFullName = spinBox->name();
+
+ QString propertyName = propertyFullName.section('%', 0, 0),
+ nsecOrSec = propertyFullName.section('%', 1, 1);
+
+ RealTime realTime = m_event.get<RealTimeT>(qstrtostr(propertyName));
+
+ if (nsecOrSec == "sec")
+ realTime.sec = value;
+ else
+ realTime.nsec = value;
+
+ m_event.set<Int>(qstrtostr(propertyName), value);
+}
+
+void
+EventEditDialog::slotBoolPropertyChanged()
+{
+ const QObject *s = sender();
+ const QCheckBox *checkBox = dynamic_cast<const QCheckBox *>(s);
+ if (!checkBox)
+ return ;
+
+ m_modified = true;
+ QString propertyName = checkBox->name();
+ bool checked = checkBox->isChecked();
+
+ m_event.set<Bool>(qstrtostr(propertyName), checked);
+}
+
+void
+EventEditDialog::slotStringPropertyChanged(const QString &value)
+{
+ const QObject *s = sender();
+ const QLineEdit *lineEdit = dynamic_cast<const QLineEdit *>(s);
+ if (!lineEdit)
+ return ;
+
+ m_modified = true;
+ QString propertyName = lineEdit->name();
+ m_event.set<String>(qstrtostr(propertyName), qstrtostr(value));
+}
+
+void
+EventEditDialog::slotPropertyDeleted()
+{
+ const QObject *s = sender();
+ const QPushButton *pushButton = dynamic_cast<const QPushButton *>(s);
+ if (!pushButton)
+ return ;
+
+ QString propertyName = pushButton->name();
+
+ if (KMessageBox::warningContinueCancel
+ (this,
+ i18n("Are you sure you want to delete the \"%1\" property?\n\n"
+ "Removing necessary properties may cause unexpected behavior.").
+ arg(propertyName),
+ i18n("Edit Event"),
+ i18n("&Delete")) != KMessageBox::Continue)
+ return ;
+
+ m_modified = true;
+ QObjectList *list = m_persistentGrid->queryList(0, propertyName, false);
+ QObjectListIt i(*list);
+ QObject *obj;
+ while ((obj = i.current()) != 0) {
+ ++i;
+ delete obj;
+ }
+ delete list;
+
+ m_event.unset(qstrtostr(propertyName));
+}
+
+void
+EventEditDialog::slotPropertyMadePersistent()
+{
+ const QObject *s = sender();
+ const QPushButton *pushButton = dynamic_cast<const QPushButton *>(s);
+ if (!pushButton)
+ return ;
+
+ QString propertyName = pushButton->name();
+
+ if (KMessageBox::warningContinueCancel
+ (this,
+ i18n("Are you sure you want to make the \"%1\" property persistent?\n\n"
+ "This could cause problems if it overrides a different "
+ "computed value later on.").
+ arg(propertyName),
+ i18n("Edit Event"),
+ i18n("Make &Persistent")) != KMessageBox::Continue)
+ return ;
+
+ QObjectList *list = m_nonPersistentGrid->queryList(0, propertyName, false);
+ QObjectListIt i(*list);
+ QObject *obj;
+ while ((obj = i.current()) != 0) {
+ ++i;
+ delete obj;
+ }
+ delete list;
+
+ m_modified = true;
+ addPersistentProperty(qstrtostr(propertyName));
+
+ PropertyType type =
+ m_originalEvent.getPropertyType(qstrtostr(propertyName));
+
+ switch (type) {
+
+ case Int:
+ m_event.set<Int>
+ (qstrtostr(propertyName),
+ m_originalEvent.get<Int>
+ (qstrtostr(propertyName)));
+ break;
+
+ case UInt:
+ m_event.set<UInt>
+ (qstrtostr(propertyName),
+ m_originalEvent.get<UInt>
+ (qstrtostr(propertyName)));
+ break;
+
+ case RealTimeT:
+ m_event.set<RealTimeT>
+ (qstrtostr(propertyName),
+ m_originalEvent.get<RealTimeT>
+ (qstrtostr(propertyName)));
+ break;
+
+ case Bool:
+ m_event.set<Bool>
+ (qstrtostr(propertyName),
+ m_originalEvent.get<Bool>
+ (qstrtostr(propertyName)));
+ break;
+
+ case String:
+ m_event.set<String>
+ (qstrtostr(propertyName),
+ m_originalEvent.get<String>
+ (qstrtostr(propertyName)));
+ break;
+ }
+}
+
+}
+#include "EventEditDialog.moc"
diff --git a/src/gui/dialogs/EventEditDialog.h b/src/gui/dialogs/EventEditDialog.h
new file mode 100644
index 0000000..337a190
--- /dev/null
+++ b/src/gui/dialogs/EventEditDialog.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_EVENTEDITDIALOG_H_
+#define _RG_EVENTEDITDIALOG_H_
+
+#include "base/Event.h"
+#include "gui/editors/notation/NotePixmapFactory.h"
+#include <string>
+#include <kdialogbase.h>
+
+
+class QWidget;
+class QString;
+class QScrollView;
+class QLabel;
+class QGrid;
+
+
+namespace Rosegarden
+{
+
+class PropertyName;
+
+
+class EventEditDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Construct an event-edit dialog showing the properties of the
+ * given event. If editable is false, the user will not be allowed
+ * to modify the event; otherwise the event will be editable and
+ * the resulting edited version can subsequently be queried
+ * through getEvent().
+ */
+ EventEditDialog(QWidget *parent,
+ const Event &event,
+ bool editable = true);
+
+ bool isModified() const { return m_modified; }
+ Event getEvent() const;
+
+public slots:
+ void slotEventTypeChanged(const QString &);
+ void slotAbsoluteTimeChanged(int value);
+ void slotDurationChanged(int value);
+ void slotSubOrderingChanged(int value);
+
+ void slotIntPropertyChanged(int);
+ void slotRealTimePropertyChanged(int);
+ void slotBoolPropertyChanged();
+ void slotStringPropertyChanged(const QString &);
+
+ void slotPropertyDeleted();
+ void slotPropertyMadePersistent();
+
+protected:
+ void addPersistentProperty(const PropertyName &);
+
+ //--------------- Data members ---------------------------------
+ NotePixmapFactory m_notePixmapFactory;
+
+ QLabel *m_durationDisplay;
+ QLabel *m_durationDisplayAux;
+
+ QGrid *m_persistentGrid;
+ QGrid *m_nonPersistentGrid;
+
+ QScrollView *m_nonPersistentView;
+
+ const Event &m_originalEvent;
+ Event m_event;
+
+ std::string m_type;
+ timeT m_absoluteTime;
+ timeT m_duration;
+ int m_subOrdering;
+
+ bool m_modified;
+};
+
+/*
+ * A simpler event editor for use by the EventView and MatrixView
+ * and people who want to remain sane.
+ */
+
+}
+
+#endif
diff --git a/src/gui/dialogs/EventFilterDialog.cpp b/src/gui/dialogs/EventFilterDialog.cpp
new file mode 100644
index 0000000..7b0c15c
--- /dev/null
+++ b/src/gui/dialogs/EventFilterDialog.cpp
@@ -0,0 +1,476 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ This file is Copyright 2003-2006
+ D. Michael McIntyre <dmmcintyr@users.sourceforge.net>
+
+ 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 "EventFilterDialog.h"
+
+#include "misc/Debug.h"
+#include "base/BaseProperties.h"
+#include "base/Event.h"
+#include "base/NotationTypes.h"
+#include "base/BasicQuantizer.h"
+#include "gui/dialogs/PitchPickerDialog.h"
+#include "gui/editors/notation/NotationStrings.h"
+#include "gui/editors/notation/NotePixmapFactory.h"
+#include "document/ConfigGroups.h"
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdialogbase.h>
+#include <klocale.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qdialog.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpixmap.h>
+#include <qpushbutton.h>
+#include <qsizepolicy.h>
+#include <qspinbox.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+EventFilterDialog::EventFilterDialog(QWidget* parent)
+ : KDialogBase(parent, "eventfilerdialog", true, i18n("Event Filter"), Ok | Cancel, Ok),
+ m_standardQuantizations(BasicQuantizer::getStandardQuantizations())
+{
+ cfg = kapp->config();
+ initDialog();
+}
+
+EventFilterDialog::~EventFilterDialog()
+{
+ // nothing here
+}
+
+void
+EventFilterDialog::initDialog()
+{
+ QVBox* mainWidget = makeVBoxMainWidget();
+
+
+ //----------[ Note Filter Widgets ]-------------------------
+
+ // Frame
+ QGroupBox* noteFrame = new QGroupBox(i18n("Note Events"), mainWidget);
+ QGridLayout* noteFrameLayout = new QGridLayout(noteFrame, 1, 1, 20, 6);
+
+ // Labels
+ QLabel* pitchFromLabel = new QLabel(i18n("lowest:"), noteFrame);
+ noteFrameLayout->addWidget(pitchFromLabel, 0, 2);
+
+ QLabel* pitchToLabel = new QLabel(i18n("highest:"), noteFrame);
+ noteFrameLayout->addWidget(pitchToLabel, 0, 4);
+
+ QLabel* pitchLabel = new QLabel(i18n("Pitch:"), noteFrame);
+ noteFrameLayout->addWidget(pitchLabel, 1, 1);
+
+ QLabel* velocityLabel = new QLabel(i18n("Velocity:"), noteFrame);
+ noteFrameLayout->addWidget(velocityLabel, 2, 1);
+
+ QLabel* durationLabel = new QLabel(i18n("Duration:"), noteFrame);
+ noteFrameLayout->addWidget(durationLabel, 3, 1);
+
+ // Include Boxes
+ m_notePitchIncludeComboBox = new QComboBox(0, noteFrame);
+ m_notePitchIncludeComboBox->insertItem(i18n("include"));
+ m_notePitchIncludeComboBox->insertItem(i18n("exclude"));
+ cfg->setGroup(EventFilterDialogConfigGroup);
+ m_notePitchIncludeComboBox->setCurrentItem(cfg->readBoolEntry("pitchinclude", 0));
+ noteFrameLayout->addWidget(m_notePitchIncludeComboBox, 1, 0);
+
+ m_noteVelocityIncludeComboBox = new QComboBox(0, noteFrame);
+ m_noteVelocityIncludeComboBox->insertItem(i18n("include"));
+ m_noteVelocityIncludeComboBox->insertItem(i18n("exclude"));
+ cfg->setGroup(EventFilterDialogConfigGroup);
+ m_noteVelocityIncludeComboBox->setCurrentItem(cfg->readBoolEntry("velocityinclude", 0));
+ noteFrameLayout->addWidget(m_noteVelocityIncludeComboBox, 2, 0);
+
+ m_noteDurationIncludeComboBox = new QComboBox(0, noteFrame);
+ m_noteDurationIncludeComboBox->insertItem(i18n("include"));
+ m_noteDurationIncludeComboBox->insertItem(i18n("exclude"));
+ cfg->setGroup(EventFilterDialogConfigGroup);
+ m_noteDurationIncludeComboBox->setCurrentItem(cfg->readBoolEntry("durationinclude", 0));
+ noteFrameLayout->addWidget(m_noteDurationIncludeComboBox, 3, 0);
+
+ // Pitch From
+ m_pitchFromSpinBox = new QSpinBox(noteFrame);
+ m_pitchFromSpinBox->setMaxValue(127);
+ cfg->setGroup(EventFilterDialogConfigGroup);
+ m_pitchFromSpinBox->setValue(cfg->readUnsignedNumEntry("pitchfrom", 0));
+ noteFrameLayout->addWidget(m_pitchFromSpinBox, 1, 2);
+ connect(m_pitchFromSpinBox, SIGNAL(valueChanged(int)),
+ SLOT(slotPitchFromChanged(int)));
+
+ m_pitchFromChooserButton = new QPushButton(i18n("edit"), noteFrame);
+ m_pitchFromChooserButton->setSizePolicy(QSizePolicy((QSizePolicy::SizeType)0,
+ (QSizePolicy::SizeType)0, 0, 0, m_pitchFromChooserButton->
+ sizePolicy().hasHeightForWidth()));
+ QToolTip::add
+ (m_pitchFromChooserButton, i18n("choose a pitch using a staff"));
+ noteFrameLayout->addWidget(m_pitchFromChooserButton, 1, 3);
+ connect(m_pitchFromChooserButton, SIGNAL(clicked()),
+ SLOT(slotPitchFromChooser()));
+
+ // Pitch To
+ m_pitchToSpinBox = new QSpinBox(noteFrame);
+ m_pitchToSpinBox->setMaxValue(127);
+ cfg->setGroup(EventFilterDialogConfigGroup);
+ m_pitchToSpinBox->setValue(cfg->readUnsignedNumEntry("pitchto", 127));
+ noteFrameLayout->addWidget(m_pitchToSpinBox, 1, 4);
+ connect(m_pitchToSpinBox, SIGNAL(valueChanged(int)),
+ SLOT(slotPitchToChanged(int)));
+
+ m_pitchToChooserButton = new QPushButton(i18n("edit"), noteFrame);
+ QToolTip::add
+ (m_pitchToChooserButton, i18n("choose a pitch using a staff"));
+ noteFrameLayout->addWidget(m_pitchToChooserButton, 1, 5);
+ connect(m_pitchToChooserButton, SIGNAL(clicked()),
+ SLOT(slotPitchToChooser()));
+
+ // Velocity From/To
+ m_velocityFromSpinBox = new QSpinBox(noteFrame);
+ m_velocityFromSpinBox->setMaxValue(127);
+ cfg->setGroup(EventFilterDialogConfigGroup);
+ m_velocityFromSpinBox->setValue(cfg->readUnsignedNumEntry("velocityfrom", 0));
+ noteFrameLayout->addWidget(m_velocityFromSpinBox, 2, 2);
+ connect(m_velocityFromSpinBox, SIGNAL(valueChanged(int)),
+ SLOT(slotVelocityFromChanged(int)));
+
+ m_velocityToSpinBox = new QSpinBox(noteFrame);
+ m_velocityToSpinBox->setMaxValue(127);
+ cfg->setGroup(EventFilterDialogConfigGroup);
+ m_velocityToSpinBox->setValue(cfg->readUnsignedNumEntry("velocityto", 127));
+ noteFrameLayout->addWidget( m_velocityToSpinBox, 2, 4 );
+ connect(m_velocityToSpinBox, SIGNAL(valueChanged(int)),
+ SLOT(slotVelocityToChanged(int)));
+
+
+ // Duration From/To
+ m_noteDurationFromComboBox = new QComboBox(0, noteFrame);
+ m_noteDurationFromComboBox->insertItem(i18n("longest"));
+ noteFrameLayout->addWidget(m_noteDurationFromComboBox, 3, 2);
+ connect(m_noteDurationFromComboBox, SIGNAL(activated(int)),
+ SLOT(slotDurationFromChanged(int)));
+
+ m_noteDurationToComboBox = new QComboBox(0, noteFrame);
+ m_noteDurationToComboBox->insertItem(i18n("longest"));
+ noteFrameLayout->addWidget(m_noteDurationToComboBox, 3, 4);
+ connect(m_noteDurationToComboBox, SIGNAL(activated(int)),
+ SLOT(slotDurationToChanged(int)));
+
+ populateDurationCombos();
+
+
+ //---------[ Buttons ]--------------------------------------
+ QFrame* privateLayoutWidget = new QFrame(mainWidget);
+ QGridLayout* buttonLayout = new QGridLayout(privateLayoutWidget, 1, 1, 20, 6);
+
+ m_buttonAll = new QPushButton(i18n("Include all"), privateLayoutWidget);
+ m_buttonAll->setAutoDefault(true);
+ QToolTip::add
+ (m_buttonAll, i18n("Include entire range of values"));
+ buttonLayout->addWidget( m_buttonAll, 0, 0 );
+
+ m_buttonNone = new QPushButton(i18n("Exclude all"), privateLayoutWidget);
+ m_buttonNone->setAutoDefault(true);
+ QToolTip::add
+ (m_buttonNone, i18n("Exclude entire range of values"));
+ buttonLayout->addWidget( m_buttonNone, 0, 1 );
+
+ connect(m_buttonAll, SIGNAL(clicked()), this, SLOT(slotToggleAll()));
+ connect(m_buttonNone, SIGNAL(clicked()), this, SLOT(slotToggleNone()));
+
+
+}
+
+void
+EventFilterDialog::populateDurationCombos()
+{
+ 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_noteDurationFromComboBox->insertItem(error ? noMap : pmap, label);
+ m_noteDurationToComboBox ->insertItem(error ? noMap : pmap, label);
+ }
+ m_noteDurationFromComboBox->insertItem(noMap, i18n("shortest"));
+ m_noteDurationToComboBox->insertItem(noMap, i18n("shortest"));
+
+ cfg->setGroup(EventFilterDialogConfigGroup);
+ m_noteDurationFromComboBox->setCurrentItem(
+ cfg->readUnsignedNumEntry("durationfrom", 0));
+ m_noteDurationToComboBox->setCurrentItem(
+ cfg->readUnsignedNumEntry("durationto", (m_noteDurationToComboBox->count() - 1)));
+}
+
+void
+EventFilterDialog::slotToggleAll()
+{
+ RG_DEBUG << "EventFilterDialog::slotToggleAll()" << endl;
+ m_pitchFromSpinBox ->setValue(0);
+ m_pitchToSpinBox ->setValue(127);
+ m_velocityFromSpinBox ->setValue(0);
+ m_velocityToSpinBox ->setValue(127);
+ m_noteDurationFromComboBox ->setCurrentItem(11); // hard coded; should be variable
+ m_noteDurationToComboBox ->setCurrentItem(0); // 0 = unlimited; 11 = 0
+}
+
+void
+EventFilterDialog::slotToggleNone()
+{
+ RG_DEBUG << "EventFilterDialog::slotToggleNone()" << endl;
+ m_pitchFromSpinBox ->setValue(0);
+ m_pitchToSpinBox ->setValue(0);
+ m_velocityFromSpinBox ->setValue(0);
+ m_velocityToSpinBox ->setValue(0);
+ m_noteDurationFromComboBox ->setCurrentItem(11);
+ m_noteDurationToComboBox ->setCurrentItem(11);
+}
+
+void
+EventFilterDialog::slotOk()
+{
+ cfg->setGroup(EventFilterDialogConfigGroup);
+
+ cfg->writeEntry("pitchinclude", m_notePitchIncludeComboBox->currentItem());
+ cfg->writeEntry("pitchfrom", m_pitchFromSpinBox->value());
+ cfg->writeEntry("pitchto", m_pitchToSpinBox->value());
+
+ cfg->writeEntry("velocityinclude", m_noteVelocityIncludeComboBox->currentItem());
+ cfg->writeEntry("velocityfrom", m_velocityFromSpinBox->value());
+ cfg->writeEntry("velocityto", m_velocityToSpinBox->value());
+
+ cfg->writeEntry("durationinclude", m_noteDurationIncludeComboBox->currentItem());
+ cfg->writeEntry("durationfrom", m_noteDurationFromComboBox->currentItem());
+ cfg->writeEntry("durationto", m_noteDurationToComboBox->currentItem());
+
+ accept();
+}
+
+void
+EventFilterDialog::slotPitchFromChanged(int pitch)
+{
+ if (pitch > m_pitchToSpinBox->value())
+ m_pitchToSpinBox->setValue(pitch);
+}
+
+void
+EventFilterDialog::slotPitchToChanged(int pitch)
+{
+ if (pitch < m_pitchFromSpinBox->value())
+ m_pitchFromSpinBox->setValue(pitch);
+}
+
+void
+EventFilterDialog::slotVelocityFromChanged(int velocity)
+{
+ if (velocity > m_velocityToSpinBox->value())
+ m_velocityToSpinBox->setValue(velocity);
+}
+
+void
+EventFilterDialog::slotVelocityToChanged(int velocity)
+{
+ if (velocity < m_velocityFromSpinBox->value())
+ m_velocityFromSpinBox->setValue(velocity);
+}
+
+void
+EventFilterDialog::slotDurationFromChanged(int index)
+{
+ if (index < m_noteDurationToComboBox->currentItem())
+ m_noteDurationToComboBox->setCurrentItem(index);
+}
+
+void
+EventFilterDialog::slotDurationToChanged(int index)
+{
+ if (index > m_noteDurationFromComboBox->currentItem())
+ m_noteDurationFromComboBox->setCurrentItem(index);
+}
+
+
+void
+EventFilterDialog::slotPitchFromChooser()
+{
+ PitchPickerDialog dialog(this, m_pitchFromSpinBox->value(), i18n("Lowest pitch"));
+
+ if (dialog.exec() == QDialog::Accepted) {
+ m_pitchFromSpinBox->setValue(dialog.getPitch());
+ }
+}
+
+void
+EventFilterDialog::slotPitchToChooser()
+{
+ PitchPickerDialog dialog(this, m_pitchToSpinBox->value(), i18n("Highest pitch"));
+
+ if (dialog.exec() == QDialog::Accepted) {
+ m_pitchToSpinBox->setValue(dialog.getPitch());
+ }
+}
+
+long
+EventFilterDialog::getDurationFromIndex(int index)
+{
+ switch (index) {
+ // 0
+ case 11:
+ return 0;
+ // 1/96
+ case 10:
+ return long(Note(Note::SixtyFourthNote).getDuration() / 3);
+ // 1/64
+ case 9 :
+ return long(Note(Note::SixtyFourthNote).getDuration());
+ // 1/48
+ case 8 :
+ return long(Note(Note::ThirtySecondNote).getDuration() / 3);
+ // 1/32
+ case 7 :
+ return long(Note(Note::ThirtySecondNote).getDuration());
+ // 1/24
+ case 6 :
+ return long(Note(Note::SixteenthNote).getDuration() / 3);
+ // 1/16
+ case 5 :
+ return long(Note(Note::SixteenthNote).getDuration());
+ // 1/8
+ case 4 :
+ return long(Note(Note::EighthNote).getDuration());
+ // 1/4
+ case 3 :
+ return long(Note(Note::QuarterNote).getDuration());
+ // 1/2
+ case 2 :
+ return long(Note(Note::HalfNote).getDuration());
+ // 1/1
+ case 1 :
+ return long(Note(Note::WholeNote).getDuration());
+ // unlimited
+ case 0 :
+ return LONG_MAX;
+ }
+ // failsafe
+ return LONG_MAX;
+}
+
+void
+EventFilterDialog::invert(EventFilterDialog::filterRange &foo)
+{
+ long c = foo.first;
+ foo.first = foo.second;
+ foo.second = c;
+}
+
+EventFilterDialog::filterRange
+EventFilterDialog::getPitch()
+{
+ EventFilterDialog::filterRange foo;
+ foo.first = m_pitchFromSpinBox->value();
+ foo.second = m_pitchToSpinBox ->value();
+ if (!pitchIsInclusive())
+ invert(foo);
+ return foo;
+}
+
+EventFilterDialog::filterRange
+EventFilterDialog::getVelocity()
+{
+ EventFilterDialog::filterRange foo;
+ foo.first = m_velocityFromSpinBox->value();
+ foo.second = m_velocityToSpinBox ->value();
+ if (!velocityIsInclusive())
+ invert(foo);
+ return foo;
+}
+
+EventFilterDialog::filterRange
+EventFilterDialog::getDuration()
+{
+ EventFilterDialog::filterRange foo;
+ foo.first = getDurationFromIndex(m_noteDurationFromComboBox->currentItem());
+ foo.second = getDurationFromIndex(m_noteDurationToComboBox ->currentItem());
+ if (!durationIsInclusive())
+ invert(foo);
+ return foo;
+}
+
+bool
+EventFilterDialog::keepEvent(Event* const &e)
+{
+ if ((*e).isa(Note::EventType)) {
+ long property = 0;
+
+ // pitch
+ (*e).get<Int>(BaseProperties::PITCH, property);
+ if (!eventInRange(getPitch(), property)) {
+ RG_DEBUG << "EventFilterDialog::keepEvent(): rejecting event; pitch " << property
+ << " out of range." << endl;
+ return false;
+ }
+ property = 0;
+
+ // velocity
+ (*e).get<Int>(BaseProperties::VELOCITY, property);
+ if (!EventFilterDialog::eventInRange(getVelocity(), property)) {
+ RG_DEBUG << "EventFilterDialog::keepEvent(): rejecting event; velocity " << property
+ << " out of range." << endl;
+ return false;
+ }
+ property = 0;
+
+ // duration
+ property = (*e).getNotationDuration();
+ if (!EventFilterDialog::eventInRange(getDuration(), property)) {
+ RG_DEBUG << "EventFilterDialog::keepEvent(): rejecting event; duration " << property
+ << " out of range." << endl;
+ return false;
+ }
+ property = 0;
+
+ return true;
+ }
+ return false;
+}
+
+}
+
+#include "EventFilterDialog.moc"
diff --git a/src/gui/dialogs/EventFilterDialog.h b/src/gui/dialogs/EventFilterDialog.h
new file mode 100644
index 0000000..0d3eb05
--- /dev/null
+++ b/src/gui/dialogs/EventFilterDialog.h
@@ -0,0 +1,170 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ This file is Copyright 2003-2006
+ D. Michael McIntyre <dmmcintyr@users.sourceforge.net>
+
+ 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_EVENTFILTERDIALOG_H_
+#define _RG_EVENTFILTERDIALOG_H_
+
+#include <kdialogbase.h>
+#include <utility>
+#include <vector>
+#include "base/Event.h"
+#include <qcheckbox.h>
+#include <qcombobox.h>
+
+class QWidget;
+class QSpinBox;
+class QPushButton;
+class QGridLayout;
+class KConfig;
+
+
+namespace Rosegarden
+{
+
+class Event;
+
+
+/**
+ * Creates a dialog box to allow the user to dial up various selection
+ * criteria used for removing events from a selection. It is up to the caller
+ * to actually manipulate the selection. After the dialog has been accepted,
+ * its filterEvent() method can be used to decide whether a particular event
+ * should continue to be selected. See matrixview.cpp slotFilterSelection()
+ * for an example of how to use this.
+ */
+class EventFilterDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+
+ EventFilterDialog(QWidget* parent);
+ ~EventFilterDialog();
+
+ KConfig *cfg;
+
+ //-------[ accessor functions ]------------------------
+
+ // NOTE: the filterRange type is used to return an A B pair with A and B set
+ // according to the state of the related include/exclude combo. If A > B
+ // then this is an inclusive range. If A < B then it's an exclusive
+ // range. This saves passing around a third variable.
+ typedef std::pair<long, long> filterRange;
+
+ filterRange getPitch();
+ filterRange getVelocity();
+ filterRange getDuration();
+
+ // returns TRUE if the property value falls with in the filterRange
+ bool eventInRange(filterRange foo, long property) {
+ if (foo.first > foo.second)
+ return (property <= foo.second || property >= foo.first);
+ else
+ return (property >= foo.first && property <= foo.second); }
+
+ // Used to do the work of deciding whether to keep or reject an event
+ // based on the state of the dialog's widgets. Returns TRUE if an event
+ // should continue to be selected. This method is the heart of the
+ // EventFilterDialog's public interface.
+ bool keepEvent(Event* const &e);
+
+protected:
+
+ //--------[ member functions ]-------------------------
+
+ // initialize the dialog
+ void initDialog();
+
+ // populate the duration combos
+ void populateDurationCombos();
+
+ // convert duration from combobox index into actual RG duration
+ // between 0 and LONG_MAX
+ long getDurationFromIndex(int index);
+
+ // simple A B swap used to flip inclusive/exclusive values
+ void invert (filterRange &);
+
+ // return inclusive/exclusive toggle states concisely for tidy code
+ bool pitchIsInclusive() { return (m_notePitchIncludeComboBox->currentItem() == 0); }
+ bool velocityIsInclusive() { return (m_noteVelocityIncludeComboBox->currentItem() == 0); }
+ bool durationIsInclusive() { return (m_noteDurationIncludeComboBox->currentItem() == 0); }
+
+protected slots:
+
+ // set widget values to include everything
+ void slotToggleAll();
+
+ // set widget values to include nothing
+ void slotToggleNone();
+
+ // write out settings to kconfig data for next time and call accept()
+ virtual void slotOk();
+
+ // update note name text display and ensure From <= To
+ void slotPitchFromChanged(int pitch);
+ void slotPitchToChanged(int pitch);
+
+ // ensure From <= To to guarantee a logical range for these sets
+ void slotVelocityFromChanged(int velocity);
+ void slotVelocityToChanged(int velocity);
+ void slotDurationFromChanged(int index);
+ void slotDurationToChanged(int index);
+
+ // create a pitch chooser widget sub-dialog to show pitch on staff
+ void slotPitchFromChooser();
+ void slotPitchToChooser();
+
+private:
+ //---------[ data members ]-----------------------------
+
+ QGridLayout* layout;
+
+ QComboBox* m_noteDurationFromComboBox;
+ QComboBox* m_noteDurationIncludeComboBox;
+ QComboBox* m_noteDurationToComboBox;
+ QComboBox* m_notePitchIncludeComboBox;
+ QComboBox* m_noteVelocityIncludeComboBox;
+
+ QPushButton* m_pitchFromChooserButton;
+ QPushButton* m_pitchToChooserButton;
+ QPushButton* m_buttonAll;
+ QPushButton* m_buttonNone;
+
+ QSpinBox* m_pitchFromSpinBox;
+ QSpinBox* m_pitchToSpinBox;
+ QSpinBox* m_velocityFromSpinBox;
+ QSpinBox* m_velocityToSpinBox;
+
+ std::vector<timeT> m_standardQuantizations;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/EventParameterDialog.cpp b/src/gui/dialogs/EventParameterDialog.cpp
new file mode 100644
index 0000000..036491e
--- /dev/null
+++ b/src/gui/dialogs/EventParameterDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "EventParameterDialog.h"
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/PropertyName.h"
+#include <kcombobox.h>
+#include <kdialogbase.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+EventParameterDialog::EventParameterDialog(
+ QWidget *parent,
+ const QString &name,
+ const PropertyName &property,
+ int startValue):
+ KDialogBase(parent, 0, true, name, Ok | Cancel),
+ m_property(property)
+{
+ QVBox *vBox = makeVBoxMainWidget();
+
+ QHBox *topBox = new QHBox(vBox);
+ QLabel *explainLabel = new QLabel(topBox);
+ QString text = i18n("Set the %1 property of the event selection:").
+ arg(strtoqstr(property));
+ explainLabel->setText(text);
+
+ QHBox *patternBox = new QHBox(vBox);
+ new QLabel(i18n("Pattern"), patternBox);
+ m_patternCombo = new KComboBox(patternBox);
+
+ // create options
+ // 0 flat
+ text = i18n("Flat - set %1 to value").arg(strtoqstr(property));
+ m_patternCombo->insertItem(text);
+
+ // 1 alternating
+ text = i18n("Alternating - set %1 to max and min on alternate events").arg(strtoqstr(property));
+ m_patternCombo->insertItem(text);
+
+ // 2 crescendo
+ text = i18n("Crescendo - set %1 rising from min to max").arg(strtoqstr(property));
+ m_patternCombo->insertItem(text);
+
+ // 3 diminuendo
+ text = i18n("Diminuendo - set %1 falling from max to min").arg(strtoqstr(property));
+ m_patternCombo->insertItem(text);
+
+ // 4 ringing
+ text = i18n("Ringing - set %1 alternating from max to min with both dying to zero").arg(strtoqstr(property));
+ m_patternCombo->insertItem(text);
+
+ connect(m_patternCombo, SIGNAL(activated(int)),
+ this, SLOT(slotPatternSelected(int)));
+
+ QHBox *value1Box = new QHBox(vBox);
+ m_value1Label = new QLabel(i18n("Value"), value1Box);
+ m_value1Combo = new KComboBox(value1Box);
+
+ QHBox *value2Box = new QHBox(vBox);
+ m_value2Label = new QLabel(i18n("Value"), value2Box);
+ m_value2Combo = new KComboBox(value2Box);
+
+ for (unsigned int i = 0; i < 128; i++) {
+ m_value1Combo->insertItem(QString("%1").arg(i));
+ m_value2Combo->insertItem(QString("%1").arg(i));
+ }
+ m_value1Combo->setCurrentItem(127);
+
+ slotPatternSelected(0);
+
+ // start value
+ m_value1Combo->setCurrentItem(startValue);
+ m_value2Combo->setCurrentItem(startValue);
+
+}
+
+void
+EventParameterDialog::slotPatternSelected(int value)
+{
+ switch (value) {
+ case 0: // flat
+ m_value1Label->setText(i18n("Value"));
+ m_value1Label->show();
+ m_value1Combo->show();
+ m_value2Label->hide();
+ m_value2Combo->hide();
+ break;
+
+ case 1: // alternating
+ m_value1Label->setText(i18n("First Value"));
+ m_value2Label->setText(i18n("Second Value"));
+ m_value1Label->show();
+ m_value1Combo->show();
+ m_value2Label->show();
+ m_value2Combo->show();
+ break;
+
+ case 2: // crescendo
+ m_value1Label->setText(i18n("Low Value"));
+ m_value2Label->setText(i18n("High Value"));
+ m_value1Label->show();
+ m_value1Combo->show();
+ m_value2Label->show();
+ m_value2Combo->show();
+ break;
+
+ case 3: // decrescendo
+ m_value1Label->setText(i18n("High Value"));
+ m_value2Label->setText(i18n("Low Value"));
+ m_value1Label->show();
+ m_value1Combo->show();
+ m_value2Label->show();
+ m_value2Combo->show();
+ break;
+
+ case 4: // ringing
+ m_value1Label->setText(i18n("First Value"));
+ m_value2Label->setText(i18n("Second Value"));
+ m_value1Label->show();
+ m_value1Combo->show();
+ m_value2Label->show();
+ m_value2Combo->show();
+ break;
+
+ default:
+ RG_DEBUG << "EventParameterDialog::slotPatternSelected - "
+ << "unrecognised pattern number" << endl;
+ break;
+ }
+
+}
+
+PropertyPattern
+EventParameterDialog::getPattern()
+{
+ return PropertyPattern(m_patternCombo->currentItem());
+}
+
+int
+EventParameterDialog::getValue1()
+{
+ return m_value1Combo->currentItem();
+}
+
+int
+EventParameterDialog::getValue2()
+{
+ return m_value2Combo->currentItem();
+}
+
+}
+#include "EventParameterDialog.moc"
diff --git a/src/gui/dialogs/EventParameterDialog.h b/src/gui/dialogs/EventParameterDialog.h
new file mode 100644
index 0000000..040e2f9
--- /dev/null
+++ b/src/gui/dialogs/EventParameterDialog.h
@@ -0,0 +1,80 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_EVENTPARAMETERDIALOG_H_
+#define _RG_EVENTPARAMETERDIALOG_H_
+
+#include "base/PropertyName.h"
+#include <kdialogbase.h>
+#include "commands/edit/SelectionPropertyCommand.h"
+
+class QWidget;
+class QString;
+class QLabel;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+
+
+class EventParameterDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ EventParameterDialog(QWidget *parent,
+ const QString &name, // name
+ const PropertyName &property, // property
+ int startValue); // start
+
+ int getValue1();
+ int getValue2();
+ PropertyPattern getPattern();
+
+public slots:
+ void slotPatternSelected(int value);
+
+protected:
+ //--------------- Data members ---------------------------------
+ PropertyName m_property;
+ PropertyPattern m_pattern;
+
+ KComboBox *m_value1Combo;
+ KComboBox *m_value2Combo;
+ KComboBox *m_patternCombo;
+
+ QLabel *m_value1Label;
+ QLabel *m_value2Label;
+
+};
+
+
+// ---------------- CompositionLengthDialog -----------
+
+}
+
+#endif
diff --git a/src/gui/dialogs/ExportDeviceDialog.cpp b/src/gui/dialogs/ExportDeviceDialog.cpp
new file mode 100644
index 0000000..ce5f52e
--- /dev/null
+++ b/src/gui/dialogs/ExportDeviceDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ExportDeviceDialog.h"
+
+#include <klocale.h>
+#include <kdialogbase.h>
+#include <qbuttongroup.h>
+#include <qlabel.h>
+#include <qradiobutton.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+ExportDeviceDialog::ExportDeviceDialog(QWidget *parent, QString deviceName) :
+ KDialogBase(parent, "exportdevicedialog", true, i18n("Export Devices..."),
+ Ok | Cancel, Ok)
+{
+ QVBox *vbox = makeVBoxMainWidget();
+ QButtonGroup *bg = new QButtonGroup(1, Qt::Horizontal,
+ i18n("Export devices"),
+ vbox);
+ m_exportAll = new QRadioButton(i18n("Export all devices"), bg);
+ m_exportOne = new QRadioButton(i18n("Export selected device only"), bg);
+ new QLabel(i18n(" (\"%1\")").arg(deviceName), bg);
+
+ m_exportOne->setChecked(true);
+}
+
+ExportDeviceDialog::ExportType
+
+ExportDeviceDialog::getExportType()
+{
+ if (m_exportAll->isChecked())
+ return ExportAll;
+ else
+ return ExportOne;
+}
+
+}
diff --git a/src/gui/dialogs/ExportDeviceDialog.h b/src/gui/dialogs/ExportDeviceDialog.h
new file mode 100644
index 0000000..21fc183
--- /dev/null
+++ b/src/gui/dialogs/ExportDeviceDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_EXPORTDEVICEDIALOG_H_
+#define _RG_EXPORTDEVICEDIALOG_H_
+
+#include <kdialogbase.h>
+#include <qstring.h>
+
+
+class QWidget;
+class QRadioButton;
+
+
+namespace Rosegarden
+{
+
+
+
+class ExportDeviceDialog : public KDialogBase
+{
+public:
+ enum ExportType { ExportOne, ExportAll };
+
+ ExportDeviceDialog(QWidget *parent, QString deviceName);
+
+ ExportType getExportType();
+
+protected:
+ QRadioButton *m_exportAll;
+ QRadioButton *m_exportOne;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/FileLocateDialog.cpp b/src/gui/dialogs/FileLocateDialog.cpp
new file mode 100644
index 0000000..4f153c8
--- /dev/null
+++ b/src/gui/dialogs/FileLocateDialog.cpp
@@ -0,0 +1,104 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "FileLocateDialog.h"
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include <kdialogbase.h>
+#include <kfiledialog.h>
+#include <qfileinfo.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+FileLocateDialog::FileLocateDialog(QWidget *parent,
+ const QString &file,
+ const QString & /*path*/):
+ KDialogBase(parent, 0, true,
+ i18n("Locate audio file"),
+ User1 | User2 | User3,
+ Ok,
+ false,
+ i18n("&Skip"),
+ i18n("Skip &All"),
+ i18n("&Locate")),
+ m_file(file)
+{
+ QHBox *w = makeHBoxMainWidget();
+ QString label =
+ i18n("Can't find file \"%1\".\n"
+ "Would you like to try and locate this file or skip it?").arg(m_file);
+
+ QLabel *labelW = new QLabel(label, w);
+ labelW->setAlignment(Qt::AlignCenter);
+ labelW->setMinimumHeight(60);
+}
+
+void
+FileLocateDialog::slotUser3()
+{
+ if (!m_file.isEmpty()) {
+ m_file = KFileDialog::getOpenFileName
+ (":WAVS",
+ i18n("%1|Requested file (%2)\n*.wav|WAV files (*.wav)")
+ .arg(QFileInfo(m_file).fileName())
+ .arg(QFileInfo(m_file).fileName()),
+ this, i18n("Select an Audio File"));
+
+ RG_DEBUG << "FileLocateDialog::slotUser3() : m_file = " << m_file << endl;
+
+ if (m_file.isEmpty()) {
+ RG_DEBUG << "FileLocateDialog::slotUser3() : reject\n";
+ reject();
+ } else {
+ QFileInfo fileInfo(m_file);
+ m_path = fileInfo.dirPath();
+ accept();
+ }
+
+ } else
+ reject();
+}
+
+void
+FileLocateDialog::slotUser1()
+{
+ reject();
+}
+
+void
+FileLocateDialog::slotUser2()
+{
+ done( -1);
+}
+
+}
+#include "FileLocateDialog.moc"
diff --git a/src/gui/dialogs/FileLocateDialog.h b/src/gui/dialogs/FileLocateDialog.h
new file mode 100644
index 0000000..1786221
--- /dev/null
+++ b/src/gui/dialogs/FileLocateDialog.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_FILELOCATEDIALOG_H_
+#define _RG_FILELOCATEDIALOG_H_
+
+#include <kdialogbase.h>
+#include <qstring.h>
+
+
+class QWidget;
+
+
+namespace Rosegarden
+{
+
+
+
+class FileLocateDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ FileLocateDialog(QWidget *parent,
+ const QString &file,
+ const QString &path);
+
+ QString getDirectory() { return m_path; }
+ QString getFilename() { return m_file; }
+
+protected:
+ virtual void slotUser1();
+ virtual void slotUser2();
+ virtual void slotUser3();
+
+ QString m_file;
+ QString m_path;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/FileMergeDialog.cpp b/src/gui/dialogs/FileMergeDialog.cpp
new file mode 100644
index 0000000..d997327
--- /dev/null
+++ b/src/gui/dialogs/FileMergeDialog.cpp
@@ -0,0 +1,84 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "FileMergeDialog.h"
+
+#include <klocale.h>
+#include <kcombobox.h>
+#include <kdialogbase.h>
+#include <qcheckbox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include "document/RosegardenGUIDoc.h"
+
+
+namespace Rosegarden
+{
+
+FileMergeDialog::FileMergeDialog(QWidget *parent,
+ QString /*fileName*/,
+ bool timingsDiffer) :
+ KDialogBase(parent, 0, true, i18n("Merge File"), Ok | Cancel | Help)
+{
+ setHelp("file-merge");
+
+ QVBox *vbox = makeVBoxMainWidget();
+
+ QHBox *hbox = new QHBox(vbox);
+ new QLabel(i18n("Merge new file "), hbox);
+
+ m_choice = new KComboBox(hbox);
+ m_choice->insertItem(i18n("At start of existing composition"));
+ m_choice->insertItem(i18n("From end of existing composition"));
+ m_useTimings = 0;
+
+ if (timingsDiffer) {
+ new QLabel(i18n("The file has different time signatures or tempos."), vbox);
+ m_useTimings = new QCheckBox(i18n("Import these as well"), vbox);
+ m_useTimings->setChecked(false);
+ }
+}
+
+int
+FileMergeDialog::getMergeOptions()
+{
+ int options = MERGE_KEEP_OLD_TIMINGS | MERGE_IN_NEW_TRACKS;
+
+ if (m_choice->currentItem() == 1) {
+ options |= MERGE_AT_END;
+ }
+
+ if (m_useTimings && m_useTimings->isChecked()) {
+ options |= MERGE_KEEP_NEW_TIMINGS;
+ }
+
+ return options;
+}
+
+}
+#include "FileMergeDialog.moc"
diff --git a/src/gui/dialogs/FileMergeDialog.h b/src/gui/dialogs/FileMergeDialog.h
new file mode 100644
index 0000000..f305cae
--- /dev/null
+++ b/src/gui/dialogs/FileMergeDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_FILEMERGEDIALOG_H_
+#define _RG_FILEMERGEDIALOG_H_
+
+#include <kdialogbase.h>
+#include <qstring.h>
+
+
+class QWidget;
+class QCheckBox;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+
+
+class FileMergeDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ FileMergeDialog(QWidget *parent, QString fileName, bool timingsDiffer);
+
+ int getMergeOptions();
+
+private:
+ KComboBox *m_choice;
+ QCheckBox *m_useTimings;
+};
+
+
+// Locate a file
+//
+
+}
+
+#endif
diff --git a/src/gui/dialogs/FloatEdit.cpp b/src/gui/dialogs/FloatEdit.cpp
new file mode 100644
index 0000000..06e8aa3
--- /dev/null
+++ b/src/gui/dialogs/FloatEdit.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "FloatEdit.h"
+
+#include "gui/widgets/HSpinBox.h"
+#include <kdialogbase.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <cmath>
+
+namespace Rosegarden
+{
+
+FloatEdit::FloatEdit(QWidget *parent,
+ const QString &title,
+ const QString &text,
+ float min,
+ float max,
+ float value,
+ float step):
+ KDialogBase(parent, "rosegardenFloatEdit", true, title, Ok | Cancel, Ok)
+{
+ QVBox *vbox = makeVBoxMainWidget();
+ QGroupBox *groupBox = new QGroupBox(1, Horizontal, text, vbox);
+ QVBox *inVbox = new QVBox(groupBox);
+
+ // Calculate decimal points according to the step size
+ //
+ double calDP = log10(step);
+ int dps = 0;
+ if (calDP < 0.0)
+ dps = int( -calDP);
+ //std::cout << "CAL DP = " << calDP << ", dps = " << dps << std::endl;
+
+ m_spin = new HSpinBox(inVbox, value, 1, min, max, dps);
+ new QLabel(QString("(min: %1, max: %2)").arg(min).arg(max), inVbox);
+}
+
+float
+FloatEdit::getValue() const
+{
+ return m_spin->valuef();
+}
+
+}
+#include "FloatEdit.moc"
diff --git a/src/gui/dialogs/FloatEdit.h b/src/gui/dialogs/FloatEdit.h
new file mode 100644
index 0000000..24f1b2c
--- /dev/null
+++ b/src/gui/dialogs/FloatEdit.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ROSEGARDENFLOATEDIT_H_
+#define _RG_ROSEGARDENFLOATEDIT_H_
+
+#include <kdialogbase.h>
+
+
+class QWidget;
+class QString;
+class QLabel;
+
+
+namespace Rosegarden
+{
+
+class HSpinBox;
+
+
+class FloatEdit : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ FloatEdit(QWidget *parent,
+ const QString &title,
+ const QString &text,
+ float min,
+ float max,
+ float value,
+ float step);
+
+ float getValue() const;
+
+protected:
+
+ QLabel *m_text;
+ HSpinBox *m_spin;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/IdentifyTextCodecDialog.cpp b/src/gui/dialogs/IdentifyTextCodecDialog.cpp
new file mode 100644
index 0000000..07b5ec1
--- /dev/null
+++ b/src/gui/dialogs/IdentifyTextCodecDialog.cpp
@@ -0,0 +1,173 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "IdentifyTextCodecDialog.h"
+
+#include <klocale.h>
+#include "misc/Strings.h"
+#include "base/NotationTypes.h"
+#include <kcombobox.h>
+#include <kdialogbase.h>
+#include <qfont.h>
+#include <qlabel.h>
+#include <qstring.h>
+#include <qtextcodec.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+IdentifyTextCodecDialog::IdentifyTextCodecDialog(QWidget *parent,
+ std::string text) :
+ KDialogBase(parent, 0, true, i18n("Choose Text Encoding"), Ok),
+ m_text(text)
+{
+ QVBox *vbox = makeVBoxMainWidget();
+ new QLabel(i18n("\nThis file contains text in an unknown language encoding.\n\nPlease select one of the following estimated text encodings\nfor use with the text in this file:\n"), vbox);
+
+ KComboBox *codecs = new KComboBox(vbox);
+
+ std::string defaultCodec;
+ QTextCodec *cc = QTextCodec::codecForContent(text.c_str(), text.length());
+ QTextCodec *codec = 0;
+
+ std::cerr << "cc is " << (cc ? cc->name() : "null") << std::endl;
+
+ std::map<std::string, QString> codecDescriptions;
+ codecDescriptions["SJIS"] = i18n("Japanese Shift-JIS");
+ codecDescriptions["UTF-8"] = i18n("Unicode variable-width");
+ codecDescriptions["ISO 8859-1"] = i18n("Western Europe");
+ codecDescriptions["ISO 8859-15"] = i18n("Western Europe + Euro");
+ codecDescriptions["ISO 8859-2"] = i18n("Eastern Europe");
+ codecDescriptions["ISO 8859-3"] = i18n("Southern Europe");
+ codecDescriptions["ISO 8859-4"] = i18n("Northern Europe");
+ codecDescriptions["ISO 8859-5"] = i18n("Cyrillic");
+ codecDescriptions["ISO 8859-6"] = i18n("Arabic");
+ codecDescriptions["ISO 8859-7"] = i18n("Greek");
+ codecDescriptions["ISO 8859-8"] = i18n("Hebrew");
+ codecDescriptions["ISO 8859-9"] = i18n("Turkish");
+ codecDescriptions["ISO 8859-10"] = i18n("Nordic");
+ codecDescriptions["ISO 8859-11"] = i18n("Thai");
+ codecDescriptions["ISO 8859-13"] = i18n("Baltic");
+ codecDescriptions["ISO 8859-14"] = i18n("Celtic");
+ codecDescriptions["SJIS"] = i18n("Japanese Shift-JIS");
+ codecDescriptions["Big5"] = i18n("Traditional Chinese");
+ codecDescriptions["GB18030"] = i18n("Simplified Chinese");
+ codecDescriptions["KOI8-R"] = i18n("Russian");
+ codecDescriptions["KOI8-U"] = i18n("Ukrainian");
+ codecDescriptions["TSCII"] = i18n("Tamil");
+
+ int i = 0;
+ int current = -1;
+
+ int selectedProbability = 0;
+ if (cc) {
+ selectedProbability = cc->heuristicContentMatch
+ (m_text.c_str(), m_text.length());
+ }
+
+ while ((codec = QTextCodec::codecForIndex(i)) != 0) {
+
+ int probability = codec->heuristicContentMatch
+ (m_text.c_str(), m_text.length());
+
+ if (probability <= 0) {
+ ++i;
+ continue;
+ }
+
+ std::string name = codec->name();
+
+ std::cerr << "codec " << name << " probability " << probability << std::endl;
+
+ if (name == "UTF-8" &&
+ (!cc || (cc->name() != name)) &&
+ probability > selectedProbability/2) {
+ std::cerr << "UTF-8 has a decent probability, selecting it instead to promote global harmony" << std::endl;
+ cc = codec;
+ }
+
+ QString description = codecDescriptions[name];
+ if (description == "") {
+ if (strtoqstr(name).left(3) == "CP ") {
+ description = i18n("Microsoft Code Page %1").
+ arg(strtoqstr(name).right(name.length() - 3));
+ }
+ }
+
+ if (description != "") {
+ description = i18n("%1 (%2)").arg(strtoqstr(name)).arg(description);
+ } else {
+ description = strtoqstr(name);
+ }
+
+ codecs->insertItem(description, 0);
+ m_codecs.push_front(name);
+ if (current >= 0) ++current;
+
+ if (cc && (name == cc->name())) {
+ current = 0;
+ }
+
+ ++i;
+ }
+
+ connect(codecs, SIGNAL(activated(int)),
+ this, SLOT(slotCodecSelected(int)));
+
+ new QLabel(i18n("\nExample text from file:"), vbox);
+ m_example = new QLabel("", vbox);
+ QFont font;
+ font.setStyleHint(QFont::TypeWriter);
+ m_example->setFont(font);
+ m_example->setPaletteForegroundColor(Qt::blue);
+ std::cerr << "calling slotCodecSelected(" << current << ")" << std::endl;
+ if (current < 0) current = 0;
+ codecs->setCurrentItem(current);
+ slotCodecSelected(current);
+}
+
+void
+IdentifyTextCodecDialog::slotCodecSelected(int i)
+{
+// std::cerr << "codec index = " << i << std::endl;
+ if (i < 0 || i >= m_codecs.size()) return;
+ std::string name = m_codecs[i];
+// std::cerr << "codecs: ";
+// for (int j = 0; j < m_codecs.size(); ++j) std::cerr << m_codecs[j] << " ";
+// std::cerr << std::endl;
+ QTextCodec *codec = QTextCodec::codecForName(strtoqstr(name));
+ if (!codec) return;
+ m_codec = qstrtostr(codec->name());
+ std::cerr << "Applying codec " << m_codec << std::endl;
+ QString outText = codec->toUnicode(m_text.c_str(), m_text.length());
+ if (outText.length() > 80) outText = outText.left(80);
+ m_example->setText("\"" + outText + "\"");
+}
+
+}
+#include "IdentifyTextCodecDialog.moc"
diff --git a/src/gui/dialogs/IdentifyTextCodecDialog.h b/src/gui/dialogs/IdentifyTextCodecDialog.h
new file mode 100644
index 0000000..288cd17
--- /dev/null
+++ b/src/gui/dialogs/IdentifyTextCodecDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_IDENTIFYTEXTCODECDIALOG_H_
+#define _RG_IDENTIFYTEXTCODECDIALOG_H_
+
+#include <string>
+#include <kdialogbase.h>
+#include <deque>
+
+
+class QWidget;
+class QLabel;
+
+
+namespace Rosegarden
+{
+
+
+
+class IdentifyTextCodecDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ IdentifyTextCodecDialog(QWidget *parent, std::string text);
+
+ std::string getCodec() const { return m_codec; }
+
+protected slots:
+ void slotCodecSelected(int);
+
+protected:
+ std::string m_text;
+ std::string m_codec;
+ std::deque<std::string> m_codecs;
+ QLabel *m_example;
+};
+
+
+/*
+ * Creates a small dialog box containing a PitchChooser widget. The
+ * info paramter provides extra information as a reminder what this particular
+ * picker is for, eg. Highest, Lowest, From, To
+ */
+
+}
+
+#endif
diff --git a/src/gui/dialogs/ImportDeviceDialog.cpp b/src/gui/dialogs/ImportDeviceDialog.cpp
new file mode 100644
index 0000000..58a6ce5
--- /dev/null
+++ b/src/gui/dialogs/ImportDeviceDialog.cpp
@@ -0,0 +1,389 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ImportDeviceDialog.h"
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include <klocale.h>
+#include "misc/Strings.h"
+#include "document/ConfigGroups.h"
+#include "base/MidiDevice.h"
+#include "base/MidiProgram.h"
+#include "document/RosegardenGUIDoc.h"
+#include "sound/SF2PatchExtractor.h"
+#include <kcombobox.h>
+#include <kconfig.h>
+#include <kdialogbase.h>
+#include <kmessagebox.h>
+#include <kurl.h>
+#include <kio/netaccess.h>
+#include <qbuttongroup.h>
+#include <qcheckbox.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qradiobutton.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+ImportDeviceDialog::ImportDeviceDialog(QWidget *parent, KURL url) :
+ KDialogBase(parent, "importdevicedialog", true,
+ i18n("Import from Device..."),
+ Ok | Cancel, Ok),
+ m_url(url),
+ m_fileDoc(0),
+ m_device(0)
+{}
+
+ImportDeviceDialog::~ImportDeviceDialog()
+{
+ if (m_fileDoc) {
+ delete m_fileDoc;
+ } else {
+ delete m_device;
+ }
+}
+
+bool
+ImportDeviceDialog::doImport()
+{
+ QVBox *mainFrame = makeVBoxMainWidget();
+
+ if (m_url.isEmpty()) {
+ reject();
+ return false;
+ }
+
+ QString target;
+ if (KIO::NetAccess::download(m_url, target) == false) {
+ KMessageBox::error(this, i18n("Cannot download file %1").arg(m_url.prettyURL()));
+ return false;
+ }
+
+ bool fileRead = false;
+ if (SF2PatchExtractor::isSF2File(target.data())) {
+ fileRead = importFromSF2(target);
+ } else {
+ fileRead = importFromRG(target);
+ }
+ if (!fileRead) {
+ KMessageBox::error
+ (this, i18n("Cannot open file %1").arg(m_url.prettyURL()));
+ reject();
+ close();
+ return false;
+ }
+ if (m_devices.size() == 0) {
+ KMessageBox::sorry
+ (this, i18n("No devices found in file %1").arg(m_url.prettyURL()));
+ reject();
+ close();
+ return false;
+ }
+
+ QGroupBox *groupBox = new QGroupBox(2, Qt::Horizontal,
+ i18n("Source device"),
+ mainFrame);
+
+ QHBox *deviceBox = new QHBox(groupBox);
+ QHBoxLayout *bl = new QHBoxLayout(deviceBox);
+ bl->addWidget(new QLabel(i18n("Import from: "), deviceBox));
+
+ bool showRenameOption = false;
+
+ if (m_devices.size() > 1) {
+ m_deviceCombo = new KComboBox(deviceBox);
+ m_deviceLabel = 0;
+ bl->addWidget(m_deviceCombo);
+ } else {
+ m_deviceCombo = 0;
+ m_deviceLabel = new QLabel(deviceBox);
+ bl->addWidget(m_deviceLabel);
+ }
+
+ bl->addStretch(10);
+
+ int count = 1;
+ for (std::vector<MidiDevice *>::iterator i = m_devices.begin();
+ i != m_devices.end(); ++i) {
+ if ((*i)->getName() != "") {
+ showRenameOption = true;
+ } else {
+ (*i)->setName(qstrtostr(i18n("Device %1").arg(count)));
+ }
+ if (m_devices.size() > 1) {
+ m_deviceCombo->insertItem(strtoqstr((*i)->getName()));
+ } else {
+ m_deviceLabel->setText(strtoqstr((*i)->getName()));
+ }
+ ++count;
+ }
+
+ QHBox *optionsBox = new QHBox(mainFrame);
+
+ QGroupBox *gb = new QGroupBox(1, Horizontal, i18n("Options"),
+ optionsBox);
+
+ m_importBanks = new QCheckBox(i18n("Import banks"), gb);
+ m_importKeyMappings = new QCheckBox(i18n("Import key mappings"), gb);
+ m_importControllers = new QCheckBox(i18n("Import controllers"), gb);
+
+ if (showRenameOption) {
+ m_rename = new QCheckBox(i18n("Import device name"), gb);
+ } else {
+ m_rename = 0;
+ }
+
+ m_buttonGroup = new QButtonGroup(1, Qt::Horizontal,
+ i18n("Bank import behavior"),
+ optionsBox);
+ m_mergeBanks = new QRadioButton(i18n("Merge banks"), m_buttonGroup);
+ m_overwriteBanks = new QRadioButton(i18n("Overwrite banks"), m_buttonGroup);
+
+ KConfig *config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+
+ m_importBanks->setChecked(config->readBoolEntry("importbanks", true));
+ m_importKeyMappings->setChecked(config->readBoolEntry("importkeymappings", true));
+ m_importControllers->setChecked(config->readBoolEntry("importcontrollers", true));
+
+ bool rename = config->readBoolEntry("importbanksrename", true);
+ if (m_rename)
+ m_rename->setChecked(rename);
+
+ bool overwrite = config->readBoolEntry("importbanksoverwrite", true);
+ if (overwrite)
+ m_buttonGroup->setButton(1);
+ else
+ m_buttonGroup->setButton(0);
+
+ return true;
+}
+
+void
+ImportDeviceDialog::slotOk()
+{
+ int index = 0;
+ if (m_deviceCombo)
+ index = m_deviceCombo->currentItem();
+ m_device = m_devices[index];
+
+ int v = m_buttonGroup->id(m_buttonGroup->selected());
+ KConfig *config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+ config->writeEntry("importbanksoverwrite", v == 1);
+ if (m_rename)
+ config->writeEntry("importbanksrename", m_rename->isChecked());
+ accept();
+}
+
+void
+ImportDeviceDialog::slotCancel()
+{
+ reject();
+}
+
+std::string ImportDeviceDialog::getDeviceName() const
+{
+ return m_device->getName();
+}
+
+const BankList& ImportDeviceDialog::getBanks() const
+{
+ return m_device->getBanks();
+}
+
+const ProgramList& ImportDeviceDialog::getPrograms() const
+{
+ return m_device->getPrograms();
+}
+
+const KeyMappingList& ImportDeviceDialog::getKeyMappings() const
+{
+ return m_device->getKeyMappings();
+}
+
+const ControlList& ImportDeviceDialog::getControllers() const
+{
+ return m_device->getControlParameters();
+}
+
+std::string ImportDeviceDialog::getLibrarianName() const
+{
+ return m_device->getLibrarianName();
+}
+
+std::string ImportDeviceDialog::getLibrarianEmail() const
+{
+ return m_device->getLibrarianEmail();
+}
+
+MidiDevice::VariationType
+ImportDeviceDialog::getVariationType() const
+{
+ return m_device->getVariationType();
+}
+
+bool
+ImportDeviceDialog::shouldImportBanks() const
+{
+ return m_importBanks->isChecked();
+}
+
+bool
+ImportDeviceDialog::shouldImportKeyMappings() const
+{
+ return m_importKeyMappings->isChecked();
+}
+
+bool
+ImportDeviceDialog::shouldImportControllers() const
+{
+ return m_importControllers->isChecked();
+}
+
+bool
+ImportDeviceDialog::shouldOverwriteBanks() const
+{
+ return m_buttonGroup->id(m_buttonGroup->selected()) != 0;
+}
+
+bool
+ImportDeviceDialog::shouldRename() const
+{
+ return m_rename ? m_rename->isChecked() : false;
+}
+
+bool
+ImportDeviceDialog::importFromRG(QString fileName)
+{
+ m_fileDoc = new RosegardenGUIDoc(RosegardenGUIApp::self(), 0, true); // skipAutoload
+
+ // Add some dummy devices for bank population when we open the document.
+ // We guess that the file won't have more than 32 devices.
+ //
+ // for (unsigned int i = 0; i < 32; i++) {
+ // m_fileDoc->getStudio().addDevice("", i, Device::Midi);
+ // }
+
+ if (!m_fileDoc->openDocument(fileName, false)) {
+ return false;
+ }
+
+ m_devices.clear();
+
+ DeviceList *list = m_fileDoc->getStudio().getDevices();
+ if (list->size() == 0) {
+ return true; // true because we successfully read the document
+ }
+
+ for (DeviceListIterator it = list->begin();
+ it != list->end(); ++it) {
+
+ MidiDevice *device =
+ dynamic_cast<MidiDevice*>(*it);
+
+ if (device) {
+ std::vector<MidiBank> banks =
+ device->getBanks();
+
+ // DMM - check for controllers too, because some users have
+ // created .rgd files that contain only controllers
+ // see bug #1183522
+ //
+ std::vector<ControlParameter> controllers =
+ device->getControlParameters();
+
+ // We've got a bank on a Device fom this file
+ // (or a device that contains controllers or key mappings)
+ //
+ if (banks.size() ||
+ controllers.size() ||
+ device->getKeyMappings().size())
+ m_devices.push_back(device);
+ }
+ }
+
+ return true;
+}
+
+bool
+ImportDeviceDialog::importFromSF2(QString filename)
+{
+ SF2PatchExtractor::Device sf2device;
+ try {
+ sf2device = SF2PatchExtractor::read(filename.data());
+
+ // These exceptions shouldn't happen -- the isSF2File call before this
+ // one should have weeded them out
+ } catch (SF2PatchExtractor::FileNotFoundException e) {
+ return false;
+ } catch (SF2PatchExtractor::WrongFileFormatException e) {
+ return false;
+ }
+
+ std::vector<MidiBank> banks;
+ std::vector<MidiProgram> programs;
+
+ for (SF2PatchExtractor::Device::const_iterator i = sf2device.begin();
+ i != sf2device.end(); ++i) {
+
+ int bankNumber = i->first;
+ const SF2PatchExtractor::Bank &sf2bank = i->second;
+
+ int msb = bankNumber / 128;
+ int lsb = bankNumber % 128;
+
+ MidiBank bank
+ (msb == 1, msb, lsb,
+ qstrtostr(i18n("Bank %1:%2").arg(msb).arg(lsb)));
+
+ banks.push_back(bank);
+
+ for (SF2PatchExtractor::Bank::const_iterator j = sf2bank.begin();
+ j != sf2bank.end(); ++j) {
+
+ MidiProgram program(bank, j->first, j->second);
+ programs.push_back(program);
+ }
+ }
+
+ MidiDevice *device = new MidiDevice
+ (0, "", MidiDevice::Play);
+ device->replaceBankList(banks);
+ device->replaceProgramList(programs);
+ m_devices.push_back(device);
+
+ return true;
+}
+
+}
+#include "ImportDeviceDialog.moc"
diff --git a/src/gui/dialogs/ImportDeviceDialog.h b/src/gui/dialogs/ImportDeviceDialog.h
new file mode 100644
index 0000000..bb79e3b
--- /dev/null
+++ b/src/gui/dialogs/ImportDeviceDialog.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_IMPORTDEVICEDIALOG_H_
+#define _RG_IMPORTDEVICEDIALOG_H_
+
+#include "base/MidiDevice.h"
+#include <string>
+#include <kdialogbase.h>
+#include <qstring.h>
+#include <vector>
+#include <kurl.h>
+
+
+class QWidget;
+class QRadioButton;
+class QLabel;
+class QCheckBox;
+class QButtonGroup;
+class ProgramList;
+class KeyMappingList;
+class KComboBox;
+class ControlList;
+class BankList;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+
+
+class ImportDeviceDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ ImportDeviceDialog(QWidget *parent, KURL url);
+ virtual ~ImportDeviceDialog();
+
+ bool doImport();
+
+ bool shouldImportBanks() const;
+ bool shouldImportKeyMappings() const;
+ bool shouldImportControllers() const;
+ bool shouldOverwriteBanks() const;
+ bool shouldRename() const;
+
+ std::string getDeviceName() const;
+ const BankList &getBanks() const;
+ const ProgramList &getPrograms() const;
+ const KeyMappingList &getKeyMappings() const;
+ const ControlList &getControllers() const;
+ std::string getLibrarianName() const;
+ std::string getLibrarianEmail() const;
+ MidiDevice::VariationType getVariationType() const;
+
+public slots:
+ void slotOk();
+ void slotCancel();
+
+protected:
+ bool importFromRG(QString fileName);
+ bool importFromSF2(QString fileName);
+
+ KURL m_url;
+
+ KComboBox *m_deviceCombo;
+ QLabel *m_deviceLabel;
+
+ QCheckBox *m_importBanks;
+ QCheckBox *m_importKeyMappings;
+ QCheckBox *m_importControllers;
+ QCheckBox *m_rename;
+
+ QButtonGroup *m_buttonGroup;
+ QRadioButton *m_mergeBanks;
+ QRadioButton *m_overwriteBanks;
+
+ RosegardenGUIDoc *m_fileDoc;
+ std::vector<MidiDevice *> m_devices;
+ MidiDevice *m_device;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/InterpretDialog.cpp b/src/gui/dialogs/InterpretDialog.cpp
new file mode 100644
index 0000000..b11e3c4
--- /dev/null
+++ b/src/gui/dialogs/InterpretDialog.cpp
@@ -0,0 +1,123 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "InterpretDialog.h"
+#include <kapplication.h>
+
+#include <klocale.h>
+#include "document/ConfigGroups.h"
+#include "commands/notation/InterpretCommand.h"
+#include <kconfig.h>
+#include <kdialogbase.h>
+#include <qcheckbox.h>
+#include <qgroupbox.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+InterpretDialog::InterpretDialog(QWidget *parent) :
+ KDialogBase(parent, 0, true, i18n("Interpret"), Ok | Cancel | Help)
+{
+ setHelp("nv-interpret");
+
+ QVBox *vbox = makeVBoxMainWidget();
+ QGroupBox *groupBox = new QGroupBox
+ (1, Horizontal, i18n("Interpretations to apply"), vbox);
+
+ m_applyTextDynamics = new QCheckBox
+ (i18n("Apply text dynamics (p, mf, ff etc)"), groupBox);
+ m_applyHairpins = new QCheckBox
+ (i18n("Apply hairpin dynamics"), groupBox);
+ m_stressBeats = new QCheckBox
+ (i18n("Stress beats"), groupBox);
+ m_articulate = new QCheckBox
+ (i18n("Articulate slurs, staccato, tenuto etc"), groupBox);
+ m_allInterpretations = new QCheckBox
+ (i18n("All available interpretations"), groupBox);
+
+ KConfig *config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+
+ m_allInterpretations->setChecked
+ (config->readBoolEntry("interpretall", true));
+ m_applyTextDynamics->setChecked
+ (config->readBoolEntry("interprettextdynamics", true));
+ m_applyHairpins->setChecked
+ (config->readBoolEntry("interprethairpins", true));
+ m_stressBeats->setChecked
+ (config->readBoolEntry("interpretstressbeats", true));
+ m_articulate->setChecked
+ (config->readBoolEntry("interpretarticulate", true));
+
+ connect(m_allInterpretations,
+ SIGNAL(clicked()), this, SLOT(slotAllBoxChanged()));
+
+ slotAllBoxChanged();
+}
+
+void
+InterpretDialog::slotAllBoxChanged()
+{
+ bool all = m_allInterpretations->isChecked();
+ m_applyTextDynamics->setEnabled(!all);
+ m_applyHairpins->setEnabled(!all);
+ m_stressBeats->setEnabled(!all);
+ m_articulate->setEnabled(!all);
+}
+
+int
+InterpretDialog::getInterpretations()
+{
+ KConfig *config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+
+ config->writeEntry("interpretall", m_allInterpretations->isChecked());
+ config->writeEntry("interprettextdynamics", m_applyTextDynamics->isChecked());
+ config->writeEntry("interprethairpins", m_applyHairpins->isChecked());
+ config->writeEntry("interpretstressbeats", m_stressBeats->isChecked());
+ config->writeEntry("interpretarticulate", m_articulate->isChecked());
+
+ if (m_allInterpretations->isChecked()) {
+ return InterpretCommand::AllInterpretations;
+ } else {
+ int in = 0;
+ if (m_applyTextDynamics->isChecked())
+ in |= InterpretCommand::ApplyTextDynamics;
+ if (m_applyHairpins->isChecked())
+ in |= InterpretCommand::ApplyHairpins;
+ if (m_stressBeats->isChecked())
+ in |= InterpretCommand::StressBeats;
+ if (m_articulate->isChecked()) {
+ in |= InterpretCommand::Articulate;
+ }
+ return in;
+ }
+}
+
+}
+#include "InterpretDialog.moc"
diff --git a/src/gui/dialogs/InterpretDialog.h b/src/gui/dialogs/InterpretDialog.h
new file mode 100644
index 0000000..75c8694
--- /dev/null
+++ b/src/gui/dialogs/InterpretDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_INTERPRETDIALOG_H_
+#define _RG_INTERPRETDIALOG_H_
+
+#include <kdialogbase.h>
+
+
+class QWidget;
+class QCheckBox;
+
+
+namespace Rosegarden
+{
+
+
+
+class InterpretDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ InterpretDialog(QWidget *parent);
+
+ // an OR from InterpretCommand's constants
+ int getInterpretations();
+
+protected slots:
+ void slotAllBoxChanged();
+
+private:
+ QCheckBox *m_allInterpretations;
+ QCheckBox *m_applyTextDynamics;
+ QCheckBox *m_applyHairpins;
+ QCheckBox *m_stressBeats;
+ QCheckBox *m_articulate;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/IntervalDialog.cpp b/src/gui/dialogs/IntervalDialog.cpp
new file mode 100644
index 0000000..061fc31
--- /dev/null
+++ b/src/gui/dialogs/IntervalDialog.cpp
@@ -0,0 +1,367 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "IntervalDialog.h"
+#include <qlayout.h>
+
+#include <iostream>
+#include <klocale.h>
+#include "misc/Strings.h"
+#include "base/MidiDevice.h"
+#include "base/NotationRules.h"
+#include <kcombobox.h>
+#include <kdialogbase.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qcheckbox.h>
+#include <qlabel.h>
+#include <qradiobutton.h>
+#include <qbuttongroup.h>
+#include <qsizepolicy.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+IntervalDialog::IntervalDialog(QWidget *parent, bool askChangeKey, bool askTransposeSegmentBack) :
+ KDialogBase(parent, 0, true, i18n("Specify Interval"), Ok | Cancel )
+{
+ QVBox *vBox = makeVBoxMainWidget();
+
+ QHBox *hBox = new QHBox( vBox );
+
+ m_referencenote = new DiatonicPitchChooser( i18n("Reference note:"), hBox );
+ m_targetnote = new DiatonicPitchChooser( i18n("Target note:"), hBox );
+
+ intervalChromatic = 0;
+ intervalDiatonic = 0;
+
+ //m_intervalPitchLabel = new QLabel( i18n("Pitch: %1").arg(intervalChromatic), hBox);
+ //m_intervalOctavesLabel = new QLabel( i18n("Octaves: %1").arg(intervalDiatonic / 7), hBox);
+ //m_intervalStepsLabel = new QLabel( i18n("Steps: %1").arg(intervalDiatonic % 7), hBox);
+
+ m_intervalLabel = new QLabel( i18n("a perfect unison"), vBox);
+ m_intervalLabel->setAlignment(Qt::AlignCenter);
+ QFont font(m_intervalLabel->font());
+ font.setItalic(true);
+ m_intervalLabel->setFont(font);
+
+ if (askChangeKey)
+ {
+ QButtonGroup *affectKeyGroup = new QButtonGroup(1, Horizontal, i18n("Effect on Key"), vBox);
+ m_transposeWithinKey = new QRadioButton(i18n("Transpose within key"), affectKeyGroup);
+ m_transposeWithinKey->setChecked(true);
+ m_transposeChangingKey = new QRadioButton(i18n("Change key for selection"), affectKeyGroup);
+ }
+ else
+ {
+ m_transposeChangingKey = NULL;
+ m_transposeWithinKey = NULL;
+ }
+
+ if (askTransposeSegmentBack)
+ {
+ m_transposeSegmentBack = new QCheckBox( i18n("Adjust segment transposition in opposite direction (maintain audible pitch)"), vBox );
+ m_transposeSegmentBack->setTristate(false);
+ m_transposeSegmentBack->setChecked(false);
+ }
+ else
+ {
+ m_transposeSegmentBack = NULL;
+ }
+
+ connect(m_referencenote, SIGNAL(noteChanged(int,int,int)),
+ this, SLOT(slotSetReferenceNote(int,int,int)));
+
+ connect(m_targetnote, SIGNAL(noteChanged(int,int,int)),
+ this, SLOT(slotSetTargetNote(int,int,int)));
+}
+
+// number of octaves the notes are apart
+int
+IntervalDialog::getOctaveDistance()
+{
+ return m_targetnote->getOctave() - m_referencenote->getOctave();
+}
+
+// chromatic distance between the steps, not taking account octaves or
+// accidentals
+int
+IntervalDialog::getStepDistanceChromatic()
+{
+ return scale_Cmajor[m_targetnote->getStep()] - scale_Cmajor[m_referencenote->getStep()];
+ // - getChromaticStepValue(m_referencestep->currentItem());
+ //return m_targetnote->getPitch() - m_referencenote->getPitch();
+}
+
+// correction due to accidentals
+int
+IntervalDialog::getAccidentalCorrectionChromatic()
+{
+ return m_targetnote->getAccidental() - m_referencenote->getAccidental();
+}
+
+int
+IntervalDialog::getDiatonicDistance()
+{
+ return getOctaveDistance() * 7 + m_targetnote->getStep() - m_referencenote->getStep();
+}
+
+int
+IntervalDialog::getChromaticDistance()
+{
+ return getOctaveDistance() * 12 + getStepDistanceChromatic() + getAccidentalCorrectionChromatic();
+}
+
+QString
+IntervalDialog::getIntervalName(int intervalDiatonic, int intervalChromatic)
+{
+ // displayInterval: an intervalDiatonic of -3 will yield a displayInterval of 3 and
+ // set the boolean 'down' to true.
+ int displayIntervalDiatonic = intervalDiatonic;
+ int displayIntervalChromatic = intervalChromatic;
+ bool down = (intervalDiatonic < 0 ||
+ (intervalDiatonic == 0 &&
+ intervalChromatic < 0));
+ if (down)
+ {
+ displayIntervalDiatonic = -displayIntervalDiatonic;
+ displayIntervalChromatic = -displayIntervalChromatic;
+ }
+
+ int octaves = displayIntervalDiatonic / 7;
+ int deviation = displayIntervalChromatic % 12 - scale_Cmajor[displayIntervalDiatonic % 7];
+ // Note (hjj):
+ // "1 octave and a diminished octave" is better than
+ // "2 octaves and a diminished unison"
+ if (displayIntervalDiatonic % 7 == 0) {
+ if (octaves > 0) {
+ deviation = (deviation < 5 ? deviation : deviation - 12);
+ } else if (octaves < 0) {
+ deviation = (deviation < 5 ? -deviation : 12 - deviation);
+ }
+ } else if (down) {
+ // Note (hjj):
+ // an augmented prime down, NOT a diminished prime down
+ deviation = -deviation;
+ }
+
+ // show the step for an unison only if the octave doesn't change, any other interval
+ // always, and augmented/dimnished unisons (modulo octaves) always.
+ bool showStep = displayIntervalDiatonic == 0 ||
+ displayIntervalDiatonic % 7 != 0 || deviation != 0;
+
+ QString textInterval = "";
+ QString textIntervalDeviated = "";
+ if (showStep)
+ {
+ switch (displayIntervalDiatonic % 7)
+ {
+ // First the diminished/perfect/augmented:
+ case 0: // unison or octaves
+ case 3: // fourth
+ case 4: // fifth
+ if (deviation == -1)
+ textIntervalDeviated += i18n("a diminished");
+ else if (deviation == 1)
+ textIntervalDeviated += i18n("an augmented");
+ else if (deviation == -2)
+ textIntervalDeviated += i18n("a doubly diminished");
+ else if (deviation == 2)
+ textIntervalDeviated += i18n("a doubly augmented");
+ else if (deviation == -3)
+ textIntervalDeviated += i18n("a triply diminished");
+ else if (deviation == 3)
+ textIntervalDeviated += i18n("a triply augmented");
+ else if (deviation == -4)
+ textIntervalDeviated += i18n("a quadruply diminished");
+ else if (deviation == 4)
+ textIntervalDeviated += i18n("a quadruply augmented");
+ else if (deviation == 0)
+ textIntervalDeviated += i18n("a perfect");
+ else
+ textIntervalDeviated += i18n("an (unknown, %1)").arg(deviation);
+ break;
+ // Then the major/minor:
+ case 1: // second
+ case 2: // third
+ case 5: // sixth
+ case 6: // seventh
+ if (deviation == -1)
+ textIntervalDeviated += i18n("a minor");
+ else if (deviation == 0)
+ textIntervalDeviated += i18n("a major");
+ else if (deviation == -2)
+ textIntervalDeviated += i18n("a diminished");
+ else if (deviation == 1)
+ textIntervalDeviated += i18n("an augmented");
+ else if (deviation == -3)
+ textIntervalDeviated += i18n("a doubly diminished");
+ else if (deviation == 2)
+ textIntervalDeviated += i18n("a doubly augmented");
+ else if (deviation == -4)
+ textIntervalDeviated += i18n("a triply diminished");
+ else if (deviation == 3)
+ textIntervalDeviated += i18n("a triply augmented");
+ else if (deviation == 4)
+ textIntervalDeviated += i18n("a quadruply augmented");
+ else if (deviation == 0)
+ textIntervalDeviated += i18n("a perfect");
+ else
+ textIntervalDeviated += i18n("an (unknown, %1)").arg(deviation);
+ break;
+ default:
+ textIntervalDeviated += i18n("an (unknown)");
+ }
+ switch (displayIntervalDiatonic % 7)
+ {
+ case 0:
+ // Note (hjj):
+ // "1 octave and a diminished octave" is better than
+ // "2 octaves and a diminished unison"
+ if (octaves > 0) {
+ textInterval += i18n("%1 octave").arg(textIntervalDeviated);
+ octaves--;
+ } else if (octaves < 0) {
+ textInterval += i18n("%1 octave").arg(textIntervalDeviated);
+ octaves++;
+ } else {
+ textInterval += i18n("%1 unison").arg(textIntervalDeviated);
+ }
+ break;
+ case 1:
+ textInterval += i18n("%1 second").arg(textIntervalDeviated);
+ break;
+ case 2:
+ textInterval += i18n("%1 third").arg(textIntervalDeviated);
+ break;
+ case 3:
+ textInterval += i18n("%1 fourth").arg(textIntervalDeviated);
+ break;
+ case 4:
+ textInterval += i18n("%1 fifth").arg(textIntervalDeviated);
+ break;
+ case 5:
+ textInterval += i18n("%1 sixth").arg(textIntervalDeviated);
+ break;
+ case 6:
+ textInterval += i18n("%1 seventh").arg(textIntervalDeviated);
+ break;
+ default:
+ textInterval += i18n("%1").arg(textIntervalDeviated);
+ }
+ }
+
+ if (displayIntervalChromatic != 0 || displayIntervalDiatonic != 0)
+ {
+ if (!down)
+ {
+ if (octaves != 0) {
+ if (showStep) {
+ return i18n("up 1 octave and %1",
+ "up %n octaves and %1",
+ octaves).arg(textInterval);
+ } else {
+ return i18n("up 1 octave",
+ "up %n octaves",
+ octaves);
+ }
+ } else {
+ return i18n("up %1").arg(textInterval);
+ }
+ }
+ else
+ {
+ if (octaves != 0) {
+ if (showStep) {
+ return i18n("down 1 octave and %1",
+ "down %n octaves and %1",
+ octaves).arg(textInterval);
+ } else {
+ return i18n("down 1 octave",
+ "down %n octaves",
+ octaves);
+ }
+ } else {
+ return i18n("down %1").arg(textInterval);
+ }
+ }
+ } else {
+ return i18n("a perfect unison");
+ }
+}
+
+void
+IntervalDialog::slotSetTargetNote(int pitch, int octave, int step)
+{
+ intervalChromatic = pitch - m_referencenote->getPitch();
+ intervalDiatonic = (octave * 7 + step) - (m_referencenote->getOctave() * 7 + m_referencenote->getStep());
+
+ m_intervalLabel->setText( getIntervalName( intervalDiatonic, intervalChromatic ) );
+}
+
+void
+IntervalDialog::slotSetReferenceNote(int pitch, int octave, int step)
+{
+ // recalculate target note based on reference note and current interval
+ int pitch_new = pitch + intervalChromatic;
+ int diatonic_new = (octave * 7 + step) + intervalDiatonic;
+ int octave_new = diatonic_new / 7;
+ int step_new = diatonic_new % 7;
+
+ m_targetnote->slotSetNote( pitch_new, octave_new, step_new );
+}
+
+bool
+IntervalDialog::getChangeKey()
+{
+ if (m_transposeChangingKey == NULL)
+ {
+ return false;
+ }
+ else
+ {
+ return m_transposeChangingKey->isChecked();
+ }
+}
+
+bool
+IntervalDialog::getTransposeSegmentBack()
+{
+ if (m_transposeSegmentBack == NULL)
+ {
+ return false;
+ }
+ else
+ {
+ return m_transposeSegmentBack->isChecked();
+ }
+}
+
+}
+#include "IntervalDialog.moc"
diff --git a/src/gui/dialogs/IntervalDialog.h b/src/gui/dialogs/IntervalDialog.h
new file mode 100644
index 0000000..b9927d2
--- /dev/null
+++ b/src/gui/dialogs/IntervalDialog.h
@@ -0,0 +1,94 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_INTERVALDIALOG_H_
+#define _RG_INTERVALDIALOG_H_
+
+#include <kdialogbase.h>
+#include <vector>
+#include "gui/application/RosegardenDCOP.h"
+#include "gui/widgets/DiatonicPitchChooser.h"
+
+
+class QWidget;
+class KComboBox;
+class QRadioButton;
+class QCheckBox;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+
+
+class IntervalDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ IntervalDialog(QWidget *parent, bool askChangeKey = false, bool askTransposeSegmentBack = false);
+
+ // Distance in semitones
+ int getChromaticDistance();
+
+ // Distance in steps
+ int getDiatonicDistance();
+
+ // Transpose within key or change the key?
+ bool getChangeKey();
+
+ // Transpose the segment itself in the opposite direction?
+ bool getTransposeSegmentBack();
+
+ static QString getIntervalName(int intervalDiatonic, int intervalChromatic);
+
+public slots:
+ void slotSetReferenceNote(int,int,int);
+ void slotSetTargetNote(int,int,int);
+
+private:
+ int getOctaveDistance();
+ int getStepDistanceChromatic();
+ int getAccidentalCorrectionChromatic();
+
+ DiatonicPitchChooser *m_referencenote;
+ DiatonicPitchChooser *m_targetnote;
+
+ QRadioButton *m_transposeWithinKey;
+ QRadioButton *m_transposeChangingKey;
+ bool changeKey;
+
+ QCheckBox *m_transposeSegmentBack;
+
+ int intervalChromatic;
+ int intervalDiatonic;
+ QLabel *m_intervalLabel;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/KeySignatureDialog.cpp b/src/gui/dialogs/KeySignatureDialog.cpp
new file mode 100644
index 0000000..c703c0a
--- /dev/null
+++ b/src/gui/dialogs/KeySignatureDialog.cpp
@@ -0,0 +1,402 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "KeySignatureDialog.h"
+
+#include <klocale.h>
+#include "misc/Strings.h"
+#include "base/NotationTypes.h"
+#include "gui/editors/notation/NotePixmapFactory.h"
+#include "gui/widgets/BigArrowButton.h"
+#include <kcombobox.h>
+#include <kdialogbase.h>
+#include <qbuttongroup.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qobject.h>
+#include <qpixmap.h>
+#include <qradiobutton.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <qcheckbox.h>
+#include <algorithm>
+
+namespace Rosegarden
+{
+
+KeySignatureDialog::KeySignatureDialog(QWidget *parent,
+ NotePixmapFactory *npf,
+ Clef clef,
+ Rosegarden::Key defaultKey,
+ bool showApplyToAll,
+ bool showConversionOptions,
+ QString explanatoryText) :
+ KDialogBase(parent, 0, true, i18n("Key Change"), Ok | Cancel | Help),
+ m_notePixmapFactory(npf),
+ m_key(defaultKey),
+ m_clef(clef),
+ m_valid(true),
+ m_ignoreComboChanges(false),
+ m_explanatoryLabel(0),
+ m_applyToAllButton(0),
+ m_noPercussionCheckBox(0)
+{
+ setHelp("nv-signatures-key");
+
+ QVBox *vbox = makeVBoxMainWidget();
+
+ QHBox *keyBox = 0;
+ QHBox *nameBox = 0;
+
+ QGroupBox *keyFrame = new QGroupBox
+ (1, Horizontal, i18n("Key signature"), vbox);
+
+ QGroupBox *transposeFrame = new QButtonGroup
+ (1, Horizontal, i18n("Key transposition"), vbox);
+
+ QGroupBox *buttonFrame = new QButtonGroup
+ (1, Horizontal, i18n("Scope"), vbox);
+
+ QButtonGroup *conversionFrame = new QButtonGroup
+ (1, Horizontal, i18n("Existing notes following key change"), vbox);
+
+ keyBox = new QHBox(keyFrame);
+ nameBox = new QHBox(keyFrame);
+
+ QLabel *explanatoryLabel = 0;
+ if (explanatoryText) {
+ explanatoryLabel = new QLabel(explanatoryText, keyFrame);
+ }
+
+ BigArrowButton *keyDown = new BigArrowButton(keyBox, Qt::LeftArrow);
+ QToolTip::add
+ (keyDown, i18n("Flatten"));
+
+ m_keyLabel = new QLabel(i18n("Key"), keyBox);
+ m_keyLabel->setAlignment(AlignVCenter | AlignHCenter);
+
+ BigArrowButton *keyUp = new BigArrowButton(keyBox, Qt::RightArrow);
+ QToolTip::add
+ (keyUp, i18n("Sharpen"));
+
+ m_keyCombo = new KComboBox(nameBox);
+ m_majorMinorCombo = new KComboBox(nameBox);
+ m_majorMinorCombo->insertItem(i18n("Major"));
+ m_majorMinorCombo->insertItem(i18n("Minor"));
+ if (m_key.isMinor()) {
+ m_majorMinorCombo->setCurrentItem(m_majorMinorCombo->count() - 1);
+ }
+
+ regenerateKeyCombo();
+ redrawKeyPixmap();
+ m_explanatoryLabel = explanatoryLabel;
+
+ m_keyLabel->setMinimumWidth(m_keyLabel->pixmap()->width());
+ m_keyLabel->setMinimumHeight(m_keyLabel->pixmap()->height());
+
+ m_yesTransposeButton =
+ new QRadioButton(i18n("Transpose key according to segment transposition"),
+ transposeFrame);
+ QRadioButton *noTransposeButton =
+ new QRadioButton(i18n("Use specified key. Do not transpose"), transposeFrame);
+ m_yesTransposeButton->setChecked(true);
+
+ // just to shut up the compiler warning about unused variable:
+ noTransposeButton->setChecked(false);
+
+ if (showApplyToAll) {
+ QRadioButton *applyToOneButton =
+ new QRadioButton(i18n("Apply to current segment only"),
+ buttonFrame);
+ m_applyToAllButton =
+ new QRadioButton(i18n("Apply to all segments at this time"),
+ buttonFrame);
+ applyToOneButton->setChecked(true);
+ m_noPercussionCheckBox =
+ new QCheckBox(i18n("Exclude percussion segments"), buttonFrame);
+ m_noPercussionCheckBox->setChecked(true);
+
+ } else {
+ m_applyToAllButton = 0;
+ buttonFrame->hide();
+ }
+
+ if (showConversionOptions) {
+ m_noConversionButton =
+ new QRadioButton
+ (i18n("Maintain current pitches"), conversionFrame);
+ m_convertButton =
+ new QRadioButton
+ (i18n("Maintain current accidentals"), conversionFrame);
+ m_transposeButton =
+ new QRadioButton
+ (i18n("Transpose into this key"), conversionFrame);
+ m_noConversionButton->setChecked(true);
+ } else {
+ m_noConversionButton = 0;
+ m_convertButton = 0;
+ m_transposeButton = 0;
+ conversionFrame->hide();
+ }
+
+ QObject::connect(keyUp, SIGNAL(clicked()), this, SLOT(slotKeyUp()));
+ QObject::connect(keyDown, SIGNAL(clicked()), this, SLOT(slotKeyDown()));
+ QObject::connect(m_keyCombo, SIGNAL(activated(const QString &)),
+ this, SLOT(slotKeyNameChanged(const QString &)));
+ QObject::connect(m_keyCombo, SIGNAL(textChanged(const QString &)),
+ this, SLOT(slotKeyNameChanged(const QString &)));
+ QObject::connect(m_majorMinorCombo, SIGNAL(activated(const QString &)),
+ this, SLOT(slotMajorMinorChanged(const QString &)));
+}
+
+KeySignatureDialog::ConversionType
+
+KeySignatureDialog::getConversionType() const
+{
+ if (m_noConversionButton && m_noConversionButton->isChecked()) {
+ return NoConversion;
+ } else if (m_convertButton && m_convertButton->isChecked()) {
+ return Convert;
+ } else if (m_transposeButton && m_transposeButton->isChecked()) {
+ return Transpose;
+ }
+ return NoConversion;
+}
+
+bool
+KeySignatureDialog::shouldApplyToAll() const
+{
+ return m_applyToAllButton && m_applyToAllButton->isChecked();
+}
+
+bool
+KeySignatureDialog::shouldBeTransposed() const
+{
+ return m_yesTransposeButton && m_yesTransposeButton->isChecked();
+}
+
+bool
+KeySignatureDialog::shouldIgnorePercussion() const
+{
+ return m_noPercussionCheckBox && m_noPercussionCheckBox->isChecked();
+}
+
+void
+KeySignatureDialog::slotKeyUp()
+{
+ bool sharp = m_key.isSharp();
+ int ac = m_key.getAccidentalCount();
+ if (ac == 0)
+ sharp = true;
+ if (sharp) {
+ if (++ac > 7)
+ ac = 7;
+ } else {
+ if (--ac < 1) {
+ ac = 0;
+ sharp = true;
+ }
+ }
+
+ try {
+ m_key = Rosegarden::Key(ac, sharp, m_key.isMinor());
+ setValid(true);
+ } catch (Rosegarden::Key::BadKeySpec s) {
+ std::cerr << s.getMessage() << std::endl;
+ setValid(false);
+ }
+
+ regenerateKeyCombo();
+ redrawKeyPixmap();
+}
+
+void
+KeySignatureDialog::slotKeyDown()
+{
+ bool sharp = m_key.isSharp();
+ int ac = m_key.getAccidentalCount();
+ if (ac == 0)
+ sharp = false;
+ if (sharp) {
+ if (--ac < 0) {
+ ac = 1;
+ sharp = false;
+ }
+ } else {
+ if (++ac > 7)
+ ac = 7;
+ }
+
+ try {
+ m_key = Rosegarden::Key(ac, sharp, m_key.isMinor());
+ setValid(true);
+ } catch (Rosegarden::Key::BadKeySpec s) {
+ std::cerr << s.getMessage() << std::endl;
+ setValid(false);
+ }
+
+ regenerateKeyCombo();
+ redrawKeyPixmap();
+}
+
+struct KeyNameComparator
+{
+ bool operator()(const Rosegarden::Key &k1, const Rosegarden::Key &k2) {
+ return (k1.getName() < k2.getName());
+ }
+};
+
+
+void
+KeySignatureDialog::regenerateKeyCombo()
+{
+ if (m_explanatoryLabel)
+ m_explanatoryLabel->hide();
+
+ m_ignoreComboChanges = true;
+ QString currentText = m_keyCombo->currentText();
+ Rosegarden::Key::KeyList keys(Rosegarden::Key::getKeys(m_key.isMinor()));
+ m_keyCombo->clear();
+
+ std::sort(keys.begin(), keys.end(), KeyNameComparator());
+ bool textSet = false;
+
+ for (Rosegarden::Key::KeyList::iterator i = keys.begin();
+ i != keys.end(); ++i) {
+
+ QString name(strtoqstr(i->getName()));
+ int space = name.find(' ');
+ if (space > 0)
+ name = name.left(space);
+
+ m_keyCombo->insertItem(name);
+
+ if (m_valid && (*i == m_key)) {
+ m_keyCombo->setCurrentItem(m_keyCombo->count() - 1);
+ textSet = true;
+ }
+ }
+
+ if (!textSet) {
+ m_keyCombo->setEditText(currentText);
+ }
+ m_ignoreComboChanges = false;
+}
+
+bool
+KeySignatureDialog::isValid() const
+{
+ return m_valid;
+}
+
+Rosegarden::Key
+KeySignatureDialog::getKey() const
+{
+ return m_key;
+}
+
+void
+KeySignatureDialog::redrawKeyPixmap()
+{
+ if (m_valid) {
+ QPixmap pmap =
+ NotePixmapFactory::toQPixmap(m_notePixmapFactory->makeKeyDisplayPixmap(m_key, m_clef));
+ m_keyLabel->setPixmap(pmap);
+ } else {
+ m_keyLabel->setText(i18n("No such key"));
+ }
+}
+
+void
+KeySignatureDialog::slotKeyNameChanged(const QString &s)
+{
+ if (m_ignoreComboChanges)
+ return ;
+
+ if (m_explanatoryLabel)
+ m_explanatoryLabel->hide();
+
+ std::string name(getKeyName(s, m_key.isMinor()));
+
+ try {
+ m_key = Rosegarden::Key(name);
+ setValid(true);
+
+ int space = name.find(' ');
+ if (space > 0)
+ name = name.substr(0, space);
+ m_keyCombo->setEditText(strtoqstr(name));
+
+ } catch (Rosegarden::Key::BadKeyName s) {
+ std::cerr << s.getMessage() << std::endl;
+ setValid(false);
+ }
+
+ redrawKeyPixmap();
+}
+
+void
+KeySignatureDialog::slotMajorMinorChanged(const QString &s)
+{
+ if (m_ignoreComboChanges)
+ return ;
+
+ std::string name(getKeyName(m_keyCombo->currentText(), s == i18n("Minor")));
+
+ try {
+ m_key = Rosegarden::Key(name);
+ setValid(true);
+ } catch (Rosegarden::Key::BadKeyName s) {
+ std::cerr << s.getMessage() << std::endl;
+ setValid(false);
+ }
+
+ regenerateKeyCombo();
+ redrawKeyPixmap();
+}
+
+void
+KeySignatureDialog::setValid(bool valid)
+{
+ m_valid = valid;
+ enableButton(Ok, m_valid);
+}
+
+std::string
+KeySignatureDialog::getKeyName(const QString &s, bool minor)
+{
+ QString u((s.length() >= 1) ? (s.left(1).upper() + s.right(s.length() - 1))
+ : s);
+
+ std::string name(qstrtostr(u));
+ name = name + " " + (minor ? "minor" : "major");
+ return name;
+}
+
+}
+#include "KeySignatureDialog.moc"
diff --git a/src/gui/dialogs/KeySignatureDialog.h b/src/gui/dialogs/KeySignatureDialog.h
new file mode 100644
index 0000000..cd4a340
--- /dev/null
+++ b/src/gui/dialogs/KeySignatureDialog.h
@@ -0,0 +1,118 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_KEYSIGNATUREDIALOG_H_
+#define _RG_KEYSIGNATUREDIALOG_H_
+
+#include "base/NotationTypes.h"
+#include <string>
+#include <kdialogbase.h>
+#include <qstring.h>
+#include <qcheckbox.h>
+
+
+class QWidget;
+class QRadioButton;
+class QLabel;
+class KComboBox;
+class QCheckBox;
+
+
+namespace Rosegarden
+{
+
+class NotePixmapFactory;
+
+
+class KeySignatureDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ enum ConversionType {
+ NoConversion,
+ Convert,
+ Transpose
+ };
+
+ KeySignatureDialog(QWidget *parent,
+ NotePixmapFactory *npf,
+ Clef clef,
+ Rosegarden::Key defaultKey =
+ Rosegarden::Key::DefaultKey,
+ bool showApplyToAll = true,
+ bool showConversionOptions = true,
+ QString explanatoryText = 0);
+
+ bool isValid() const;
+ ::Rosegarden::Key getKey() const;
+
+ bool shouldApplyToAll() const;
+ bool shouldBeTransposed() const;
+ ConversionType getConversionType() const;
+ bool shouldIgnorePercussion() const;
+
+public slots:
+ void slotKeyUp();
+ void slotKeyDown();
+ void slotKeyNameChanged(const QString &);
+ void slotMajorMinorChanged(const QString &);
+
+protected:
+
+ void redrawKeyPixmap();
+ void regenerateKeyCombo();
+ void setValid(bool valid);
+ std::string getKeyName(const QString &s, bool minor);
+
+ //--------------- Data members ---------------------------------
+
+ NotePixmapFactory *m_notePixmapFactory;
+
+ Rosegarden::Key m_key;
+ Clef m_clef;
+ bool m_valid;
+ bool m_ignoreComboChanges;
+
+ QLabel *m_keyLabel;
+ KComboBox *m_keyCombo;
+ KComboBox *m_majorMinorCombo;
+ QLabel *m_explanatoryLabel;
+
+ QRadioButton *m_applyToAllButton;
+ QRadioButton *m_yesTransposeButton;
+
+ QRadioButton *m_noConversionButton;
+ QRadioButton *m_convertButton;
+ QRadioButton *m_transposeButton;
+
+ QCheckBox *m_noPercussionCheckBox;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/LilyPondOptionsDialog.cpp b/src/gui/dialogs/LilyPondOptionsDialog.cpp
new file mode 100644
index 0000000..f693467
--- /dev/null
+++ b/src/gui/dialogs/LilyPondOptionsDialog.cpp
@@ -0,0 +1,363 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "LilyPondOptionsDialog.h"
+#include "document/io/LilyPondExporter.h"
+#include "gui/configuration/HeadersConfigurationPage.h"
+
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include "document/ConfigGroups.h"
+#include "document/RosegardenGUIDoc.h"
+#include "misc/Strings.h"
+#include <kcombobox.h>
+#include <klineedit.h>
+#include <kconfig.h>
+#include <kdialogbase.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qstring.h>
+#include <qtabwidget.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <iostream>
+
+namespace Rosegarden
+{
+
+LilyPondOptionsDialog::LilyPondOptionsDialog(QWidget *parent,
+ RosegardenGUIDoc *doc,
+ QString windowCaption,
+ QString heading) :
+ KDialogBase(parent, 0, true,
+ (windowCaption = "" ? i18n("LilyPond Export/Preview") : windowCaption),
+ Apply | Ok | Cancel),
+ m_doc(doc)
+{
+ setHelp("file-printing");
+
+ KConfig *config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+
+ QVBox * mainbox = makeVBoxMainWidget();
+
+ //
+ // Arrange options in "General" and "Advanced" tabs.
+ //
+
+ QTabWidget * tabWidget = new QTabWidget(mainbox);
+
+ QFrame *generalFrame;
+ QFrame *advancedFrame;
+ QGridLayout *generalGrid;
+ QGridLayout *advancedGrid;
+
+ generalFrame = new QFrame();
+ tabWidget->addTab(generalFrame, i18n("General options"));
+
+ generalGrid = new QGridLayout(generalFrame, 1, 1, 5, 5);
+
+ advancedFrame = new QFrame();
+ tabWidget->addTab(advancedFrame, i18n("Advanced options"));
+
+ advancedGrid = new QGridLayout(advancedFrame, 1, 1, 5, 5);
+
+ m_headersPage = new HeadersConfigurationPage(this, m_doc);
+ tabWidget->addTab(m_headersPage, i18n("Headers"));
+
+ m_headersPage->setSpacing(5);
+ m_headersPage->setMargin(5);
+
+ //
+ // LilyPond export: Basic options
+ //
+
+ QGroupBox *basicOptionsBox = new QGroupBox
+ (1, Horizontal,
+ i18n("Basic options"), generalFrame);
+ generalGrid->addWidget(basicOptionsBox, 0, 0);
+
+ QFrame *frameBasic = new QFrame(basicOptionsBox);
+ QGridLayout *layoutBasic = new QGridLayout(frameBasic, 3, 2, 10, 5);
+
+ layoutBasic->addWidget(new QLabel(
+ i18n("Compatibility level"), frameBasic), 0, 0);
+
+ m_lilyLanguage = new KComboBox(frameBasic);
+ // See also setDefaultLilyPondVersion below
+ m_lilyLanguage->insertItem(i18n("LilyPond %1").arg("2.6"));
+ m_lilyLanguage->insertItem(i18n("LilyPond %1").arg("2.8"));
+ m_lilyLanguage->insertItem(i18n("LilyPond %1").arg("2.10"));
+ m_lilyLanguage->insertItem(i18n("LilyPond %1").arg("2.12"));
+ m_lilyLanguage->setCurrentItem(config->readUnsignedNumEntry("lilylanguage", 0));
+ layoutBasic->addWidget(m_lilyLanguage, 0, 1);
+
+ layoutBasic->addWidget(new QLabel(
+ i18n("Paper size"), frameBasic), 1, 0);
+
+ QHBoxLayout *hboxPaper = new QHBoxLayout( frameBasic );
+ m_lilyPaperSize = new KComboBox(frameBasic);
+ m_lilyPaperSize->insertItem(i18n("A3"));
+ m_lilyPaperSize->insertItem(i18n("A4"));
+ m_lilyPaperSize->insertItem(i18n("A5"));
+ m_lilyPaperSize->insertItem(i18n("A6"));
+ m_lilyPaperSize->insertItem(i18n("Legal"));
+ m_lilyPaperSize->insertItem(i18n("US Letter"));
+ m_lilyPaperSize->insertItem(i18n("Tabloid"));
+ m_lilyPaperSize->insertItem(i18n("do not specify"));
+ int defaultPaperSize = 1; // A4
+ if (KGlobal::locale()->country() == "us" ||
+ KGlobal::locale()->country() == "US") defaultPaperSize = 5; // Letter
+ m_lilyPaperSize->setCurrentItem(config->readUnsignedNumEntry
+ ("lilypapersize", defaultPaperSize));
+
+ m_lilyPaperLandscape = new QCheckBox(i18n("Landscape"), frameBasic);
+ m_lilyPaperLandscape->setChecked(config->readBoolEntry("lilypaperlandscape", false));
+
+ hboxPaper->addWidget( m_lilyPaperSize );
+ hboxPaper->addWidget( new QLabel( " ", frameBasic ) ); // fixed-size spacer
+ hboxPaper->addWidget( m_lilyPaperLandscape );
+ layoutBasic->addLayout(hboxPaper, 1, 1);
+
+ layoutBasic->addWidget(new QLabel(
+ i18n("Font size"), frameBasic), 2, 0);
+
+ m_lilyFontSize = new KComboBox(frameBasic);
+ int sizes[] = { 11, 13, 16, 19, 20, 23, 26 };
+ for (int i = 0; i < sizeof(sizes)/sizeof(sizes[0]); ++i) {
+ m_lilyFontSize->insertItem(i18n("%1 pt").arg(sizes[i]));
+ }
+ m_lilyFontSize->setCurrentItem(config->readUnsignedNumEntry
+ ("lilyfontsize", 4));
+ layoutBasic->addWidget(m_lilyFontSize, 2, 1);
+
+ //
+ // LilyPond export: Staff level options
+ //
+
+ QGroupBox *staffOptionsBox = new QGroupBox
+ (1, Horizontal,
+ i18n("Staff level options"), generalFrame);
+ generalGrid->addWidget(staffOptionsBox, 1, 0);
+
+ QFrame *frameStaff = new QFrame(staffOptionsBox);
+ QGridLayout *layoutStaff = new QGridLayout(frameStaff, 2, 2, 10, 5);
+
+ layoutStaff->addWidget(new QLabel(
+ i18n("Export content"), frameStaff), 0, 0);
+
+ m_lilyExportSelection = new KComboBox(frameStaff);
+ m_lilyExportSelection->insertItem(i18n("All tracks"));
+ m_lilyExportSelection->insertItem(i18n("Non-muted tracks"));
+ m_lilyExportSelection->insertItem(i18n("Selected track"));
+ m_lilyExportSelection->insertItem(i18n("Selected segments"));
+ m_lilyExportSelection->setCurrentItem(config->readUnsignedNumEntry("lilyexportselection", 1));
+
+ layoutStaff->addWidget(m_lilyExportSelection, 0, 1);
+
+ m_lilyExportStaffMerge = new QCheckBox(
+ i18n("Merge tracks that have the same name"), frameStaff);
+ m_lilyExportStaffMerge->setChecked(config->readBoolEntry("lilyexportstaffmerge", false));
+ layoutStaff->addMultiCellWidget(m_lilyExportStaffMerge, 1, 1, 0, 1);
+
+ //
+ // LilyPond export: Notation options
+ //
+
+ QGroupBox *notationOptionsBox = new QGroupBox
+ (1, Horizontal,
+ i18n("Notation options"), generalFrame);
+ generalGrid->addWidget(notationOptionsBox, 2, 0);
+
+ QFrame *frameNotation = new QFrame(notationOptionsBox);
+ QGridLayout *layoutNotation = new QGridLayout(frameNotation, 4, 2, 10, 5);
+
+ m_lilyTempoMarks = new KComboBox( frameNotation );
+ m_lilyTempoMarks->insertItem(i18n("None"));
+ m_lilyTempoMarks->insertItem(i18n("First"));
+ m_lilyTempoMarks->insertItem(i18n("All"));
+ m_lilyTempoMarks->setCurrentItem(config->readUnsignedNumEntry("lilyexporttempomarks", 0));
+
+ layoutNotation->addWidget( new QLabel(
+ i18n("Export tempo marks "), frameNotation), 0, 0 );
+ layoutNotation->addWidget(m_lilyTempoMarks, 0, 1);
+
+ m_lilyExportLyrics = new QCheckBox(
+ i18n("Export lyrics"), frameNotation);
+ // default to lyric export == false because if you export the default
+ // empty "- - -" lyrics, crap results ensue, and people will know if they
+ // do need to export the lyrics - DMM
+ // fixed, no "- - -" lyrics are generated for an empty lyrics
+ // default again into lyrics - HJJ
+ m_lilyExportLyrics->setChecked(config->readBoolEntry("lilyexportlyrics", true));
+ layoutNotation->addMultiCellWidget(m_lilyExportLyrics, 1, 1, 0, 1);
+
+ m_lilyExportBeams = new QCheckBox(
+ i18n("Export beamings"), frameNotation);
+ m_lilyExportBeams->setChecked(config->readBoolEntry("lilyexportbeamings", false));
+ layoutNotation->addMultiCellWidget(m_lilyExportBeams, 2, 2, 0, 1);
+
+ // recycle this for a new option to ignore the track brackets (so it is less
+ // obnoxious to print single parts where brackets are in place)
+ m_lilyExportStaffGroup = new QCheckBox(
+ i18n("Export track staff brackets"), frameNotation);
+ m_lilyExportStaffGroup->setChecked(config->readBoolEntry("lilyexportstaffbrackets", true));
+ layoutNotation->addMultiCellWidget(m_lilyExportStaffGroup, 3, 3, 0, 1);
+
+ generalGrid->setRowStretch(4, 10);
+
+ //
+ // LilyPond export: Advanced options
+ //
+
+ QGroupBox *advancedLayoutOptionsBox = new QGroupBox
+ (1, Horizontal,
+ i18n("Layout options"), advancedFrame);
+ advancedGrid->addWidget(advancedLayoutOptionsBox, 0, 0);
+
+ QFrame *frameAdvancedLayout = new QFrame(advancedLayoutOptionsBox);
+ QGridLayout *layoutAdvancedLayout = new QGridLayout(frameAdvancedLayout, 2, 2, 10, 5);
+
+ m_lilyLyricsHAlignment = new KComboBox( frameAdvancedLayout );
+ m_lilyLyricsHAlignment->insertItem(i18n("Left"));
+ m_lilyLyricsHAlignment->insertItem(i18n("Center"));
+ m_lilyLyricsHAlignment->insertItem(i18n("Right"));
+ m_lilyLyricsHAlignment->setCurrentItem(config->readUnsignedNumEntry("lilylyricshalignment", 0));
+
+ layoutAdvancedLayout->addWidget(new QLabel(
+ i18n("Lyrics alignment"), frameAdvancedLayout), 0, 0);
+ layoutAdvancedLayout->addWidget(m_lilyLyricsHAlignment, 0, 1);
+
+ m_lilyRaggedBottom = new QCheckBox(
+ i18n("Ragged bottom (systems will not be spread vertically across the page)"), frameAdvancedLayout);
+ m_lilyRaggedBottom->setChecked(config->readBoolEntry("lilyraggedbottom", false));
+ layoutAdvancedLayout->addMultiCellWidget(m_lilyRaggedBottom, 1, 2, 0, 1);
+
+ QGroupBox *miscOptionsBox = new QGroupBox
+ (1, Horizontal,
+ i18n("Miscellaneous options"), advancedFrame);
+ advancedGrid->addWidget(miscOptionsBox, 1, 0);
+
+ QFrame *frameMisc = new QFrame(miscOptionsBox);
+ QGridLayout *layoutMisc = new QGridLayout(frameMisc, 2, 2, 10, 5);
+
+ m_lilyExportPointAndClick = new QCheckBox(
+ i18n("Enable \"point and click\" debugging"), frameMisc);
+ m_lilyExportPointAndClick->setChecked(config->readBoolEntry("lilyexportpointandclick", false));
+ layoutMisc->addMultiCellWidget(m_lilyExportPointAndClick, 0, 0, 0, 1);
+
+ m_lilyExportMidi = new QCheckBox(
+ i18n("Export \\midi block"), frameMisc);
+ m_lilyExportMidi->setChecked(config->readBoolEntry("lilyexportmidi", false));
+ layoutMisc->addMultiCellWidget(m_lilyExportMidi, 1, 1, 0, 1);
+
+ m_lilyMarkerMode = new KComboBox(frameMisc);
+ m_lilyMarkerMode->insertItem(i18n("No markers"));
+ m_lilyMarkerMode->insertItem(i18n("Rehearsal marks"));
+ m_lilyMarkerMode->insertItem(i18n("Marker text"));
+ m_lilyMarkerMode->setCurrentItem(config->readUnsignedNumEntry("lilyexportmarkermode", 0));
+
+ layoutMisc->addWidget( new QLabel(
+ i18n("Export markers"), frameMisc),2, 0 );
+ layoutMisc->addWidget(m_lilyMarkerMode, 2, 1);
+
+ advancedGrid->setRowStretch(2, 10);
+
+ resize(minimumSize());
+}
+
+void
+LilyPondOptionsDialog::slotApply()
+{
+ KConfig *config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+
+ config->writeEntry("lilylanguage", m_lilyLanguage->currentItem());
+ config->writeEntry("lilypapersize", m_lilyPaperSize->currentItem());
+ config->writeEntry("lilypaperlandscape", m_lilyPaperLandscape->isChecked());
+ config->writeEntry("lilyfontsize", m_lilyFontSize->currentItem());
+ config->writeEntry("lilyraggedbottom", m_lilyRaggedBottom->isChecked());
+ config->writeEntry("lilyexportlyrics", m_lilyExportLyrics->isChecked());
+ config->writeEntry("lilyexportmidi", m_lilyExportMidi->isChecked());
+ config->writeEntry("lilyexporttempomarks", m_lilyTempoMarks->currentItem());
+ config->writeEntry("lilyexportselection", m_lilyExportSelection->currentItem());
+ config->writeEntry("lilyexportpointandclick", m_lilyExportPointAndClick->isChecked());
+ config->writeEntry("lilyexportbeamings", m_lilyExportBeams->isChecked());
+ config->writeEntry("lilyexportstaffmerge", m_lilyExportStaffMerge->isChecked());
+ config->writeEntry("lilyexportstaffbrackets", m_lilyExportStaffGroup->isChecked());
+ config->writeEntry("lilylyricshalignment", m_lilyLyricsHAlignment->currentItem());
+ config->writeEntry("lilyexportmarkermode", m_lilyMarkerMode->currentItem());
+ m_headersPage->apply();
+}
+
+void
+LilyPondOptionsDialog::slotOk()
+{
+ slotApply();
+ accept();
+}
+
+void
+LilyPondOptionsDialog::setDefaultLilyPondVersion(QString version)
+{
+ KConfig *config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+ int index = -1;
+ bool unstable = false;
+ if (version == "2.6" || version.startsWith("2.6.")) {
+ index = 0;
+ } else if (version == "2.7" || version.startsWith("2.7.")) {
+ unstable = true;
+ index = 1;
+ } else if (version == "2.8" || version.startsWith("2.8.")) {
+ index = 1;
+ } else if (version == "2.9" || version.startsWith("2.9.")) {
+ unstable = true;
+ index = 2;
+ } else if (version == "2.10" || version.startsWith("2.10.")) {
+ index = 2;
+ } else if (version == "2.11" || version.startsWith("2.11.")) {
+ unstable = true;
+ index = 3;
+ } else if (version == "2.12" || version.startsWith("2.12.")) {
+ index = 3;
+ }
+ if (unstable) {
+ std::cerr << "\nWARNING: Unstable LilyPond version detected, selecting next language version up\n" << std::endl;
+ }
+ if (index >= 0) {
+ config->writeEntry("lilylanguage", index);
+ }
+}
+
+}
+#include "LilyPondOptionsDialog.moc"
diff --git a/src/gui/dialogs/LilyPondOptionsDialog.h b/src/gui/dialogs/LilyPondOptionsDialog.h
new file mode 100644
index 0000000..a8f2476
--- /dev/null
+++ b/src/gui/dialogs/LilyPondOptionsDialog.h
@@ -0,0 +1,86 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_LILYPONDOPTIONSDIALOG_H_
+#define _RG_LILYPONDOPTIONSDIALOG_H_
+
+#include <kdialogbase.h>
+#include <qstring.h>
+
+#include "gui/configuration/HeadersConfigurationPage.h"
+
+class QWidget;
+class QCheckBox;
+class QComboBox;
+class QLineEdit;
+class QLineEdit;
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class HeadersConfigurationPage;
+
+class LilyPondOptionsDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ LilyPondOptionsDialog(QWidget *parent,
+ RosegardenGUIDoc *doc,
+ QString windowCaption = "",
+ QString heading = "");
+
+ static void setDefaultLilyPondVersion(QString version);
+
+public slots:
+ void slotApply();
+ void slotOk();
+
+protected:
+ RosegardenGUIDoc *m_doc;
+ QComboBox *m_lilyLanguage;
+ QComboBox *m_lilyPaperSize;
+ QComboBox *m_lilyFontSize;
+ QComboBox *m_lilyTempoMarks;
+ QComboBox *m_lilyExportSelection;
+ QComboBox *m_lilyLyricsHAlignment;
+ QCheckBox *m_lilyPaperLandscape;
+ QCheckBox *m_lilyRaggedBottom;
+ QCheckBox *m_lilyExportLyrics;
+ QCheckBox *m_lilyExportMidi;
+ QCheckBox *m_lilyExportPointAndClick;
+ QCheckBox *m_lilyExportBeams;
+ QCheckBox *m_lilyExportStaffMerge;
+ QCheckBox *m_lilyExportStaffGroup;
+ QComboBox *m_lilyMarkerMode;
+ HeadersConfigurationPage *m_headersPage;
+
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/LyricEditDialog.cpp b/src/gui/dialogs/LyricEditDialog.cpp
new file mode 100644
index 0000000..4dfeba2
--- /dev/null
+++ b/src/gui/dialogs/LyricEditDialog.cpp
@@ -0,0 +1,253 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "LyricEditDialog.h"
+
+#include "base/Event.h"
+#include "base/BaseProperties.h"
+#include <klocale.h>
+#include "misc/Strings.h"
+#include "misc/Debug.h"
+#include "base/Composition.h"
+#include "base/NotationTypes.h"
+#include "base/Segment.h"
+#include <kdialogbase.h>
+#include <qgroupbox.h>
+#include <qregexp.h>
+#include <qstring.h>
+#include <qtextedit.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <kcombobox.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+
+
+namespace Rosegarden
+{
+
+LyricEditDialog::LyricEditDialog(QWidget *parent,
+ Segment *segment) :
+ KDialogBase(parent, 0, true, i18n("Edit Lyrics"), Ok | Cancel | Help),
+ m_segment(segment),
+ m_verseCount(0)
+{
+ setHelp("nv-text-lyrics");
+
+ QVBox *vbox = makeVBoxMainWidget();
+
+ QGroupBox *groupBox = new QGroupBox
+ (1, Horizontal, i18n("Lyrics for this segment"), vbox);
+
+ QHBox *hbox = new QHBox(groupBox);
+ hbox->setSpacing(5);
+// new QLabel(i18n("Verse:"), hbox);
+ m_verseNumber = new KComboBox(hbox);
+ m_verseNumber->setEditable(false);
+ connect(m_verseNumber, SIGNAL(activated(int)), this, SLOT(slotVerseNumberChanged(int)));
+ m_verseAddButton = new QPushButton(i18n("Add Verse"), hbox);
+ connect(m_verseAddButton, SIGNAL(clicked()), this, SLOT(slotAddVerse()));
+ QFrame *f = new QFrame(hbox);
+ hbox->setStretchFactor(f, 10);
+
+ m_textEdit = new QTextEdit(groupBox);
+ m_textEdit->setTextFormat(Qt::PlainText);
+
+ m_textEdit->setMinimumWidth(300);
+ m_textEdit->setMinimumHeight(200);
+
+ unparse();
+
+ for (int i = 0; i < m_verseCount; ++i) {
+ m_verseNumber->insertItem(i18n("Verse %1").arg(i + 1));
+ }
+ m_currentVerse = 0;
+ if (m_verseCount == 12) m_verseAddButton->setEnabled(false);
+}
+
+void
+LyricEditDialog::slotVerseNumberChanged(int verse)
+{
+ NOTATION_DEBUG << "LyricEditDialog::slotVerseNumberChanged(" << verse << ")" << endl;
+ QString text = m_textEdit->text();
+ m_texts[m_currentVerse] = text;
+ m_textEdit->setText(m_texts[verse]);
+ m_currentVerse = verse;
+}
+
+void
+LyricEditDialog::slotAddVerse()
+{
+ NOTATION_DEBUG << "LyricEditDialog::slotAddVerse" << endl;
+ m_verseCount++;
+ m_texts.push_back(m_skeleton);
+ m_verseNumber->insertItem(i18n("Verse %1").arg(m_verseCount));
+ m_verseNumber->setCurrentItem(m_verseCount - 1);
+ slotVerseNumberChanged(m_verseCount - 1);
+ if (m_verseCount == 12) m_verseAddButton->setEnabled(false);
+}
+
+void
+LyricEditDialog::countVerses()
+{
+ m_verseCount = 1;
+
+ for (Segment::iterator i = m_segment->begin();
+ m_segment->isBeforeEndMarker(i); ++i) {
+
+ if ((*i)->isa(Text::EventType)) {
+
+ std::string textType;
+ if ((*i)->get<String>(Text::TextTypePropertyName, textType) &&
+ textType == Text::Lyric) {
+
+ long verse = 0;
+ (*i)->get<Int>(Text::LyricVersePropertyName, verse);
+
+ if (verse >= m_verseCount) m_verseCount = verse + 1;
+ }
+ }
+ }
+}
+
+void
+LyricEditDialog::unparse()
+{
+ // This and SetLyricsCommand::execute() are opposites that will
+ // need to be kept in sync with any changes in one another. (They
+ // should really both be in a common lyric management class.)
+
+ countVerses();
+
+ Composition *comp = m_segment->getComposition();
+
+ bool firstNote = true;
+ timeT lastTime = m_segment->getStartTime();
+ int lastBarNo = comp->getBarNumber(lastTime);
+ std::map<int, bool> haveLyric;
+
+ QString fragment = QString("[%1] ").arg(lastBarNo + 1);
+
+ m_skeleton = fragment;
+ m_texts.clear();
+ for (size_t v = 0; v < m_verseCount; ++v) {
+ m_texts.push_back(fragment);
+ haveLyric[v] = false;
+ }
+
+ for (Segment::iterator i = m_segment->begin();
+ m_segment->isBeforeEndMarker(i); ++i) {
+
+ bool isNote = (*i)->isa(Note::EventType);
+ bool isLyric = false;
+
+ if (!isNote) {
+ if ((*i)->isa(Text::EventType)) {
+ std::string textType;
+ if ((*i)->get<String>(Text::TextTypePropertyName, textType) &&
+ textType == Text::Lyric) {
+ isLyric = true;
+ }
+ }
+ } else {
+ if ((*i)->has(BaseProperties::TIED_BACKWARD) &&
+ (*i)->get<Bool>(BaseProperties::TIED_BACKWARD)) {
+ continue;
+ }
+ }
+
+ if (!isNote && !isLyric) continue;
+
+ timeT myTime = (*i)->getNotationAbsoluteTime();
+ int myBarNo = comp->getBarNumber(myTime);
+
+ if (myBarNo > lastBarNo) {
+
+ fragment = "";
+
+ while (myBarNo > lastBarNo) {
+ fragment += " /";
+ ++lastBarNo;
+ }
+
+ fragment += QString("\n[%1] ").arg(myBarNo + 1);
+
+ m_skeleton += fragment;
+ for (size_t v = 0; v < m_verseCount; ++v) m_texts[v] += fragment;
+ }
+
+ if (isNote) {
+ if ((myTime > lastTime) || firstNote) {
+ m_skeleton += " .";
+ for (size_t v = 0; v < m_verseCount; ++v) {
+ if (!haveLyric[v]) m_texts[v] += " .";
+ haveLyric[v] = false;
+ }
+ lastTime = myTime;
+ firstNote = false;
+ }
+ }
+
+ if (isLyric) {
+
+ std::string ssyllable;
+ (*i)->get<String>(Text::TextPropertyName, ssyllable);
+
+ long verse = 0;
+ (*i)->get<Int>(Text::LyricVersePropertyName, verse);
+
+ QString syllable(strtoqstr(ssyllable));
+ syllable.replace(QRegExp("\\s+"), "~");
+
+ m_texts[verse] += " " + syllable;
+ haveLyric[verse] = true;
+ }
+ }
+
+ if (!m_texts.empty()) {
+ m_textEdit->setText(m_texts[0]);
+ } else {
+ m_texts.push_back(m_skeleton);
+ }
+}
+
+int
+LyricEditDialog::getVerseCount() const
+{
+ return m_verseCount;
+}
+
+QString
+LyricEditDialog::getLyricData(int verse) const
+{
+ if (verse == m_verseNumber->currentItem()) {
+ return m_textEdit->text();
+ } else {
+ return m_texts[verse];
+ }
+}
+
+}
+#include "LyricEditDialog.moc"
diff --git a/src/gui/dialogs/LyricEditDialog.h b/src/gui/dialogs/LyricEditDialog.h
new file mode 100644
index 0000000..f4a5154
--- /dev/null
+++ b/src/gui/dialogs/LyricEditDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_LYRICEDITDIALOG_H_
+#define _RG_LYRICEDITDIALOG_H_
+
+#include <kdialogbase.h>
+#include <qstring.h>
+#include <vector>
+
+
+class QWidget;
+class QTextEdit;
+class QComboBox;
+class QPushButton;
+
+
+namespace Rosegarden
+{
+
+class Segment;
+
+
+class LyricEditDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ LyricEditDialog(QWidget *parent, Segment *segment);
+
+ int getVerseCount() const;
+ QString getLyricData(int verse) const;
+
+protected slots:
+ void slotVerseNumberChanged(int);
+ void slotAddVerse();
+
+protected:
+ Segment *m_segment;
+
+ int m_currentVerse;
+ QComboBox *m_verseNumber;
+ QTextEdit *m_textEdit;
+ QPushButton *m_verseAddButton;
+
+ int m_verseCount;
+ std::vector<QString> m_texts;
+ QString m_skeleton;
+
+ void countVerses();
+ void unparse();
+};
+
+}
+
+#endif
diff --git a/src/gui/dialogs/MakeOrnamentDialog.cpp b/src/gui/dialogs/MakeOrnamentDialog.cpp
new file mode 100644
index 0000000..7e82a22
--- /dev/null
+++ b/src/gui/dialogs/MakeOrnamentDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "MakeOrnamentDialog.h"
+
+#include <klocale.h>
+#include "gui/widgets/PitchChooser.h"
+#include <kdialogbase.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+MakeOrnamentDialog::MakeOrnamentDialog(QWidget *parent, QString defaultName,
+ int defaultBasePitch) :
+ KDialogBase(parent, "makeornamentdialog", true, i18n("Make Ornament"),
+ Ok | Cancel, Ok)
+{
+ QVBox *vbox = makeVBoxMainWidget();
+ QGroupBox *nameBox = new QGroupBox(2, Vertical, i18n("Name"), vbox);
+
+ new QLabel(i18n("The name is used to identify both the ornament\nand the triggered segment that stores\nthe ornament's notes."), nameBox);
+
+ QHBox *hbox = new QHBox(nameBox);
+ new QLabel(i18n("Name: "), hbox);
+ m_name = new QLineEdit(defaultName, hbox);
+
+ m_pitch = new PitchChooser(i18n("Base pitch"), vbox, defaultBasePitch);
+}
+
+QString
+MakeOrnamentDialog::getName() const
+{
+ return m_name->text();
+}
+
+int
+MakeOrnamentDialog::getBasePitch() const
+{
+ return m_pitch->getPitch();
+}
+
+}
+#include "MakeOrnamentDialog.moc"
diff --git a/src/gui/dialogs/MakeOrnamentDialog.h b/src/gui/dialogs/MakeOrnamentDialog.h
new file mode 100644
index 0000000..3f1957b
--- /dev/null
+++ b/src/gui/dialogs/MakeOrnamentDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_MAKEORNAMENTDIALOG_H_
+#define _RG_MAKEORNAMENTDIALOG_H_
+
+#include <kdialogbase.h>
+#include <qstring.h>
+
+
+class QWidget;
+class QLineEdit;
+
+
+namespace Rosegarden
+{
+
+class PitchChooser;
+
+
+class MakeOrnamentDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ MakeOrnamentDialog(QWidget *parent, QString defaultName, int defaultBasePitch);
+
+ QString getName() const;
+ int getBasePitch() const;
+
+protected:
+ QLineEdit *m_name;
+ PitchChooser *m_pitch;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/ManageMetronomeDialog.cpp b/src/gui/dialogs/ManageMetronomeDialog.cpp
new file mode 100644
index 0000000..a0f73d6
--- /dev/null
+++ b/src/gui/dialogs/ManageMetronomeDialog.cpp
@@ -0,0 +1,508 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ManageMetronomeDialog.h"
+#include <qlayout.h>
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/Composition.h"
+#include "base/Device.h"
+#include "base/Instrument.h"
+#include "base/MidiDevice.h"
+#include "base/MidiProgram.h"
+#include "base/RealTime.h"
+#include "base/Studio.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/editors/parameters/InstrumentParameterBox.h"
+#include "gui/seqmanager/SequenceManager.h"
+#include "gui/studio/StudioControl.h"
+#include "gui/widgets/PitchChooser.h"
+#include "sound/MappedEvent.h"
+#include <kcombobox.h>
+#include <kdialogbase.h>
+#include <qcheckbox.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qspinbox.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+ManageMetronomeDialog::ManageMetronomeDialog(QWidget *parent,
+ RosegardenGUIDoc *doc) :
+ KDialogBase(parent, 0, true, i18n("Metronome"), Ok | Apply | Close | Help),
+ m_doc(doc)
+{
+ setHelp("studio-metronome");
+
+ QHBox *hbox = makeHBoxMainWidget();
+
+ // I think having this as well probably just overcomplicates things
+ m_instrumentParameterBox = 0;
+ // m_instrumentParameterBox = new InstrumentParameterBox(doc, hbox);
+
+ QVBox *vbox = new QVBox(hbox);
+
+ QGroupBox *deviceBox = new QGroupBox
+ (1, Horizontal, i18n("Metronome Instrument"), vbox);
+
+ QFrame *frame = new QFrame(deviceBox);
+ QGridLayout *layout = new QGridLayout(frame, 2, 2, 10, 5);
+
+ layout->addWidget(new QLabel(i18n("Device"), frame), 0, 0);
+ m_metronomeDevice = new KComboBox(frame);
+ layout->addWidget(m_metronomeDevice, 0, 1);
+
+ DeviceList *devices = doc->getStudio().getDevices();
+ DeviceListConstIterator it;
+
+ Studio &studio = m_doc->getStudio();
+ DeviceId deviceId = studio.getMetronomeDevice();
+
+ for (it = devices->begin(); it != devices->end(); it++) {
+ MidiDevice *dev =
+ dynamic_cast<MidiDevice*>(*it);
+
+ if (dev && dev->getDirection() == MidiDevice::Play) {
+ QString label = strtoqstr(dev->getName());
+ QString connection = strtoqstr(dev->getConnection());
+ label += " - ";
+ if (connection == "")
+ label += i18n("No connection");
+ else
+ label += connection;
+ m_metronomeDevice->insertItem(label);
+ if (dev->getId() == deviceId) {
+ m_metronomeDevice->setCurrentItem(m_metronomeDevice->count() - 1);
+ }
+ }
+ }
+
+ layout->addWidget(new QLabel(i18n("Instrument"), frame), 1, 0);
+ m_metronomeInstrument = new KComboBox(frame);
+ connect(m_metronomeInstrument, SIGNAL(activated(int)), this, SLOT(slotSetModified()));
+ connect(m_metronomeInstrument, SIGNAL(activated(int)), this, SLOT(slotInstrumentChanged(int)));
+ layout->addWidget(m_metronomeInstrument, 1, 1);
+
+ QGroupBox *beatBox = new QGroupBox
+ (1, Horizontal, i18n("Beats"), vbox);
+
+ frame = new QFrame(beatBox);
+ layout = new QGridLayout(frame, 4, 2, 10, 5);
+
+ layout->addWidget(new QLabel(i18n("Resolution"), frame), 0, 0);
+ m_metronomeResolution = new KComboBox(frame);
+ m_metronomeResolution->insertItem(i18n("None"));
+ m_metronomeResolution->insertItem(i18n("Bars only"));
+ m_metronomeResolution->insertItem(i18n("Bars and beats"));
+ m_metronomeResolution->insertItem(i18n("Bars, beats, and divisions"));
+ connect(m_metronomeResolution, SIGNAL(activated(int)), this, SLOT(slotResolutionChanged(int)));
+ layout->addWidget(m_metronomeResolution, 0, 1);
+
+ layout->addWidget(new QLabel(i18n("Bar velocity"), frame), 1, 0);
+ m_metronomeBarVely = new QSpinBox(frame);
+ m_metronomeBarVely->setMinValue(0);
+ m_metronomeBarVely->setMaxValue(127);
+ connect(m_metronomeBarVely, SIGNAL(valueChanged(int)), this, SLOT(slotSetModified()));
+ layout->addWidget(m_metronomeBarVely, 1, 1);
+
+ layout->addWidget(new QLabel(i18n("Beat velocity"), frame), 2, 0);
+ m_metronomeBeatVely = new QSpinBox(frame);
+ m_metronomeBeatVely->setMinValue(0);
+ m_metronomeBeatVely->setMaxValue(127);
+ connect(m_metronomeBeatVely, SIGNAL(valueChanged(int)), this, SLOT(slotSetModified()));
+ layout->addWidget(m_metronomeBeatVely, 2, 1);
+
+ layout->addWidget(new QLabel(i18n("Sub-beat velocity"), frame), 3, 0);
+ m_metronomeSubBeatVely = new QSpinBox(frame);
+ m_metronomeSubBeatVely->setMinValue(0);
+ m_metronomeSubBeatVely->setMaxValue(127);
+ connect(m_metronomeSubBeatVely, SIGNAL(valueChanged(int)), this, SLOT(slotSetModified()));
+ layout->addWidget(m_metronomeSubBeatVely, 3, 1);
+
+ vbox = new QVBox(hbox);
+
+ m_metronomePitch = new PitchChooser(i18n("Pitch"), vbox, 60);
+ connect(m_metronomePitch, SIGNAL(pitchChanged(int)), this, SLOT(slotPitchChanged(int)));
+ connect(m_metronomePitch, SIGNAL(preview(int)), this, SLOT(slotPreviewPitch(int)));
+
+ m_metronomePitchSelector = new KComboBox(m_metronomePitch);
+ m_metronomePitchSelector->insertItem(i18n("for Bar"));
+ m_metronomePitchSelector->insertItem(i18n("for Beat"));
+ m_metronomePitchSelector->insertItem(i18n("for Sub-beat"));
+ connect(m_metronomePitchSelector, SIGNAL(activated(int)), this, SLOT(slotPitchSelectorChanged(int)));
+
+ QGroupBox *enableBox = new QGroupBox
+ (1, Horizontal, i18n("Metronome Activated"), vbox);
+ m_playEnabled = new QCheckBox(i18n("Playing"), enableBox);
+ m_recordEnabled = new QCheckBox(i18n("Recording"), enableBox);
+ connect(m_playEnabled, SIGNAL(clicked()), this, SLOT(slotSetModified()));
+ connect(m_recordEnabled, SIGNAL(clicked()), this, SLOT(slotSetModified()));
+
+ // populate the dialog
+ populate(m_metronomeDevice->currentItem());
+
+ // connect up the device list
+ connect(m_metronomeDevice, SIGNAL(activated(int)),
+ this, SLOT(populate(int)));
+ // connect up the device list
+ connect(m_metronomeDevice, SIGNAL(activated(int)),
+ this, SLOT(slotSetModified()));
+
+ setModified(false);
+}
+
+void
+ManageMetronomeDialog::slotResolutionChanged(int depth)
+{
+ m_metronomeBeatVely->setEnabled(depth > 1);
+ m_metronomeSubBeatVely->setEnabled(depth > 2);
+ slotSetModified();
+}
+
+void
+ManageMetronomeDialog::populate(int deviceIndex)
+{
+ m_metronomeInstrument->clear();
+
+ DeviceList *devices = m_doc->getStudio().getDevices();
+ DeviceListConstIterator it;
+ int count = 0;
+ MidiDevice *dev = 0;
+
+ for (it = devices->begin(); it != devices->end(); it++) {
+ dev = dynamic_cast<MidiDevice*>(*it);
+
+ if (dev && dev->getDirection() == MidiDevice::Play) {
+ if (count == deviceIndex)
+ break;
+
+ count++;
+ }
+ }
+
+ // sanity
+ if (count < 0 || dev == 0) {
+ if (m_instrumentParameterBox)
+ m_instrumentParameterBox->useInstrument(0);
+ return ;
+ }
+
+ // populate instrument list
+ InstrumentList list = dev->getPresentationInstruments();
+ InstrumentList::iterator iit;
+
+ const MidiMetronome *metronome = dev->getMetronome();
+
+ // if we've got no metronome against this device then create one
+ if (metronome == 0) {
+ InstrumentId id = SystemInstrumentBase;
+
+ for (iit = list.begin(); iit != list.end(); ++iit) {
+ if ((*iit)->isPercussion()) {
+ id = (*iit)->getId();
+ break;
+ }
+ }
+
+ dev->setMetronome(MidiMetronome(id));
+
+ metronome = dev->getMetronome();
+ }
+
+ // metronome should now be set but we still check it
+ if (metronome) {
+ int position = 0;
+ int count = 0;
+ for (iit = list.begin(); iit != list.end(); ++iit) {
+ QString iname(strtoqstr((*iit)->getPresentationName()));
+ QString pname(strtoqstr((*iit)->getProgramName()));
+ if (pname != "")
+ iname += " (" + pname + ")";
+
+ bool used = false;
+ for (Composition::trackcontainer::iterator tit =
+ m_doc->getComposition().getTracks().begin();
+ tit != m_doc->getComposition().getTracks().end(); ++tit) {
+
+ if (tit->second->getInstrument() == (*iit)->getId()) {
+ used = true;
+ break;
+ }
+ }
+
+ // if (used) iname = i18n("%1 [used]").arg(iname);
+
+ m_metronomeInstrument->insertItem(iname);
+
+ if ((*iit)->getId() == metronome->getInstrument()) {
+ position = count;
+ }
+ count++;
+ }
+ m_metronomeInstrument->setCurrentItem(position);
+ slotInstrumentChanged(position);
+
+ m_barPitch = metronome->getBarPitch();
+ m_beatPitch = metronome->getBeatPitch();
+ m_subBeatPitch = metronome->getSubBeatPitch();
+ slotPitchSelectorChanged(0);
+ m_metronomeResolution->setCurrentItem(metronome->getDepth());
+ m_metronomeBarVely->setValue(metronome->getBarVelocity());
+ m_metronomeBeatVely->setValue(metronome->getBeatVelocity());
+ m_metronomeSubBeatVely->setValue(metronome->getSubBeatVelocity());
+ m_playEnabled->setChecked(m_doc->getComposition().usePlayMetronome());
+ m_recordEnabled->setChecked(m_doc->getComposition().useRecordMetronome());
+ slotResolutionChanged(metronome->getDepth());
+ }
+}
+
+void
+ManageMetronomeDialog::slotInstrumentChanged(int i)
+{
+ if (!m_instrumentParameterBox)
+ return ;
+
+ int deviceIndex = m_metronomeDevice->currentItem();
+
+ DeviceList *devices = m_doc->getStudio().getDevices();
+ DeviceListConstIterator it;
+ int count = 0;
+ MidiDevice *dev = 0;
+
+ for (it = devices->begin(); it != devices->end(); it++) {
+ dev = dynamic_cast<MidiDevice*>(*it);
+
+ if (dev && dev->getDirection() == MidiDevice::Play) {
+ if (count == deviceIndex)
+ break;
+
+ count++;
+ }
+ }
+
+ // sanity
+ if (count < 0 || dev == 0) {
+ m_instrumentParameterBox->useInstrument(0);
+ return ;
+ }
+
+ // populate instrument list
+ InstrumentList list = dev->getPresentationInstruments();
+
+ if (i < 0 || i >= (int)list.size())
+ return ;
+
+ m_instrumentParameterBox->useInstrument(list[i]);
+}
+
+void
+ManageMetronomeDialog::slotOk()
+{
+ slotApply();
+ accept();
+}
+
+void
+ManageMetronomeDialog::slotSetModified()
+{
+ setModified(true);
+}
+
+void
+ManageMetronomeDialog::setModified(bool value)
+{
+ if (m_modified == value)
+ return ;
+
+ if (value) {
+ enableButtonApply(true);
+ } else {
+ enableButtonApply(false);
+ }
+
+ m_modified = value;
+}
+
+void
+ManageMetronomeDialog::slotApply()
+{
+ Studio &studio = m_doc->getStudio();
+
+ DeviceList *devices = m_doc->getStudio().getDevices();
+ DeviceListConstIterator it;
+ int count = 0;
+ MidiDevice *dev = 0;
+
+ for (it = devices->begin(); it != devices->end(); it++) {
+ dev = dynamic_cast<MidiDevice*>(*it);
+
+ if (dev && dev->getDirection() == MidiDevice::Play) {
+ if (count == m_metronomeDevice->currentItem())
+ break;
+
+ count++;
+ }
+ }
+
+ if (!dev) {
+ std::cerr << "Warning: ManageMetronomeDialog::slotApply: no " << m_metronomeDevice->currentItem() << "th device" << std::endl;
+ return ;
+ }
+
+ DeviceId deviceId = dev->getId();
+ studio.setMetronomeDevice(deviceId);
+
+ if (dev->getMetronome() == 0)
+ return ;
+ MidiMetronome metronome(*dev->getMetronome());
+
+ // get instrument
+ InstrumentList list = dev->getPresentationInstruments();
+
+ Instrument *inst =
+ list[m_metronomeInstrument->currentItem()];
+
+ if (inst) {
+ metronome.setInstrument(inst->getId());
+ }
+
+ metronome.setBarPitch(m_barPitch);
+ metronome.setBeatPitch(m_beatPitch);
+ metronome.setSubBeatPitch(m_subBeatPitch);
+
+ metronome.setDepth(
+ m_metronomeResolution->currentItem());
+
+ metronome.setBarVelocity(
+ MidiByte(m_metronomeBarVely->value()));
+
+ metronome.setBeatVelocity(
+ MidiByte(m_metronomeBeatVely->value()));
+
+ metronome.setSubBeatVelocity(
+ MidiByte(m_metronomeSubBeatVely->value()));
+
+ dev->setMetronome(metronome);
+
+ m_doc->getComposition().setPlayMetronome(m_playEnabled->isChecked());
+ m_doc->getComposition().setRecordMetronome(m_recordEnabled->isChecked());
+
+ m_doc->getSequenceManager()->metronomeChanged(inst->getId(), true);
+ m_doc->slotDocumentModified();
+ setModified(false);
+}
+
+void
+ManageMetronomeDialog::slotPreviewPitch(int pitch)
+{
+ RG_DEBUG << "ManageMetronomeDialog::slotPreviewPitch" << endl;
+
+ DeviceList *devices = m_doc->getStudio().getDevices();
+ DeviceListConstIterator it;
+ int count = 0;
+ MidiDevice *dev = 0;
+
+ for (it = devices->begin(); it != devices->end(); it++) {
+ dev = dynamic_cast<MidiDevice*>(*it);
+
+ if (dev && dev->getDirection() == MidiDevice::Play) {
+ if (count == m_metronomeDevice->currentItem())
+ break;
+
+ count++;
+ }
+ }
+
+ if (!dev)
+ return ;
+
+ const MidiMetronome *metronome = dev->getMetronome();
+ if (metronome == 0)
+ return ;
+
+ InstrumentList list = dev->getPresentationInstruments();
+
+ Instrument *inst =
+ list[m_metronomeInstrument->currentItem()];
+
+ if (inst) {
+ RG_DEBUG << "ManageMetronomeDialog::slotPreviewPitch"
+ << " - previewing" << endl;
+ MappedEvent mE(inst->getId(),
+ MappedEvent::MidiNoteOneShot,
+ pitch,
+ MidiMaxValue,
+ RealTime::zeroTime,
+ RealTime(0, 10000000),
+ RealTime::zeroTime);
+
+ StudioControl::sendMappedEvent(mE);
+ }
+}
+
+void
+ManageMetronomeDialog::slotPitchChanged(int pitch)
+{
+ switch (m_metronomePitchSelector->currentItem()) {
+ case 0:
+ m_barPitch = pitch;
+ break;
+ case 1:
+ m_beatPitch = pitch;
+ break;
+ case 2:
+ m_subBeatPitch = pitch;
+ break;
+ }
+ setModified(true);
+}
+
+void
+ManageMetronomeDialog::slotPitchSelectorChanged(int selection)
+{
+ switch (selection) {
+ case 0:
+ m_metronomePitch->slotSetPitch(m_barPitch);
+ break;
+ case 1:
+ m_metronomePitch->slotSetPitch(m_beatPitch);
+ break;
+ case 2:
+ m_metronomePitch->slotSetPitch(m_subBeatPitch);
+ break;
+ }
+}
+
+}
+#include "ManageMetronomeDialog.moc"
diff --git a/src/gui/dialogs/ManageMetronomeDialog.h b/src/gui/dialogs/ManageMetronomeDialog.h
new file mode 100644
index 0000000..08b806c
--- /dev/null
+++ b/src/gui/dialogs/ManageMetronomeDialog.h
@@ -0,0 +1,94 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_MANAGEMETRONOMEDIALOG_H_
+#define _RG_MANAGEMETRONOMEDIALOG_H_
+
+#include "base/MidiProgram.h"
+#include <kdialogbase.h>
+
+
+class QWidget;
+class QSpinBox;
+class QCheckBox;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class PitchChooser;
+class InstrumentParameterBox;
+
+
+class ManageMetronomeDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ ManageMetronomeDialog(QWidget *parent, RosegardenGUIDoc *doc);
+
+ void setModified(bool value);
+
+public slots:
+ void slotOk();
+ void slotApply();
+ void slotSetModified();
+ void slotResolutionChanged(int);
+ void slotPreviewPitch(int);
+ void slotInstrumentChanged(int);
+ void slotPitchSelectorChanged(int);
+ void slotPitchChanged(int);
+ void populate(int dev);
+
+protected:
+
+ //--------------- Data members ---------------------------------
+
+ RosegardenGUIDoc *m_doc;
+
+ KComboBox *m_metronomeDevice;
+ KComboBox *m_metronomeInstrument;
+ KComboBox *m_metronomeResolution;
+ KComboBox *m_metronomePitchSelector;
+ PitchChooser *m_metronomePitch;
+ QSpinBox *m_metronomeBarVely;
+ QSpinBox *m_metronomeBeatVely;
+ QSpinBox *m_metronomeSubBeatVely;
+ InstrumentParameterBox *m_instrumentParameterBox;
+ QCheckBox *m_playEnabled;
+ QCheckBox *m_recordEnabled;
+
+ bool m_modified;
+ MidiByte m_barPitch;
+ MidiByte m_beatPitch;
+ MidiByte m_subBeatPitch;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/MarkerModifyDialog.cpp b/src/gui/dialogs/MarkerModifyDialog.cpp
new file mode 100644
index 0000000..69e658b
--- /dev/null
+++ b/src/gui/dialogs/MarkerModifyDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "MarkerModifyDialog.h"
+#include <qlayout.h>
+
+#include <klocale.h>
+#include "base/Composition.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/widgets/TimeWidget.h"
+#include <kdialogbase.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qspinbox.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include "misc/Strings.h"
+
+
+namespace Rosegarden
+{
+
+MarkerModifyDialog::MarkerModifyDialog(QWidget *parent,
+ Composition *composition,
+ int time,
+ const QString &name,
+ const QString &des):
+ KDialogBase(parent, 0, true, i18n("Edit Marker"), Ok | Cancel)
+{
+ initialise(composition, time, name, des);
+}
+
+MarkerModifyDialog::MarkerModifyDialog(QWidget *parent,
+ Composition *composition,
+ Marker *marker) :
+ KDialogBase(parent, 0, true, i18n("Edit Marker"), Ok | Cancel)
+{
+ initialise(composition, marker->getTime(),
+ strtoqstr(marker->getName()),
+ strtoqstr(marker->getDescription()));
+}
+
+void
+MarkerModifyDialog::initialise(Composition *composition,
+ int time,
+ const QString &name,
+ const QString &des)
+{
+ m_originalTime = time;
+
+ QVBox *vbox = makeVBoxMainWidget();
+
+ m_timeEdit = new TimeWidget(i18n("Marker Time"), vbox, composition,
+ time);
+
+ /*!!!
+
+ layout->addWidget(new QLabel(i18n("Absolute Time:"), frame), 0, 0);
+ m_timeEdit = new QSpinBox(frame);
+ layout->addWidget(m_timeEdit, 0, 1);
+
+ m_timeEdit->setMinValue(INT_MIN);
+ m_timeEdit->setMaxValue(INT_MAX);
+ m_timeEdit->setLineStep(
+ Note(Note::Shortest).getDuration());
+ m_timeEdit->setValue(time);
+ */
+ QGroupBox *groupBox = new QGroupBox
+ (1, Horizontal, i18n("Marker Properties"), vbox);
+
+ QFrame *frame = new QFrame(groupBox);
+
+ QGridLayout *layout = new QGridLayout(frame, 2, 2, 5, 5);
+
+ layout->addWidget(new QLabel(i18n("Text:"), frame), 0, 0);
+ m_nameEdit = new QLineEdit(name, frame);
+ layout->addWidget(m_nameEdit, 0, 1);
+
+ layout->addWidget(new QLabel(i18n("Description:"), frame), 1, 0);
+ m_desEdit = new QLineEdit(des, frame);
+ layout->addWidget(m_desEdit, 1, 1);
+
+ m_nameEdit->selectAll();
+ m_nameEdit->setFocus();
+}
+
+}
+#include "MarkerModifyDialog.moc"
diff --git a/src/gui/dialogs/MarkerModifyDialog.h b/src/gui/dialogs/MarkerModifyDialog.h
new file mode 100644
index 0000000..5b87b14
--- /dev/null
+++ b/src/gui/dialogs/MarkerModifyDialog.h
@@ -0,0 +1,84 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_MARKERMODIFYDIALOG_H_
+#define _RG_MARKERMODIFYDIALOG_H_
+
+#include <kdialogbase.h>
+#include <qstring.h>
+#include <qlineedit.h>
+
+#include "base/Marker.h"
+#include "gui/widgets/TimeWidget.h"
+
+
+namespace Rosegarden
+{
+
+class TimeWidget;
+class RosegardenGUIDoc;
+class Composition;
+
+
+class MarkerModifyDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ MarkerModifyDialog(QWidget *parent,
+ Composition *composition,
+ int time,
+ const QString &name,
+ const QString &des);
+
+ MarkerModifyDialog(QWidget *parent,
+ Composition *composition,
+ Marker *marker);
+
+ QString getName() const { return m_nameEdit->text(); }
+ QString getDescription() const { return m_desEdit->text(); }
+ int getTime() const { return m_timeEdit->getTime(); }
+ int getOriginalTime() const { return m_originalTime; }
+
+protected:
+ void initialise(Composition *composition,
+ int time,
+ const QString &name,
+ const QString &des);
+
+ RosegardenGUIDoc *m_doc;
+
+ TimeWidget *m_timeEdit;
+ QLineEdit *m_nameEdit;
+ QLineEdit *m_desEdit;
+
+ int m_originalTime;
+};
+
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/PasteNotationDialog.cpp b/src/gui/dialogs/PasteNotationDialog.cpp
new file mode 100644
index 0000000..0c725d5
--- /dev/null
+++ b/src/gui/dialogs/PasteNotationDialog.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "PasteNotationDialog.h"
+
+#include <klocale.h>
+#include "commands/edit/PasteEventsCommand.h"
+#include <kdialogbase.h>
+#include <qbuttongroup.h>
+#include <qcheckbox.h>
+#include <qobject.h>
+#include <qradiobutton.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+PasteNotationDialog::PasteNotationDialog(QWidget *parent,
+ PasteEventsCommand::PasteType defaultType) :
+ KDialogBase(parent, 0, true, i18n("Paste"), Ok | Cancel | Help ),
+ m_defaultType(defaultType)
+{
+ setHelp("nv-paste-types");
+
+ QVBox *vbox = makeVBoxMainWidget();
+
+ QButtonGroup *pasteTypeGroup = new QButtonGroup
+ (1, Horizontal, i18n("Paste type"), vbox);
+
+ PasteEventsCommand::PasteTypeMap pasteTypes =
+ PasteEventsCommand::getPasteTypes();
+
+ for (PasteEventsCommand::PasteTypeMap::iterator i = pasteTypes.begin();
+ i != pasteTypes.end(); ++i) {
+
+ QRadioButton *button = new QRadioButton(i->second, pasteTypeGroup);
+ button->setChecked(m_defaultType == i->first);
+ QObject::connect(button, SIGNAL(clicked()),
+ this, SLOT(slotPasteTypeChanged()));
+
+ m_pasteTypeButtons.push_back(button);
+ }
+
+ QButtonGroup *setAsDefaultGroup = new QButtonGroup
+ (1, Horizontal, i18n("Options"), vbox);
+
+ m_setAsDefaultButton = new QCheckBox
+ (i18n("Make this the default paste type"), setAsDefaultGroup);
+ m_setAsDefaultButton->setChecked(true);
+}
+
+PasteEventsCommand::PasteType
+PasteNotationDialog::getPasteType() const
+{
+ for (unsigned int i = 0; i < m_pasteTypeButtons.size(); ++i) {
+ if (m_pasteTypeButtons[i]->isChecked()) {
+ return (PasteEventsCommand::PasteType)i;
+ }
+ }
+
+ return PasteEventsCommand::Restricted;
+}
+
+bool
+PasteNotationDialog::setAsDefault() const
+{
+ return m_setAsDefaultButton->isChecked();
+}
+
+void
+PasteNotationDialog::slotPasteTypeChanged()
+{
+ m_setAsDefaultButton->setChecked(m_defaultType == getPasteType());
+}
+
+}
+#include "PasteNotationDialog.moc"
diff --git a/src/gui/dialogs/PasteNotationDialog.h b/src/gui/dialogs/PasteNotationDialog.h
new file mode 100644
index 0000000..213eaf8
--- /dev/null
+++ b/src/gui/dialogs/PasteNotationDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_PASTENOTATIONDIALOG_H_
+#define _RG_PASTENOTATIONDIALOG_H_
+
+#include "commands/edit/PasteEventsCommand.h"
+#include <kdialogbase.h>
+#include <vector>
+
+
+class QWidget;
+class QRadioButton;
+class QCheckBox;
+
+
+namespace Rosegarden
+{
+
+
+
+class PasteNotationDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ PasteNotationDialog(QWidget *parent,
+ PasteEventsCommand::PasteType defaultType);
+
+ PasteEventsCommand::PasteType getPasteType() const;
+ bool setAsDefault() const;
+
+public slots:
+ void slotPasteTypeChanged();
+
+protected:
+
+ //--------------- Data members ---------------------------------
+
+ std::vector<QRadioButton *> m_pasteTypeButtons;
+ QCheckBox *m_setAsDefaultButton;
+
+ PasteEventsCommand::PasteType m_defaultType;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/PitchDialog.cpp b/src/gui/dialogs/PitchDialog.cpp
new file mode 100644
index 0000000..05fed08
--- /dev/null
+++ b/src/gui/dialogs/PitchDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "PitchDialog.h"
+
+#include <klocale.h>
+#include "gui/widgets/PitchChooser.h"
+#include <kdialogbase.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+PitchDialog::PitchDialog(QWidget *parent, QString title, int defaultPitch) :
+ KDialogBase(parent, 0, true, title, User1 | Ok)
+{
+ QVBox *vbox = makeVBoxMainWidget();
+ m_pitchChooser = new PitchChooser(title, vbox, defaultPitch);
+
+ setButtonText(User1, i18n("Reset"));
+ connect(this, SIGNAL(user1Clicked()),
+ m_pitchChooser, SLOT(slotResetToDefault()));
+}
+
+int
+PitchDialog::getPitch() const
+{
+ return m_pitchChooser->getPitch();
+}
+
+}
+#include "PitchDialog.moc"
diff --git a/src/gui/dialogs/PitchDialog.h b/src/gui/dialogs/PitchDialog.h
new file mode 100644
index 0000000..72e6381
--- /dev/null
+++ b/src/gui/dialogs/PitchDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_PITCHDIALOG_H_
+#define _RG_PITCHDIALOG_H_
+
+#include <kdialogbase.h>
+#include <qstring.h>
+
+
+class QWidget;
+
+
+namespace Rosegarden
+{
+
+class PitchChooser;
+
+
+class PitchDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ PitchDialog(QWidget *parent, QString title, int defaultPitch = 60);
+
+ int getPitch() const;
+
+protected:
+ PitchChooser *m_pitchChooser;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/PitchPickerDialog.cpp b/src/gui/dialogs/PitchPickerDialog.cpp
new file mode 100644
index 0000000..ddd1f23
--- /dev/null
+++ b/src/gui/dialogs/PitchPickerDialog.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "PitchPickerDialog.h"
+#include <klocale.h>
+#include <kdialogbase.h>
+#include <qlayout.h>
+#include <qframe.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+PitchPickerDialog::PitchPickerDialog(QWidget *parent, int initialPitch, QString text) :
+ KDialogBase(parent, 0, true, i18n("Pitch Selector"), Ok | Cancel)
+{
+ QVBox *vBox = makeVBoxMainWidget();
+
+ QFrame *frame = new QFrame(vBox);
+
+ QGridLayout *layout = new QGridLayout(frame, 4, 3, 10, 5);
+
+ m_pitch = new PitchChooser(text, frame, initialPitch);
+ layout->addMultiCellWidget(m_pitch, 0, 0, 0, 2, Qt::AlignHCenter);
+}
+
+PitchPickerDialog::~PitchPickerDialog()
+{
+ // Nothing here...
+}
+
+}
+#include "PitchPickerDialog.moc"
diff --git a/src/gui/dialogs/PitchPickerDialog.h b/src/gui/dialogs/PitchPickerDialog.h
new file mode 100644
index 0000000..ebd0a6d
--- /dev/null
+++ b/src/gui/dialogs/PitchPickerDialog.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_PITCHPICKERDIALOG_H_
+#define _RG_PITCHPICKERDIALOG_H_
+
+#include "gui/widgets/PitchChooser.h"
+#include <kdialogbase.h>
+
+
+class QWidget;
+
+
+namespace Rosegarden
+{
+
+class PitchPickerDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+
+ PitchPickerDialog(QWidget* parent, int initialPitch, QString text);
+ ~PitchPickerDialog();
+
+ int getPitch() { return m_pitch->getPitch(); }
+
+private:
+ PitchChooser* m_pitch;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/QuantizeDialog.cpp b/src/gui/dialogs/QuantizeDialog.cpp
new file mode 100644
index 0000000..b934dd5
--- /dev/null
+++ b/src/gui/dialogs/QuantizeDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "QuantizeDialog.h"
+
+#include <klocale.h>
+#include "base/Quantizer.h"
+#include "gui/widgets/QuantizeParameters.h"
+#include <kdialogbase.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+QuantizeDialog::QuantizeDialog(QWidget *parent, bool inNotation) :
+ KDialogBase(parent, 0, true, i18n("Quantize"), Ok | Cancel | Details | Help)
+{
+ setHelp("quantization");
+
+ QVBox *vbox = makeVBoxMainWidget();
+
+ m_quantizeFrame =
+ new QuantizeParameters
+ (vbox, inNotation ? QuantizeParameters::Notation :
+ QuantizeParameters::Grid,
+ true, false, 0);
+
+ setButtonText(Details, i18n("Advanced"));
+ setDetailsWidget(m_quantizeFrame->getAdvancedWidget());
+ m_quantizeFrame->getAdvancedWidget()->hide();
+
+ m_quantizeFrame->adjustSize();
+ vbox->adjustSize();
+ adjustSize();
+}
+
+Quantizer *
+QuantizeDialog::getQuantizer() const
+{
+ return m_quantizeFrame->getQuantizer();
+}
+
+}
+#include "QuantizeDialog.moc"
diff --git a/src/gui/dialogs/QuantizeDialog.h b/src/gui/dialogs/QuantizeDialog.h
new file mode 100644
index 0000000..a787dd1
--- /dev/null
+++ b/src/gui/dialogs/QuantizeDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_QUANTIZEDIALOG_H_
+#define _RG_QUANTIZEDIALOG_H_
+
+#include <kdialogbase.h>
+
+
+class QWidget;
+
+
+namespace Rosegarden
+{
+
+class Quantizer;
+class QuantizeParameters;
+
+
+class QuantizeDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ QuantizeDialog(QWidget *parent, bool inNotation = false);
+
+ /// Returned quantizer object is on heap -- caller must delete
+ Quantizer *getQuantizer() const;
+
+protected:
+ QuantizeParameters *m_quantizeFrame;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/RescaleDialog.cpp b/src/gui/dialogs/RescaleDialog.cpp
new file mode 100644
index 0000000..d99a6fb
--- /dev/null
+++ b/src/gui/dialogs/RescaleDialog.cpp
@@ -0,0 +1,131 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "RescaleDialog.h"
+
+#include <klocale.h>
+#include "document/ConfigGroups.h"
+#include "base/Composition.h"
+#include "gui/widgets/TimeWidget.h"
+#include <kconfig.h>
+#include <kdialogbase.h>
+#include <qcheckbox.h>
+#include <qgroupbox.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <kapplication.h>
+
+
+namespace Rosegarden
+{
+
+RescaleDialog::RescaleDialog(QWidget *parent,
+ Composition *composition,
+ timeT startTime,
+ timeT originalDuration,
+ bool showCloseGapOption,
+ bool constrainToCompositionDuration) :
+ KDialogBase(parent, 0, true, i18n("Rescale"), User1 | Ok | Cancel)
+{
+ QVBox *vbox = makeVBoxMainWidget();
+
+ m_newDuration = new TimeWidget
+ (i18n("Duration of selection"), vbox, composition,
+ startTime, originalDuration, true,
+ constrainToCompositionDuration);
+
+ if (showCloseGapOption) {
+ QGroupBox *optionBox = new QGroupBox(1, Horizontal, i18n("Options"), vbox);
+ m_closeGap = new QCheckBox(i18n("Adjust times of following events accordingly"),
+ optionBox);
+ KConfig *config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+ m_closeGap->setChecked
+ (config->readBoolEntry("rescaledialogadjusttimes", true));
+ } else {
+ m_closeGap = 0;
+ }
+
+ setButtonText(User1, i18n("Reset"));
+ connect(this, SIGNAL(user1Clicked()),
+ m_newDuration, SLOT(slotResetToDefault()));
+}
+
+timeT
+RescaleDialog::getNewDuration()
+{
+ return m_newDuration->getTime();
+}
+
+bool
+RescaleDialog::shouldCloseGap()
+{
+ if (m_closeGap) {
+ KConfig *config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+ config->writeEntry("rescaledialogadjusttimes", m_closeGap->isChecked());
+ return m_closeGap->isChecked();
+ } else {
+ return true;
+ }
+}
+
+/*
+int
+RescaleDialog::getMultiplier()
+{
+ return m_to;
+}
+
+int
+RescaleDialog::getDivisor()
+{
+ return m_from;
+}
+
+void
+RescaleDialog::slotFromChanged(int i)
+{
+ m_from = i + 1;
+ int perTenThou = m_to * 10000 / m_from;
+ m_percent->setText(QString("%1.%2%").
+ arg(perTenThou / 100).
+ arg(perTenThou % 100));
+}
+
+void
+RescaleDialog::slotToChanged(int i)
+{
+ m_to = i + 1;
+ int perTenThou = m_to * 10000 / m_from;
+ m_percent->setText(QString("%1.%2%").
+ arg(perTenThou / 100).
+ arg(perTenThou % 100));
+}
+*/
+
+}
+#include "RescaleDialog.moc"
diff --git a/src/gui/dialogs/RescaleDialog.h b/src/gui/dialogs/RescaleDialog.h
new file mode 100644
index 0000000..196dd87
--- /dev/null
+++ b/src/gui/dialogs/RescaleDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_RESCALEDIALOG_H_
+#define _RG_RESCALEDIALOG_H_
+
+#include <kdialogbase.h>
+#include "base/Event.h"
+
+
+class QWidget;
+class QCheckBox;
+
+
+namespace Rosegarden
+{
+
+class TimeWidget;
+class Composition;
+
+
+class RescaleDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ RescaleDialog(QWidget *parent,
+ Composition *composition, // for TimeWidget calculations
+ timeT startTime,
+ timeT originalDuration,
+ bool showCloseGapOption,
+ bool constrainToCompositionDuration);
+
+ timeT getNewDuration();
+ bool shouldCloseGap();
+
+protected:
+ TimeWidget *m_newDuration;
+ QCheckBox *m_closeGap;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/ShowSequencerStatusDialog.cpp b/src/gui/dialogs/ShowSequencerStatusDialog.cpp
new file mode 100644
index 0000000..d98933c
--- /dev/null
+++ b/src/gui/dialogs/ShowSequencerStatusDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ShowSequencerStatusDialog.h"
+
+#include <klocale.h>
+#include <kdialogbase.h>
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <qlabel.h>
+#include <qstring.h>
+#include <qtextedit.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include "gui/application/RosegardenApplication.h"
+
+
+namespace Rosegarden
+{
+
+ShowSequencerStatusDialog::ShowSequencerStatusDialog(QWidget *parent) :
+ KDialogBase(parent, 0, true, i18n("Sequencer status"), Close)
+{
+ QVBox *vbox = makeVBoxMainWidget();
+
+ new QLabel(i18n("Sequencer status:"), vbox);
+
+ QString status(i18n("Status not available."));
+
+ QCString replyType;
+ QByteArray replyData;
+ QByteArray data;
+
+ if (!rgapp->sequencerCall("getStatusLog()", replyType, replyData)) {
+ status = i18n("Sequencer is not running or is not responding.");
+ }
+
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ QString result;
+ streamIn >> result;
+ if (!result) {
+ status = i18n("Sequencer is not returning a valid status report.");
+ } else {
+ status = result;
+ }
+
+ QTextEdit *text = new QTextEdit(vbox);
+ text->setTextFormat(Qt::PlainText);
+ text->setReadOnly(true);
+ text->setMinimumWidth(500);
+ text->setMinimumHeight(200);
+
+ text->setText(status);
+}
+
+}
+#include "ShowSequencerStatusDialog.moc"
diff --git a/src/gui/dialogs/ShowSequencerStatusDialog.h b/src/gui/dialogs/ShowSequencerStatusDialog.h
new file mode 100644
index 0000000..ce21ab1
--- /dev/null
+++ b/src/gui/dialogs/ShowSequencerStatusDialog.h
@@ -0,0 +1,54 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_SHOWSEQUENCERSTATUSDIALOG_H_
+#define _RG_SHOWSEQUENCERSTATUSDIALOG_H_
+
+#include <kdialogbase.h>
+
+
+class QWidget;
+
+
+namespace Rosegarden
+{
+
+
+
+class ShowSequencerStatusDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ ShowSequencerStatusDialog(QWidget *parent);
+};
+
+
+// Timer dialog for counting down
+//
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/SimpleEventEditDialog.cpp b/src/gui/dialogs/SimpleEventEditDialog.cpp
new file mode 100644
index 0000000..ca6b76a
--- /dev/null
+++ b/src/gui/dialogs/SimpleEventEditDialog.cpp
@@ -0,0 +1,1061 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "SimpleEventEditDialog.h"
+#include <qlayout.h>
+
+#include "base/BaseProperties.h"
+#include "base/Event.h"
+#include "base/MidiTypes.h"
+#include "base/NotationTypes.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/editors/guitar/Chord.h"
+#include "misc/Strings.h"
+#include "PitchDialog.h"
+#include "TimeDialog.h"
+#include <kcombobox.h>
+#include <kdialogbase.h>
+#include <kfiledialog.h>
+#include <klocale.h>
+#include <qcheckbox.h>
+#include <qdialog.h>
+#include <qfile.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qpushbutton.h>
+#include <qspinbox.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+SimpleEventEditDialog::SimpleEventEditDialog(QWidget *parent,
+ RosegardenGUIDoc *doc,
+ const Event &event,
+ bool inserting) :
+ KDialogBase(parent, 0, true,
+ i18n(inserting ? "Insert Event" : "Edit Event"), Ok | Cancel),
+ m_event(event),
+ m_doc(doc),
+ m_type(event.getType()),
+ m_absoluteTime(event.getAbsoluteTime()),
+ m_duration(event.getDuration()),
+ m_modified(false)
+{
+ QVBox *vbox = makeVBoxMainWidget();
+
+ QGroupBox *groupBox = new QGroupBox
+ (1, Horizontal, i18n("Event Properties"), vbox);
+
+ QFrame *frame = new QFrame(groupBox);
+
+ QGridLayout *layout = new QGridLayout(frame, 7, 3, 5, 5);
+
+ layout->addWidget(new QLabel(i18n("Event type:"), frame), 0, 0);
+
+ if (inserting) {
+
+ m_typeLabel = 0;
+
+ m_typeCombo = new KComboBox(frame);
+ layout->addWidget(m_typeCombo, 0, 1);
+
+ m_typeCombo->insertItem(strtoqstr(Note::EventType));
+ m_typeCombo->insertItem(strtoqstr(Controller::EventType));
+ m_typeCombo->insertItem(strtoqstr(KeyPressure::EventType));
+ m_typeCombo->insertItem(strtoqstr(ChannelPressure::EventType));
+ m_typeCombo->insertItem(strtoqstr(ProgramChange::EventType));
+ m_typeCombo->insertItem(strtoqstr(SystemExclusive::EventType));
+ m_typeCombo->insertItem(strtoqstr(PitchBend::EventType));
+ m_typeCombo->insertItem(strtoqstr(Indication::EventType));
+ m_typeCombo->insertItem(strtoqstr(Text::EventType));
+ m_typeCombo->insertItem(strtoqstr(Note::EventRestType));
+ m_typeCombo->insertItem(strtoqstr(Clef::EventType));
+ m_typeCombo->insertItem(strtoqstr(::Rosegarden::Key::EventType));
+ m_typeCombo->insertItem(strtoqstr(Guitar::Chord::EventType));
+
+ // Connect up the combos
+ //
+ connect(m_typeCombo, SIGNAL(activated(int)),
+ SLOT(slotEventTypeChanged(int)));
+
+ } else {
+
+ m_typeCombo = 0;
+
+ m_typeLabel = new QLabel(frame);
+ layout->addWidget(m_typeLabel, 0, 1);
+ }
+
+ m_timeLabel = new QLabel(i18n("Absolute time:"), frame);
+ layout->addWidget(m_timeLabel, 1, 0);
+ m_timeSpinBox = new QSpinBox(INT_MIN, INT_MAX, Note(Note::Shortest).getDuration(), frame);
+ m_timeEditButton = new QPushButton("edit", frame);
+ layout->addWidget(m_timeSpinBox, 1, 1);
+ layout->addWidget(m_timeEditButton, 1, 2);
+
+ connect(m_timeSpinBox, SIGNAL(valueChanged(int)),
+ SLOT(slotAbsoluteTimeChanged(int)));
+ connect(m_timeEditButton, SIGNAL(released()),
+ SLOT(slotEditAbsoluteTime()));
+
+ m_durationLabel = new QLabel(i18n("Duration:"), frame);
+ layout->addWidget(m_durationLabel, 2, 0);
+ m_durationSpinBox = new QSpinBox(0, INT_MAX, Note(Note::Shortest).getDuration(), frame);
+ m_durationEditButton = new QPushButton("edit", frame);
+ layout->addWidget(m_durationSpinBox, 2, 1);
+ layout->addWidget(m_durationEditButton, 2, 2);
+
+ connect(m_durationSpinBox, SIGNAL(valueChanged(int)),
+ SLOT(slotDurationChanged(int)));
+ connect(m_durationEditButton, SIGNAL(released()),
+ SLOT(slotEditDuration()));
+
+ m_pitchLabel = new QLabel(i18n("Pitch:"), frame);
+ layout->addWidget(m_pitchLabel, 3, 0);
+ m_pitchSpinBox = new QSpinBox(frame);
+ m_pitchEditButton = new QPushButton("edit", frame);
+ layout->addWidget(m_pitchSpinBox, 3, 1);
+ layout->addWidget(m_pitchEditButton, 3, 2);
+
+ connect(m_pitchSpinBox, SIGNAL(valueChanged(int)),
+ SLOT(slotPitchChanged(int)));
+ connect(m_pitchEditButton, SIGNAL(released()),
+ SLOT(slotEditPitch()));
+
+ m_pitchSpinBox->setMinValue(MidiMinValue);
+ m_pitchSpinBox->setMaxValue(MidiMaxValue);
+
+ m_controllerLabel = new QLabel(i18n("Controller name:"), frame);
+ m_controllerLabelValue = new QLabel(i18n("<none>"), frame);
+ m_controllerLabelValue->setAlignment(QLabel::AlignRight);
+
+ layout->addWidget(m_controllerLabel, 4, 0);
+ layout->addWidget(m_controllerLabelValue, 4, 1);
+
+ m_velocityLabel = new QLabel(i18n("Velocity:"), frame);
+ layout->addWidget(m_velocityLabel, 5, 0);
+ m_velocitySpinBox = new QSpinBox(frame);
+ layout->addWidget(m_velocitySpinBox, 5, 1);
+
+ connect(m_velocitySpinBox, SIGNAL(valueChanged(int)),
+ SLOT(slotVelocityChanged(int)));
+
+ m_velocitySpinBox->setMinValue(MidiMinValue);
+ m_velocitySpinBox->setMaxValue(MidiMaxValue);
+
+ m_metaLabel = new QLabel(i18n("Meta string:"), frame);
+ layout->addWidget(m_metaLabel, 6, 0);
+ m_metaEdit = new QLineEdit(frame);
+ layout->addWidget(m_metaEdit, 6, 1);
+
+ m_sysexLoadButton = new QPushButton(i18n("Load data"), frame);
+ layout->addWidget(m_sysexLoadButton, 6, 2);
+ m_sysexSaveButton = new QPushButton(i18n("Save data"), frame);
+ layout->addWidget(m_sysexSaveButton, 4, 2);
+
+ connect(m_metaEdit, SIGNAL(textChanged(const QString &)),
+ SLOT(slotMetaChanged(const QString &)));
+ connect(m_sysexLoadButton, SIGNAL(released()),
+ SLOT(slotSysexLoad()));
+ connect(m_sysexSaveButton, SIGNAL(released()),
+ SLOT(slotSysexSave()));
+
+ m_notationGroupBox = new QGroupBox
+ (1, Horizontal, i18n("Notation Properties"), vbox);
+
+ frame = new QFrame(m_notationGroupBox);
+
+ layout = new QGridLayout(frame, 3, 3, 5, 5);
+
+ m_lockNotationValues = new QCheckBox(i18n("Lock to changes in performed values"), frame);
+ layout->addMultiCellWidget(m_lockNotationValues, 0, 0, 0, 2);
+ m_lockNotationValues->setChecked(true);
+
+ connect(m_lockNotationValues, SIGNAL(released()),
+ SLOT(slotLockNotationChanged()));
+
+ m_notationTimeLabel = new QLabel(i18n("Notation time:"), frame);
+ layout->addWidget(m_notationTimeLabel, 1, 0);
+ m_notationTimeSpinBox = new QSpinBox(INT_MIN, INT_MAX, Note(Note::Shortest).getDuration(), frame);
+ m_notationTimeEditButton = new QPushButton("edit", frame);
+ layout->addWidget(m_notationTimeSpinBox, 1, 1);
+ layout->addWidget(m_notationTimeEditButton, 1, 2);
+
+ connect(m_notationTimeSpinBox, SIGNAL(valueChanged(int)),
+ SLOT(slotNotationAbsoluteTimeChanged(int)));
+ connect(m_notationTimeEditButton, SIGNAL(released()),
+ SLOT(slotEditNotationAbsoluteTime()));
+
+ m_notationDurationLabel = new QLabel(i18n("Notation duration:"), frame);
+ layout->addWidget(m_notationDurationLabel, 2, 0);
+ m_notationDurationSpinBox = new QSpinBox(0, INT_MAX, Note(Note::Shortest).getDuration(), frame);
+ m_notationDurationEditButton = new QPushButton("edit", frame);
+ layout->addWidget(m_notationDurationSpinBox, 2, 1);
+ layout->addWidget(m_notationDurationEditButton, 2, 2);
+
+ connect(m_notationDurationSpinBox, SIGNAL(valueChanged(int)),
+ SLOT(slotNotationDurationChanged(int)));
+ connect(m_notationDurationEditButton, SIGNAL(released()),
+ SLOT(slotEditNotationDuration()));
+
+ setupForEvent();
+}
+
+void
+SimpleEventEditDialog::setupForEvent()
+{
+ using BaseProperties::PITCH;
+ using BaseProperties::VELOCITY;
+
+ if (m_typeCombo) {
+ m_typeCombo->blockSignals(true);
+ }
+ m_timeSpinBox->blockSignals(true);
+ m_notationTimeSpinBox->blockSignals(true);
+ m_durationSpinBox->blockSignals(true);
+ m_notationDurationSpinBox->blockSignals(true);
+ m_pitchSpinBox->blockSignals(true);
+ m_velocitySpinBox->blockSignals(true);
+ m_metaEdit->blockSignals(true);
+
+ m_pitchSpinBox->setMinValue(MidiMinValue);
+ m_pitchSpinBox->setMaxValue(MidiMaxValue);
+
+ // Some common settings
+ //
+ m_durationLabel->setText(i18n("Absolute time:"));
+ m_timeLabel->show();
+ m_timeSpinBox->show();
+ m_timeEditButton->show();
+ m_timeSpinBox->setValue(m_event.getAbsoluteTime());
+
+ m_durationLabel->setText(i18n("Duration:"));
+ m_durationLabel->show();
+ m_durationSpinBox->show();
+ m_durationEditButton->show();
+ m_durationSpinBox->setValue(m_event.getDuration());
+
+ m_notationGroupBox->hide();
+ m_lockNotationValues->setChecked(true);
+
+ if (m_typeLabel)
+ m_typeLabel->setText(strtoqstr(m_event.getType()));
+
+ m_absoluteTime = m_event.getAbsoluteTime();
+ m_notationAbsoluteTime = m_event.getNotationAbsoluteTime();
+ m_duration = m_event.getDuration();
+ m_notationDuration = m_event.getNotationDuration();
+
+ m_sysexLoadButton->hide();
+ m_sysexSaveButton->hide();
+
+ if (m_type == Note::EventType) {
+ m_notationGroupBox->show();
+ m_notationTimeSpinBox->setValue(m_notationAbsoluteTime);
+ m_notationDurationSpinBox->setValue(m_notationDuration);
+
+ m_pitchLabel->show();
+ m_pitchLabel->setText(i18n("Note pitch:"));
+ m_pitchSpinBox->show();
+ m_pitchEditButton->show();
+
+ m_controllerLabel->hide();
+ m_controllerLabelValue->hide();
+
+ m_velocityLabel->show();
+ m_velocityLabel->setText(i18n("Note velocity:"));
+ m_velocitySpinBox->show();
+
+ m_metaLabel->hide();
+ m_metaEdit->hide();
+
+ try {
+ m_pitchSpinBox->setValue(m_event.get<Int>(PITCH));
+ } catch (Event::NoData) {
+ m_pitchSpinBox->setValue(60);
+ }
+
+ try {
+ m_velocitySpinBox->setValue(m_event.get<Int>(VELOCITY));
+ } catch (Event::NoData) {
+ m_velocitySpinBox->setValue(100);
+ }
+
+ if (m_typeCombo)
+ m_typeCombo->setCurrentItem(0);
+
+ } else if (m_type == Controller::EventType) {
+
+ m_durationLabel->hide();
+ m_durationSpinBox->hide();
+ m_durationEditButton->hide();
+
+ m_pitchLabel->show();
+ m_pitchLabel->setText(i18n("Controller number:"));
+ m_pitchSpinBox->show();
+ m_pitchEditButton->hide();
+
+ m_controllerLabel->show();
+ m_controllerLabelValue->show();
+ m_controllerLabel->setText(i18n("Controller name:"));
+
+ m_velocityLabel->show();
+ m_velocityLabel->setText(i18n("Controller value:"));
+ m_velocitySpinBox->show();
+
+ m_metaLabel->hide();
+ m_metaEdit->hide();
+
+ try {
+ m_pitchSpinBox->setValue(m_event.get<Int>
+ (Controller::NUMBER));
+ } catch (Event::NoData) {
+ m_pitchSpinBox->setValue(0);
+ }
+
+ try {
+ m_velocitySpinBox->setValue(m_event.get<Int>
+ (Controller::VALUE));
+ } catch (Event::NoData) {
+ m_velocitySpinBox->setValue(0);
+ }
+
+ if (m_typeCombo)
+ m_typeCombo->setCurrentItem(1);
+
+ } else if (m_type == KeyPressure::EventType) {
+
+ m_durationLabel->hide();
+ m_durationSpinBox->hide();
+ m_durationEditButton->hide();
+
+ m_pitchLabel->show();
+ m_pitchLabel->setText(i18n("Key pitch:"));
+ m_pitchSpinBox->show();
+ m_pitchEditButton->show();
+
+ m_controllerLabel->hide();
+ m_controllerLabelValue->hide();
+
+ m_velocityLabel->show();
+ m_velocityLabel->setText(i18n("Key pressure:"));
+ m_velocitySpinBox->show();
+
+ m_metaLabel->hide();
+ m_metaEdit->hide();
+
+ try {
+ m_pitchSpinBox->setValue(m_event.get<Int>
+ (KeyPressure::PITCH));
+ } catch (Event::NoData) {
+ m_pitchSpinBox->setValue(0);
+ }
+
+ try {
+ m_velocitySpinBox->setValue(m_event.get<Int>
+ (KeyPressure::PRESSURE));
+ } catch (Event::NoData) {
+ m_velocitySpinBox->setValue(0);
+ }
+
+ if (m_typeCombo)
+ m_typeCombo->setCurrentItem(2);
+
+ } else if (m_type == ChannelPressure::EventType) {
+
+ m_durationLabel->hide();
+ m_durationSpinBox->hide();
+ m_durationEditButton->hide();
+
+ m_pitchLabel->show();
+ m_pitchLabel->setText(i18n("Channel pressure:"));
+ m_pitchSpinBox->show();
+ m_pitchEditButton->hide();
+
+ m_controllerLabel->hide();
+ m_controllerLabelValue->hide();
+
+ m_velocityLabel->hide();
+ m_velocitySpinBox->hide();
+
+ m_metaLabel->hide();
+ m_metaEdit->hide();
+
+ try {
+ m_pitchSpinBox->setValue(m_event.get<Int>
+ (ChannelPressure::PRESSURE));
+ } catch (Event::NoData) {
+ m_pitchSpinBox->setValue(0);
+ }
+
+ if (m_typeCombo)
+ m_typeCombo->setCurrentItem(3);
+
+ } else if (m_type == ProgramChange::EventType) {
+
+ m_durationLabel->hide();
+ m_durationSpinBox->hide();
+ m_durationEditButton->hide();
+
+ m_pitchSpinBox->setMinValue(MidiMinValue + 1);
+ m_pitchSpinBox->setMaxValue(MidiMaxValue + 1);
+
+ m_pitchLabel->show();
+ m_pitchLabel->setText(i18n("Program change:"));
+ m_pitchSpinBox->show();
+ m_pitchEditButton->hide();
+
+ m_controllerLabel->hide();
+ m_controllerLabelValue->hide();
+
+ m_velocityLabel->hide();
+ m_velocitySpinBox->hide();
+
+ m_metaLabel->hide();
+ m_metaEdit->hide();
+
+ try {
+ m_pitchSpinBox->setValue(m_event.get<Int>
+ (ProgramChange::PROGRAM) + 1);
+ } catch (Event::NoData) {
+ m_pitchSpinBox->setValue(0);
+ }
+
+ if (m_typeCombo)
+ m_typeCombo->setCurrentItem(4);
+
+ } else if (m_type == SystemExclusive::EventType) {
+
+ m_durationLabel->hide();
+ m_durationSpinBox->hide();
+ m_durationEditButton->hide();
+
+ m_pitchLabel->hide();
+ m_pitchSpinBox->hide();
+ m_pitchEditButton->hide();
+
+ m_controllerLabel->show();
+ m_controllerLabelValue->show();
+
+ m_velocityLabel->hide();
+ m_velocitySpinBox->hide();
+
+ m_metaLabel->show();
+ m_metaEdit->show();
+
+ m_sysexLoadButton->show();
+ m_sysexSaveButton->show();
+
+ m_controllerLabel->setText(i18n("Data length:"));
+ m_metaLabel->setText(i18n("Data:"));
+ try {
+ SystemExclusive sysEx(m_event);
+ m_controllerLabelValue->setText(QString("%1").
+ arg(sysEx.getRawData().length()));
+ m_metaEdit->setText(strtoqstr(sysEx.getHexData()));
+ } catch (...) {
+ m_controllerLabelValue->setText("0");
+ }
+
+ if (m_typeCombo)
+ m_typeCombo->setCurrentItem(5);
+
+ } else if (m_type == PitchBend::EventType) {
+
+ m_durationLabel->hide();
+ m_durationSpinBox->hide();
+ m_durationEditButton->hide();
+
+ m_pitchLabel->show();
+ m_pitchLabel->setText(i18n("Pitchbend MSB:"));
+ m_pitchSpinBox->show();
+ m_pitchEditButton->hide();
+
+ m_controllerLabel->hide();
+ m_controllerLabelValue->hide();
+
+ m_velocityLabel->show();
+ m_velocityLabel->setText(i18n("Pitchbend LSB:"));
+ m_velocitySpinBox->show();
+
+ m_metaLabel->hide();
+ m_metaEdit->hide();
+
+ try {
+ m_pitchSpinBox->setValue(m_event.get<Int>
+ (PitchBend::MSB));
+ } catch (Event::NoData) {
+ m_pitchSpinBox->setValue(0);
+ }
+
+ try {
+ m_velocitySpinBox->setValue(m_event.get<Int>
+ (PitchBend::LSB));
+ } catch (Event::NoData) {
+ m_velocitySpinBox->setValue(0);
+ }
+
+ if (m_typeCombo)
+ m_typeCombo->setCurrentItem(6);
+
+ } else if (m_type == Indication::EventType) {
+
+ m_pitchLabel->hide();
+ m_pitchSpinBox->hide();
+ m_pitchEditButton->hide();
+
+ m_controllerLabel->hide();
+ m_controllerLabelValue->hide();
+
+ m_velocityLabel->hide();
+ m_velocitySpinBox->hide();
+
+ m_metaLabel->show();
+ m_metaEdit->show();
+ m_metaLabel->setText(i18n("Indication:"));
+
+ try {
+ Indication ind(m_event);
+ m_metaEdit->setText(strtoqstr(ind.getIndicationType()));
+ m_durationSpinBox->setValue(ind.getIndicationDuration());
+ } catch (...) {
+ m_metaEdit->setText(i18n("<none>"));
+ }
+
+ if (m_typeCombo)
+ m_typeCombo->setCurrentItem(7);
+
+ } else if (m_type == Text::EventType) {
+
+ m_durationLabel->hide();
+ m_durationSpinBox->hide();
+ m_durationEditButton->hide();
+
+ m_pitchLabel->hide();
+ m_pitchSpinBox->hide();
+ m_pitchEditButton->hide();
+
+ m_controllerLabel->show();
+ m_controllerLabelValue->show();
+
+ m_velocityLabel->hide();
+ m_velocitySpinBox->hide();
+
+ m_metaLabel->show();
+ m_metaEdit->show();
+
+ m_controllerLabel->setText(i18n("Text type:"));
+ m_metaLabel->setText(i18n("Text:"));
+
+ // get the text event
+ try {
+ Text text(m_event);
+ m_controllerLabelValue->setText(strtoqstr(text.getTextType()));
+ m_metaEdit->setText(strtoqstr(text.getText()));
+ } catch (...) {
+ m_controllerLabelValue->setText(i18n("<none>"));
+ m_metaEdit->setText(i18n("<none>"));
+ }
+
+ if (m_typeCombo)
+ m_typeCombo->setCurrentItem(8);
+
+ } else if (m_type == Note::EventRestType) {
+
+ m_pitchLabel->hide();
+ m_pitchSpinBox->hide();
+ m_pitchEditButton->hide();
+
+ m_controllerLabel->hide();
+ m_controllerLabelValue->hide();
+
+ m_velocityLabel->hide();
+ m_velocitySpinBox->hide();
+
+ m_metaLabel->hide();
+ m_metaEdit->hide();
+
+ if (m_typeCombo)
+ m_typeCombo->setCurrentItem(9);
+
+ } else if (m_type == Clef::EventType) {
+
+ m_durationLabel->hide();
+ m_durationSpinBox->hide();
+ m_durationEditButton->hide();
+
+ m_pitchLabel->hide();
+ m_pitchSpinBox->hide();
+ m_pitchEditButton->hide();
+
+ m_controllerLabel->show();
+ m_controllerLabelValue->show();
+
+ m_controllerLabel->setText(i18n("Clef type:"));
+
+ try {
+ Clef clef(m_event);
+ m_controllerLabelValue->setText(strtoqstr(clef.getClefType()));
+ } catch (...) {
+ m_controllerLabelValue->setText(i18n("<none>"));
+ }
+
+ m_velocityLabel->hide();
+ m_velocitySpinBox->hide();
+
+ m_metaLabel->hide();
+ m_metaEdit->hide();
+
+ if (m_typeCombo)
+ m_typeCombo->setCurrentItem(10);
+
+ } else if (m_type == ::Rosegarden::Key::EventType) {
+
+ m_durationLabel->hide();
+ m_durationSpinBox->hide();
+ m_durationEditButton->hide();
+
+ m_pitchLabel->hide();
+ m_pitchSpinBox->hide();
+ m_pitchEditButton->hide();
+
+ m_controllerLabel->show();
+ m_controllerLabelValue->show();
+
+ m_controllerLabel->setText(i18n("Key name:"));
+
+ try {
+ ::Rosegarden::Key key(m_event);
+ m_controllerLabelValue->setText(strtoqstr(key.getName()));
+ } catch (...) {
+ m_controllerLabelValue->setText(i18n("<none>"));
+ }
+
+ m_velocityLabel->hide();
+ m_velocitySpinBox->hide();
+
+ m_metaLabel->hide();
+ m_metaEdit->hide();
+
+ if (m_typeCombo)
+ m_typeCombo->setCurrentItem(11);
+
+ } else if (m_type == Guitar::Chord::EventType) {
+
+ m_durationLabel->hide();
+ m_durationSpinBox->hide();
+ m_durationEditButton->hide();
+
+ m_pitchLabel->hide();
+ m_pitchSpinBox->hide();
+ m_pitchEditButton->hide();
+
+ m_controllerLabel->hide();
+ m_controllerLabelValue->hide();
+
+ m_velocityLabel->hide();
+ m_velocitySpinBox->hide();
+
+ m_metaLabel->hide();
+ m_metaEdit->hide();
+
+ // m_controllerLabel->setText(i18n("Text type:"));
+ // m_metaLabel->setText(i18n("Chord:"));
+
+ // get the fingering event
+ try {
+ Guitar::Chord chord( m_event );
+ } catch (...) {
+ // m_controllerLabelValue->setText(i18n("<none>"));
+ // m_metaEdit->setText(i18n("<none>"));
+ }
+
+ if (m_typeCombo)
+ m_typeCombo->setCurrentItem(12);
+
+ } else {
+
+ m_durationLabel->setText(i18n("Unsupported event type:"));
+ m_durationLabel->show();
+ m_durationSpinBox->hide();
+ m_durationEditButton->hide();
+
+ m_pitchLabel->hide();
+ m_pitchSpinBox->hide();
+ m_pitchEditButton->hide();
+
+ m_controllerLabel->hide();
+ m_controllerLabelValue->show();
+ m_controllerLabelValue->setText(strtoqstr(m_type));
+
+ m_velocityLabel->hide();
+ m_velocitySpinBox->hide();
+
+ m_metaLabel->hide();
+ m_metaEdit->hide();
+
+ if (m_typeCombo)
+ m_typeCombo->setEnabled(false);
+ }
+
+ if (m_typeCombo)
+ m_typeCombo->blockSignals(false);
+ m_timeSpinBox->blockSignals(false);
+ m_notationTimeSpinBox->blockSignals(false);
+ m_durationSpinBox->blockSignals(false);
+ m_notationDurationSpinBox->blockSignals(false);
+ m_pitchSpinBox->blockSignals(false);
+ m_velocitySpinBox->blockSignals(false);
+ m_metaEdit->blockSignals(false);
+
+ slotLockNotationChanged();
+}
+
+Event
+SimpleEventEditDialog::getEvent()
+{
+ bool useSeparateNotationValues =
+ (m_event.getType() == Note::EventType);
+
+ if (m_typeCombo) {
+
+ int subordering = 0;
+ if (m_type == Indication::EventType) {
+ subordering = Indication::EventSubOrdering;
+ } else if (m_type == Clef::EventType) {
+ subordering = Clef::EventSubOrdering;
+ } else if (m_type == ::Rosegarden::Key::EventType) {
+ subordering = ::Rosegarden::Key::EventSubOrdering;
+ } else if (m_type == Text::EventType) {
+ subordering = Text::EventSubOrdering;
+ } else if (m_type == Note::EventRestType) {
+ subordering = Note::EventRestSubOrdering;
+ } else if (m_type == PitchBend::EventType) {
+ subordering = PitchBend::EventSubOrdering;
+ } else if (m_type == Controller::EventType) {
+ subordering = Controller::EventSubOrdering;
+ } else if (m_type == KeyPressure::EventType) {
+ subordering = KeyPressure::EventSubOrdering;
+ } else if (m_type == ChannelPressure::EventType) {
+ subordering = ChannelPressure::EventSubOrdering;
+ } else if (m_type == ProgramChange::EventType) {
+ subordering = ProgramChange::EventSubOrdering;
+ } else if (m_type == SystemExclusive::EventType) {
+ subordering = SystemExclusive::EventSubOrdering;
+ }
+
+ m_event = Event(m_type,
+ m_absoluteTime,
+ m_duration,
+ subordering,
+ (useSeparateNotationValues ?
+ m_notationAbsoluteTime : m_absoluteTime),
+ (useSeparateNotationValues ?
+ m_notationDuration : m_duration));
+
+ // ensure these are set on m_event correctly
+ slotPitchChanged(m_pitchSpinBox->value());
+ slotVelocityChanged(m_velocitySpinBox->value());
+ }
+
+ Event event(m_event,
+ m_absoluteTime,
+ m_duration,
+ m_event.getSubOrdering(),
+ (useSeparateNotationValues ?
+ m_notationAbsoluteTime : m_absoluteTime),
+ (useSeparateNotationValues ?
+ m_notationDuration : m_duration));
+
+ // Values from the pitch and velocity spin boxes should already
+ // have been set on m_event (and thus on event) by slotPitchChanged
+ // and slotVelocityChanged. Absolute time and duration were set in
+ // the event ctor above; that just leaves the meta values.
+
+ if (m_type == Indication::EventType) {
+
+ event.set<String>(Indication::IndicationTypePropertyName,
+ qstrtostr(m_metaEdit->text()));
+
+ } else if (m_type == Text::EventType) {
+
+ event.set<String>(Text::TextTypePropertyName,
+ qstrtostr(m_controllerLabelValue->text()));
+ event.set<String>(Text::TextPropertyName,
+ qstrtostr(m_metaEdit->text()));
+
+ } else if (m_type == Clef::EventType) {
+
+ event.set<String>(Clef::ClefPropertyName,
+ qstrtostr(m_controllerLabelValue->text()));
+
+ } else if (m_type == ::Rosegarden::Key::EventType) {
+
+ event.set<String>(::Rosegarden::Key::KeyPropertyName,
+ qstrtostr(m_controllerLabelValue->text()));
+
+ } else if (m_type == SystemExclusive::EventType) {
+
+ event.set<String>(SystemExclusive::DATABLOCK,
+ qstrtostr(m_metaEdit->text()));
+
+ }
+
+ return event;
+}
+
+void
+SimpleEventEditDialog::slotEventTypeChanged(int value)
+{
+ m_type = qstrtostr(m_typeCombo->text(value));
+ m_modified = true;
+
+ if (m_type != m_event.getType())
+ Event m_event(m_type, m_absoluteTime, m_duration);
+
+ setupForEvent();
+
+ // update whatever pitch and velocity correspond to
+ if (!m_pitchSpinBox->isHidden())
+ slotPitchChanged(m_pitchSpinBox->value());
+ if (!m_velocitySpinBox->isHidden())
+ slotVelocityChanged(m_velocitySpinBox->value());
+}
+
+void
+SimpleEventEditDialog::slotAbsoluteTimeChanged(int value)
+{
+ m_absoluteTime = value;
+
+ if (m_notationGroupBox->isHidden()) {
+ m_notationAbsoluteTime = value;
+ } else if (m_lockNotationValues->isChecked()) {
+ m_notationAbsoluteTime = value;
+ m_notationTimeSpinBox->setValue(value);
+ }
+
+ m_modified = true;
+}
+
+void
+SimpleEventEditDialog::slotNotationAbsoluteTimeChanged(int value)
+{
+ m_notationAbsoluteTime = value;
+ m_modified = true;
+}
+
+void
+SimpleEventEditDialog::slotDurationChanged(int value)
+{
+ m_duration = value;
+
+ if (m_notationGroupBox->isHidden()) {
+ m_notationDuration = value;
+ } else if (m_lockNotationValues->isChecked()) {
+ m_notationDuration = value;
+ m_notationDurationSpinBox->setValue(value);
+ }
+
+ m_modified = true;
+}
+
+void
+SimpleEventEditDialog::slotNotationDurationChanged(int value)
+{
+ m_notationDuration = value;
+ m_modified = true;
+}
+
+void
+SimpleEventEditDialog::slotPitchChanged(int value)
+{
+ m_modified = true;
+
+ if (m_type == Note::EventType) {
+ m_event.set<Int>(BaseProperties::PITCH, value);
+
+ } else if (m_type == Controller::EventType) {
+ m_event.set<Int>(Controller::NUMBER, value);
+
+ } else if (m_type == KeyPressure::EventType) {
+ m_event.set<Int>(KeyPressure::PITCH, value);
+
+ } else if (m_type == ChannelPressure::EventType) {
+ m_event.set<Int>(ChannelPressure::PRESSURE, value);
+
+ } else if (m_type == ProgramChange::EventType) {
+ if (value < 1)
+ value = 1;
+ m_event.set<Int>(ProgramChange::PROGRAM, value - 1);
+
+ } else if (m_type == PitchBend::EventType) {
+ m_event.set<Int>(PitchBend::MSB, value);
+ }
+ //!!!??? sysex?
+}
+
+void
+SimpleEventEditDialog::slotVelocityChanged(int value)
+{
+ m_modified = true;
+
+ if (m_type == Note::EventType) {
+ m_event.set<Int>(BaseProperties::VELOCITY, value);
+
+ } else if (m_type == Controller::EventType) {
+ m_event.set<Int>(Controller::VALUE, value);
+
+ } else if (m_type == KeyPressure::EventType) {
+ m_event.set<Int>(KeyPressure::PRESSURE, value);
+
+ } else if (m_type == PitchBend::EventType) {
+ m_event.set<Int>(PitchBend::LSB, value);
+ }
+}
+
+void
+SimpleEventEditDialog::slotMetaChanged(const QString &)
+{
+ m_modified = true;
+}
+
+void
+SimpleEventEditDialog::slotLockNotationChanged()
+{
+ bool enable = !m_lockNotationValues->isChecked();
+ m_notationTimeSpinBox->setEnabled(enable);
+ m_notationTimeEditButton->setEnabled(enable);
+ m_notationDurationSpinBox->setEnabled(enable);
+ m_notationDurationEditButton->setEnabled(enable);
+}
+
+void
+SimpleEventEditDialog::slotEditAbsoluteTime()
+{
+ TimeDialog dialog(this, i18n("Edit Event Time"),
+ &m_doc->getComposition(),
+ m_timeSpinBox->value(),
+ true);
+ if (dialog.exec() == QDialog::Accepted) {
+ m_timeSpinBox->setValue(dialog.getTime());
+ }
+}
+
+void
+SimpleEventEditDialog::slotEditNotationAbsoluteTime()
+{
+ TimeDialog dialog(this, i18n("Edit Event Notation Time"),
+ &m_doc->getComposition(),
+ m_notationTimeSpinBox->value(),
+ true);
+ if (dialog.exec() == QDialog::Accepted) {
+ m_notationTimeSpinBox->setValue(dialog.getTime());
+ }
+}
+
+void
+SimpleEventEditDialog::slotEditDuration()
+{
+ TimeDialog dialog(this, i18n("Edit Duration"),
+ &m_doc->getComposition(),
+ m_timeSpinBox->value(),
+ m_durationSpinBox->value(),
+ true);
+ if (dialog.exec() == QDialog::Accepted) {
+ m_durationSpinBox->setValue(dialog.getTime());
+ }
+}
+
+void
+SimpleEventEditDialog::slotEditNotationDuration()
+{
+ TimeDialog dialog(this, i18n("Edit Notation Duration"),
+ &m_doc->getComposition(),
+ m_notationTimeSpinBox->value(),
+ m_notationDurationSpinBox->value(),
+ true);
+ if (dialog.exec() == QDialog::Accepted) {
+ m_notationDurationSpinBox->setValue(dialog.getTime());
+ }
+}
+
+void
+SimpleEventEditDialog::slotEditPitch()
+{
+ PitchDialog dialog(this, i18n("Edit Pitch"), m_pitchSpinBox->value());
+ if (dialog.exec() == QDialog::Accepted) {
+ m_pitchSpinBox->setValue(dialog.getPitch());
+ }
+}
+
+void
+SimpleEventEditDialog::slotSysexLoad()
+{
+ QString path = KFileDialog::getOpenFileName(":SYSTEMEXCLUSIVE",
+ i18n("*.syx|System exclusive files (*.syx)"),
+ this, i18n("Load System Exclusive data in File"));
+ if (path.isNull())
+ return ;
+
+ QFile file(path);
+ file.open(IO_ReadOnly);
+ std::string s;
+ unsigned char c;
+ while (((c = (unsigned char)file.getch()) != 0xf0) && (file.status() == IO_Ok))
+ ;
+ while ( file.status() == IO_Ok ) {
+ s += c;
+ if (c == 0xf7 )
+ break;
+ c = (unsigned char)file.getch();
+ }
+ file.close();
+ m_metaEdit->setText(strtoqstr(SystemExclusive::toHex(s)));
+}
+
+void
+SimpleEventEditDialog::slotSysexSave()
+{
+ QString path = KFileDialog::getSaveFileName(":SYSTEMEXCLUSIVE",
+ i18n("*.syx|System exclusive files (*.syx)"),
+ this, i18n("Save System Exclusive data to..."));
+ if (path.isNull())
+ return ;
+
+ QFile file(path);
+ file.open(IO_WriteOnly);
+ SystemExclusive sysEx(m_event);
+ file.writeBlock(sysEx.getRawData().c_str(), sysEx.getRawData().length());
+ file.close();
+}
+
+}
+#include "SimpleEventEditDialog.moc"
diff --git a/src/gui/dialogs/SimpleEventEditDialog.h b/src/gui/dialogs/SimpleEventEditDialog.h
new file mode 100644
index 0000000..60b8441
--- /dev/null
+++ b/src/gui/dialogs/SimpleEventEditDialog.h
@@ -0,0 +1,134 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_SIMPLEEVENTEDITDIALOG_H_
+#define _RG_SIMPLEEVENTEDITDIALOG_H_
+
+#include "base/Event.h"
+#include <string>
+#include <kdialogbase.h>
+
+
+class QWidget;
+class QString;
+class QSpinBox;
+class QPushButton;
+class QLineEdit;
+class QLabel;
+class QGroupBox;
+class QCheckBox;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+
+
+class SimpleEventEditDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ SimpleEventEditDialog(QWidget *parent,
+ RosegardenGUIDoc *doc,
+ const Event &event,
+ bool inserting = false); // inserting or editing
+
+ bool isModified() const { return m_modified; }
+ Event getEvent();
+
+ // Setup the dialog for a new event type
+ void setupForEvent();
+
+public slots:
+ void slotEventTypeChanged(int value);
+ void slotAbsoluteTimeChanged(int value);
+ void slotDurationChanged(int value);
+ void slotNotationAbsoluteTimeChanged(int value);
+ void slotNotationDurationChanged(int value);
+ void slotPitchChanged(int value);
+ void slotVelocityChanged(int value);
+ void slotMetaChanged(const QString &);
+ void slotEditAbsoluteTime();
+ void slotEditNotationAbsoluteTime();
+ void slotEditDuration();
+ void slotEditNotationDuration();
+ void slotLockNotationChanged();
+ void slotEditPitch();
+ void slotSysexLoad();
+ void slotSysexSave();
+
+protected:
+ Event m_event;
+ RosegardenGUIDoc *m_doc;
+
+ std::string m_type;
+ timeT m_absoluteTime;
+ timeT m_notationAbsoluteTime;
+ timeT m_duration;
+ timeT m_notationDuration;
+
+ KComboBox *m_typeCombo;
+ QLabel *m_typeLabel;
+
+ QLabel *m_timeLabel;
+ QLabel *m_durationLabel;
+ QLabel *m_pitchLabel;
+ QLabel *m_velocityLabel;
+ QLabel *m_metaLabel;
+ QLabel *m_controllerLabel;
+ QLabel *m_controllerLabelValue;
+
+ QSpinBox *m_timeSpinBox;
+ QSpinBox *m_durationSpinBox;
+ QSpinBox *m_pitchSpinBox;
+ QSpinBox *m_velocitySpinBox;
+
+ QPushButton *m_timeEditButton;
+ QPushButton *m_durationEditButton;
+ QPushButton *m_pitchEditButton;
+ QPushButton *m_sysexLoadButton;
+ QPushButton *m_sysexSaveButton;
+
+ QGroupBox *m_notationGroupBox;
+ QLabel *m_notationTimeLabel;
+ QLabel *m_notationDurationLabel;
+ QSpinBox *m_notationTimeSpinBox;
+ QSpinBox *m_notationDurationSpinBox;
+ QPushButton *m_notationTimeEditButton;
+ QPushButton *m_notationDurationEditButton;
+ QCheckBox *m_lockNotationValues;
+
+ QLineEdit *m_metaEdit;
+
+ bool m_modified;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/SplitByPitchDialog.cpp b/src/gui/dialogs/SplitByPitchDialog.cpp
new file mode 100644
index 0000000..9b3dffa
--- /dev/null
+++ b/src/gui/dialogs/SplitByPitchDialog.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "SplitByPitchDialog.h"
+
+#include <klocale.h>
+#include "commands/segment/SegmentSplitByPitchCommand.h"
+#include "gui/general/ClefIndex.h"
+#include "gui/widgets/PitchChooser.h"
+#include <kcombobox.h>
+#include <kdialogbase.h>
+#include <qcheckbox.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <qlayout.h>
+
+
+namespace Rosegarden
+{
+
+SplitByPitchDialog::SplitByPitchDialog(QWidget *parent) :
+ KDialogBase(parent, 0, true, i18n("Split by Pitch"), Ok | Cancel)
+{
+ QVBox *vBox = makeVBoxMainWidget();
+
+ QFrame *frame = new QFrame(vBox);
+
+ QGridLayout *layout = new QGridLayout(frame, 4, 3, 10, 5);
+
+ m_pitch = new PitchChooser(i18n("Starting split pitch"), frame, 60);
+ layout->addMultiCellWidget(m_pitch, 0, 0, 0, 2, Qt::AlignHCenter);
+
+ m_range = new QCheckBox(i18n("Range up and down to follow music"), frame);
+ layout->addMultiCellWidget(m_range,
+ 1, 1, // fromRow, toRow
+ 0, 2 // fromCol, toCol
+ );
+
+ m_duplicate = new QCheckBox(i18n("Duplicate non-note events"), frame);
+ layout->addMultiCellWidget(m_duplicate, 2, 2, 0, 2);
+
+ layout->addWidget(new QLabel(i18n("Clef handling:"), frame), 3, 0);
+
+ m_clefs = new KComboBox(frame);
+ m_clefs->insertItem(i18n("Leave clefs alone"));
+ m_clefs->insertItem(i18n("Guess new clefs"));
+ m_clefs->insertItem(i18n("Use treble and bass clefs"));
+ layout->addMultiCellWidget(m_clefs, 3, 3, 1, 2);
+
+ m_range->setChecked(true);
+ m_duplicate->setChecked(true);
+ m_clefs->setCurrentItem(2);
+}
+
+int
+SplitByPitchDialog::getPitch()
+{
+ return m_pitch->getPitch();
+}
+
+bool
+SplitByPitchDialog::getShouldRange()
+{
+ return m_range->isChecked();
+}
+
+bool
+SplitByPitchDialog::getShouldDuplicateNonNoteEvents()
+{
+ return m_duplicate->isChecked();
+}
+
+int
+SplitByPitchDialog::getClefHandling()
+{
+ switch (m_clefs->currentItem()) {
+ case 0:
+ return (int)SegmentSplitByPitchCommand::LeaveClefs;
+ case 1:
+ return (int)SegmentSplitByPitchCommand::RecalculateClefs;
+ default:
+ return (int)SegmentSplitByPitchCommand::UseTrebleAndBassClefs;
+ }
+}
+
+}
+#include "SplitByPitchDialog.moc"
diff --git a/src/gui/dialogs/SplitByPitchDialog.h b/src/gui/dialogs/SplitByPitchDialog.h
new file mode 100644
index 0000000..40a6fb8
--- /dev/null
+++ b/src/gui/dialogs/SplitByPitchDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_SPLITBYPITCHDIALOG_H_
+#define _RG_SPLITBYPITCHDIALOG_H_
+
+#include <kdialogbase.h>
+
+
+class QWidget;
+class QCheckBox;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class PitchChooser;
+
+
+class SplitByPitchDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ SplitByPitchDialog(QWidget *parent);
+
+ int getPitch();
+
+ bool getShouldRange();
+ bool getShouldDuplicateNonNoteEvents();
+ int getClefHandling(); // actually SegmentSplitByPitchCommand::ClefHandling
+
+private:
+ PitchChooser *m_pitch;
+
+ QCheckBox *m_range;
+ QCheckBox *m_duplicate;
+ KComboBox *m_clefs;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/SplitByRecordingSrcDialog.cpp b/src/gui/dialogs/SplitByRecordingSrcDialog.cpp
new file mode 100644
index 0000000..cc61bfa
--- /dev/null
+++ b/src/gui/dialogs/SplitByRecordingSrcDialog.cpp
@@ -0,0 +1,114 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "SplitByRecordingSrcDialog.h"
+#include <qlayout.h>
+
+#include <klocale.h>
+#include "misc/Strings.h"
+#include "base/MidiDevice.h"
+#include "document/RosegardenGUIDoc.h"
+#include <kcombobox.h>
+#include <kdialogbase.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qsizepolicy.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+SplitByRecordingSrcDialog::SplitByRecordingSrcDialog(QWidget *parent, RosegardenGUIDoc *doc) :
+ KDialogBase(parent, 0, true, i18n("Split by Recording Source"), Ok | Cancel )
+{
+ QVBox *vBox = makeVBoxMainWidget();
+
+ QGroupBox *groupBox = new QGroupBox
+ (1, Horizontal, i18n("Recording Source"), vBox);
+ QFrame *frame = new QFrame(groupBox);
+ QGridLayout *layout = new QGridLayout(frame, 2, 2, 10, 5);
+
+ layout->addWidget(new QLabel( i18n("Channel:"), frame ), 0, 0);
+ m_channel = new KComboBox( frame );
+ m_channel->setSizeLimit( 17 );
+ layout->addWidget(m_channel, 0, 1);
+ QSpacerItem *spacer = new QSpacerItem( 1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum );
+ layout->addItem( spacer, 0, 2 );
+
+ m_channel->insertItem(i18n("any"));
+ for (int i = 1; i < 17; ++i) {
+ m_channel->insertItem(QString::number(i));
+ }
+
+ layout->addWidget(new QLabel( i18n("Device:"), frame ), 1, 0);
+ m_device = new KComboBox( frame );
+ layout->addMultiCellWidget( m_device, 1, 1, 1, 2 );
+
+ m_deviceIds.clear();
+ m_deviceIds.push_back( -1);
+ m_device->insertItem(i18n("any"));
+
+ DeviceList *devices = doc->getStudio().getDevices();
+ DeviceListConstIterator it;
+ for (it = devices->begin(); it != devices->end(); it++) {
+ MidiDevice *dev =
+ dynamic_cast<MidiDevice*>(*it);
+ if (dev && dev->getDirection() == MidiDevice::Record) {
+ QString label = QString::number(dev->getId());
+ label += ": ";
+ label += strtoqstr(dev->getName());
+ QString connection = strtoqstr(dev->getConnection());
+ label += " - ";
+ if (connection == "")
+ label += i18n("No connection");
+ else
+ label += connection;
+ m_device->insertItem(label);
+ m_deviceIds.push_back(dev->getId());
+ }
+ }
+
+ m_channel->setCurrentItem(0);
+ m_device->setCurrentItem(0);
+}
+
+int
+SplitByRecordingSrcDialog::getChannel()
+{
+ return m_channel->currentItem() - 1;
+}
+
+int
+SplitByRecordingSrcDialog::getDevice()
+{
+ return m_deviceIds[m_device->currentItem()];
+}
+
+}
+#include "SplitByRecordingSrcDialog.moc"
diff --git a/src/gui/dialogs/SplitByRecordingSrcDialog.h b/src/gui/dialogs/SplitByRecordingSrcDialog.h
new file mode 100644
index 0000000..af982a2
--- /dev/null
+++ b/src/gui/dialogs/SplitByRecordingSrcDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_SPLITBYRECORDINGSRCDIALOG_H_
+#define _RG_SPLITBYRECORDINGSRCDIALOG_H_
+
+#include <kdialogbase.h>
+#include <vector>
+#include "gui/application/RosegardenDCOP.h"
+
+
+class QWidget;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+
+
+class SplitByRecordingSrcDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ SplitByRecordingSrcDialog(QWidget *parent, RosegardenGUIDoc *doc);
+
+ int getChannel();
+ int getDevice();
+
+private:
+ std::vector<int> m_deviceIds;
+ KComboBox *m_channel;
+ KComboBox *m_device;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/TempoDialog.cpp b/src/gui/dialogs/TempoDialog.cpp
new file mode 100644
index 0000000..3896fde
--- /dev/null
+++ b/src/gui/dialogs/TempoDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "TempoDialog.h"
+#include <qlayout.h>
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "base/Composition.h"
+#include "base/NotationTypes.h"
+#include "base/RealTime.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/editors/notation/NotePixmapFactory.h"
+#include "gui/widgets/TimeWidget.h"
+#include "gui/widgets/HSpinBox.h"
+#include <kdialogbase.h>
+#include <qbuttongroup.h>
+#include <qcheckbox.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qradiobutton.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+TempoDialog::TempoDialog(QWidget *parent, RosegardenGUIDoc *doc,
+ bool timeEditable):
+ KDialogBase(parent, 0, true, i18n("Insert Tempo Change"), Ok | Cancel | Help),
+ m_doc(doc),
+ m_tempoTime(0)
+{
+ setHelp("tempo");
+
+ QVBox *vbox = makeVBoxMainWidget();
+ QGroupBox *groupBox = new QGroupBox(1, Horizontal, i18n("Tempo"), vbox);
+
+ QFrame *frame = new QFrame(groupBox);
+ QGridLayout *layout = new QGridLayout(frame, 4, 3, 5, 5);
+
+ // Set tempo
+ layout->addWidget(new QLabel(i18n("New tempo:"), frame), 0, 1);
+ m_tempoValueSpinBox = new HSpinBox(frame, 0, 100000, 0.0, 1000.0, 5);
+ layout->addWidget(m_tempoValueSpinBox, 0, 2);
+
+ connect(m_tempoValueSpinBox, SIGNAL(valueChanged(const QString &)),
+ SLOT(slotTempoChanged(const QString &)));
+
+ m_tempoTap= new QPushButton(i18n("Tap"), frame);
+ layout->addWidget(m_tempoTap, 0, 3);
+ connect(m_tempoTap, SIGNAL(clicked()), SLOT(slotTapClicked()));
+
+
+ m_tempoConstant = new QRadioButton(i18n("Tempo is fixed until the following tempo change"), frame);
+ m_tempoRampToNext = new QRadioButton(i18n("Tempo ramps to the following tempo"), frame);
+ m_tempoRampToTarget = new QRadioButton(i18n("Tempo ramps to:"), frame);
+
+ // m_tempoTargetCheckBox = new QCheckBox(i18n("Ramping to:"), frame);
+ m_tempoTargetSpinBox = new HSpinBox(frame, 0, 100000, 0.0, 1000.0, 5);
+
+ // layout->addMultiCellWidget(m_tempoTargetCheckBox, 1, 1, 0, 1, AlignRight);
+ // layout->addWidget(m_tempoTargetSpinBox, 1, 2);
+
+ layout->addMultiCellWidget(m_tempoConstant, 1, 1, 1, 2);
+ layout->addMultiCellWidget(m_tempoRampToNext, 2, 2, 1, 2);
+ layout->addWidget(m_tempoRampToTarget, 3, 1);
+ layout->addWidget(m_tempoTargetSpinBox, 3, 2);
+
+ // connect(m_tempoTargetCheckBox, SIGNAL(clicked()),
+ // SLOT(slotTargetCheckBoxClicked()));
+ connect(m_tempoConstant, SIGNAL(clicked()),
+ SLOT(slotTempoConstantClicked()));
+ connect(m_tempoRampToNext, SIGNAL(clicked()),
+ SLOT(slotTempoRampToNextClicked()));
+ connect(m_tempoRampToTarget, SIGNAL(clicked()),
+ SLOT(slotTempoRampToTargetClicked()));
+ connect(m_tempoTargetSpinBox, SIGNAL(valueChanged(const QString &)),
+ SLOT(slotTargetChanged(const QString &)));
+
+ m_tempoBeatLabel = new QLabel(frame);
+ layout->addWidget(m_tempoBeatLabel, 0, 4);
+
+ m_tempoBeat = new QLabel(frame);
+ layout->addWidget(m_tempoBeat, 0, 5);
+
+ m_tempoBeatsPerMinute = new QLabel(frame);
+ layout->addWidget(m_tempoBeatsPerMinute, 0, 6);
+
+ m_timeEditor = 0;
+
+ if (timeEditable) {
+ m_timeEditor = new TimeWidget
+ (i18n("Time of tempo change"),
+ vbox, &m_doc->getComposition(), 0, true);
+ populateTempo();
+ return ;
+ }
+
+ // Scope Box
+ QButtonGroup *scopeGroup = new QButtonGroup(1, Horizontal,
+ i18n("Scope"), vbox);
+
+// new QLabel(scopeBox);
+
+ QVBox *scopeBox = new QVBox(scopeGroup);
+
+ scopeBox->setSpacing(5);
+ scopeBox->setMargin(5);
+
+ QHBox *currentBox = new QHBox(scopeBox);
+ new QLabel(i18n("The pointer is currently at "), currentBox);
+ m_tempoTimeLabel = new QLabel(currentBox);
+ m_tempoBarLabel = new QLabel(currentBox);
+ QLabel *spare = new QLabel(currentBox);
+ currentBox->setStretchFactor(spare, 20);
+
+ m_tempoStatusLabel = new QLabel(scopeBox);
+
+// new QLabel(scopeBox);
+
+ QHBox *changeWhereBox = new QHBox(scopeBox);
+ spare = new QLabel(" ", changeWhereBox);
+ QVBox *changeWhereVBox = new QVBox(changeWhereBox);
+ changeWhereBox->setStretchFactor(changeWhereVBox, 20);
+
+ m_tempoChangeHere = new QRadioButton
+ (i18n("Apply this tempo from here onwards"),
+ changeWhereVBox);
+
+ m_tempoChangeBefore = new QRadioButton
+ (i18n("Replace the last tempo change"),
+ changeWhereVBox);
+ m_tempoChangeBeforeAt = new QLabel(changeWhereVBox);
+ m_tempoChangeBeforeAt->hide();
+
+ m_tempoChangeStartOfBar = new QRadioButton
+ (i18n("Apply this tempo from the start of this bar"), changeWhereVBox);
+
+ m_tempoChangeGlobal = new QRadioButton
+ (i18n("Apply this tempo to the whole composition"), changeWhereVBox);
+
+ QHBox *optionHBox = new QHBox(changeWhereVBox);
+ new QLabel(" ", optionHBox);
+ m_defaultBox = new QCheckBox
+ (i18n("Also make this the default tempo"), optionHBox);
+ spare = new QLabel(optionHBox);
+ optionHBox->setStretchFactor(spare, 20);
+
+// new QLabel(scopeBox);
+
+ connect(m_tempoChangeHere, SIGNAL(clicked()),
+ SLOT(slotActionChanged()));
+ connect(m_tempoChangeBefore, SIGNAL(clicked()),
+ SLOT(slotActionChanged()));
+ connect(m_tempoChangeStartOfBar, SIGNAL(clicked()),
+ SLOT(slotActionChanged()));
+ connect(m_tempoChangeGlobal, SIGNAL(clicked()),
+ SLOT(slotActionChanged()));
+
+ m_tempoChangeHere->setChecked(true);
+
+ // disable initially
+ m_defaultBox->setEnabled(false);
+
+ populateTempo();
+}
+
+TempoDialog::~TempoDialog()
+{}
+
+void
+TempoDialog::setTempoPosition(timeT time)
+{
+ m_tempoTime = time;
+ populateTempo();
+}
+
+void
+TempoDialog::populateTempo()
+{
+ Composition &comp = m_doc->getComposition();
+ tempoT tempo = comp.getTempoAtTime(m_tempoTime);
+ std::pair<bool, tempoT> ramping(false, tempo);
+
+ int tempoChangeNo = comp.getTempoChangeNumberAt(m_tempoTime);
+ if (tempoChangeNo >= 0) {
+ tempo = comp.getTempoChange(tempoChangeNo).second;
+ ramping = comp.getTempoRamping(tempoChangeNo, false);
+ }
+
+ m_tempoValueSpinBox->setValue(tempo);
+
+ if (ramping.first) {
+ if (ramping.second) {
+ m_tempoTargetSpinBox->setEnabled(true);
+ m_tempoTargetSpinBox->setValue(ramping.second);
+ m_tempoConstant->setChecked(false);
+ m_tempoRampToNext->setChecked(false);
+ m_tempoRampToTarget->setChecked(true);
+ } else {
+ ramping = comp.getTempoRamping(tempoChangeNo, true);
+ m_tempoTargetSpinBox->setEnabled(false);
+ m_tempoTargetSpinBox->setValue(ramping.second);
+ m_tempoConstant->setChecked(false);
+ m_tempoRampToNext->setChecked(true);
+ m_tempoRampToTarget->setChecked(false);
+ }
+ } else {
+ m_tempoTargetSpinBox->setEnabled(false);
+ m_tempoTargetSpinBox->setValue(tempo);
+ m_tempoConstant->setChecked(true);
+ m_tempoRampToNext->setChecked(false);
+ m_tempoRampToTarget->setChecked(false);
+ }
+
+ // m_tempoTargetCheckBox->setChecked(ramping.first);
+ m_tempoTargetSpinBox->setEnabled(ramping.first);
+
+ updateBeatLabels(comp.getTempoQpm(tempo));
+
+ if (m_timeEditor) {
+ m_timeEditor->slotSetTime(m_tempoTime);
+ return ;
+ }
+
+ RealTime tempoTime = comp.getElapsedRealTime(m_tempoTime);
+ QString milliSeconds;
+ milliSeconds.sprintf("%03d", tempoTime.msec());
+ m_tempoTimeLabel->setText(i18n("%1.%2 s,").arg(tempoTime.sec)
+ .arg(milliSeconds));
+
+ int barNo = comp.getBarNumber(m_tempoTime);
+ if (comp.getBarStart(barNo) == m_tempoTime) {
+ m_tempoBarLabel->setText
+ (i18n("at the start of measure %1.").arg(barNo + 1));
+ m_tempoChangeStartOfBar->setEnabled(false);
+ } else {
+ m_tempoBarLabel->setText(
+ i18n("in the middle of measure %1.").arg(barNo + 1));
+ m_tempoChangeStartOfBar->setEnabled(true);
+ }
+
+ m_tempoChangeBefore->setEnabled(false);
+ m_tempoChangeBeforeAt->setEnabled(false);
+
+ bool havePrecedingTempo = false;
+
+ if (tempoChangeNo >= 0) {
+
+ timeT lastTempoTime = comp.getTempoChange(tempoChangeNo).first;
+ if (lastTempoTime < m_tempoTime) {
+
+ RealTime lastRT = comp.getElapsedRealTime(lastTempoTime);
+ QString lastms;
+ lastms.sprintf("%03d", lastRT.msec());
+ int lastBar = comp.getBarNumber(lastTempoTime);
+ m_tempoChangeBeforeAt->setText
+ (i18n(" (at %1.%2 s, in measure %3)").arg(lastRT.sec)
+ .arg(lastms).arg(lastBar + 1));
+ m_tempoChangeBeforeAt->show();
+
+ m_tempoChangeBefore->setEnabled(true);
+ m_tempoChangeBeforeAt->setEnabled(true);
+
+ havePrecedingTempo = true;
+ }
+ }
+
+ if (comp.getTempoChangeCount() > 0) {
+
+ if (havePrecedingTempo) {
+ m_tempoStatusLabel->hide();
+ } else {
+ m_tempoStatusLabel->setText
+ (i18n("There are no preceding tempo changes."));
+ }
+
+ m_tempoChangeGlobal->setEnabled(true);
+
+ } else {
+
+ m_tempoStatusLabel->setText
+ (i18n("There are no other tempo changes."));
+
+ m_tempoChangeGlobal->setEnabled(false);
+ }
+
+ m_defaultBox->setEnabled(false);
+}
+
+void
+TempoDialog::updateBeatLabels(double qpm)
+{
+ Composition &comp = m_doc->getComposition();
+
+ // If the time signature's beat is not a crotchet, need to show
+ // bpm separately
+
+ timeT beat = comp.getTimeSignatureAt(m_tempoTime).getBeatDuration();
+ if (beat == Note(Note::Crotchet).getDuration()) {
+ m_tempoBeatLabel->setText(i18n(" bpm"));
+ m_tempoBeatLabel->show();
+ m_tempoBeat->hide();
+ m_tempoBeatsPerMinute->hide();
+ } else {
+ // m_tempoBeatLabel->setText(" (");
+ m_tempoBeatLabel->setText(" ");
+
+ timeT error = 0;
+ m_tempoBeat->setPixmap(NotePixmapFactory::toQPixmap
+ (NotePixmapFactory::makeNoteMenuPixmap(beat, error)));
+ m_tempoBeat->setMaximumWidth(25);
+ if (error)
+ m_tempoBeat->setPixmap(NotePixmapFactory::toQPixmap
+ (NotePixmapFactory::makeToolbarPixmap
+ ("menu-no-note")));
+
+ m_tempoBeatsPerMinute->setText
+ // (QString("= %1 )").arg
+ (QString("= %1 ").arg
+ (int(qpm * Note(Note::Crotchet).getDuration() / beat)));
+ m_tempoBeatLabel->show();
+ m_tempoBeat->show();
+ m_tempoBeatsPerMinute->show();
+ }
+}
+
+void
+TempoDialog::slotTempoChanged(const QString &)
+{
+ updateBeatLabels(double(m_tempoValueSpinBox->valuef()));
+}
+
+void
+TempoDialog::slotTargetChanged(const QString &)
+{
+ //...
+}
+
+void
+TempoDialog::slotTempoConstantClicked()
+{
+ m_tempoRampToNext->setChecked(false);
+ m_tempoRampToTarget->setChecked(false);
+ m_tempoTargetSpinBox->setEnabled(false);
+}
+
+void
+TempoDialog::slotTempoRampToNextClicked()
+{
+ m_tempoConstant->setChecked(false);
+ m_tempoRampToTarget->setChecked(false);
+ m_tempoTargetSpinBox->setEnabled(false);
+}
+
+void
+TempoDialog::slotTempoRampToTargetClicked()
+{
+ m_tempoConstant->setChecked(false);
+ m_tempoRampToNext->setChecked(false);
+ m_tempoTargetSpinBox->setEnabled(true);
+}
+
+void
+TempoDialog::slotActionChanged()
+{
+ m_defaultBox->setEnabled(m_tempoChangeGlobal->isChecked());
+}
+
+void
+TempoDialog::slotOk()
+{
+ tempoT tempo = m_tempoValueSpinBox->value();
+ RG_DEBUG << "Tempo is " << tempo << endl;
+
+ tempoT target = -1;
+ if (m_tempoRampToNext->isChecked()) {
+ target = 0;
+ } else if (m_tempoRampToTarget->isChecked()) {
+ target = m_tempoTargetSpinBox->value();
+ }
+
+ RG_DEBUG << "Target is " << target << endl;
+
+ if (m_timeEditor) {
+
+ emit changeTempo(m_timeEditor->getTime(),
+ tempo,
+ target,
+ AddTempo);
+
+ } else {
+
+ TempoDialogAction action = AddTempo;
+
+ if (m_tempoChangeBefore->isChecked()) {
+ action = ReplaceTempo;
+ } else if (m_tempoChangeStartOfBar->isChecked()) {
+ action = AddTempoAtBarStart;
+ } else if (m_tempoChangeGlobal->isChecked()) {
+ action = GlobalTempo;
+ if (m_defaultBox->isChecked()) {
+ action = GlobalTempoWithDefault;
+ }
+ }
+
+ emit changeTempo(m_tempoTime,
+ tempo,
+ target,
+ action);
+ }
+
+ KDialogBase::slotOk();
+}
+
+void
+TempoDialog::slotTapClicked()
+{
+ QTime now = QTime::currentTime();
+
+ if (m_tapMinusOne != QTime()) {
+
+ int ms1 = m_tapMinusOne.msecsTo(now);
+
+ if (ms1 < 10000) {
+
+ int msec = ms1;
+
+ if (m_tapMinusTwo != QTime()) {
+ int ms2 = m_tapMinusTwo.msecsTo(m_tapMinusOne);
+ if (ms2 < 10000) {
+ msec = (ms1 + ms2) / 2;
+ }
+ }
+
+ int bpm = 60000 / msec;
+ m_tempoValueSpinBox->setValue(bpm * 100000);
+ }
+ }
+
+ m_tapMinusTwo = m_tapMinusOne;
+ m_tapMinusOne = now;
+}
+
+
+}
+
+#include "TempoDialog.moc"
diff --git a/src/gui/dialogs/TempoDialog.h b/src/gui/dialogs/TempoDialog.h
new file mode 100644
index 0000000..dd3edf1
--- /dev/null
+++ b/src/gui/dialogs/TempoDialog.h
@@ -0,0 +1,128 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_TEMPODIALOG_H_
+#define _RG_TEMPODIALOG_H_
+
+#include <kdialogbase.h>
+#include "base/Event.h"
+#include "base/Composition.h"
+#include <qpushbutton.h>
+#include <qdatetime.h>
+
+
+class QWidget;
+class QString;
+class QRadioButton;
+class QLabel;
+class QCheckBox;
+
+
+namespace Rosegarden
+{
+
+class TimeWidget;
+class RosegardenGUIDoc;
+class HSpinBox;
+
+
+class TempoDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ enum TempoDialogAction {
+ AddTempo,
+ ReplaceTempo,
+ AddTempoAtBarStart,
+ GlobalTempo,
+ GlobalTempoWithDefault
+ };
+
+ TempoDialog(QWidget *parent, RosegardenGUIDoc *doc,
+ bool timeEditable = false);
+ ~TempoDialog();
+
+ // Set the position at which we're checking the tempo
+ //
+ void setTempoPosition(timeT time);
+
+public slots:
+ virtual void slotOk();
+ void slotActionChanged();
+ void slotTempoChanged(const QString &);
+ void slotTempoConstantClicked();
+ void slotTempoRampToNextClicked();
+ void slotTempoRampToTargetClicked();
+ void slotTargetChanged(const QString &);
+ void slotTapClicked();
+
+signals:
+ // Return results in this signal
+ //
+ void changeTempo(timeT, // tempo change time
+ tempoT, // tempo value
+ tempoT, // target tempo value
+ TempoDialog::TempoDialogAction); // tempo action
+
+protected:
+ void populateTempo();
+ void updateBeatLabels(double newTempo);
+
+ //--------------- Data members ---------------------------------
+
+ RosegardenGUIDoc *m_doc;
+ timeT m_tempoTime;
+ HSpinBox *m_tempoValueSpinBox;
+ QPushButton *m_tempoTap;
+ QTime m_tapMinusTwo;
+ QTime m_tapMinusOne;
+
+ QRadioButton *m_tempoConstant;
+ QRadioButton *m_tempoRampToNext;
+ QRadioButton *m_tempoRampToTarget;
+ HSpinBox *m_tempoTargetSpinBox;
+
+ QLabel *m_tempoBeatLabel;
+ QLabel *m_tempoBeat;
+ QLabel *m_tempoBeatsPerMinute;
+
+ TimeWidget *m_timeEditor;
+
+ QLabel *m_tempoTimeLabel;
+ QLabel *m_tempoBarLabel;
+ QLabel *m_tempoStatusLabel;
+
+ QRadioButton *m_tempoChangeHere;
+ QRadioButton *m_tempoChangeBefore;
+ QLabel *m_tempoChangeBeforeAt;
+ QRadioButton *m_tempoChangeStartOfBar;
+ QRadioButton *m_tempoChangeGlobal;
+ QCheckBox *m_defaultBox;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/TextEventDialog.cpp b/src/gui/dialogs/TextEventDialog.cpp
new file mode 100644
index 0000000..156b5d1
--- /dev/null
+++ b/src/gui/dialogs/TextEventDialog.cpp
@@ -0,0 +1,593 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "TextEventDialog.h"
+#include <kapplication.h>
+
+#include <klocale.h>
+#include "misc/Strings.h"
+#include "document/ConfigGroups.h"
+#include "base/NotationTypes.h"
+#include "gui/editors/notation/NotePixmapFactory.h"
+#include <kcombobox.h>
+#include <kconfig.h>
+#include <kdialogbase.h>
+#include <qbitmap.h>
+#include <qgrid.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qobject.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <qspinbox.h>
+
+namespace Rosegarden
+{
+
+TextEventDialog::TextEventDialog(QWidget *parent,
+ NotePixmapFactory *npf,
+ Text defaultText,
+ int maxLength) :
+ KDialogBase(parent, 0, true, i18n("Text"), Ok | Cancel | Help),
+ m_notePixmapFactory(npf),
+ m_styles(Text::getUserStyles()) /*,
+ //m_directives(Text::getLilyPondDirectives()) */
+{
+ setHelp("nv-text");
+ QVBox *vbox = makeVBoxMainWidget();
+
+ QGroupBox *entryBox = new QGroupBox
+ (1, Horizontal, i18n("Specification"), vbox);
+ QGroupBox *exampleBox = new QGroupBox
+ (1, Horizontal, i18n("Preview"), vbox);
+
+ QGrid *entryGrid = new QGrid(2, QGrid::Horizontal, entryBox);
+
+ new QLabel(i18n("Text: "), entryGrid);
+ m_text = new QLineEdit(entryGrid);
+ m_text->setText(strtoqstr(defaultText.getText()));
+ if (maxLength > 0)
+ m_text->setMaxLength(maxLength);
+
+ // style combo
+ new QLabel(i18n("Style: "), entryGrid);
+ m_typeCombo = new KComboBox(entryGrid);
+
+ for (unsigned int i = 0; i < m_styles.size(); ++i)
+ {
+
+ std::string style = m_styles[i];
+
+ // if the style is in this list, we can i18n it (kludgy):
+
+ if (style == Text::Dynamic) { // index //
+ m_typeCombo->insertItem(i18n("Dynamic")); // 0
+
+ } else if (style == Text::Direction) {
+ m_typeCombo->insertItem(i18n("Direction")); // 1
+
+ } else if (style == Text::LocalDirection) {
+ m_typeCombo->insertItem(i18n("Local Direction")); // 2
+
+ } else if (style == Text::Tempo) {
+ m_typeCombo->insertItem(i18n("Tempo")); // 3
+
+ } else if (style == Text::LocalTempo) {
+ m_typeCombo->insertItem(i18n("Local Tempo")); // 4
+
+ } else if (style == Text::Lyric) {
+ m_typeCombo->insertItem(i18n("Lyric")); // 5
+
+ } else if (style == Text::Chord) {
+ m_typeCombo->insertItem(i18n("Chord")); // 6
+
+ } else if (style == Text::Annotation) {
+ m_typeCombo->insertItem(i18n("Annotation")); // 7
+
+ } else if (style == Text::LilyPondDirective) {
+ m_typeCombo->insertItem(i18n("LilyPond Directive")); // 8
+
+ } else {
+ // not i18n()-able
+
+ std::string styleName;
+ styleName += (char)toupper(style[0]);
+ styleName += style.substr(1);
+
+ int uindex = styleName.find('_');
+ if (uindex > 0) {
+ styleName =
+ styleName.substr(0, uindex) + " " +
+ styleName.substr(uindex + 1);
+ }
+
+ m_typeCombo->insertItem(strtoqstr(styleName));
+ }
+
+ if (style == defaultText.getTextType()) {
+ m_typeCombo->setCurrentItem(m_typeCombo->count() - 1);
+ }
+ }
+
+ m_verseLabel = new QLabel(i18n("Verse: "), entryGrid);
+ m_verseLabel->hide();
+ m_verseSpin = new QSpinBox(entryGrid);
+ m_verseSpin->setMinValue(1);
+ m_verseSpin->setMaxValue(12);
+ m_verseSpin->setLineStep(1);
+ m_verseSpin->setValue(defaultText.getVerse() + 1);
+ m_verseSpin->hide();
+
+ // dynamic shortcuts combo
+ m_dynamicShortcutLabel = new QLabel(i18n("Dynamic: "), entryGrid);
+ m_dynamicShortcutLabel->hide();
+
+ m_dynamicShortcutCombo = new KComboBox(entryGrid);
+ m_dynamicShortcutCombo->insertItem(i18n("ppp"));
+ m_dynamicShortcutCombo->insertItem(i18n("pp"));
+ m_dynamicShortcutCombo->insertItem(i18n("p"));
+ m_dynamicShortcutCombo->insertItem(i18n("mp"));
+ m_dynamicShortcutCombo->insertItem(i18n("mf"));
+ m_dynamicShortcutCombo->insertItem(i18n("f"));
+ m_dynamicShortcutCombo->insertItem(i18n("ff"));
+ m_dynamicShortcutCombo->insertItem(i18n("fff"));
+ m_dynamicShortcutCombo->insertItem(i18n("rfz"));
+ m_dynamicShortcutCombo->insertItem(i18n("sf"));
+ m_dynamicShortcutCombo->hide();
+
+ // direction shortcuts combo
+ m_directionShortcutLabel = new QLabel(i18n("Direction: "), entryGrid);
+ m_directionShortcutLabel->hide();
+
+ m_directionShortcutCombo = new KComboBox(entryGrid);
+ // note, the " ," is a breath mark; the extra spaces are a cheap hack to
+ // try to improve the probability of Rosegarden drawing the blasted thing
+ // where it's supposed to go, without the need to micro-diddle each and
+ // every bliffin' one. (Micro-diddling is not exportable to LilyPond
+ // either, is it? I rather doubt it.)
+ m_directionShortcutCombo->insertItem(i18n(" ,"));
+ m_directionShortcutCombo->insertItem(i18n("D.C. al Fine"));
+ m_directionShortcutCombo->insertItem(i18n("D.S. al Fine"));
+ m_directionShortcutCombo->insertItem(i18n("Fine"));
+ m_directionShortcutCombo->insertItem(i18n("D.S. al Coda"));
+ m_directionShortcutCombo->insertItem(i18n("to Coda"));
+ m_directionShortcutCombo->insertItem(i18n("Coda"));
+ m_directionShortcutCombo->hide();
+
+ // local direction shortcuts combo
+ m_localDirectionShortcutLabel = new QLabel(i18n("Local Direction: "), entryGrid);
+ m_localDirectionShortcutLabel->hide();
+
+ m_localDirectionShortcutCombo = new KComboBox(entryGrid);
+ m_localDirectionShortcutCombo->insertItem(i18n("accel."));
+ m_localDirectionShortcutCombo->insertItem(i18n("ritard."));
+ m_localDirectionShortcutCombo->insertItem(i18n("ralletando"));
+ m_localDirectionShortcutCombo->insertItem(i18n("a tempo"));
+ m_localDirectionShortcutCombo->insertItem(i18n("legato"));
+ m_localDirectionShortcutCombo->insertItem(i18n("simile"));
+ m_localDirectionShortcutCombo->insertItem(i18n("pizz."));
+ m_localDirectionShortcutCombo->insertItem(i18n("arco"));
+ m_localDirectionShortcutCombo->insertItem(i18n("non vib."));
+ m_localDirectionShortcutCombo->insertItem(i18n("sul pont."));
+ m_localDirectionShortcutCombo->insertItem(i18n("sul tasto"));
+ m_localDirectionShortcutCombo->insertItem(i18n("con legno"));
+ m_localDirectionShortcutCombo->insertItem(i18n("sul tasto"));
+ m_localDirectionShortcutCombo->insertItem(i18n("sul G"));
+ m_localDirectionShortcutCombo->insertItem(i18n("ordinario"));
+ m_localDirectionShortcutCombo->insertItem(i18n("Muta in "));
+ m_localDirectionShortcutCombo->insertItem(i18n("volti subito "));
+ m_localDirectionShortcutCombo->insertItem(i18n("soli"));
+ m_localDirectionShortcutCombo->insertItem(i18n("div."));
+ m_localDirectionShortcutCombo->hide();
+
+ // tempo shortcuts combo
+ m_tempoShortcutLabel = new QLabel(i18n("Tempo: "), entryGrid);
+ m_tempoShortcutLabel->hide();
+
+ m_tempoShortcutCombo = new KComboBox(entryGrid);
+ m_tempoShortcutCombo->insertItem(i18n("Grave"));
+ m_tempoShortcutCombo->insertItem(i18n("Adagio"));
+ m_tempoShortcutCombo->insertItem(i18n("Largo"));
+ m_tempoShortcutCombo->insertItem(i18n("Lento"));
+ m_tempoShortcutCombo->insertItem(i18n("Andante"));
+ m_tempoShortcutCombo->insertItem(i18n("Moderato"));
+ m_tempoShortcutCombo->insertItem(i18n("Allegretto"));
+ m_tempoShortcutCombo->insertItem(i18n("Allegro"));
+ m_tempoShortcutCombo->insertItem(i18n("Vivace"));
+ m_tempoShortcutCombo->insertItem(i18n("Presto"));
+ m_tempoShortcutCombo->insertItem(i18n("Prestissimo"));
+ m_tempoShortcutCombo->insertItem(i18n("Maestoso"));
+ m_tempoShortcutCombo->insertItem(i18n("Sostenuto"));
+ m_tempoShortcutCombo->insertItem(i18n("Tempo Primo"));
+ m_tempoShortcutCombo->hide();
+
+ // local tempo shortcuts combo (duplicates the non-local version, because
+ // nobody is actually sure what is supposed to distinguish Tempo from
+ // Local Tempo, or what this text style is supposed to be good for in the
+ // way of standard notation)
+ m_localTempoShortcutLabel = new QLabel(i18n("Local Tempo: "), entryGrid);
+ m_localTempoShortcutLabel->hide();
+
+ m_localTempoShortcutCombo = new KComboBox(entryGrid);
+ m_localTempoShortcutCombo->insertItem(i18n("Grave"));
+ m_localTempoShortcutCombo->insertItem(i18n("Adagio"));
+ m_localTempoShortcutCombo->insertItem(i18n("Largo"));
+ m_localTempoShortcutCombo->insertItem(i18n("Lento"));
+ m_localTempoShortcutCombo->insertItem(i18n("Andante"));
+ m_localTempoShortcutCombo->insertItem(i18n("Moderato"));
+ m_localTempoShortcutCombo->insertItem(i18n("Allegretto"));
+ m_localTempoShortcutCombo->insertItem(i18n("Allegro"));
+ m_localTempoShortcutCombo->insertItem(i18n("Vivace"));
+ m_localTempoShortcutCombo->insertItem(i18n("Presto"));
+ m_localTempoShortcutCombo->insertItem(i18n("Prestissimo"));
+ m_localTempoShortcutCombo->insertItem(i18n("Maestoso"));
+ m_localTempoShortcutCombo->insertItem(i18n("Sostenuto"));
+ m_localTempoShortcutCombo->insertItem(i18n("Tempo Primo"));
+ m_localTempoShortcutCombo->hide();
+
+ // LilyPond directive combo
+ m_directiveLabel = new QLabel(i18n("Directive: "), entryGrid);
+ m_directiveLabel->hide();
+
+ m_lilyPondDirectiveCombo = new KComboBox(entryGrid);
+ m_lilyPondDirectiveCombo->hide();
+
+ // not i18nable, because the directive exporter currently depends on the
+ // textual contents of these strings, not some more abstract associated
+ // type label
+ m_lilyPondDirectiveCombo->insertItem(Text::Segno);
+ m_lilyPondDirectiveCombo->insertItem(Text::Coda);
+ m_lilyPondDirectiveCombo->insertItem(Text::Alternate1);
+ m_lilyPondDirectiveCombo->insertItem(Text::Alternate2);
+ m_lilyPondDirectiveCombo->insertItem(Text::BarDouble);
+ m_lilyPondDirectiveCombo->insertItem(Text::BarEnd);
+ m_lilyPondDirectiveCombo->insertItem(Text::BarDot);
+ m_lilyPondDirectiveCombo->insertItem(Text::Gliss);
+ m_lilyPondDirectiveCombo->insertItem(Text::Arpeggio);
+ // m_lilyPondDirectiveCombo->insertItem(Text::ArpeggioUp);
+ // m_lilyPondDirectiveCombo->insertItem(Text::ArpeggioDn);
+ m_lilyPondDirectiveCombo->insertItem(Text::Tiny);
+ m_lilyPondDirectiveCombo->insertItem(Text::Small);
+ m_lilyPondDirectiveCombo->insertItem(Text::NormalSize);
+
+ QVBox *exampleVBox = new QVBox(exampleBox);
+
+ int ls = m_notePixmapFactory->getLineSpacing();
+
+ int mapWidth = 200;
+ QPixmap map(mapWidth, ls * 5 + 1);
+ QBitmap mask(mapWidth, ls * 5 + 1);
+
+ map.fill();
+ mask.fill(Qt::color0);
+
+ QPainter p, pm;
+
+ p.begin(&map);
+ pm.begin(&mask);
+
+ p.setPen(Qt::black);
+ pm.setPen(Qt::white);
+
+ for (int i = 0; i < 5; ++i)
+ {
+ p.drawLine(0, ls * i, mapWidth - 1, ls * i);
+ pm.drawLine(0, ls * i, mapWidth - 1, ls * i);
+ }
+
+ p.end();
+ pm.end();
+
+ map.setMask(mask);
+
+ m_staffAboveLabel = new QLabel("staff", exampleVBox);
+ m_staffAboveLabel->setPixmap(map);
+
+ m_textExampleLabel = new QLabel(i18n("Example"), exampleVBox);
+
+ m_staffBelowLabel = new QLabel("staff", exampleVBox);
+ m_staffBelowLabel->setPixmap(map);
+
+ // restore last setting for shortcut combos
+ KConfig *config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+
+ m_dynamicShortcutCombo->setCurrentItem(config->readNumEntry("dynamic_shortcut", 0));
+ m_directionShortcutCombo->setCurrentItem(config->readNumEntry("direction_shortcut", 0));
+ m_localDirectionShortcutCombo->setCurrentItem(config->readNumEntry("local_direction_shortcut", 0));
+ m_tempoShortcutCombo->setCurrentItem(config->readNumEntry("tempo_shortcut", 0));
+ m_localTempoShortcutCombo->setCurrentItem(config->readNumEntry("local_tempo_shortcut", 0));
+ m_lilyPondDirectiveCombo->setCurrentItem(config->readNumEntry("lilyPond_directive_combo", 0));
+
+ m_prevChord = config->readEntry("previous_chord", "");
+ m_prevLyric = config->readEntry("previous_lyric", "");
+ m_prevAnnotation = config->readEntry("previous_annotation", "");
+
+ QObject::connect(m_text, SIGNAL(textChanged(const QString &)),
+ this, SLOT(slotTextChanged(const QString &)));
+ QObject::connect(m_typeCombo, SIGNAL(activated(const QString &)),
+ this, SLOT(slotTypeChanged(const QString &)));
+ QObject::connect(this, SIGNAL(okClicked()), this, SLOT(slotOK()));
+ QObject::connect(m_dynamicShortcutCombo, SIGNAL(activated(const QString &)),
+ this, SLOT(slotDynamicShortcutChanged(const QString &)));
+ QObject::connect(m_directionShortcutCombo, SIGNAL(activated(const QString &)),
+ this, SLOT(slotDirectionShortcutChanged(const QString &)));
+ QObject::connect(m_localDirectionShortcutCombo, SIGNAL(activated(const QString &)),
+ this, SLOT(slotLocalDirectionShortcutChanged(const QString &)));
+ QObject::connect(m_tempoShortcutCombo, SIGNAL(activated(const QString &)),
+ this, SLOT(slotTempoShortcutChanged(const QString &)));
+ QObject::connect(m_localTempoShortcutCombo, SIGNAL(activated(const QString &)),
+ this, SLOT(slotLocalTempoShortcutChanged(const QString &)));
+ QObject::connect(m_lilyPondDirectiveCombo, SIGNAL(activated(const QString &)),
+ this, SLOT(slotLilyPondDirectiveChanged(const QString &)));
+
+ m_text->setFocus();
+ slotTypeChanged(strtoqstr(getTextType()));
+
+ // a hacky little fix for #1512143, to restore the capability to edit
+ // existing annotations and other whatnots
+ //!!! tacking another one of these on the bottom strikes me as lame in the
+ // extreme, but it works, and it costs little, and other solutions I can
+ // imagine would cost so much more.
+ m_text->setText(strtoqstr(defaultText.getText()));
+}
+
+Text
+TextEventDialog::getText() const
+{
+ Text text(getTextString(), getTextType());
+ text.setVerse(m_verseSpin->value() - 1);
+ return text;
+}
+
+std::string
+TextEventDialog::getTextType() const
+{
+ return m_styles[m_typeCombo->currentItem()];
+}
+
+std::string
+TextEventDialog::getTextString() const
+{
+ return std::string(qstrtostr(m_text->text()));
+}
+
+void
+TextEventDialog::slotTextChanged(const QString &qtext)
+{
+ std::string type(getTextType());
+
+ QString qtrunc(qtext);
+ if (qtrunc.length() > 20)
+ qtrunc = qtrunc.left(20) + "...";
+ std::string text(qstrtostr(qtrunc));
+ if (text == "")
+ text = "Sample";
+
+ Text rtext(text, type);
+ m_textExampleLabel->setPixmap
+ (NotePixmapFactory::toQPixmap(m_notePixmapFactory->makeTextPixmap(rtext)));
+}
+
+void
+TextEventDialog::slotTypeChanged(const QString &)
+{
+ std::string type(getTextType());
+
+ QString qtrunc(m_text->text());
+ if (qtrunc.length() > 20)
+ qtrunc = qtrunc.left(20) + "...";
+ std::string text(qstrtostr(qtrunc));
+ if (text == "")
+ text = "Sample";
+
+ Text rtext(text, type);
+ m_textExampleLabel->setPixmap
+ (NotePixmapFactory::toQPixmap(m_notePixmapFactory->makeTextPixmap(rtext)));
+
+ //
+ // swap widgets in and out, depending on the current text type
+ //
+ if (type == Text::Dynamic) {
+ m_dynamicShortcutLabel->show();
+ m_dynamicShortcutCombo->show();
+ slotDynamicShortcutChanged(text);
+ } else {
+ m_dynamicShortcutLabel->hide();
+ m_dynamicShortcutCombo->hide();
+ }
+
+ if (type == Text::Direction) {
+ m_directionShortcutLabel->show();
+ m_directionShortcutCombo->show();
+ slotDirectionShortcutChanged(text);
+ } else {
+ m_directionShortcutLabel->hide();
+ m_directionShortcutCombo->hide();
+ }
+
+ if (type == Text::LocalDirection) {
+ m_localDirectionShortcutLabel->show();
+ m_localDirectionShortcutCombo->show();
+ slotLocalDirectionShortcutChanged(text);
+ } else {
+ m_localDirectionShortcutLabel->hide();
+ m_localDirectionShortcutCombo->hide();
+ }
+
+ if (type == Text::Tempo) {
+ m_tempoShortcutLabel->show();
+ m_tempoShortcutCombo->show();
+ slotTempoShortcutChanged(text);
+ } else {
+ m_tempoShortcutLabel->hide();
+ m_tempoShortcutCombo->hide();
+ }
+
+ if (type == Text::LocalTempo) {
+ m_localTempoShortcutLabel->show();
+ m_localTempoShortcutCombo->show();
+ slotLocalTempoShortcutChanged(text);
+ } else {
+ m_localTempoShortcutLabel->hide();
+ m_localTempoShortcutCombo->hide();
+ }
+
+ // restore previous text of appropriate type
+ if (type == Text::Lyric)
+ m_text->setText(m_prevLyric);
+ else if (type == Text::Chord)
+ m_text->setText(m_prevChord);
+ else if (type == Text::Annotation)
+ m_text->setText(m_prevAnnotation);
+
+ //
+ // LilyPond directives only taking temporary residence here; will move out
+ // into some new class eventually
+ //
+ if (type == Text::LilyPondDirective) {
+ m_lilyPondDirectiveCombo->show();
+ m_directiveLabel->show();
+ m_staffAboveLabel->hide();
+ m_staffBelowLabel->show();
+ m_text->setReadOnly(true);
+ m_text->setEnabled(false);
+ slotLilyPondDirectiveChanged(text);
+ } else {
+ m_lilyPondDirectiveCombo->hide();
+ m_directiveLabel->hide();
+ m_text->setReadOnly(false);
+ m_text->setEnabled(true);
+
+ if (type == Text::Dynamic ||
+ type == Text::LocalDirection ||
+ type == Text::UnspecifiedType ||
+ type == Text::Lyric ||
+ type == Text::Annotation) {
+
+ m_staffAboveLabel->show();
+ m_staffBelowLabel->hide();
+
+ } else {
+ m_staffAboveLabel->hide();
+ m_staffBelowLabel->show();
+
+ }
+
+ if (type == Text::Lyric) {
+ m_verseLabel->show();
+ m_verseSpin->show();
+ }
+ }
+}
+
+void
+TextEventDialog::slotOK()
+{
+ // store last setting for shortcut combos
+ KConfig *config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+
+ config->writeEntry("dynamic_shortcut", m_dynamicShortcutCombo->currentItem());
+ config->writeEntry("direction_shortcut", m_directionShortcutCombo->currentItem());
+ config->writeEntry("local_direction_shortcut", m_localDirectionShortcutCombo->currentItem());
+ config->writeEntry("tempo_shortcut", m_tempoShortcutCombo->currentItem());
+ config->writeEntry("local_tempo_shortcut", m_localTempoShortcutCombo->currentItem());
+ // temporary home:
+ config->writeEntry("lilyPond_directive_combo", m_lilyPondDirectiveCombo->currentItem());
+
+ // store last chord, lyric, annotation, depending on what's currently in
+ // the text entry widget
+ int index = m_typeCombo->currentItem();
+ if (index == 5)
+ config->writeEntry("previous_chord", m_text->text());
+ else if (index == 6)
+ config->writeEntry("previous_lyric", m_text->text());
+ else if (index == 7)
+ config->writeEntry("previous_annotation", m_text->text());
+}
+
+void
+TextEventDialog::slotDynamicShortcutChanged(const QString &text)
+{
+ if (text == "" || text == "Sample") {
+ m_text->setText(strtoqstr(m_dynamicShortcutCombo->currentText()));
+ } else {
+ m_text->setText(text);
+ }
+}
+
+void
+TextEventDialog::slotDirectionShortcutChanged(const QString &text)
+{
+ if (text == "" || text == "Sample") {
+ m_text->setText(strtoqstr(m_directionShortcutCombo->currentText()));
+ } else {
+ m_text->setText(text);
+ }
+}
+
+void
+TextEventDialog::slotLocalDirectionShortcutChanged(const QString &text)
+{
+ if (text == "" || text == "Sample") {
+ m_text->setText(strtoqstr(m_localDirectionShortcutCombo->currentText()));
+ } else {
+ m_text->setText(text);
+ }
+}
+
+void
+TextEventDialog::slotTempoShortcutChanged(const QString &text)
+{
+ if (text == "" || text == "Sample") {
+ m_text->setText(strtoqstr(m_tempoShortcutCombo->currentText()));
+ } else {
+ m_text->setText(text);
+ }
+}
+
+void
+TextEventDialog::slotLocalTempoShortcutChanged(const QString &text)
+{
+ if (text == "" || text == "Sample") {
+ m_text->setText(strtoqstr(m_localTempoShortcutCombo->currentText()));
+ } else {
+ m_text->setText(text);
+ }
+}
+
+void
+TextEventDialog::slotLilyPondDirectiveChanged(const QString &)
+{
+ m_text->setText(strtoqstr(m_lilyPondDirectiveCombo->currentText()));
+}
+
+}
+#include "TextEventDialog.moc"
diff --git a/src/gui/dialogs/TextEventDialog.h b/src/gui/dialogs/TextEventDialog.h
new file mode 100644
index 0000000..0f389b0
--- /dev/null
+++ b/src/gui/dialogs/TextEventDialog.h
@@ -0,0 +1,129 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_TEXTEVENTDIALOG_H_
+#define _RG_TEXTEVENTDIALOG_H_
+
+#include "base/NotationTypes.h"
+#include <string>
+#include <kdialogbase.h>
+#include <qstring.h>
+#include <vector>
+
+
+class QWidget;
+class QLineEdit;
+class QLabel;
+class KComboBox;
+class QSpinBox;
+
+namespace Rosegarden
+{
+
+class NotePixmapFactory;
+
+
+class TextEventDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ TextEventDialog(QWidget *parent,
+ NotePixmapFactory *npf,
+ Text defaultText,
+ int maxLength = -1); // for Qt default
+
+ Text getText() const;
+
+public slots:
+ void slotTextChanged(const QString &);
+ void slotTypeChanged(const QString &);
+
+ /*
+ * Save previous state of assorted widgets for restoration in the next
+ * instance
+ */
+ void slotOK();
+
+ // convenience canned texts
+ void slotDynamicShortcutChanged(const QString &);
+ void slotDirectionShortcutChanged(const QString &);
+ void slotLocalDirectionShortcutChanged(const QString &);
+ void slotTempoShortcutChanged(const QString &);
+ void slotLocalTempoShortcutChanged(const QString &);
+
+ //
+ // special LilyPond directives, initial phase, as cheap text events; will
+ // eventually move out of Text, and out of this dialog into
+ // some other less cheesy interface
+ //
+ void slotLilyPondDirectiveChanged(const QString &);
+
+protected:
+
+ std::string getTextType() const;
+ std::string getTextString() const;
+
+ //--------------- Data members ---------------------------------
+
+ QLineEdit *m_text;
+ KComboBox *m_typeCombo;
+ QSpinBox *m_verseSpin;
+ KComboBox *m_dynamicShortcutCombo;
+ KComboBox *m_directionShortcutCombo;
+ KComboBox *m_localDirectionShortcutCombo;
+ KComboBox *m_tempoShortcutCombo;
+ KComboBox *m_localTempoShortcutCombo;
+ // temporary home:
+ KComboBox *m_lilyPondDirectiveCombo;
+
+
+ QLabel *m_staffAboveLabel;
+ QLabel *m_textExampleLabel;
+ QLabel *m_staffBelowLabel;
+ QLabel *m_dynamicShortcutLabel;
+ QLabel *m_directionShortcutLabel;
+ QLabel *m_localDirectionShortcutLabel;
+ QLabel *m_tempoShortcutLabel;
+ QLabel *m_localTempoShortcutLabel;
+ QLabel *m_verseLabel;
+ // temporary home:
+ QLabel *m_directiveLabel;
+
+ QString m_prevChord;
+ QString m_prevLyric;
+ QString m_prevAnnotation;
+
+ NotePixmapFactory *m_notePixmapFactory;
+ std::vector<std::string> m_styles;
+// std::vector<std::string> m_directives;
+
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/TimeDialog.cpp b/src/gui/dialogs/TimeDialog.cpp
new file mode 100644
index 0000000..40d1da2
--- /dev/null
+++ b/src/gui/dialogs/TimeDialog.cpp
@@ -0,0 +1,80 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "TimeDialog.h"
+
+#include <klocale.h>
+#include "base/Composition.h"
+#include "gui/widgets/TimeWidget.h"
+#include <kdialogbase.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+TimeDialog::TimeDialog(QWidget *parent, QString title,
+ Composition *composition,
+ timeT defaultTime,
+ bool constrainToCompositionDuration) :
+ KDialogBase(parent, 0, true, title, User1 | Ok | Cancel)
+{
+ QVBox *vbox = makeVBoxMainWidget();
+ m_timeWidget = new TimeWidget
+ (title, vbox, composition, defaultTime, true,
+ constrainToCompositionDuration);
+
+ setButtonText(User1, i18n("Reset"));
+ connect(this, SIGNAL(user1Clicked()),
+ m_timeWidget, SLOT(slotResetToDefault()));
+}
+
+TimeDialog::TimeDialog(QWidget *parent, QString title,
+ Composition *composition,
+ timeT startTime,
+ timeT defaultTime,
+ bool constrainToCompositionDuration) :
+ KDialogBase(parent, 0, true, title, User1 | Ok | Cancel)
+{
+ QVBox *vbox = makeVBoxMainWidget();
+ m_timeWidget = new TimeWidget
+ (title, vbox, composition, startTime, defaultTime, true,
+ constrainToCompositionDuration);
+
+ setButtonText(User1, i18n("Reset"));
+ connect(this, SIGNAL(user1Clicked()),
+ m_timeWidget, SLOT(slotResetToDefault()));
+}
+
+timeT
+TimeDialog::getTime() const
+{
+ return m_timeWidget->getTime();
+}
+
+}
+#include "TimeDialog.moc"
diff --git a/src/gui/dialogs/TimeDialog.h b/src/gui/dialogs/TimeDialog.h
new file mode 100644
index 0000000..e12a007
--- /dev/null
+++ b/src/gui/dialogs/TimeDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_TIMEDIALOG_H_
+#define _RG_TIMEDIALOG_H_
+
+#include <kdialogbase.h>
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QWidget;
+
+
+namespace Rosegarden
+{
+
+class TimeWidget;
+class Composition;
+
+
+class TimeDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ /// for absolute times
+ TimeDialog(QWidget *parent, QString title, Composition *composition,
+ timeT defaultTime, bool constrainToCompositionDuration);
+
+ /// for durations
+ TimeDialog(QWidget *parent, QString title, Composition *composition,
+ timeT startTime, timeT defaultDuration,
+ bool constrainToCompositionDuration);
+
+ timeT getTime() const;
+
+protected:
+ TimeWidget *m_timeWidget;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/TimeSignatureDialog.cpp b/src/gui/dialogs/TimeSignatureDialog.cpp
new file mode 100644
index 0000000..082f123
--- /dev/null
+++ b/src/gui/dialogs/TimeSignatureDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "TimeSignatureDialog.h"
+#include <kapplication.h>
+
+#include <klocale.h>
+#include "document/ConfigGroups.h"
+#include "base/Composition.h"
+#include "base/NotationTypes.h"
+#include "gui/widgets/TimeWidget.h"
+#include "gui/widgets/BigArrowButton.h"
+#include <kconfig.h>
+#include <kdialogbase.h>
+#include <qbuttongroup.h>
+#include <qcheckbox.h>
+#include <qfont.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qobject.h>
+#include <qradiobutton.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+TimeSignatureDialog::TimeSignatureDialog(QWidget *parent,
+ Composition *composition,
+ timeT insertionTime,
+ TimeSignature sig,
+ bool timeEditable,
+ QString explanatoryText) :
+ KDialogBase(parent, 0, true, i18n("Time Signature"), Ok | Cancel | Help),
+ m_composition(composition),
+ m_timeSignature(sig),
+ m_time(insertionTime),
+ m_numLabel(0),
+ m_denomLabel(0),
+ m_explanatoryLabel(0),
+ m_commonTimeButton(0),
+ m_hideSignatureButton(0),
+ m_normalizeRestsButton(0),
+ m_asGivenButton(0),
+ m_startOfBarButton(0),
+ m_timeEditor(0)
+{
+ static QFont *timeSigFont = 0;
+
+ if (timeSigFont == 0) {
+ timeSigFont = new QFont("new century schoolbook", 8, QFont::Bold);
+ timeSigFont->setPixelSize(20);
+ }
+
+ QVBox *vbox = makeVBoxMainWidget();
+ QGroupBox *groupBox = new QGroupBox
+ (1, Horizontal, i18n("Time signature"), vbox);
+ QHBox *numBox = new QHBox(groupBox);
+ QHBox *denomBox = new QHBox(groupBox);
+
+ QLabel *explanatoryLabel = 0;
+ if (explanatoryText) {
+ explanatoryLabel = new QLabel(explanatoryText, groupBox);
+ }
+
+ BigArrowButton *numDown = new BigArrowButton(numBox, Qt::LeftArrow);
+ BigArrowButton *denomDown = new BigArrowButton(denomBox, Qt::LeftArrow);
+
+ m_numLabel = new QLabel
+ (QString("%1").arg(m_timeSignature.getNumerator()), numBox);
+ m_denomLabel = new QLabel
+ (QString("%1").arg(m_timeSignature.getDenominator()), denomBox);
+
+ m_numLabel->setAlignment(AlignHCenter | AlignVCenter);
+ m_denomLabel->setAlignment(AlignHCenter | AlignVCenter);
+
+ m_numLabel->setFont(*timeSigFont);
+ m_denomLabel->setFont(*timeSigFont);
+
+ BigArrowButton *numUp = new BigArrowButton(numBox, Qt::RightArrow);
+ BigArrowButton *denomUp = new BigArrowButton(denomBox, Qt::RightArrow);
+
+ QObject::connect(numDown, SIGNAL(clicked()), this, SLOT(slotNumDown()));
+ QObject::connect(numUp, SIGNAL(clicked()), this, SLOT(slotNumUp()));
+ QObject::connect(denomDown, SIGNAL(clicked()), this, SLOT(slotDenomDown()));
+ QObject::connect(denomUp, SIGNAL(clicked()), this, SLOT(slotDenomUp()));
+
+ if (timeEditable) {
+
+ m_timeEditor = new TimeWidget
+ (i18n("Time where signature takes effect"),
+ vbox,
+ composition,
+ m_time,
+ true);
+
+ m_asGivenButton = 0;
+ m_startOfBarButton = 0;
+
+ } else {
+
+ m_timeEditor = 0;
+
+ groupBox = new QButtonGroup(1, Horizontal, i18n("Scope"), vbox);
+
+ int barNo = composition->getBarNumber(m_time);
+ bool atStartOfBar = (m_time == composition->getBarStart(barNo));
+
+ if (!atStartOfBar) {
+
+ QString scopeText;
+
+ if (barNo != 0 || !atStartOfBar) {
+ if (atStartOfBar) {
+ scopeText = QString
+ (i18n("Insertion point is at start of measure %1."))
+ .arg(barNo + 1);
+ } else {
+ scopeText = QString
+ (i18n("Insertion point is in the middle of measure %1."))
+ .arg(barNo + 1);
+ }
+ } else {
+ scopeText = QString
+ (i18n("Insertion point is at start of composition."));
+ }
+
+ new QLabel(scopeText, groupBox);
+ m_asGivenButton = new QRadioButton
+ (i18n("Start measure %1 here").arg(barNo + 2), groupBox);
+
+ if (!atStartOfBar) {
+ m_startOfBarButton = new QRadioButton
+ (i18n("Change time from start of measure %1")
+ .arg(barNo + 1), groupBox);
+ m_startOfBarButton->setChecked(true);
+ } else {
+ m_asGivenButton->setChecked(true);
+ }
+ } else {
+ new QLabel(i18n("Time change will take effect at the start of measure %1.")
+ .arg(barNo + 1), groupBox);
+ }
+ }
+
+ groupBox = new QGroupBox(1, Horizontal, i18n("Options"), vbox);
+ KConfig *config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+
+ m_hideSignatureButton = new QCheckBox
+ (i18n("Hide the time signature"), groupBox);
+ m_hideSignatureButton->setChecked
+ (config->readBoolEntry("timesigdialogmakehidden", false));
+
+ m_hideBarsButton = new QCheckBox
+ (i18n("Hide the affected bar lines"), groupBox);
+ m_hideBarsButton->setChecked
+ (config->readBoolEntry("timesigdialogmakehiddenbars", false));
+
+ m_commonTimeButton = new QCheckBox
+ (i18n("Show as common time"), groupBox);
+ m_commonTimeButton->setChecked
+ (config->readBoolEntry("timesigdialogshowcommon", true));
+
+ m_normalizeRestsButton = new QCheckBox
+ (i18n("Correct the durations of following measures"), groupBox);
+ m_normalizeRestsButton->setChecked
+ (config->readBoolEntry("timesigdialognormalize", true));
+
+ QObject::connect(m_hideSignatureButton, SIGNAL(clicked()), this,
+ SLOT(slotUpdateCommonTimeButton()));
+ slotUpdateCommonTimeButton();
+ m_explanatoryLabel = explanatoryLabel;
+
+ setHelp("time-signature");
+}
+
+TimeSignature
+TimeSignatureDialog::getTimeSignature() const
+{
+ KConfig *config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+
+ config->writeEntry("timesigdialogmakehidden", m_hideSignatureButton->isChecked());
+ config->writeEntry("timesigdialogmakehiddenbars", m_hideBarsButton->isChecked());
+ config->writeEntry("timesigdialogshowcommon", m_commonTimeButton->isChecked());
+ config->writeEntry("timesigdialognormalize", m_normalizeRestsButton->isChecked());
+
+ TimeSignature ts(m_timeSignature.getNumerator(),
+ m_timeSignature.getDenominator(),
+ (m_commonTimeButton &&
+ m_commonTimeButton->isEnabled() &&
+ m_commonTimeButton->isChecked()),
+ (m_hideSignatureButton &&
+ m_hideSignatureButton->isEnabled() &&
+ m_hideSignatureButton->isChecked()),
+ (m_hideBarsButton &&
+ m_hideBarsButton->isEnabled() &&
+ m_hideBarsButton->isChecked()));
+ return ts;
+}
+
+void
+TimeSignatureDialog::slotNumDown()
+{
+ int n = m_timeSignature.getNumerator();
+ if (--n >= 1) {
+ m_timeSignature = TimeSignature(n, m_timeSignature.getDenominator());
+ m_numLabel->setText(QString("%1").arg(n));
+ }
+ slotUpdateCommonTimeButton();
+}
+
+void
+TimeSignatureDialog::slotNumUp()
+{
+ int n = m_timeSignature.getNumerator();
+ if (++n <= 99) {
+ m_timeSignature = TimeSignature(n, m_timeSignature.getDenominator());
+ m_numLabel->setText(QString("%1").arg(n));
+ }
+ slotUpdateCommonTimeButton();
+}
+
+void
+TimeSignatureDialog::slotDenomDown()
+{
+ int n = m_timeSignature.getDenominator();
+ if ((n /= 2) >= 1) {
+ m_timeSignature = TimeSignature(m_timeSignature.getNumerator(), n);
+ m_denomLabel->setText(QString("%1").arg(n));
+ }
+ slotUpdateCommonTimeButton();
+}
+
+void
+TimeSignatureDialog::slotDenomUp()
+{
+ int n = m_timeSignature.getDenominator();
+ if ((n *= 2) <= 64) {
+ m_timeSignature = TimeSignature(m_timeSignature.getNumerator(), n);
+ m_denomLabel->setText(QString("%1").arg(n));
+ }
+ slotUpdateCommonTimeButton();
+}
+
+void
+TimeSignatureDialog::slotUpdateCommonTimeButton()
+{
+ if (m_explanatoryLabel)
+ m_explanatoryLabel->hide();
+ if (!m_hideSignatureButton || !m_hideSignatureButton->isChecked()) {
+ if (m_timeSignature.getDenominator() == m_timeSignature.getNumerator()) {
+ if (m_timeSignature.getNumerator() == 4) {
+ m_commonTimeButton->setText(i18n("Display as common time"));
+ m_commonTimeButton->setEnabled(true);
+ return ;
+ } else if (m_timeSignature.getNumerator() == 2) {
+ m_commonTimeButton->setText(i18n("Display as cut common time"));
+ m_commonTimeButton->setEnabled(true);
+ return ;
+ }
+ }
+ }
+ m_commonTimeButton->setEnabled(false);
+}
+
+timeT
+TimeSignatureDialog::getTime() const
+{
+ if (m_timeEditor) {
+ return m_timeEditor->getTime();
+ } else if (m_asGivenButton && m_asGivenButton->isChecked()) {
+ return m_time;
+ } else if (m_startOfBarButton && m_startOfBarButton->isChecked()) {
+ int barNo = m_composition->getBarNumber(m_time);
+ return m_composition->getBarStart(barNo);
+ } else {
+ return m_time;
+ }
+}
+
+bool
+TimeSignatureDialog::shouldNormalizeRests() const
+{
+ return (m_normalizeRestsButton && m_normalizeRestsButton->isEnabled() &&
+ m_normalizeRestsButton->isChecked());
+}
+
+}
+#include "TimeSignatureDialog.moc"
diff --git a/src/gui/dialogs/TimeSignatureDialog.h b/src/gui/dialogs/TimeSignatureDialog.h
new file mode 100644
index 0000000..330134c
--- /dev/null
+++ b/src/gui/dialogs/TimeSignatureDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_TIMESIGNATUREDIALOG_H_
+#define _RG_TIMESIGNATUREDIALOG_H_
+
+#include "base/NotationTypes.h"
+#include <kdialogbase.h>
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QWidget;
+class QRadioButton;
+class QLabel;
+class QCheckBox;
+
+
+namespace Rosegarden
+{
+
+class TimeWidget;
+class Composition;
+
+
+class TimeSignatureDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ TimeSignatureDialog(QWidget *parent,
+ Composition *composition,
+ timeT insertionTime,
+ TimeSignature defaultSig =
+ TimeSignature::DefaultTimeSignature,
+ bool timeEditable = false,
+ QString explanatoryText = 0);
+
+ TimeSignature getTimeSignature() const;
+
+ timeT getTime() const;
+ bool shouldNormalizeRests() const;
+
+public slots:
+ void slotNumUp();
+ void slotNumDown();
+ void slotDenomUp();
+ void slotDenomDown();
+ void slotUpdateCommonTimeButton();
+
+protected:
+ //--------------- Data members ---------------------------------
+
+ Composition *m_composition;
+ TimeSignature m_timeSignature;
+ timeT m_time;
+
+ QLabel *m_numLabel;
+ QLabel *m_denomLabel;
+ QLabel *m_explanatoryLabel;
+
+ QCheckBox *m_commonTimeButton;
+ QCheckBox *m_hideSignatureButton;
+ QCheckBox *m_hideBarsButton;
+ QCheckBox *m_normalizeRestsButton;
+
+ QRadioButton *m_asGivenButton;
+ QRadioButton *m_startOfBarButton;
+
+ TimeWidget *m_timeEditor;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/TransportDialog.cpp b/src/gui/dialogs/TransportDialog.cpp
new file mode 100644
index 0000000..115a528
--- /dev/null
+++ b/src/gui/dialogs/TransportDialog.cpp
@@ -0,0 +1,1164 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "TransportDialog.h"
+
+#include <klocale.h>
+#include <kstddirs.h>
+#include "base/Composition.h"
+#include "base/NotationTypes.h"
+#include "base/RealTime.h"
+#include "misc/Debug.h"
+#include "gui/application/RosegardenApplication.h"
+#include "gui/general/MidiPitchLabel.h"
+#include "gui/studio/StudioControl.h"
+#include "gui/widgets/Label.h"
+#include "sound/MappedEvent.h"
+#include "document/ConfigGroups.h"
+#include <kconfig.h>
+#include <kglobal.h>
+#include <qaccel.h>
+#include <qcolor.h>
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <qfont.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qpalette.h>
+#include <qpixmap.h>
+#include <qpushbutton.h>
+#include <qstring.h>
+#include <qtimer.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+TransportDialog::TransportDialog(QWidget *parent,
+ const char *name,
+ WFlags flags):
+ QWidget(parent, name, WType_TopLevel | WStyle_DialogBorder | WStyle_Minimize | WStyle_SysMenu | WDestructiveClose),
+ m_transport(0),
+ m_lastTenHours(0),
+ m_lastUnitHours(0),
+ m_lastTenMinutes(0),
+ m_lastUnitMinutes(0),
+ m_lastTenSeconds(0),
+ m_lastUnitSeconds(0),
+ m_lastTenths(0),
+ m_lastHundreths(0),
+ m_lastThousandths(0),
+ m_lastTenThousandths(0),
+ m_lastNegative(false),
+ m_lastMode(RealMode),
+ m_currentMode(RealMode),
+ m_tempo(0),
+ m_numerator(0),
+ m_denominator(0),
+ m_framesPerSecond(24),
+ m_bitsPerFrame(80),
+ m_isExpanded(true),
+ m_haveOriginalBackground(false),
+ m_isBackgroundSet(false),
+ m_sampleRate(0)
+{
+ m_transport = new RosegardenTransport(this);
+
+ setCaption(i18n("Rosegarden Transport"));
+
+ resetFonts();
+
+ initModeMap();
+
+ // set the LCD frame background to black
+ //
+ m_transport->LCDBoxFrame->setBackgroundColor(Qt::black);
+
+ // set all the pixmap backgrounds to black to avoid
+ // flickering when we update
+ //
+ m_transport->TenThousandthsPixmap->setBackgroundColor(Qt::black);
+ m_transport->ThousandthsPixmap->setBackgroundColor(Qt::black);
+ m_transport->HundredthsPixmap->setBackgroundColor(Qt::black);
+ m_transport->TenthsPixmap->setBackgroundColor(Qt::black);
+ m_transport->UnitSecondsPixmap->setBackgroundColor(Qt::black);
+ m_transport->TenSecondsPixmap->setBackgroundColor(Qt::black);
+ m_transport->UnitMinutesPixmap->setBackgroundColor(Qt::black);
+ m_transport->TenMinutesPixmap->setBackgroundColor(Qt::black);
+ m_transport->UnitHoursPixmap->setBackgroundColor(Qt::black);
+ m_transport->TenHoursPixmap->setBackgroundColor(Qt::black);
+ m_transport->NegativePixmap->setBackgroundColor(Qt::black);
+
+ // unset the negative sign to begin with
+ m_transport->NegativePixmap->clear();
+
+ // Set our toggle buttons
+ //
+ m_transport->PlayButton->setToggleButton(true);
+ m_transport->RecordButton->setToggleButton(true);
+
+// Disable the loop button if JACK transport enabled, because this
+// causes a nasty race condition, and it just seems our loops are not JACK compatible
+// #1240039 - DMM
+// KConfig* config = rgapp->config();
+// config->setGroup(SequencerOptionsConfigGroup);
+// if (config->readBoolEntry("jacktransport", false))
+// {
+// m_transport->LoopButton->setEnabled(false);
+// }
+
+ // fix and hold the size of the dialog
+ //
+ setMinimumSize(m_transport->width(), m_transport->height());
+ setMaximumSize(m_transport->width(), m_transport->height());
+
+ loadPixmaps();
+
+ // Create Midi label timers
+ m_midiInTimer = new QTimer(this);
+ m_midiOutTimer = new QTimer(this);
+ m_clearMetronomeTimer = new QTimer(this);
+
+ connect(m_midiInTimer, SIGNAL(timeout()),
+ SLOT(slotClearMidiInLabel()));
+
+ connect(m_midiOutTimer, SIGNAL(timeout()),
+ SLOT(slotClearMidiOutLabel()));
+
+ connect(m_clearMetronomeTimer, SIGNAL(timeout()),
+ SLOT(slotResetBackground()));
+
+ m_transport->TimeDisplayLabel->hide();
+ m_transport->ToEndLabel->hide();
+
+ connect(m_transport->TimeDisplayButton, SIGNAL(clicked()),
+ SLOT(slotChangeTimeDisplay()));
+
+ connect(m_transport->ToEndButton, SIGNAL(clicked()),
+ SLOT(slotChangeToEnd()));
+
+ connect(m_transport->LoopButton, SIGNAL(clicked()),
+ SLOT(slotLoopButtonClicked()));
+
+ connect(m_transport->PanelOpenButton, SIGNAL(clicked()),
+ SLOT(slotPanelOpenButtonClicked()));
+
+ connect(m_transport->PanelCloseButton, SIGNAL(clicked()),
+ SLOT(slotPanelCloseButtonClicked()));
+
+ connect(m_transport->PanicButton, SIGNAL(clicked()), SIGNAL(panic()));
+
+ m_panelOpen = *m_transport->PanelOpenButton->pixmap();
+ m_panelClosed = *m_transport->PanelCloseButton->pixmap();
+
+
+ connect(m_transport->SetStartLPButton, SIGNAL(clicked()), SLOT(slotSetStartLoopingPointAtMarkerPos()));
+ connect(m_transport->SetStopLPButton, SIGNAL(clicked()), SLOT(slotSetStopLoopingPointAtMarkerPos()));
+
+ // clear labels
+ //
+ slotClearMidiInLabel();
+ slotClearMidiOutLabel();
+
+ // and by default we close the lower panel
+ //
+ int rfh = m_transport->RecordingFrame->height();
+ m_transport->RecordingFrame->hide();
+ setFixedSize(width(), height() - rfh);
+ m_transport->PanelOpenButton->setPixmap(m_panelClosed);
+
+ // and since by default we show real time (not SMPTE), by default
+ // we hide the small colon pixmaps
+ //
+ m_transport->SecondColonPixmap->hide();
+ m_transport->HundredthColonPixmap->hide();
+
+ // We have to specify these settings in this class (copied
+ // from rosegardentransport.cpp) as we're using a specialised
+ // widgets for TempoDisplay. Ugly but works - does mean that
+ // if the rest of the Transport ever changes then this code
+ // will have to as well.
+ //
+ QPalette pal;
+ pal.setColor(QColorGroup::Foreground, QColor(192, 216, 255));
+
+ m_transport->TempoDisplay->setPalette(pal);
+ m_transport->TempoDisplay->setAlignment(int(QLabel::AlignVCenter | QLabel::AlignRight));
+
+ m_transport->TimeSigDisplay->setPalette(pal);
+ m_transport->TimeSigDisplay->setAlignment(int(QLabel::AlignVCenter | QLabel::AlignRight));
+
+ QFont localFont(m_transport->OutDisplay->font() );
+ localFont.setFamily( "lucida" );
+ localFont.setBold( TRUE );
+
+ m_transport->TempoDisplay->setFont( localFont );
+ m_transport->TimeSigDisplay->setFont( localFont );
+
+ connect(m_transport->TempoDisplay, SIGNAL(doubleClicked()),
+ this, SLOT(slotEditTempo()));
+
+ connect(m_transport->TempoDisplay, SIGNAL(scrollWheel(int)),
+ this, SIGNAL(scrollTempo(int)));
+
+ connect(m_transport->TimeSigDisplay, SIGNAL(doubleClicked()),
+ this, SLOT(slotEditTimeSignature()));
+
+ // toil through the individual pixmaps
+ connect(m_transport->NegativePixmap, SIGNAL(doubleClicked()),
+ this, SLOT(slotEditTime()));
+ connect(m_transport->TenHoursPixmap, SIGNAL(doubleClicked()),
+ this, SLOT(slotEditTime()));
+ connect(m_transport->UnitHoursPixmap, SIGNAL(doubleClicked()),
+ this, SLOT(slotEditTime()));
+ connect(m_transport->HourColonPixmap, SIGNAL(doubleClicked()),
+ this, SLOT(slotEditTime()));
+ connect(m_transport->TenMinutesPixmap, SIGNAL(doubleClicked()),
+ this, SLOT(slotEditTime()));
+ connect(m_transport->UnitMinutesPixmap, SIGNAL(doubleClicked()),
+ this, SLOT(slotEditTime()));
+ connect(m_transport->MinuteColonPixmap, SIGNAL(doubleClicked()),
+ this, SLOT(slotEditTime()));
+ connect(m_transport->TenSecondsPixmap, SIGNAL(doubleClicked()),
+ this, SLOT(slotEditTime()));
+ connect(m_transport->UnitSecondsPixmap, SIGNAL(doubleClicked()),
+ this, SLOT(slotEditTime()));
+ connect(m_transport->SecondColonPixmap, SIGNAL(doubleClicked()),
+ this, SLOT(slotEditTime()));
+ connect(m_transport->TenthsPixmap, SIGNAL(doubleClicked()),
+ this, SLOT(slotEditTime()));
+ connect(m_transport->HundredthsPixmap, SIGNAL(doubleClicked()),
+ this, SLOT(slotEditTime()));
+ connect(m_transport->HundredthColonPixmap, SIGNAL(doubleClicked()),
+ this, SLOT(slotEditTime()));
+ connect(m_transport->TenThousandthsPixmap, SIGNAL(doubleClicked()),
+ this, SLOT(slotEditTime()));
+ connect(m_transport->ThousandthsPixmap, SIGNAL(doubleClicked()),
+ this, SLOT(slotEditTime()));
+
+ // accelerator object
+ //
+ m_accelerators = new QAccel(this);
+}
+
+TransportDialog::~TransportDialog()
+{
+ if (isVisible()) {
+ KConfig* config = rgapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+ config->writeEntry("transportx", x());
+ config->writeEntry("transporty", y());
+ }
+}
+
+std::string
+TransportDialog::getCurrentModeAsString()
+{
+ bool found = false;
+ for (std::map<std::string, TimeDisplayMode>::iterator iter = m_modeMap.begin();
+ iter != m_modeMap.end() && !found;
+ iter++)
+ {
+ if (iter->second == m_currentMode) {
+ return iter->first;
+ }
+ }
+
+ // we shouldn't get here unless the map is not well-configured
+ RG_DEBUG << "TransportDialog::getCurrentModeAsString: could not map current mode "
+ << m_currentMode << " to string." << endl;
+ throw Exception("could not map current mode to string.");
+}
+
+void
+TransportDialog::initModeMap()
+{
+ m_modeMap["RealMode"] = RealMode;
+ m_modeMap["SMPTEMode"] = SMPTEMode;
+ m_modeMap["BarMode"] = BarMode;
+ m_modeMap["BarMetronomeMode"] = BarMetronomeMode;
+ m_modeMap["FrameMode"] = FrameMode;
+}
+
+void
+TransportDialog::show()
+{
+ KConfig* config = rgapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+ int x = config->readNumEntry("transportx", -1);
+ int y = config->readNumEntry("transporty", -1);
+ if (x >= 0 && y >= 0) {
+ int dw = QApplication::desktop()->availableGeometry(QPoint(x, y)).width();
+ int dh = QApplication::desktop()->availableGeometry(QPoint(x, y)).height();
+ if (x + m_transport->width() > dw) x = dw - m_transport->width();
+ if (y + m_transport->height() > dh) y = dh - m_transport->height();
+ move(x, y);
+// std::cerr << "TransportDialog::show(): moved to " << x << "," << y << std::endl;
+ QWidget::show();
+// std::cerr << "TransportDialog::show(): now at " << this->x() << "," << this->y() << std::endl;
+ } else {
+ QWidget::show();
+ }
+}
+
+void
+TransportDialog::hide()
+{
+ if (isVisible()) {
+ KConfig* config = rgapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+ config->writeEntry("transportx", x());
+ config->writeEntry("transporty", y());
+ }
+ QWidget::hide();
+}
+
+void
+TransportDialog::loadPixmaps()
+{
+ m_lcdList.clear();
+ QString fileName;
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+
+ for (int i = 0; i < 10; i++) {
+ fileName = QString("%1/transport/led-%2.xpm").arg(pixmapDir).arg(i);
+ if (!m_lcdList[i].load(fileName)) {
+ std::cerr << "TransportDialog - failed to load pixmap \""
+ << fileName << "\"" << std::endl;
+ }
+ }
+
+ // Load the "negative" sign pixmap
+ //
+ fileName = QString("%1/transport/led--.xpm").arg(pixmapDir);
+ m_lcdNegative.load(fileName);
+
+}
+
+void
+TransportDialog::resetFonts()
+{
+ resetFont(m_transport->TimeSigLabel);
+ resetFont(m_transport->TimeSigDisplay);
+ resetFont(m_transport->TempoLabel);
+ resetFont(m_transport->TempoDisplay);
+ resetFont(m_transport->DivisionLabel);
+ resetFont(m_transport->DivisionDisplay);
+ resetFont(m_transport->InLabel);
+ resetFont(m_transport->InDisplay);
+ resetFont(m_transport->OutLabel);
+ resetFont(m_transport->OutDisplay);
+ resetFont(m_transport->ToEndLabel);
+ resetFont(m_transport->TimeDisplayLabel);
+}
+
+void
+TransportDialog::resetFont(QWidget *w)
+{
+ QFont font = w->font();
+ font.setPixelSize(10);
+ w->setFont(font);
+}
+
+void
+TransportDialog::setSMPTEResolution(int framesPerSecond,
+ int bitsPerFrame)
+{
+ m_framesPerSecond = framesPerSecond;
+ m_bitsPerFrame = bitsPerFrame;
+}
+
+void
+TransportDialog::getSMPTEResolution(int &framesPerSecond,
+ int &bitsPerFrame)
+{
+ framesPerSecond = m_framesPerSecond;
+ bitsPerFrame = m_bitsPerFrame;
+}
+
+void
+TransportDialog::computeSampleRate()
+{
+ if (m_sampleRate == 0) {
+
+ QCString replyType;
+ QByteArray replyData;
+ m_sampleRate = 0;
+
+ if (rgapp->sequencerCall("getSampleRate()", replyType, replyData)) {
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ unsigned int result;
+ streamIn >> result;
+ m_sampleRate = result;
+ } else {
+ m_sampleRate = -1;
+ }
+ }
+
+}
+
+void
+TransportDialog::cycleThroughModes()
+{
+ switch (m_currentMode) {
+
+ case RealMode:
+ if (m_sampleRate > 0)
+ m_currentMode = FrameMode;
+ else
+ m_currentMode = BarMode;
+ break;
+
+ case FrameMode:
+ m_currentMode = BarMode;
+ break;
+
+ case SMPTEMode:
+ m_currentMode = BarMode;
+ break;
+
+ case BarMode:
+ m_currentMode = BarMetronomeMode;
+ break;
+
+ case BarMetronomeMode:
+ m_currentMode = RealMode;
+ break;
+ }
+}
+
+void
+TransportDialog::displayTime()
+{
+ switch (m_currentMode) {
+ case RealMode:
+ m_clearMetronomeTimer->stop();
+ m_transport->TimeDisplayLabel->hide();
+ break;
+
+ case SMPTEMode:
+ m_clearMetronomeTimer->stop();
+ m_transport->TimeDisplayLabel->setText("SMPTE"); // DO NOT i18n
+ m_transport->TimeDisplayLabel->show();
+ break;
+
+ case BarMode:
+ m_clearMetronomeTimer->stop();
+ m_transport->TimeDisplayLabel->setText("BAR"); // DO NOT i18n
+ m_transport->TimeDisplayLabel->show();
+ break;
+
+ case BarMetronomeMode:
+ m_clearMetronomeTimer->start(1700, FALSE);
+ m_transport->TimeDisplayLabel->setText("MET"); // DO NOT i18n
+ m_transport->TimeDisplayLabel->show();
+ break;
+
+ case FrameMode:
+ m_clearMetronomeTimer->stop();
+ m_transport->TimeDisplayLabel->setText(QString("%1").arg(m_sampleRate));
+ m_transport->TimeDisplayLabel->show();
+ break;
+ }
+}
+
+void
+TransportDialog::setNewMode(const std::string& newModeAsString)
+{
+ TimeDisplayMode newMode = RealMode; // default value if not found
+
+ std::map<std::string, TimeDisplayMode>::iterator iter =
+ m_modeMap.find(newModeAsString);
+
+ if (iter != m_modeMap.end()) {
+ // value found
+ newMode = iter->second;
+ } else {
+ // don't fail: use default value set at declaration
+ }
+
+ setNewMode(newMode);
+}
+
+void
+TransportDialog::setNewMode(const TimeDisplayMode& newMode)
+{
+ computeSampleRate();
+
+ m_currentMode = newMode;
+
+ displayTime();
+}
+
+
+void
+TransportDialog::slotChangeTimeDisplay()
+{
+ computeSampleRate();
+
+ cycleThroughModes();
+
+ displayTime();
+}
+
+void
+TransportDialog::slotChangeToEnd()
+{
+ if (m_transport->ToEndButton->isOn()) {
+ m_transport->ToEndLabel->show();
+ } else {
+ m_transport->ToEndLabel->hide();
+ }
+}
+
+bool
+TransportDialog::isShowingTimeToEnd()
+{
+ return m_transport->ToEndButton->isOn();
+}
+
+void
+TransportDialog::displayRealTime(const RealTime &rt)
+{
+ RealTime st = rt;
+
+ slotResetBackground();
+
+ if (m_lastMode != RealMode) {
+ m_transport->HourColonPixmap->show();
+ m_transport->MinuteColonPixmap->show();
+ m_transport->SecondColonPixmap->hide();
+ m_transport->HundredthColonPixmap->hide();
+ m_lastMode = RealMode;
+ }
+
+ // If time is negative then reverse the time and set the minus flag
+ //
+ if (st < RealTime::zeroTime) {
+ st = RealTime::zeroTime - st;
+ if (!m_lastNegative) {
+ m_transport->NegativePixmap->setPixmap(m_lcdNegative);
+ m_lastNegative = true;
+ }
+ } else // don't show the flag
+ {
+ if (m_lastNegative) {
+ m_transport->NegativePixmap->clear();
+ m_lastNegative = false;
+ }
+ }
+
+ m_tenThousandths = ( st.usec() / 100 ) % 10;
+ m_thousandths = ( st.usec() / 1000 ) % 10;
+ m_hundreths = ( st.usec() / 10000 ) % 10;
+ m_tenths = ( st.usec() / 100000 ) % 10;
+
+ m_unitSeconds = ( st.sec ) % 10;
+ m_tenSeconds = ( st.sec / 10 ) % 6;
+
+ m_unitMinutes = ( st.sec / 60 ) % 10;
+ m_tenMinutes = ( st.sec / 600 ) % 6;
+
+ m_unitHours = ( st.sec / 3600 ) % 10;
+ m_tenHours = (st.sec / 36000 ) % 10;
+
+ updateTimeDisplay();
+}
+
+void
+TransportDialog::displayFrameTime(const RealTime &rt)
+{
+ RealTime st = rt;
+
+ slotResetBackground();
+
+ if (m_lastMode != FrameMode) {
+ m_transport->HourColonPixmap->hide();
+ m_transport->MinuteColonPixmap->hide();
+ m_transport->SecondColonPixmap->hide();
+ m_transport->HundredthColonPixmap->hide();
+ m_lastMode = FrameMode;
+ }
+
+ // If time is negative then reverse the time and set the minus flag
+ //
+ if (st < RealTime::zeroTime) {
+ st = RealTime::zeroTime - st;
+ if (!m_lastNegative) {
+ m_transport->NegativePixmap->setPixmap(m_lcdNegative);
+ m_lastNegative = true;
+ }
+ } else // don't show the flag
+ {
+ if (m_lastNegative) {
+ m_transport->NegativePixmap->clear();
+ m_lastNegative = false;
+ }
+ }
+
+ long frame = RealTime::realTime2Frame(st, m_sampleRate);
+
+ m_tenThousandths = frame % 10;
+ frame /= 10;
+ m_thousandths = frame % 10;
+ frame /= 10;
+ m_hundreths = frame % 10;
+ frame /= 10;
+ m_tenths = frame % 10;
+ frame /= 10;
+ m_unitSeconds = frame % 10;
+ frame /= 10;
+ m_tenSeconds = frame % 10;
+ frame /= 10;
+ m_unitMinutes = frame % 10;
+ frame /= 10;
+ m_tenMinutes = frame % 10;
+ frame /= 10;
+ m_unitHours = frame % 10;
+ frame /= 10;
+ m_tenHours = frame % 10;
+ frame /= 10;
+
+ updateTimeDisplay();
+}
+
+void
+TransportDialog::displaySMPTETime(const RealTime &rt)
+{
+ RealTime st = rt;
+
+ slotResetBackground();
+
+ if (m_lastMode != SMPTEMode) {
+ m_transport->HourColonPixmap->show();
+ m_transport->MinuteColonPixmap->show();
+ m_transport->SecondColonPixmap->show();
+ m_transport->HundredthColonPixmap->show();
+ m_lastMode = SMPTEMode;
+ }
+
+ // If time is negative then reverse the time and set the minus flag
+ //
+ if (st < RealTime::zeroTime) {
+ st = RealTime::zeroTime - st;
+ if (!m_lastNegative) {
+ m_transport->NegativePixmap->setPixmap(m_lcdNegative);
+ m_lastNegative = true;
+ }
+ } else // don't show the flag
+ {
+ if (m_lastNegative) {
+ m_transport->NegativePixmap->clear();
+ m_lastNegative = false;
+ }
+ }
+
+ m_tenThousandths =
+ (( st.usec() * m_framesPerSecond * m_bitsPerFrame) / 1000000 ) % 10;
+ m_thousandths =
+ (( st.usec() * m_framesPerSecond * m_bitsPerFrame) / 10000000 ) %
+ (m_bitsPerFrame / 10);
+ m_hundreths =
+ (( st.usec() * m_framesPerSecond) / 1000000 ) % 10;
+ m_tenths =
+ (( st.usec() * m_framesPerSecond) / 10000000 ) % 10;
+
+ m_unitSeconds = ( st.sec ) % 10;
+ m_tenSeconds = ( st.sec / 10 ) % 6;
+
+ m_unitMinutes = ( st.sec / 60 ) % 10;
+ m_tenMinutes = ( st.sec / 600 ) % 6;
+
+ m_unitHours = ( st.sec / 3600 ) % 10;
+ m_tenHours = ( st.sec / 36000 ) % 10;
+
+ updateTimeDisplay();
+}
+
+void
+TransportDialog::displayBarTime(int bar, int beat, int unit)
+{
+ if (m_lastMode != BarMode) {
+ m_transport->HourColonPixmap->hide();
+ m_transport->MinuteColonPixmap->show();
+ m_transport->SecondColonPixmap->hide();
+ m_transport->HundredthColonPixmap->hide();
+ m_lastMode = BarMode;
+ }
+
+ // If time is negative then reverse the time and set the minus flag
+ //
+ if (bar < 0) {
+ bar = -bar;
+ if (!m_lastNegative) {
+ m_transport->NegativePixmap->setPixmap(m_lcdNegative);
+ m_lastNegative = true;
+ }
+ } else // don't show the flag
+ {
+ if (m_lastNegative) {
+ m_transport->NegativePixmap->clear();
+ m_lastNegative = false;
+ }
+ }
+
+ if (m_currentMode == BarMetronomeMode && unit < 2) {
+ if (beat == 1) {
+ slotSetBackground(Qt::red);
+ } else {
+ slotSetBackground(Qt::cyan);
+ }
+ } else {
+ slotResetBackground();
+ }
+
+ m_tenThousandths = ( unit ) % 10;
+ m_thousandths = ( unit / 10 ) % 10;
+ m_hundreths = ( unit / 100 ) % 10;
+ m_tenths = ( unit / 1000 ) % 10;
+
+ if (m_tenths == 0) {
+ m_tenths = -1;
+ if (m_hundreths == 0) {
+ m_hundreths = -1;
+ if (m_thousandths == 0) {
+ m_thousandths = -1;
+ }
+ }
+ }
+
+ m_unitSeconds = ( beat ) % 10;
+ m_tenSeconds = ( beat / 10 ) % 6;
+
+ if (m_tenSeconds == 0) {
+ m_tenSeconds = -1;
+ }
+
+ m_unitMinutes = ( bar ) % 10;
+ m_tenMinutes = ( bar / 10 ) % 10;
+
+ m_unitHours = ( bar / 100 ) % 10;
+ m_tenHours = ( bar / 1000 ) % 10;
+
+ if (m_tenHours == 0) {
+ m_tenHours = -1;
+ if (m_unitHours == 0) {
+ m_unitHours = -1;
+ if (m_tenMinutes == 0) {
+ m_tenMinutes = -1;
+ }
+ }
+ }
+
+ updateTimeDisplay();
+}
+
+void
+TransportDialog::updateTimeDisplay()
+{
+ if (m_tenThousandths != m_lastTenThousandths) {
+ if (m_tenThousandths < 0)
+ m_transport->TenThousandthsPixmap->clear();
+ else
+ m_transport->TenThousandthsPixmap->setPixmap(m_lcdList[m_tenThousandths]);
+ m_lastTenThousandths = m_tenThousandths;
+ }
+
+ if (m_thousandths != m_lastThousandths) {
+ if (m_thousandths < 0)
+ m_transport->ThousandthsPixmap->clear();
+ else
+ m_transport->ThousandthsPixmap->setPixmap(m_lcdList[m_thousandths]);
+ m_lastThousandths = m_thousandths;
+ }
+
+ if (m_hundreths != m_lastHundreths) {
+ if (m_hundreths < 0)
+ m_transport->HundredthsPixmap->clear();
+ else
+ m_transport->HundredthsPixmap->setPixmap(m_lcdList[m_hundreths]);
+ m_lastHundreths = m_hundreths;
+ }
+
+ if (m_tenths != m_lastTenths) {
+ if (m_tenths < 0)
+ m_transport->TenthsPixmap->clear();
+ else
+ m_transport->TenthsPixmap->setPixmap(m_lcdList[m_tenths]);
+ m_lastTenths = m_tenths;
+ }
+
+ if (m_unitSeconds != m_lastUnitSeconds) {
+ if (m_unitSeconds < 0)
+ m_transport->UnitSecondsPixmap->clear();
+ else
+ m_transport->UnitSecondsPixmap->setPixmap(m_lcdList[m_unitSeconds]);
+ m_lastUnitSeconds = m_unitSeconds;
+ }
+
+ if (m_tenSeconds != m_lastTenSeconds) {
+ if (m_tenSeconds < 0)
+ m_transport->TenSecondsPixmap->clear();
+ else
+ m_transport->TenSecondsPixmap->setPixmap(m_lcdList[m_tenSeconds]);
+ m_lastTenSeconds = m_tenSeconds;
+ }
+
+ if (m_unitMinutes != m_lastUnitMinutes) {
+ if (m_unitMinutes < 0)
+ m_transport->UnitMinutesPixmap->clear();
+ else
+ m_transport->UnitMinutesPixmap->setPixmap(m_lcdList[m_unitMinutes]);
+ m_lastUnitMinutes = m_unitMinutes;
+ }
+
+ if (m_tenMinutes != m_lastTenMinutes) {
+ if (m_tenMinutes < 0)
+ m_transport->TenMinutesPixmap->clear();
+ else
+ m_transport->TenMinutesPixmap->setPixmap(m_lcdList[m_tenMinutes]);
+ m_lastTenMinutes = m_tenMinutes;
+ }
+
+ if (m_unitHours != m_lastUnitHours) {
+ if (m_unitHours < 0)
+ m_transport->UnitHoursPixmap->clear();
+ else
+ m_transport->UnitHoursPixmap->setPixmap(m_lcdList[m_unitHours]);
+ m_lastUnitHours = m_unitHours;
+ }
+
+ if (m_tenHours != m_lastTenHours) {
+ if (m_tenHours < 0)
+ m_transport->TenHoursPixmap->clear();
+ else
+ m_transport->TenHoursPixmap->setPixmap(m_lcdList[m_tenHours]);
+ m_lastTenHours = m_tenHours;
+ }
+}
+
+void
+TransportDialog::setTempo(const tempoT &tempo)
+{
+ if (m_tempo == tempo)
+ return ;
+ m_tempo = tempo;
+
+ // Send the quarter note length to the sequencer - shouldn't
+ // really hang this off here but at least it's a single point
+ // where the tempo should always be consistent. Quarter Note
+ // Length is sent (MIDI CLOCK) at 24ppqn.
+ //
+ double qnD = 60.0 / Composition::getTempoQpm(tempo);
+ RealTime qnTime =
+ RealTime(long(qnD),
+ long((qnD - double(long(qnD))) * 1000000000.0));
+
+ StudioControl::sendQuarterNoteLength(qnTime);
+
+ QString tempoString;
+ tempoString.sprintf("%4.3f", Composition::getTempoQpm(tempo));
+
+ m_transport->TempoDisplay->setText(tempoString);
+}
+
+void
+TransportDialog::setTimeSignature(const TimeSignature &timeSig)
+{
+ int numerator = timeSig.getNumerator();
+ int denominator = timeSig.getDenominator();
+ if (m_numerator == numerator && m_denominator == denominator)
+ return ;
+ m_numerator = numerator;
+ m_denominator = denominator;
+
+ QString timeSigString;
+ timeSigString.sprintf("%d/%d", numerator, denominator);
+ m_transport->TimeSigDisplay->setText(timeSigString);
+}
+
+void
+TransportDialog::setMidiInLabel(const MappedEvent *mE)
+{
+ assert(mE > 0);
+
+ switch (mE->getType()) {
+ case MappedEvent::MidiNote:
+ case MappedEvent::MidiNoteOneShot:
+ {
+ // don't do anything if we've got an effective NOTE OFF
+ //
+ if (mE->getVelocity() == 0)
+ return ;
+
+ MidiPitchLabel mPL(mE->getPitch());
+ m_transport->InDisplay->setText
+ (mPL.getQString() +
+ QString(" %1").arg(mE->getVelocity()));
+ }
+ break;
+
+ case MappedEvent::MidiPitchBend:
+ m_transport->InDisplay->setText(i18n("PITCH WHEEL"));
+ break;
+
+ case MappedEvent::MidiController:
+ m_transport->InDisplay->setText(i18n("CONTROLLER"));
+ break;
+
+ case MappedEvent::MidiProgramChange:
+ m_transport->InDisplay->setText(i18n("PROG CHNGE"));
+ break;
+
+ case MappedEvent::MidiKeyPressure:
+ case MappedEvent::MidiChannelPressure:
+ m_transport->InDisplay->setText(i18n("PRESSURE"));
+ break;
+
+ case MappedEvent::MidiSystemMessage:
+ m_transport->InDisplay->setText(i18n("SYS MESSAGE"));
+ break;
+
+ default: // do nothing
+ return ;
+ }
+
+ // Reset the timer if it's already running
+ //
+ if (m_midiInTimer->isActive())
+ m_midiInTimer->stop();
+
+ // 1.5 second timeout for MIDI event
+ //
+ m_midiInTimer->start(1500, true);
+}
+
+void
+TransportDialog::slotClearMidiInLabel()
+{
+ m_transport->InDisplay->setText(i18n(QString("NO EVENTS")));
+
+ // also, just to be sure:
+ slotResetBackground();
+}
+
+void
+TransportDialog::setMidiOutLabel(const MappedEvent *mE)
+{
+ assert(mE > 0);
+
+ switch (mE->getType()) {
+ case MappedEvent::MidiNote:
+ case MappedEvent::MidiNoteOneShot:
+ {
+ MidiPitchLabel mPL(mE->getPitch());
+ m_transport->OutDisplay->setText
+ (mPL.getQString() +
+ QString(" %1").arg(mE->getVelocity()));
+ }
+ break;
+
+ case MappedEvent::MidiPitchBend:
+ m_transport->OutDisplay->setText(i18n("PITCH WHEEL"));
+ break;
+
+ case MappedEvent::MidiController:
+ m_transport->OutDisplay->setText(i18n("CONTROLLER"));
+ break;
+
+ case MappedEvent::MidiProgramChange:
+ m_transport->OutDisplay->setText(i18n("PROG CHNGE"));
+ break;
+
+ case MappedEvent::MidiKeyPressure:
+ case MappedEvent::MidiChannelPressure:
+ m_transport->OutDisplay->setText(i18n("PRESSURE"));
+ break;
+
+ case MappedEvent::MidiSystemMessage:
+ m_transport->OutDisplay->setText(i18n("SYS MESSAGE"));
+ break;
+
+ default: // do nothing
+ return ;
+ }
+
+ // Reset the timer if it's already running
+ //
+ if (m_midiOutTimer->isActive())
+ m_midiOutTimer->stop();
+
+ // 200 millisecond timeout
+ //
+ m_midiOutTimer->start(200, true);
+}
+
+void
+TransportDialog::slotClearMidiOutLabel()
+{
+ m_transport->OutDisplay->setText(i18n(QString("NO EVENTS")));
+}
+
+void
+TransportDialog::closeEvent (QCloseEvent * /*e*/)
+{
+ //e->accept(); // accept the close event here
+ emit closed();
+}
+
+void
+TransportDialog::slotLoopButtonClicked()
+{
+ // disable if JACK transport has been set #1240039 - DMM
+ // KConfig* config = rgapp->config();
+ // config->setGroup(SequencerOptionsConfigGroup);
+ // if (config->readBoolEntry("jacktransport", false))
+ // {
+ // //!!! - this will fail silently
+ // m_transport->LoopButton->setEnabled(false);
+ // m_transport->LoopButton->setOn(false);
+ // return;
+ // }
+
+ if (m_transport->LoopButton->isOn()) {
+ emit setLoop();
+ } else {
+ emit unsetLoop();
+ }
+}
+
+void
+TransportDialog::slotSetStartLoopingPointAtMarkerPos()
+{
+ emit setLoopStartTime();
+}
+
+void
+TransportDialog::slotSetStopLoopingPointAtMarkerPos()
+{
+ emit setLoopStopTime();
+}
+
+void
+TransportDialog::slotPanelOpenButtonClicked()
+{
+ int rfh = m_transport->RecordingFrame->height();
+
+ if (m_transport->RecordingFrame->isVisible()) {
+ m_transport->RecordingFrame->hide();
+ setFixedSize(width(), height() - rfh);
+ m_transport->PanelOpenButton->setPixmap(m_panelClosed);
+ m_isExpanded = false;
+ } else {
+ setFixedSize(width(), height() + rfh);
+ m_transport->RecordingFrame->show();
+ m_transport->PanelOpenButton->setPixmap(m_panelOpen);
+ m_isExpanded = true;
+ }
+}
+
+void
+TransportDialog::slotPanelCloseButtonClicked()
+{
+ int rfh = m_transport->RecordingFrame->height();
+
+ if (m_transport->RecordingFrame->isVisible()) {
+ m_transport->RecordingFrame->hide();
+ setFixedSize(width(), height() - rfh);
+ m_transport->PanelOpenButton->setPixmap(m_panelClosed);
+ m_isExpanded = false;
+ }
+}
+
+bool
+TransportDialog::isExpanded()
+{
+ return m_isExpanded;
+}
+
+void
+TransportDialog::slotEditTempo()
+{
+ emit editTempo(this);
+}
+
+void
+TransportDialog::slotEditTimeSignature()
+{
+ emit editTimeSignature(this);
+}
+
+void
+TransportDialog::slotEditTime()
+{
+ emit editTransportTime(this);
+}
+
+void
+TransportDialog::slotSetBackground(QColor c)
+{
+ if (!m_haveOriginalBackground) {
+ m_originalBackground = m_transport->LCDBoxFrame->paletteBackgroundColor();
+ m_haveOriginalBackground = true;
+ }
+
+ m_transport->LCDBoxFrame->setPaletteBackgroundColor(c);
+ m_transport->NegativePixmap->setPaletteBackgroundColor(c);
+ m_transport->TenHoursPixmap->setPaletteBackgroundColor(c);
+ m_transport->UnitHoursPixmap->setPaletteBackgroundColor(c);
+ m_transport->TimeDisplayLabel->setPaletteBackgroundColor(c);
+
+ /* this is a bit more thorough, but too slow and flickery:
+
+ const QObjectList *children = m_transport->LCDBoxFrame->children();
+ QObjectListIt it(*children);
+ QObject *obj;
+
+ while ((obj = it.current()) != 0) {
+
+ QWidget *w = dynamic_cast<QWidget *>(obj);
+ if (w) {
+ w->setPaletteBackgroundColor(c);
+ }
+ ++it;
+ }
+
+ */
+
+ m_isBackgroundSet = true;
+}
+
+void
+TransportDialog::slotResetBackground()
+{
+ if (m_isBackgroundSet) {
+ slotSetBackground(m_originalBackground);
+ }
+ m_isBackgroundSet = false;
+}
+
+}
+#include "TransportDialog.moc"
diff --git a/src/gui/dialogs/TransportDialog.h b/src/gui/dialogs/TransportDialog.h
new file mode 100644
index 0000000..e5c4948
--- /dev/null
+++ b/src/gui/dialogs/TransportDialog.h
@@ -0,0 +1,231 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ROSEGARDENTRANSPORTDIALOG_H_
+#define _RG_ROSEGARDENTRANSPORTDIALOG_H_
+
+#include <map>
+#include <kdockwidget.h>
+#include <qcolor.h>
+#include <qpixmap.h>
+#include "base/Composition.h" // for tempoT
+#include "RosegardenTransport.h" // generated by uic
+
+class RosegardenTransport;
+class QWidget;
+class QTimer;
+class QPushButton;
+class QCloseEvent;
+class QAccel;
+
+
+namespace Rosegarden
+{
+
+class TimeSignature;
+class RealTime;
+class MappedEvent;
+
+
+class TransportDialog : public QWidget
+{
+Q_OBJECT
+public:
+ TransportDialog(QWidget *parent=0,
+ const char *name=0,
+ WFlags flags = /*Qt::WStyle_StaysOnTop |*/
+ Qt::WStyle_NormalBorder);
+ ~TransportDialog();
+
+ enum TimeDisplayMode { RealMode, SMPTEMode, BarMode, BarMetronomeMode, FrameMode };
+
+ std::string getCurrentModeAsString();
+ TimeDisplayMode getCurrentMode() { return m_currentMode; }
+ void setNewMode(const std::string& newModeAsString);
+ void setNewMode(const TimeDisplayMode& newMode);
+ bool isShowingTimeToEnd();
+ bool isExpanded();
+
+ void displayRealTime(const RealTime &rt);
+ void displaySMPTETime(const RealTime &rt);
+ void displayFrameTime(const RealTime &rt);
+ void displayBarTime(int bar, int beat, int unit);
+
+ void setTempo(const tempoT &tempo);
+ void setTimeSignature(const TimeSignature &timeSig);
+
+ void setSMPTEResolution(int framesPerSecond, int bitsPerFrame);
+ void getSMPTEResolution(int &framesPerSecond, int &bitsPerFrame);
+
+ // Called indirectly from the sequencer and from the GUI to
+ // show incoming and outgoing MIDI events on the Transport
+ //
+ void setMidiInLabel(const MappedEvent *mE);
+ void setMidiOutLabel(const MappedEvent *mE);
+
+ // Return the accelerator object
+ //
+ QAccel* getAccelerators() { return m_accelerators; }
+
+ // RosegardenTransport member accessors
+ QPushButton* MetronomeButton() { return m_transport->MetronomeButton; }
+ QPushButton* SoloButton() { return m_transport->SoloButton; }
+ QPushButton* LoopButton() { return m_transport->LoopButton; }
+ QPushButton* PlayButton() { return m_transport->PlayButton; }
+ QPushButton* StopButton() { return m_transport->StopButton; }
+ QPushButton* FfwdButton() { return m_transport->FfwdButton; }
+ QPushButton* RewindButton() { return m_transport->RewindButton; }
+ QPushButton* RecordButton() { return m_transport->RecordButton; }
+ QPushButton* RewindEndButton() { return m_transport->RewindEndButton; }
+ QPushButton* FfwdEndButton() { return m_transport->FfwdEndButton; }
+ QPushButton* TimeDisplayButton() { return m_transport->TimeDisplayButton; }
+ QPushButton* ToEndButton() { return m_transport->ToEndButton; }
+
+ virtual void show();
+ virtual void hide();
+
+protected:
+ virtual void closeEvent(QCloseEvent * e);
+ void computeSampleRate();
+ void cycleThroughModes();
+ void displayTime();
+
+public slots:
+
+ // These two slots are activated by QTimers
+ //
+ void slotClearMidiInLabel();
+ void slotClearMidiOutLabel();
+
+ // These just change the little labels that say what
+ // mode we're in, nothing else
+ //
+ void slotChangeTimeDisplay();
+ void slotChangeToEnd();
+
+ void slotLoopButtonClicked();
+
+ void slotPanelOpenButtonClicked();
+ void slotPanelCloseButtonClicked();
+
+ void slotEditTempo();
+ void slotEditTimeSignature();
+ void slotEditTime();
+
+ void slotSetBackground(QColor);
+ void slotResetBackground();
+
+ void slotSetStartLoopingPointAtMarkerPos();
+ void slotSetStopLoopingPointAtMarkerPos();
+
+signals:
+ void closed();
+
+ // Set and unset the loop at the RosegardenGUIApp
+ //
+ void setLoop();
+ void unsetLoop();
+ void setLoopStartTime();
+ void setLoopStopTime();
+
+ void editTempo(QWidget *);
+ void editTimeSignature(QWidget *);
+ void editTransportTime(QWidget *);
+ void scrollTempo(int);
+ void panic();
+
+private:
+ void loadPixmaps();
+ void resetFonts();
+ void resetFont(QWidget *);
+ void initModeMap();
+
+ //--------------- Data members ---------------------------------
+
+ RosegardenTransport* m_transport;
+
+ std::map<int, QPixmap> m_lcdList;
+ QPixmap m_lcdNegative;
+
+ int m_lastTenHours;
+ int m_lastUnitHours;
+ int m_lastTenMinutes;
+ int m_lastUnitMinutes;
+ int m_lastTenSeconds;
+ int m_lastUnitSeconds;
+ int m_lastTenths;
+ int m_lastHundreths;
+ int m_lastThousandths;
+ int m_lastTenThousandths;
+
+ bool m_lastNegative;
+ TimeDisplayMode m_lastMode;
+ TimeDisplayMode m_currentMode;
+
+ int m_tenHours;
+ int m_unitHours;
+ int m_tenMinutes;
+ int m_unitMinutes;
+ int m_tenSeconds;
+ int m_unitSeconds;
+ int m_tenths;
+ int m_hundreths;
+ int m_thousandths;
+ int m_tenThousandths;
+
+ tempoT m_tempo;
+ int m_numerator;
+ int m_denominator;
+
+ int m_framesPerSecond;
+ int m_bitsPerFrame;
+
+ QTimer *m_midiInTimer;
+ QTimer *m_midiOutTimer;
+ QTimer *m_clearMetronomeTimer;
+
+ QPixmap m_panelOpen;
+ QPixmap m_panelClosed;
+
+ void updateTimeDisplay();
+
+ QAccel *m_accelerators;
+ bool m_isExpanded;
+
+ bool m_haveOriginalBackground;
+ bool m_isBackgroundSet;
+ QColor m_originalBackground;
+
+ int m_sampleRate;
+
+ std::map<std::string, TimeDisplayMode> m_modeMap;
+};
+
+
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/TriggerSegmentDialog.cpp b/src/gui/dialogs/TriggerSegmentDialog.cpp
new file mode 100644
index 0000000..a5064e1
--- /dev/null
+++ b/src/gui/dialogs/TriggerSegmentDialog.cpp
@@ -0,0 +1,181 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "TriggerSegmentDialog.h"
+#include <kapplication.h>
+
+#include "base/BaseProperties.h"
+#include <klocale.h>
+#include "misc/Strings.h"
+#include "document/ConfigGroups.h"
+#include "base/Composition.h"
+#include "base/TriggerSegment.h"
+#include <kcombobox.h>
+#include <kconfig.h>
+#include <kdialogbase.h>
+#include <qcheckbox.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <qlayout.h>
+
+
+namespace Rosegarden
+{
+
+TriggerSegmentDialog::TriggerSegmentDialog(QWidget *parent,
+ Composition *composition) :
+ KDialogBase(parent, "triggersegmentdialog", true, i18n("Trigger Segment"),
+ Ok | Cancel, Ok),
+ m_composition(composition)
+{
+ QVBox *vbox = makeVBoxMainWidget();
+
+ QFrame *frame = new QFrame(vbox);
+ QGridLayout *layout = new QGridLayout(frame, 3, 2, 5, 5);
+
+ QLabel *label = new QLabel(i18n("Trigger segment: "), frame);
+ layout->addWidget(label, 0, 0);
+
+ m_segment = new KComboBox(frame);
+ layout->addWidget(m_segment, 0, 1);
+
+ int n = 1;
+ for (Composition::triggersegmentcontaineriterator i =
+ m_composition->getTriggerSegments().begin();
+ i != m_composition->getTriggerSegments().end(); ++i) {
+ m_segment->insertItem
+ (QString("%1. %2").arg(n++).arg(strtoqstr((*i)->getSegment()->getLabel())));
+ }
+
+ label = new QLabel(i18n("Perform with timing: "), frame);
+ layout->addWidget(label, 1, 0);
+
+ m_adjustTime = new KComboBox(frame);
+ layout->addWidget(m_adjustTime, 1, 1);
+
+ m_adjustTime->insertItem(i18n("As stored"));
+ m_adjustTime->insertItem(i18n("Truncate if longer than note"));
+ m_adjustTime->insertItem(i18n("End at same time as note"));
+ m_adjustTime->insertItem(i18n("Stretch or squash segment to note duration"));
+
+ m_retune = new QCheckBox(i18n("Adjust pitch to note"), frame);
+ m_retune->setChecked(true);
+
+ layout->addWidget(m_retune, 2, 1);
+
+ setupFromConfig();
+}
+
+void
+TriggerSegmentDialog::setupFromConfig()
+{
+ KConfig *config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+
+ int seg = config->readNumEntry("triggersegmentlastornament", 0);
+ std::string timing = qstrtostr
+ (config->readEntry
+ ("triggersegmenttiming",
+ strtoqstr(BaseProperties::TRIGGER_SEGMENT_ADJUST_SQUISH)));
+ bool retune = config->readBoolEntry("triggersegmentretune", true);
+
+ if (seg >= 0 && seg < m_segment->count())
+ m_segment->setCurrentItem(seg);
+
+ if (timing == BaseProperties::TRIGGER_SEGMENT_ADJUST_NONE) {
+ m_adjustTime->setCurrentItem(0);
+ } else if (timing == BaseProperties::TRIGGER_SEGMENT_ADJUST_SQUISH) {
+ m_adjustTime->setCurrentItem(3);
+ } else if (timing == BaseProperties::TRIGGER_SEGMENT_ADJUST_SYNC_START) {
+ m_adjustTime->setCurrentItem(1);
+ } else if (timing == BaseProperties::TRIGGER_SEGMENT_ADJUST_SYNC_END) {
+ m_adjustTime->setCurrentItem(2);
+ }
+
+ m_retune->setChecked(retune);
+}
+
+TriggerSegmentId
+TriggerSegmentDialog::getId() const
+{
+ int ix = m_segment->currentItem();
+
+ for (Composition::triggersegmentcontaineriterator i =
+ m_composition->getTriggerSegments().begin();
+ i != m_composition->getTriggerSegments().end(); ++i) {
+
+ if (ix == 0)
+ return (*i)->getId();
+ --ix;
+ }
+
+ return 0;
+}
+
+bool
+TriggerSegmentDialog::getRetune() const
+{
+ return m_retune->isChecked();
+}
+
+std::string
+TriggerSegmentDialog::getTimeAdjust() const
+{
+ int option = m_adjustTime->currentItem();
+
+ switch (option) {
+
+ case 0:
+ return BaseProperties::TRIGGER_SEGMENT_ADJUST_NONE;
+ case 1:
+ return BaseProperties::TRIGGER_SEGMENT_ADJUST_SYNC_START;
+ case 2:
+ return BaseProperties::TRIGGER_SEGMENT_ADJUST_SYNC_END;
+ case 3:
+ return BaseProperties::TRIGGER_SEGMENT_ADJUST_SQUISH;
+
+ default:
+ return BaseProperties::TRIGGER_SEGMENT_ADJUST_NONE;
+ }
+}
+
+void
+TriggerSegmentDialog::slotOk()
+{
+ KConfig *config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+
+ config->writeEntry("triggersegmenttiming", strtoqstr(getTimeAdjust()));
+ config->writeEntry("triggersegmentretune", m_retune->isChecked());
+ config->writeEntry("triggersegmentlastornament", m_segment->currentItem());
+
+ accept();
+}
+
+}
+#include "TriggerSegmentDialog.moc"
diff --git a/src/gui/dialogs/TriggerSegmentDialog.h b/src/gui/dialogs/TriggerSegmentDialog.h
new file mode 100644
index 0000000..3f74f45
--- /dev/null
+++ b/src/gui/dialogs/TriggerSegmentDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_TRIGGERSEGMENTDIALOG_H_
+#define _RG_TRIGGERSEGMENTDIALOG_H_
+
+#include "base/TriggerSegment.h"
+#include <string>
+#include <kdialogbase.h>
+
+
+class QWidget;
+class QCheckBox;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class Composition;
+
+
+class TriggerSegmentDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ TriggerSegmentDialog(QWidget *parent, Composition *);
+
+ TriggerSegmentId getId() const;
+ bool getRetune() const;
+ std::string getTimeAdjust() const;
+
+public slots:
+ void slotOk();
+
+protected:
+ void setupFromConfig();
+
+ Composition *m_composition;
+ KComboBox *m_segment;
+ QCheckBox *m_retune;
+ KComboBox *m_adjustTime;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/TupletDialog.cpp b/src/gui/dialogs/TupletDialog.cpp
new file mode 100644
index 0000000..ed1c583
--- /dev/null
+++ b/src/gui/dialogs/TupletDialog.cpp
@@ -0,0 +1,365 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "TupletDialog.h"
+#include <qlayout.h>
+
+#include <klocale.h>
+#include "base/NotationTypes.h"
+#include "gui/editors/notation/NotationStrings.h"
+#include "gui/editors/notation/NotePixmapFactory.h"
+#include <kcombobox.h>
+#include <kdialogbase.h>
+#include <qcheckbox.h>
+#include <qframe.h>
+#include <qgrid.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qobject.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+TupletDialog::TupletDialog(QWidget *parent, Note::Type defaultUnitType,
+ timeT maxDuration) :
+ KDialogBase(parent, 0, true, i18n("Tuplet"), Ok | Cancel | Help),
+ m_maxDuration(maxDuration)
+{
+ setHelp("nv-tuplets");
+ QVBox *vbox = makeVBoxMainWidget();
+
+ QGroupBox *timingBox = new QGroupBox
+ (1, Horizontal, i18n("New timing for tuplet group"), vbox);
+
+ if (m_maxDuration > 0) {
+
+ // bit of a sanity check
+ if (maxDuration < Note(Note::Semiquaver).getDuration()) {
+ maxDuration = Note(Note::Semiquaver).getDuration();
+ }
+
+ Note::Type maxUnitType =
+ Note::getNearestNote(maxDuration / 2, 0).getNoteType();
+ if (defaultUnitType > maxUnitType)
+ defaultUnitType = maxUnitType;
+ }
+
+ QFrame *timingFrame = new QFrame(timingBox);
+ QGridLayout *timingLayout = new QGridLayout(timingFrame, 3, 3, 5, 5);
+
+ timingLayout->addWidget(new QLabel(i18n("Play "), timingFrame), 0, 0);
+
+ m_untupledCombo = new KComboBox(timingFrame);
+ timingLayout->addWidget(m_untupledCombo, 0, 1);
+
+ m_unitCombo = new KComboBox(timingFrame);
+ timingLayout->addWidget(m_unitCombo, 0, 2);
+
+ for (Note::Type t = Note::Shortest; t <= Note::Longest; ++t) {
+ Note note(t);
+ timeT duration(note.getDuration());
+ if (maxDuration > 0 && (2 * duration > maxDuration))
+ break;
+ timeT e; // error factor, ignore
+ m_unitCombo->insertItem(NotePixmapFactory::toQPixmap
+ (NotePixmapFactory::makeNoteMenuPixmap(duration, e)),
+ NotationStrings::makeNoteMenuLabel(duration, false, e, true));
+ if (defaultUnitType == t) {
+ m_unitCombo->setCurrentItem(m_unitCombo->count() - 1);
+ }
+ }
+
+ timingLayout->addWidget(new QLabel(i18n("in the time of "), timingFrame), 1, 0);
+
+ m_tupledCombo = new KComboBox(timingFrame);
+ timingLayout->addWidget(m_tupledCombo, 1, 1);
+
+ m_hasTimingAlready = new QCheckBox
+ (i18n("Timing is already correct: update display only"), timingFrame);
+ m_hasTimingAlready->setChecked(false);
+ timingLayout->addMultiCellWidget(m_hasTimingAlready, 2, 2, 0, 2);
+
+ connect(m_hasTimingAlready, SIGNAL(clicked()), this, SLOT(slotHasTimingChanged()));
+
+ updateUntupledCombo();
+ updateTupledCombo();
+
+ m_timingDisplayBox = new QGroupBox
+ (1, Horizontal, i18n("Timing calculations"), vbox);
+
+ QGrid *timingDisplayGrid = new QGrid(3, QGrid::Horizontal, m_timingDisplayBox);
+
+ if (maxDuration > 0) {
+
+ new QLabel(i18n("Selected region:"), timingDisplayGrid);
+ new QLabel("", timingDisplayGrid);
+ m_selectionDurationDisplay = new QLabel("x", timingDisplayGrid);
+ m_selectionDurationDisplay->setAlignment(int(QLabel::AlignVCenter |
+ QLabel::AlignRight));
+ } else {
+ m_selectionDurationDisplay = 0;
+ }
+
+ new QLabel(i18n("Group with current timing:"), timingDisplayGrid);
+ m_untupledDurationCalculationDisplay = new QLabel("x", timingDisplayGrid);
+ m_untupledDurationDisplay = new QLabel("x", timingDisplayGrid);
+ m_untupledDurationDisplay->setAlignment(int(QLabel::AlignVCenter |
+ QLabel::AlignRight));
+
+ new QLabel(i18n("Group with new timing:"), timingDisplayGrid);
+ m_tupledDurationCalculationDisplay = new QLabel("x", timingDisplayGrid);
+ m_tupledDurationDisplay = new QLabel("x", timingDisplayGrid);
+ m_tupledDurationDisplay->setAlignment(int(QLabel::AlignVCenter |
+ QLabel::AlignRight));
+
+ new QLabel(i18n("Gap created by timing change:"), timingDisplayGrid);
+ m_newGapDurationCalculationDisplay = new QLabel("x", timingDisplayGrid);
+ m_newGapDurationDisplay = new QLabel("x", timingDisplayGrid);
+ m_newGapDurationDisplay->setAlignment(int(QLabel::AlignVCenter |
+ QLabel::AlignRight));
+
+ if (maxDuration > 0) {
+
+ new QLabel(i18n("Unchanged at end of selection:"), timingDisplayGrid);
+ m_unchangedDurationCalculationDisplay = new QLabel
+ ("x", timingDisplayGrid);
+ m_unchangedDurationDisplay = new QLabel("x", timingDisplayGrid);
+ m_unchangedDurationDisplay->setAlignment(int(QLabel::AlignVCenter |
+ QLabel::AlignRight));
+
+ } else {
+ m_unchangedDurationDisplay = 0;
+ }
+
+ updateTimingDisplays();
+
+ QObject::connect(m_unitCombo, SIGNAL(activated(const QString &)),
+ this, SLOT(slotUnitChanged(const QString &)));
+
+ QObject::connect(m_untupledCombo, SIGNAL(activated(const QString &)),
+ this, SLOT(slotUntupledChanged(const QString &)));
+ QObject::connect(m_untupledCombo, SIGNAL(textChanged(const QString &)),
+ this, SLOT(slotUntupledChanged(const QString &)));
+
+ QObject::connect(m_tupledCombo, SIGNAL(activated(const QString &)),
+ this, SLOT(slotTupledChanged(const QString &)));
+ QObject::connect(m_tupledCombo, SIGNAL(textChanged(const QString &)),
+ this, SLOT(slotTupledChanged(const QString &)));
+}
+
+void
+TupletDialog::slotHasTimingChanged()
+{
+ updateUntupledCombo();
+ updateTupledCombo();
+ m_timingDisplayBox->setEnabled(!m_hasTimingAlready->isChecked());
+}
+
+Note::Type
+TupletDialog::getUnitType() const
+{
+ return Note::Shortest + m_unitCombo->currentItem();
+}
+
+int
+TupletDialog::getUntupledCount() const
+{
+ bool isNumeric = true;
+ int count = m_untupledCombo->currentText().toInt(&isNumeric);
+ if (count == 0 || !isNumeric)
+ return 1;
+ else
+ return count;
+}
+
+int
+TupletDialog::getTupledCount() const
+{
+ bool isNumeric = true;
+ int count = m_tupledCombo->currentText().toInt(&isNumeric);
+ if (count == 0 || !isNumeric)
+ return 1;
+ else
+ return count;
+}
+
+bool
+TupletDialog::hasTimingAlready() const
+{
+ return m_hasTimingAlready->isChecked();
+}
+
+void
+TupletDialog::updateUntupledCombo()
+{
+ // Untupled combo can contain numbers up to the maximum
+ // duration divided by the unit duration. If there's no
+ // maximum, we'll have to put in some likely values and
+ // allow the user to edit it. Both the numerical combos
+ // should possibly be spinboxes, except I think I like
+ // being able to "suggest" a few values
+
+ int maxValue = 12;
+
+ if (m_maxDuration) {
+ if (m_hasTimingAlready->isChecked()) {
+ maxValue = (m_maxDuration * 2) / Note(getUnitType()).getDuration();
+ } else {
+ maxValue = m_maxDuration / Note(getUnitType()).getDuration();
+ }
+ }
+
+ QString previousText = m_untupledCombo->currentText();
+ if (previousText.toInt() == 0) {
+ if (maxValue < 3)
+ previousText = QString("%1").arg(maxValue);
+ else
+ previousText = "3";
+ }
+
+ m_untupledCombo->clear();
+ bool setText = false;
+
+ for (int i = 1; i <= maxValue; ++i) {
+ QString text = QString("%1").arg(i);
+ m_untupledCombo->insertItem(text);
+ if (m_hasTimingAlready->isChecked()) {
+ if (i == (m_maxDuration * 3) / (Note(getUnitType()).getDuration()*2)) {
+ m_untupledCombo->setCurrentItem(m_untupledCombo->count() - 1);
+ }
+ } else if (text == previousText) {
+ m_untupledCombo->setCurrentItem(m_untupledCombo->count() - 1);
+ setText = true;
+ }
+ }
+
+ if (!setText) {
+ m_untupledCombo->setEditText(previousText);
+ }
+}
+
+void
+TupletDialog::updateTupledCombo()
+{
+ // should contain all positive integers less than the
+ // largest value in the untupled combo. In principle
+ // we can support values larger, but we can't quite
+ // do the tupleting transformation yet
+
+ int untupled = getUntupledCount();
+
+ QString previousText = m_tupledCombo->currentText();
+ if (previousText.toInt() == 0 ||
+ previousText.toInt() > untupled) {
+ if (untupled < 2)
+ previousText = QString("%1").arg(untupled);
+ else
+ previousText = "2";
+ }
+
+ m_tupledCombo->clear();
+
+ for (int i = 1; i < untupled; ++i) {
+ QString text = QString("%1").arg(i);
+ m_tupledCombo->insertItem(text);
+ if (m_hasTimingAlready->isChecked()) {
+ if (i == m_maxDuration / Note(getUnitType()).getDuration()) {
+ m_tupledCombo->setCurrentItem(m_tupledCombo->count() - 1);
+ }
+ } else if (text == previousText) {
+ m_tupledCombo->setCurrentItem(m_tupledCombo->count() - 1);
+ }
+ }
+}
+
+void
+TupletDialog::updateTimingDisplays()
+{
+ timeT unitDuration = Note(getUnitType()).getDuration();
+
+ int untupledCount = getUntupledCount();
+ int tupledCount = getTupledCount();
+
+ timeT untupledDuration = unitDuration * untupledCount;
+ timeT tupledDuration = unitDuration * tupledCount;
+
+ if (m_selectionDurationDisplay) {
+ m_selectionDurationDisplay->setText(QString("%1").arg(m_maxDuration));
+ }
+
+ m_untupledDurationCalculationDisplay->setText
+ (QString(" %1 x %2 = ").arg(untupledCount).arg(unitDuration));
+ m_untupledDurationDisplay->setText
+ (QString("%1").arg(untupledDuration));
+
+ m_tupledDurationCalculationDisplay->setText
+ (QString(" %1 x %2 = ").arg(tupledCount).arg(unitDuration));
+ m_tupledDurationDisplay->setText
+ (QString("%1").arg(tupledDuration));
+
+ m_newGapDurationCalculationDisplay->setText
+ (QString(" %1 - %2 = ").arg(untupledDuration).arg(tupledDuration));
+ m_newGapDurationDisplay->setText
+ (QString("%1").arg(untupledDuration - tupledDuration));
+
+ if (m_selectionDurationDisplay && m_unchangedDurationDisplay) {
+ if (m_maxDuration != untupledDuration) {
+ m_unchangedDurationCalculationDisplay->setText
+ (QString(" %1 - %2 = ").arg(m_maxDuration).arg(untupledDuration));
+ } else {
+ m_unchangedDurationCalculationDisplay->setText("");
+ }
+ m_unchangedDurationDisplay->setText
+ (QString("%1").arg(m_maxDuration - untupledDuration));
+ }
+}
+
+void
+TupletDialog::slotUnitChanged(const QString &)
+{
+ updateUntupledCombo();
+ updateTupledCombo();
+ updateTimingDisplays();
+}
+
+void
+TupletDialog::slotUntupledChanged(const QString &)
+{
+ updateTupledCombo();
+ updateTimingDisplays();
+}
+
+void
+TupletDialog::slotTupledChanged(const QString &)
+{
+ updateTimingDisplays();
+}
+
+}
+#include "TupletDialog.moc"
diff --git a/src/gui/dialogs/TupletDialog.h b/src/gui/dialogs/TupletDialog.h
new file mode 100644
index 0000000..bc7252b
--- /dev/null
+++ b/src/gui/dialogs/TupletDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_TUPLETDIALOG_H_
+#define _RG_TUPLETDIALOG_H_
+
+#include "base/NotationTypes.h"
+#include <kdialogbase.h>
+#include "base/Event.h"
+
+
+class QWidget;
+class QString;
+class QLabel;
+class QGroupBox;
+class QCheckBox;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+
+
+class TupletDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ TupletDialog(QWidget *parent,
+ Note::Type defaultUnitType,
+ timeT maxDuration = 0);
+
+ Note::Type getUnitType() const;
+ int getUntupledCount() const;
+ int getTupledCount() const;
+ bool hasTimingAlready() const;
+
+public slots:
+ void slotUnitChanged(const QString &);
+ void slotUntupledChanged(const QString &);
+ void slotTupledChanged(const QString &);
+ void slotHasTimingChanged();
+
+protected:
+
+ void updateUntupledCombo();
+ void updateTupledCombo();
+ void updateTimingDisplays();
+
+ //--------------- Data members ---------------------------------
+
+ KComboBox *m_unitCombo;
+ KComboBox *m_untupledCombo;
+ KComboBox *m_tupledCombo;
+
+ QCheckBox *m_hasTimingAlready;
+
+ QGroupBox *m_timingDisplayBox;
+ QLabel *m_selectionDurationDisplay;
+ QLabel *m_untupledDurationCalculationDisplay;
+ QLabel *m_untupledDurationDisplay;
+ QLabel *m_tupledDurationCalculationDisplay;
+ QLabel *m_tupledDurationDisplay;
+ QLabel *m_newGapDurationCalculationDisplay;
+ QLabel *m_newGapDurationDisplay;
+ QLabel *m_unchangedDurationCalculationDisplay;
+ QLabel *m_unchangedDurationDisplay;
+
+ timeT m_maxDuration;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/UnusedAudioSelectionDialog.cpp b/src/gui/dialogs/UnusedAudioSelectionDialog.cpp
new file mode 100644
index 0000000..0a44168
--- /dev/null
+++ b/src/gui/dialogs/UnusedAudioSelectionDialog.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "UnusedAudioSelectionDialog.h"
+
+#include <klocale.h>
+#include <kdialogbase.h>
+#include <klistview.h>
+#include <qfileinfo.h>
+#include <qlabel.h>
+#include <qlistview.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+UnusedAudioSelectionDialog::UnusedAudioSelectionDialog(QWidget *parent,
+ QString introductoryText,
+ std::vector<QString> fileNames,
+ bool offerCancel) :
+ KDialogBase(parent, 0, true, i18n("Select Unused Audio Files"), (offerCancel ? (Ok | Cancel) : Ok))
+{
+ QVBox *vbox = makeVBoxMainWidget();
+ new QLabel(introductoryText, vbox);
+
+ m_listView = new KListView(vbox);
+
+ m_listView->addColumn(i18n("File name"));
+ m_listView->addColumn(i18n("File size"));
+ m_listView->addColumn(i18n("Last modified date"));
+
+ for (unsigned int i = 0; i < fileNames.size(); ++i) {
+ QString fileName = fileNames[i];
+ QFileInfo info(fileName);
+ QString fileSize = i18n(" (not found) ");
+ QString fileDate;
+ if (info.exists()) {
+ fileSize = QString(" %1 ").arg(info.size());
+ fileDate = QString(" %1 ").arg(info.lastModified().toString());
+ }
+ QListViewItem *item = new KListViewItem
+ (m_listView, fileName, fileSize, fileDate);
+ }
+
+ m_listView->setSelectionMode(QListView::Multi);
+}
+
+std::vector<QString>
+UnusedAudioSelectionDialog::getSelectedAudioFileNames() const
+{
+ std::vector<QString> selectedNames;
+
+ QListViewItem *item = m_listView->firstChild();
+
+ while (item) {
+
+ if (m_listView->isSelected(item)) {
+ selectedNames.push_back(item->text(0));
+ }
+
+ item = item->nextSibling();
+ }
+
+ return selectedNames;
+}
+
+}
diff --git a/src/gui/dialogs/UnusedAudioSelectionDialog.h b/src/gui/dialogs/UnusedAudioSelectionDialog.h
new file mode 100644
index 0000000..e11301a
--- /dev/null
+++ b/src/gui/dialogs/UnusedAudioSelectionDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_UNUSEDAUDIOSELECTIONDIALOG_H_
+#define _RG_UNUSEDAUDIOSELECTIONDIALOG_H_
+
+#include <kdialogbase.h>
+#include <qstring.h>
+#include <vector>
+
+
+class QWidget;
+class QListView;
+
+
+namespace Rosegarden
+{
+
+
+
+class UnusedAudioSelectionDialog : public KDialogBase
+{
+public:
+ UnusedAudioSelectionDialog(QWidget *,
+ QString introductoryText,
+ std::vector<QString> fileNames,
+ bool offerCancel = true);
+
+ std::vector<QString> getSelectedAudioFileNames() const;
+
+protected:
+ QListView *m_listView;
+};
+
+
+
+
+}
+
+#endif
diff --git a/src/gui/dialogs/UseOrnamentDialog.cpp b/src/gui/dialogs/UseOrnamentDialog.cpp
new file mode 100644
index 0000000..971f170
--- /dev/null
+++ b/src/gui/dialogs/UseOrnamentDialog.cpp
@@ -0,0 +1,264 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "UseOrnamentDialog.h"
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include "base/BaseProperties.h"
+#include <klocale.h>
+#include "misc/Strings.h"
+#include "document/ConfigGroups.h"
+#include "base/Composition.h"
+#include "base/NotationTypes.h"
+#include "base/TriggerSegment.h"
+#include "gui/editors/notation/NotePixmapFactory.h"
+#include <kcombobox.h>
+#include <kconfig.h>
+#include <kdialogbase.h>
+#include <qcheckbox.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+UseOrnamentDialog::UseOrnamentDialog(QWidget *parent,
+ Composition *composition) :
+ KDialogBase(parent, "useornamentdialog", true, i18n("Use Ornament"),
+ Ok | Cancel, Ok),
+ m_composition(composition)
+{
+ QVBox *vbox = makeVBoxMainWidget();
+ QLabel *label;
+
+ QGroupBox *notationBox = new QGroupBox(1, Horizontal, i18n("Notation"), vbox);
+
+ QFrame *frame = new QFrame(notationBox);
+ QGridLayout *layout = new QGridLayout(frame, 4, 1, 5, 5);
+
+ label = new QLabel(i18n("Display as: "), frame);
+ layout->addWidget(label, 0, 0);
+
+ m_mark = new KComboBox(frame);
+ layout->addWidget(m_mark, 0, 1);
+
+ m_marks.push_back(Marks::Trill);
+ m_marks.push_back(Marks::LongTrill);
+ m_marks.push_back(Marks::TrillLine);
+ m_marks.push_back(Marks::Turn);
+ m_marks.push_back(Marks::Mordent);
+ m_marks.push_back(Marks::MordentInverted);
+ m_marks.push_back(Marks::MordentLong);
+ m_marks.push_back(Marks::MordentLongInverted);
+
+ const QString markLabels[] = {
+ i18n("Trill"), i18n("Trill with line"), i18n("Trill line only"),
+ i18n("Turn"), i18n("Mordent"), i18n("Inverted mordent"),
+ i18n("Long mordent"), i18n("Long inverted mordent"),
+ };
+
+ for (size_t i = 0; i < m_marks.size(); ++i) {
+ m_mark->insertItem(NotePixmapFactory::toQPixmap
+ (NotePixmapFactory::makeMarkMenuPixmap(m_marks[i])),
+ markLabels[i]);
+ }
+ m_mark->insertItem(i18n("Text mark"));
+
+ connect(m_mark, SIGNAL(activated(int)), this, SLOT(slotMarkChanged(int)));
+
+ m_textLabel = new QLabel(i18n(" Text: "), frame);
+ layout->addWidget(m_textLabel, 0, 2);
+
+ m_text = new QLineEdit(frame);
+ layout->addWidget(m_text, 0, 3);
+
+ QGroupBox *performBox = new QGroupBox(1, Horizontal, i18n("Performance"), vbox);
+
+ frame = new QFrame(performBox);
+ layout = new QGridLayout(frame, 3, 2, 5, 5);
+
+ label = new QLabel(i18n("Perform using triggered segment: "), frame);
+ layout->addWidget(label, 0, 0);
+
+ m_ornament = new KComboBox(frame);
+ layout->addWidget(m_ornament, 0, 1);
+
+ int n = 1;
+ for (Composition::triggersegmentcontaineriterator i =
+ m_composition->getTriggerSegments().begin();
+ i != m_composition->getTriggerSegments().end(); ++i) {
+ m_ornament->insertItem
+ (QString("%1. %2").arg(n++).arg(strtoqstr((*i)->getSegment()->getLabel())));
+ }
+
+ label = new QLabel(i18n("Perform with timing: "), frame);
+ layout->addWidget(label, 1, 0);
+
+ m_adjustTime = new KComboBox(frame);
+ layout->addWidget(m_adjustTime, 1, 1);
+
+ m_adjustTime->insertItem(i18n("As stored"));
+ m_adjustTime->insertItem(i18n("Truncate if longer than note"));
+ m_adjustTime->insertItem(i18n("End at same time as note"));
+ m_adjustTime->insertItem(i18n("Stretch or squash segment to note duration"));
+
+ m_retune = new QCheckBox(i18n("Adjust pitch to note"), frame);
+ m_retune->setChecked(true);
+
+ layout->addWidget(m_retune, 2, 1);
+
+ setupFromConfig();
+}
+
+void
+UseOrnamentDialog::setupFromConfig()
+{
+ KConfig *config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+
+ Mark mark = qstrtostr(config->readEntry("useornamentmark", "trill"));
+ int seg = config->readNumEntry("useornamentlastornament", 0);
+ std::string timing = qstrtostr
+ (config->readEntry
+ ("useornamenttiming",
+ strtoqstr(BaseProperties::TRIGGER_SEGMENT_ADJUST_SQUISH)));
+ bool retune = config->readBoolEntry("useornamentretune", true);
+
+ size_t i = 0;
+ for (i = 0; i < m_marks.size(); ++i) {
+ if (mark == m_marks[i]) {
+ m_mark->setCurrentItem(i);
+ m_text->setEnabled(false);
+ break;
+ }
+ }
+ if (i >= m_marks.size()) {
+ m_mark->setCurrentItem(m_marks.size());
+ m_text->setEnabled(true);
+ m_text->setText(strtoqstr(Marks::getTextFromMark(mark)));
+ }
+
+ if (seg >= 0 && seg < m_ornament->count())
+ m_ornament->setCurrentItem(seg);
+
+ if (timing == BaseProperties::TRIGGER_SEGMENT_ADJUST_NONE) {
+ m_adjustTime->setCurrentItem(0);
+ } else if (timing == BaseProperties::TRIGGER_SEGMENT_ADJUST_SQUISH) {
+ m_adjustTime->setCurrentItem(3);
+ } else if (timing == BaseProperties::TRIGGER_SEGMENT_ADJUST_SYNC_START) {
+ m_adjustTime->setCurrentItem(1);
+ } else if (timing == BaseProperties::TRIGGER_SEGMENT_ADJUST_SYNC_END) {
+ m_adjustTime->setCurrentItem(2);
+ }
+
+ m_retune->setChecked(retune);
+}
+
+TriggerSegmentId
+UseOrnamentDialog::getId() const
+{
+ int ix = m_ornament->currentItem();
+
+ for (Composition::triggersegmentcontaineriterator i =
+ m_composition->getTriggerSegments().begin();
+ i != m_composition->getTriggerSegments().end(); ++i) {
+
+ if (ix == 0)
+ return (*i)->getId();
+ --ix;
+ }
+
+ return 0;
+}
+
+Mark
+UseOrnamentDialog::getMark() const
+{
+ if (int(m_marks.size()) > m_mark->currentItem())
+ return m_marks[m_mark->currentItem()];
+ else
+ return Marks::getTextMark(qstrtostr(m_text->text()));
+}
+
+bool
+UseOrnamentDialog::getRetune() const
+{
+ return m_retune->isChecked();
+}
+
+std::string
+UseOrnamentDialog::getTimeAdjust() const
+{
+ int option = m_adjustTime->currentItem();
+
+ switch (option) {
+
+ case 0:
+ return BaseProperties::TRIGGER_SEGMENT_ADJUST_NONE;
+ case 1:
+ return BaseProperties::TRIGGER_SEGMENT_ADJUST_SYNC_START;
+ case 2:
+ return BaseProperties::TRIGGER_SEGMENT_ADJUST_SYNC_END;
+ case 3:
+ return BaseProperties::TRIGGER_SEGMENT_ADJUST_SQUISH;
+
+ default:
+ return BaseProperties::TRIGGER_SEGMENT_ADJUST_NONE;
+ }
+}
+
+void
+UseOrnamentDialog::slotMarkChanged(int i)
+{
+ if (i == 2) {
+ m_text->setEnabled(true);
+ } else {
+ m_text->setEnabled(false);
+ }
+}
+
+void
+UseOrnamentDialog::slotOk()
+{
+ KConfig *config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+
+ config->writeEntry("useornamentmark", strtoqstr(getMark()));
+ config->writeEntry("useornamenttiming", strtoqstr(getTimeAdjust()));
+ config->writeEntry("useornamentretune", m_retune->isChecked());
+ config->writeEntry("useornamentlastornament", m_ornament->currentItem());
+
+ accept();
+}
+
+}
+#include "UseOrnamentDialog.moc"
diff --git a/src/gui/dialogs/UseOrnamentDialog.h b/src/gui/dialogs/UseOrnamentDialog.h
new file mode 100644
index 0000000..d721329
--- /dev/null
+++ b/src/gui/dialogs/UseOrnamentDialog.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_USEORNAMENTDIALOG_H_
+#define _RG_USEORNAMENTDIALOG_H_
+
+#include "base/TriggerSegment.h"
+#include "base/NotationTypes.h"
+#include <string>
+#include <kdialogbase.h>
+#include <vector>
+
+
+class QWidget;
+class QLineEdit;
+class QLabel;
+class QCheckBox;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class Composition;
+
+
+class UseOrnamentDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ UseOrnamentDialog(QWidget *parent, Composition *);
+
+ TriggerSegmentId getId() const;
+ Mark getMark() const;
+ bool getRetune() const;
+ std::string getTimeAdjust() const;
+
+public slots:
+ void slotOk();
+ void slotMarkChanged(int);
+
+protected:
+ void setupFromConfig();
+
+ std::vector<Mark> m_marks;
+
+ Composition *m_composition;
+ KComboBox *m_ornament;
+ KComboBox *m_mark;
+ QLabel *m_textLabel;
+ QLineEdit *m_text;
+ QCheckBox *m_retune;
+ KComboBox *m_adjustTime;
+};
+
+
+}
+
+#endif
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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <bownie@bownie.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ This file is Copyright 2007-2008
+ Yves Guillemot <yc.guillemot@wanadoo.fr>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ This file is Copyright 2007-2008
+ Yves Guillemot <yc.guillemot@wanadoo.fr>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <bownie@bownie.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <bownie@bownie.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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
+ //## <slowpic@web.de> 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ This file is Copyright 2007-2008
+ Yves Guillemot <yc.guillemot@wanadoo.fr>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ This file is Copyright 2007-2008
+ Yves Guillemot <yc.guillemot@wanadoo.fr>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <mcs@astro.caltech.edu>.
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <mcs@astro.caltech.edu>.
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <plcl@users.sourceforge.net>
+ D. Michael McIntyre <dmmcintyr@users.sourceforge.net>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <plcl@users.sourceforge.net>
+ D. Michael McIntyre <dmmcintyr@users.sourceforge.net>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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
diff --git a/src/gui/general/ActiveItem.cpp b/src/gui/general/ActiveItem.cpp
new file mode 100644
index 0000000..00d967f
--- /dev/null
+++ b/src/gui/general/ActiveItem.cpp
@@ -0,0 +1,32 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ActiveItem.h"
+
+
+
+namespace Rosegarden
+{
+}
diff --git a/src/gui/general/ActiveItem.h b/src/gui/general/ActiveItem.h
new file mode 100644
index 0000000..f8f5339
--- /dev/null
+++ b/src/gui/general/ActiveItem.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ACTIVEITEM_H_
+#define _RG_ACTIVEITEM_H_
+
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * An interface for canvas items which are capable of handling
+ * mouse events
+ */
+class ActiveItem
+{
+public:
+ virtual void handleMousePress(QMouseEvent*) = 0;
+ virtual void handleMouseMove(QMouseEvent*) = 0;
+ virtual void handleMouseRelease(QMouseEvent*) = 0;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/general/BarLine.cpp b/src/gui/general/BarLine.cpp
new file mode 100644
index 0000000..42bb936
--- /dev/null
+++ b/src/gui/general/BarLine.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "BarLine.h"
+
+#include <qpainter.h>
+
+namespace Rosegarden {
+
+void
+BarLine::drawShape(QPainter &painter)
+{
+ int bx = int(x());
+ int by = int(y());
+
+ switch (m_style) {
+
+ case LinedStaff::PlainBar:
+ painter.drawRect(bx, by, m_baseBarThickness, m_barLineHeight);
+ break;
+
+ case LinedStaff::DoubleBar:
+ painter.drawRect(bx, by, m_baseBarThickness, m_barLineHeight);
+ painter.drawRect(bx + m_baseBarThickness * 3, by,
+ m_baseBarThickness, m_barLineHeight);
+ break;
+
+ case LinedStaff::HeavyDoubleBar:
+ bx -= m_baseBarThickness * 5;
+ painter.drawRect(bx, by, m_baseBarThickness, m_barLineHeight);
+ painter.drawRect(bx + m_baseBarThickness * 3, by,
+ m_baseBarThickness * 3, m_barLineHeight);
+ break;
+
+ case LinedStaff::RepeatEndBar:
+ bx -= m_baseBarThickness * 5 + m_lineSpacing * 2 / 3;
+ painter.drawEllipse(bx, by + m_barLineHeight / 2 - (m_lineSpacing * 2 / 3),
+ m_lineSpacing / 3, m_lineSpacing / 3);
+ painter.drawEllipse(bx, by + m_barLineHeight / 2 + (m_lineSpacing / 3),
+ m_lineSpacing / 3, m_lineSpacing / 3);
+ bx += m_lineSpacing * 2 / 3;
+ painter.drawRect(bx, by, m_baseBarThickness, m_barLineHeight);
+ painter.drawRect(bx + m_baseBarThickness * 3, by,
+ m_baseBarThickness * 3, m_barLineHeight);
+ break;
+
+ case LinedStaff::RepeatStartBar:
+
+ if (m_inset > 0) {
+ painter.drawRect(bx, by, m_baseBarThickness, m_barLineHeight);
+ bx += m_inset;
+ }
+
+ painter.drawRect(bx, by, m_baseBarThickness * 3, m_barLineHeight);
+ painter.drawRect(bx + m_baseBarThickness * 5, by,
+ m_baseBarThickness, m_barLineHeight);
+ bx += m_baseBarThickness * 6 + (m_lineSpacing / 3);
+ painter.drawEllipse(bx, by + m_barLineHeight / 2 - (m_lineSpacing * 2 / 3),
+ m_lineSpacing / 3, m_lineSpacing / 3);
+ painter.drawEllipse(bx, by + m_barLineHeight / 2 + (m_lineSpacing / 3),
+ m_lineSpacing / 3, m_lineSpacing / 3);
+ break;
+
+ case LinedStaff::RepeatBothBar:
+
+ if (m_inset > 0) {
+ painter.drawRect(bx, by, m_baseBarThickness, m_barLineHeight);
+ bx += m_inset;
+ }
+
+ bx -= m_baseBarThickness * 4 + m_lineSpacing * 2 / 3;
+ painter.drawEllipse(bx, by + m_barLineHeight / 2 - (m_lineSpacing * 2 / 3),
+ m_lineSpacing / 3, m_lineSpacing / 3);
+ painter.drawEllipse(bx, by + m_barLineHeight / 2 + (m_lineSpacing / 3),
+ m_lineSpacing / 3, m_lineSpacing / 3);
+ bx += m_lineSpacing * 2 / 3;
+ painter.drawRect(bx, by, m_baseBarThickness, m_barLineHeight);
+ painter.drawRect(bx + m_baseBarThickness * 3, by,
+ m_baseBarThickness * 3, m_barLineHeight);
+ painter.drawRect(bx + m_baseBarThickness * 8, by,
+ m_baseBarThickness, m_barLineHeight);
+ bx += m_baseBarThickness * 9 + (m_lineSpacing / 3);
+ painter.drawEllipse(bx, by + m_barLineHeight / 2 - (m_lineSpacing * 2 / 3),
+ m_lineSpacing / 3, m_lineSpacing / 3);
+ painter.drawEllipse(bx, by + m_barLineHeight / 2 + (m_lineSpacing / 3),
+ m_lineSpacing / 3, m_lineSpacing / 3);
+
+ break;
+
+ case LinedStaff::NoVisibleBar:
+ break;
+ }
+}
+
+QPointArray
+BarLine::areaPoints() const
+{
+ int bx = int(x());
+ int by = int(y());
+ int x0 = bx, y0 = by, x1, y1 = by + m_barLineHeight;
+
+ switch (m_style) {
+
+ case LinedStaff::PlainBar:
+ x1 = x0 + m_baseBarThickness;
+ break;
+
+ case LinedStaff::DoubleBar:
+ x1 = x0 + m_baseBarThickness * 4;
+ break;
+
+ case LinedStaff::HeavyDoubleBar:
+ x0 -= m_baseBarThickness * 6;
+ x1 = bx;
+ break;
+
+ case LinedStaff::RepeatEndBar:
+ x0 -= m_baseBarThickness * 6 + m_lineSpacing * 2 / 3;
+ x1 = bx;
+ break;
+
+ case LinedStaff::RepeatStartBar:
+ x1 = x0 + m_baseBarThickness * 6 + m_lineSpacing * 2 / 3 + m_inset;
+ break;
+
+ case LinedStaff::RepeatBothBar:
+ x0 -= m_baseBarThickness * 4 + m_lineSpacing * 2 / 3;
+ x1 = x0 + m_baseBarThickness * 9 + m_lineSpacing * 2 / 3 + m_inset;
+ break;
+
+ case LinedStaff::NoVisibleBar:
+ x1 = x0 + 1;
+ break;
+ }
+
+ QPointArray p(4);
+ p[0] = QPoint(x0, y0);
+ p[1] = QPoint(x1, y0);
+ p[2] = QPoint(x1, y1);
+ p[3] = QPoint(x0, y1);
+ return p;
+}
+
+}
diff --git a/src/gui/general/BarLine.h b/src/gui/general/BarLine.h
new file mode 100644
index 0000000..124bc06
--- /dev/null
+++ b/src/gui/general/BarLine.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_BARLINE_H_
+#define _RG_BARLINE_H_
+
+#include "LinedStaff.h"
+#include <qcanvas.h>
+
+namespace Rosegarden {
+
+class BarLine : public QCanvasPolygonalItem
+{
+public:
+ BarLine(QCanvas *canvas, double layoutX,
+ int barLineHeight, int baseBarThickness, int lineSpacing,
+ int inset, LinedStaff::BarStyle style) :
+ QCanvasPolygonalItem(canvas),
+ m_layoutX(layoutX),
+ m_barLineHeight(barLineHeight),
+ m_baseBarThickness(baseBarThickness),
+ m_lineSpacing(lineSpacing),
+ m_inset(inset),
+ m_style(style) { }
+
+ double getLayoutX() const { return m_layoutX; }
+
+ virtual QPointArray areaPoints() const;
+ virtual void drawShape(QPainter &);
+
+protected:
+ double m_layoutX;
+ int m_barLineHeight;
+ int m_baseBarThickness;
+ int m_lineSpacing;
+ int m_inset;
+ LinedStaff::BarStyle m_style;
+};
+
+}
+
+#endif /*BARLINE_H_*/
diff --git a/src/gui/general/BaseTool.cpp b/src/gui/general/BaseTool.cpp
new file mode 100644
index 0000000..4c33610
--- /dev/null
+++ b/src/gui/general/BaseTool.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "BaseTool.h"
+
+#include "misc/Debug.h"
+#include <kxmlguifactory.h>
+#include <qcursor.h>
+#include <qobject.h>
+#include <qpopupmenu.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+BaseTool::BaseTool(const QString& menuName, KXMLGUIFactory* factory, QObject* parent)
+ : QObject(parent),
+ m_menuName(menuName),
+ m_menu(0),
+ m_parentFactory(factory)
+{}
+
+BaseTool::~BaseTool()
+{
+ RG_DEBUG << "BaseTool::~BaseTool()\n";
+
+ // delete m_menu;
+ // m_parentView->factory()->removeClient(this);
+ // m_instance = 0;
+}
+
+void BaseTool::ready()
+{}
+
+void BaseTool::stow()
+{}
+
+void BaseTool::showMenu()
+{
+ if (!hasMenu())
+ return ;
+
+ if (!m_menu)
+ createMenu();
+
+ if (m_menu)
+ m_menu->exec(QCursor::pos());
+ else
+ RG_DEBUG << "BaseTool::showMenu() : no menu to show\n";
+}
+
+QString BaseTool::getCurrentContextHelp() const
+{
+ return m_contextHelp;
+}
+
+void BaseTool::setContextHelp(const QString &help)
+{
+ m_contextHelp = help;
+ emit showContextHelp(m_contextHelp);
+}
+
+}
+
+#include "BaseTool.moc"
+
diff --git a/src/gui/general/BaseTool.h b/src/gui/general/BaseTool.h
new file mode 100644
index 0000000..71e6410
--- /dev/null
+++ b/src/gui/general/BaseTool.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_BASETOOL_H_
+#define _RG_BASETOOL_H_
+
+#include <qobject.h>
+#include <qstring.h>
+
+
+class QPopupMenu;
+class KXMLGUIFactory;
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * BaseTool : base tool class, just handles RMB menu creation and
+ * handling by a BaseToolBox
+ *
+ */
+class BaseTool : public QObject
+{
+ Q_OBJECT
+
+ friend class BaseToolBox;
+
+public:
+
+ virtual ~BaseTool();
+
+ /**
+ * Is called by the parent View (EditView or SegmentCanvas) when
+ * the tool is set as current.
+ * Add any setup here (e.g. setting the mouse cursor shape)
+ */
+ virtual void ready();
+
+ /**
+ * Is called by the parent View (EditView or SegmentCanvas) after
+ * the tool is put away.
+ * Add any cleanup here
+ */
+ virtual void stow();
+
+ /**
+ * Show the menu if there is one
+ */
+ virtual void showMenu();
+
+ /**
+ * Retrieve current status-line type help for the tool, if any
+ */
+ virtual QString getCurrentContextHelp() const;
+
+signals:
+ void showContextHelp(const QString &);
+
+protected:
+ /**
+ * Create a new BaseTool
+ *
+ * \a menuName : the name of the menu defined in the XML rc file
+ */
+ BaseTool(const QString& menuName, KXMLGUIFactory*, QObject* parent);
+
+ virtual void createMenu() = 0;
+ virtual bool hasMenu() { return false; }
+
+ virtual void setContextHelp(const QString &help);
+ virtual void clearContextHelp() { setContextHelp(""); }
+
+ //--------------- Data members ---------------------------------
+
+ QString m_menuName;
+ QPopupMenu* m_menu;
+
+ KXMLGUIFactory* m_parentFactory;
+
+ QString m_contextHelp;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/general/BaseToolBox.cpp b/src/gui/general/BaseToolBox.cpp
new file mode 100644
index 0000000..9e2fda9
--- /dev/null
+++ b/src/gui/general/BaseToolBox.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "BaseToolBox.h"
+
+#include "BaseTool.h"
+#include <qobject.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+BaseToolBox::BaseToolBox(QWidget* parent)
+ : QObject(parent),
+ m_tools(17, // default size, from the Qt docs
+ false) // but we want it to be case insensitive
+{
+ //m_tools.setAutoDelete(true);
+}
+
+BaseTool* BaseToolBox::getTool(const QString& toolName)
+{
+ BaseTool* tool = m_tools[toolName];
+
+ if (!tool) tool = createTool(toolName);
+
+ connect(tool, SIGNAL(showContextHelp(const QString &)),
+ this, SIGNAL(showContextHelp(const QString &)));
+
+ return tool;
+}
+
+}
+#include "BaseToolBox.moc"
diff --git a/src/gui/general/BaseToolBox.h b/src/gui/general/BaseToolBox.h
new file mode 100644
index 0000000..2a0242f
--- /dev/null
+++ b/src/gui/general/BaseToolBox.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_BASETOOLBOX_H_
+#define _RG_BASETOOLBOX_H_
+
+#include <qdict.h>
+#include <qobject.h>
+
+
+class QWidget;
+class QString;
+
+
+namespace Rosegarden
+{
+
+class BaseTool;
+
+
+/**
+ * BaseToolBox : maintains a single instance of each registered tool
+ *
+ * Tools are fetched from a name
+ */
+class BaseToolBox : public QObject
+{
+ Q_OBJECT
+
+public:
+ BaseToolBox(QWidget* parent);
+
+ virtual BaseTool* getTool(const QString& toolName);
+
+signals:
+ void showContextHelp(const QString &);
+
+protected:
+ virtual BaseTool* createTool(const QString& toolName) = 0;
+
+ QDict<BaseTool> m_tools;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/general/CanvasCursor.cpp b/src/gui/general/CanvasCursor.cpp
new file mode 100644
index 0000000..5f04794
--- /dev/null
+++ b/src/gui/general/CanvasCursor.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "CanvasCursor.h"
+
+#include "GUIPalette.h"
+#include <qcanvas.h>
+#include <qpen.h>
+
+
+namespace Rosegarden
+{
+
+CanvasCursor::CanvasCursor(QCanvas* c, int width)
+ : QCanvasRectangle(c),
+ m_width(width)
+{
+ QPen pen(GUIPalette::getColour(GUIPalette::Pointer));
+ // pen.setWidth(width);
+ setPen(pen);
+ setBrush(GUIPalette::getColour(GUIPalette::Pointer));
+}
+
+void CanvasCursor::updateHeight()
+{
+ setSize(m_width, canvas()->height());
+ // setPoints(0, 0, 0, canvas()->height());
+}
+
+}
diff --git a/src/gui/general/CanvasCursor.h b/src/gui/general/CanvasCursor.h
new file mode 100644
index 0000000..694e9df
--- /dev/null
+++ b/src/gui/general/CanvasCursor.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_CANVASCURSOR_H_
+#define _RG_CANVASCURSOR_H_
+
+
+#include <qcanvas.h>
+
+class QCanvas;
+
+
+namespace Rosegarden
+{
+
+
+
+class CanvasCursor : public QCanvasRectangle
+{
+public:
+ CanvasCursor(QCanvas*, int width);
+ void updateHeight();
+// virtual QRect boundingRect() const;
+protected:
+ int m_width;
+};
+
+
+
+
+}
+
+#endif
diff --git a/src/gui/general/CanvasItemGC.cpp b/src/gui/general/CanvasItemGC.cpp
new file mode 100644
index 0000000..6e6afb2
--- /dev/null
+++ b/src/gui/general/CanvasItemGC.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "CanvasItemGC.h"
+
+#include "misc/Debug.h"
+
+#include <qcanvas.h>
+
+namespace Rosegarden
+{
+
+void CanvasItemGC::mark(QCanvasItem* item)
+{
+ if (!item)
+ return ;
+
+ item->hide();
+ // RG_DEBUG << "CanvasItemGC::mark() : "
+ // << item << std::endl;
+ m_garbage.push_back(item);
+}
+
+void CanvasItemGC::gc()
+{
+ for (unsigned int i = 0; i < m_garbage.size(); ++i) {
+ // RG_DEBUG << "CanvasItemGC::gc() : delete "
+ // << m_garbage[i] << "\n";
+ delete m_garbage[i];
+ }
+
+ m_garbage.clear();
+}
+
+void CanvasItemGC::flush()
+{
+ m_garbage.clear();
+}
+
+std::vector<QCanvasItem*> CanvasItemGC::m_garbage;
+
+}
diff --git a/src/gui/general/CanvasItemGC.h b/src/gui/general/CanvasItemGC.h
new file mode 100644
index 0000000..db1833b
--- /dev/null
+++ b/src/gui/general/CanvasItemGC.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_CANVASITEMGC_H_
+#define _RG_CANVASITEMGC_H_
+
+#include <vector>
+
+
+class QCanvasItem;
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * A pseudo GC in which CanvasItems whose ownership isn't clear cut
+ * can be put for periodical removal.
+ *
+ * This is especially for SegmentItems which can put their repeat
+ * rectangles when they're being deleted.
+ *
+ * The problem this solves is a classic ownership/double deletion
+ * case. The SegmentCanvas deletes all its items on destruction. But
+ * the SegmentItems have an auxiliary "repeat rectangle" which is a
+ * QCanvasRectangle, that needs to be deleted when the SegmentItem is
+ * itself deleted.
+ *
+ * However, if the SegmentItem deletes its repeat rectangle, then when
+ * the SegmentCanvas destruction occurs, the SegmentCanvas dtor will
+ * get a list of all its children (QCanvas::allItems()), containing
+ * both SegmentItems and their repeat rectangles. Deleting a
+ * SegmentItem will delete its repeat rectangle, which will still be
+ * present in the all children list which the SegmentCanvas dtor is
+ * iterating over.
+ *
+ * So a solution is simply to push to-be-deleted repeat rectangles on
+ * this GC, which should be processed on canvas updates, for instance.
+ *
+ */
+class CanvasItemGC
+{
+public:
+ /// mark the given item for GC
+ static void mark(QCanvasItem*);
+
+ /// GC all marked items
+ static void gc();
+
+ /// Forget all marked items - don't delete them
+ static void flush();
+
+protected:
+ static std::vector<QCanvasItem*> m_garbage;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/general/CategoryElement.cpp b/src/gui/general/CategoryElement.cpp
new file mode 100644
index 0000000..2199ce6
--- /dev/null
+++ b/src/gui/general/CategoryElement.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "CategoryElement.h"
+
+#include "misc/Debug.h"
+#include "PresetElement.h"
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+CategoryElement::CategoryElement(QString name) :
+ m_name(name)
+{}
+
+CategoryElement::~CategoryElement()
+{
+ // nothing to do
+}
+
+void
+CategoryElement::addPreset(QString name,
+ int clef,
+ int transpose,
+ int highAm,
+ int lowAm,
+ int highPro,
+ int lowPro)
+{
+ RG_DEBUG << "CategoryElement::addPreset(...): adding new PresetElement" << endl;
+
+ PresetElement e(name, clef, transpose, highAm, lowAm,
+ highPro, lowPro);
+ m_categoryPresets.push_back(e);
+}
+
+}
diff --git a/src/gui/general/CategoryElement.h b/src/gui/general/CategoryElement.h
new file mode 100644
index 0000000..eeb88a5
--- /dev/null
+++ b/src/gui/general/CategoryElement.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_CATEGORYELEMENT_H_
+#define _RG_CATEGORYELEMENT_H_
+
+#include "PresetElement.h"
+#include <qstring.h>
+
+
+
+
+namespace Rosegarden
+{
+
+
+/*
+ * A container class for storing a collection of PresetElement objects grouped
+ * into the same musical category (eg. Flutes, Clarinets, Strings)
+ */
+class CategoryElement
+{
+public:
+ CategoryElement(QString name);
+ ~CategoryElement();
+
+ void addPreset(QString name,
+ int clef,
+ int transpose,
+ int highAm,
+ int lowAm,
+ int highPro,
+ int lowPro);
+
+ QString getName() { return m_name; }
+
+ ElementContainer getPresets() { return m_categoryPresets; }
+ PresetElement getPresetByIndex(int index) { return m_categoryPresets [index]; }
+
+private:
+ QString m_name;
+ ElementContainer m_categoryPresets;
+}; // CategoryElement
+
+typedef std::vector<CategoryElement> CategoriesContainer;
+
+}
+
+#endif
diff --git a/src/gui/general/ClefIndex.cpp b/src/gui/general/ClefIndex.cpp
new file mode 100644
index 0000000..68ad488
--- /dev/null
+++ b/src/gui/general/ClefIndex.cpp
@@ -0,0 +1,100 @@
+// -*- c-basic-offset: 4 -*-
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <bownie@bownie.com>
+
+ 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 "gui/general/ClefIndex.h"
+#include "misc/Debug.h"
+
+namespace Rosegarden
+{
+
+const Rosegarden::Clef clefIndexToClef(int index)
+{
+ Rosegarden::Clef clef;
+
+ // insert an initial clef from track parameters
+ switch (index) {
+ case TrebleClef: clef = Clef(Clef::Treble); break;
+ case BassClef: clef = Clef(Clef::Bass); break;
+ case CrotalesClef: clef = Clef(Clef::Treble, 2); break;
+ case XylophoneClef: clef = Clef(Clef::Treble, 1); break;
+ case GuitarClef: clef = Clef(Clef::Treble, -1); break;
+ case ContrabassClef: clef = Clef(Clef::Bass, -1); break;
+ case CelestaClef: clef = Clef(Clef::Bass, 2); break;
+ case OldCelestaClef: clef = Clef(Clef::Bass, 1); break;
+ case FrenchClef: clef = Clef(Clef::French); break;
+ case SopranoClef: clef = Clef(Clef::Soprano); break;
+ case MezzosopranoClef: clef = Clef(Clef::Mezzosoprano); break;
+ case AltoClef: clef = Clef(Clef::Alto); break;
+ case TenorClef: clef = Clef(Clef::Tenor); break;
+ case BaritoneClef: clef = Clef(Clef::Baritone); break;
+ case VarbaritoneClef: clef = Clef(Clef::Varbaritone); break;
+ case SubbassClef: clef = Clef(Clef::Subbass); break;
+ default: clef = Clef(Clef::Treble); break;
+ }
+ return clef;
+}
+
+const int clefNameToClefIndex(QString s)
+{
+ int m_elClef = 0;
+ if (s) {
+ if (s == "treble")
+ m_elClef = TrebleClef;
+ else if (s == "bass")
+ m_elClef = BassClef;
+ else if (s == "crotales")
+ m_elClef = CrotalesClef;
+ else if (s == "xylophone")
+ m_elClef = XylophoneClef;
+ else if (s == "guitar")
+ m_elClef = GuitarClef;
+ else if (s == "contrabass")
+ m_elClef = ContrabassClef;
+ else if (s == "celesta")
+ m_elClef = CelestaClef;
+ else if (s == "oldCelesta")
+ m_elClef = OldCelestaClef;
+ else if (s == "french")
+ m_elClef = FrenchClef;
+ else if (s == "soprano")
+ m_elClef = SopranoClef;
+ else if (s == "mezzosoprano")
+ m_elClef = MezzosopranoClef;
+ else if (s == "alto")
+ m_elClef = AltoClef;
+ else if (s == "tenor")
+ m_elClef = TenorClef;
+ else if (s == "baritone")
+ m_elClef = BaritoneClef;
+ else if (s == "varbaritone")
+ m_elClef = VarbaritoneClef;
+ else if (s == "subbass")
+ m_elClef = SubbassClef;
+ else if (s == "two-bar")
+ m_elClef = TwoBarClef;
+ else {
+ RG_DEBUG << "startElement: processed unrecognized clef type: " << s << endl;
+ }
+ }
+ return m_elClef;
+}
+
+} \ No newline at end of file
diff --git a/src/gui/general/ClefIndex.h b/src/gui/general/ClefIndex.h
new file mode 100644
index 0000000..74e3fc8
--- /dev/null
+++ b/src/gui/general/ClefIndex.h
@@ -0,0 +1,59 @@
+// -*- c-basic-offset: 4 -*-
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <bownie@bownie.com>
+
+ 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 _CLEF_INDEX_H_
+#define _CLEF_INDEX_H_
+
+#include "base/NotationTypes.h"
+#include <qstring.h>
+
+// used variously by TPB, SPB, PresetHandler to correlate combo box indices to
+// clef types
+enum { TrebleClef = 0, // G clef, line 2
+ BassClef, // F clef, line 4
+ CrotalesClef, // G clef, line 2, 15 above
+ XylophoneClef, // G clef, line 2, 8 above
+ GuitarClef, // G clef, line 2, 8 below
+ ContrabassClef, // F clef, line 4, 8 below
+ CelestaClef, // F clef, line 4, 15 above
+ OldCelestaClef, // F clef, line 4, 8 above
+ FrenchClef, // G clef, line 1
+ SopranoClef, // C clef, line 1
+ MezzosopranoClef, // C clef, line 2
+ AltoClef, // C clef, line 3
+ TenorClef, // C clef, line 4
+ BaritoneClef, // C clef, line 5
+ VarbaritoneClef, // F clef, line 3
+ SubbassClef, // F clef, line 5
+ TwoBarClef // percussion clef //!!! doesn't exist yet!
+ };
+
+namespace Rosegarden
+{
+
+const Clef clefIndexToClef(int index);
+
+const int clefNameToClefIndex(QString s);
+
+}
+
+#endif // _CLEF_INDEX_H_
diff --git a/src/gui/general/EditTool.cpp b/src/gui/general/EditTool.cpp
new file mode 100644
index 0000000..52e7b11
--- /dev/null
+++ b/src/gui/general/EditTool.cpp
@@ -0,0 +1,143 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "EditTool.h"
+
+#include "misc/Debug.h"
+#include "base/Event.h"
+#include "BaseTool.h"
+#include "base/ViewElement.h"
+#include "EditView.h"
+#include "RosegardenCanvasView.h"
+#include <kxmlguiclient.h>
+#include <qevent.h>
+#include <qpopupmenu.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+EditTool::EditTool(const QString& menuName, EditView* view)
+ : BaseTool(menuName, view->factory(), view),
+ m_parentView(view)
+{}
+
+void EditTool::handleMousePress(timeT time,
+ int height,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement* el)
+{
+ RG_DEBUG << "EditTool::handleMousePress : mouse button = "
+ << e->button() << endl;
+
+ switch (e->button()) {
+
+ case Qt::LeftButton:
+ if (e->type() == QEvent::MouseButtonDblClick) {
+ RG_DEBUG << "EditTool::handleMousePress: it's a double-click"
+ << endl;
+ handleMouseDoubleClick(time, height, staffNo, e, el);
+ } else {
+ RG_DEBUG << "EditTool::handleMousePress: it's a single-click"
+ << endl;
+ handleLeftButtonPress(time, height, staffNo, e, el);
+ }
+ break;
+
+ case Qt::RightButton:
+ handleRightButtonPress(time, height, staffNo, e, el);
+ break;
+
+ case Qt::MidButton:
+ handleMidButtonPress(time, height, staffNo, e, el);
+ break;
+
+ default:
+ RG_DEBUG << "EditTool::handleMousePress : no button mouse press\n";
+ break;
+ }
+}
+
+void EditTool::handleMidButtonPress(timeT,
+ int, int,
+ QMouseEvent*,
+ ViewElement*)
+{}
+
+void EditTool::handleRightButtonPress(timeT,
+ int, int,
+ QMouseEvent*,
+ ViewElement*)
+{
+ showMenu();
+}
+
+void EditTool::handleMouseDoubleClick(timeT,
+ int, int,
+ QMouseEvent*,
+ ViewElement*)
+{
+ // nothing
+}
+
+int EditTool::handleMouseMove(timeT, int, QMouseEvent*)
+{
+ return RosegardenCanvasView::NoFollow;
+}
+
+void EditTool::handleMouseRelease(timeT, int, QMouseEvent*)
+{}
+
+void EditTool::createMenu(QString rcFileName)
+{
+ setRCFileName(rcFileName);
+ createMenu();
+}
+
+void EditTool::createMenu()
+{
+ RG_DEBUG << "BaseTool::createMenu() " << m_rcFileName << " - " << m_menuName << endl;
+
+ setXMLFile(m_rcFileName);
+ m_parentFactory->addClient(this);
+
+ QWidget* tmp = m_parentFactory->container(m_menuName, this);
+
+ if (!tmp)
+ RG_DEBUG << "BaseTool::createMenu(" << m_rcFileName
+ << ") : menu creation failed (name : "
+ << m_menuName << ")\n";
+
+ m_menu = dynamic_cast<QPopupMenu*>(tmp);
+}
+
+bool EditTool::hasMenu()
+{
+ return !m_rcFileName.isEmpty();
+}
+
+}
diff --git a/src/gui/general/EditTool.h b/src/gui/general/EditTool.h
new file mode 100644
index 0000000..17937d1
--- /dev/null
+++ b/src/gui/general/EditTool.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_EDITTOOL_H_
+#define _RG_EDITTOOL_H_
+
+#include "BaseTool.h"
+#include <kxmlguiclient.h>
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class Event;
+class EditView;
+
+
+/**
+ * Edit tool base class.
+ *
+ * A EditTool represents one of the items on an edition view
+ * toolbar. It handles mouse click events for the EditView ('State'
+ * design pattern).
+ *
+ * A EditTool 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
+ * EditView 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
+ * EditToolBox 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 EditView#setTool()
+ * @see EditToolBox
+ */
+class EditTool : public BaseTool, public KXMLGUIClient
+{
+ friend class EditToolBox;
+
+public:
+
+ /**
+ * Dispatch the event to Left/Middle/Right MousePress
+ */
+ virtual void handleMousePress(timeT time,
+ int height,
+ int staffNo,
+ QMouseEvent *event,
+ ViewElement*);
+
+ /**
+ * Main operation of the tool
+ */
+ virtual void handleLeftButtonPress(timeT time,
+ int height,
+ int staffNo,
+ QMouseEvent *event,
+ ViewElement*) = 0;
+
+ /**
+ * Do nothing
+ */
+ virtual void handleMidButtonPress(timeT time,
+ int height,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement*);
+
+ /**
+ * Show option menu
+ */
+ virtual void handleRightButtonPress(timeT time,
+ int height,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement*);
+
+ /**
+ * Do nothing
+ */
+ virtual void handleMouseDoubleClick(timeT time,
+ int height,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement*);
+
+ /**
+ * Do nothing.
+ * Implementations of handleMouseMove should return true if
+ * they want the canvas to scroll to the position the mouse
+ * moved to following the method's return.
+ */
+ virtual int handleMouseMove(timeT time,
+ int height,
+ QMouseEvent*);
+
+ /**
+ * Do nothing
+ */
+ virtual void handleMouseRelease(timeT time,
+ int height,
+ QMouseEvent*);
+
+ /**
+ * Respond to an event being deleted -- it may be the one the tool
+ * is remembering as the current event.
+ */
+ virtual void handleEventRemoved(Event *) { }
+
+
+protected:
+ /**
+ * Create a new EditTool
+ *
+ * \a menuName : the name of the menu defined in the XML rc file
+ */
+ EditTool(const QString& menuName, EditView*);
+
+ void setRCFileName(QString rcfilename) { m_rcFileName = rcfilename; }
+
+ virtual void createMenu();
+ virtual void createMenu(QString rcFileName);
+ virtual bool hasMenu();
+
+ //--------------- Data members ---------------------------------
+ QString m_rcFileName;
+
+ EditView* m_parentView;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/general/EditToolBox.cpp b/src/gui/general/EditToolBox.cpp
new file mode 100644
index 0000000..c2e24a9
--- /dev/null
+++ b/src/gui/general/EditToolBox.cpp
@@ -0,0 +1,56 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "EditToolBox.h"
+
+#include "BaseToolBox.h"
+#include "EditTool.h"
+#include "EditView.h"
+#include <qobject.h>
+#include <qstring.h>
+#include <kmessagebox.h>
+
+namespace Rosegarden
+{
+
+EditToolBox::EditToolBox(EditView *parent)
+ : BaseToolBox(parent),
+ m_parentView(parent)
+{
+}
+
+EditTool* EditToolBox::getTool(const QString& toolName)
+{
+ return dynamic_cast<EditTool*>(BaseToolBox::getTool(toolName));
+}
+
+EditTool* EditToolBox::createTool(const QString&)
+{
+ KMessageBox::error(0, "EditToolBox::createTool called - this should never happen");
+ return 0;
+}
+
+}
+#include "EditToolBox.moc"
diff --git a/src/gui/general/EditToolBox.h b/src/gui/general/EditToolBox.h
new file mode 100644
index 0000000..0115558
--- /dev/null
+++ b/src/gui/general/EditToolBox.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_EDITTOOLBOX_H_
+#define _RG_EDITTOOLBOX_H_
+
+#include "BaseToolBox.h"
+#include "EditTool.h"
+
+
+class QString;
+
+
+namespace Rosegarden
+{
+
+class EditView;
+
+
+/**
+ * EditToolBox : specialized toolbox for EditViews (notation, matrix...)
+ *
+ */
+class EditToolBox : public BaseToolBox
+{
+ Q_OBJECT
+public:
+ EditToolBox(EditView* parent);
+
+ virtual EditTool* getTool(const QString& toolName);
+
+protected:
+ virtual EditTool* createTool(const QString& toolName);
+
+ //--------------- Data members ---------------------------------
+
+ EditView* m_parentView;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/general/EditView.cpp b/src/gui/general/EditView.cpp
new file mode 100644
index 0000000..a36b385
--- /dev/null
+++ b/src/gui/general/EditView.cpp
@@ -0,0 +1,1717 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "EditView.h"
+#include <qlayout.h>
+
+#include "base/BaseProperties.h"
+#include <klocale.h>
+#include <kconfig.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "ActiveItem.h"
+#include "base/AnalysisTypes.h"
+#include "base/Composition.h"
+#include "base/CompositionTimeSliceAdapter.h"
+#include "base/Controllable.h"
+#include "base/ControlParameter.h"
+#include "base/Device.h"
+#include "base/Event.h"
+#include "base/Exception.h"
+#include "base/Instrument.h"
+#include "base/MidiDevice.h"
+#include "base/MidiProgram.h"
+#include "base/MidiTypes.h"
+#include "base/NotationTypes.h"
+#include "base/Profiler.h"
+#include "base/Property.h"
+#include "base/PropertyName.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "base/SoftSynthDevice.h"
+#include "base/Staff.h"
+#include "base/Studio.h"
+#include "base/ViewElement.h"
+#include "commands/edit/InvertCommand.h"
+#include "commands/edit/MoveCommand.h"
+#include "commands/edit/RescaleCommand.h"
+#include "commands/edit/RetrogradeCommand.h"
+#include "commands/edit/RetrogradeInvertCommand.h"
+#include "commands/edit/TransposeCommand.h"
+#include "commands/segment/AddTempoChangeCommand.h"
+#include "commands/segment/AddTimeSignatureAndNormalizeCommand.h"
+#include "commands/segment/AddTimeSignatureCommand.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "EditViewBase.h"
+#include "gui/dialogs/RescaleDialog.h"
+#include "gui/dialogs/TempoDialog.h"
+#include "gui/dialogs/IntervalDialog.h"
+#include "gui/dialogs/TimeSignatureDialog.h"
+#include "gui/rulers/StandardRuler.h"
+#include "gui/kdeext/KTmpStatusMsg.h"
+#include "gui/kdeext/QCanvasGroupableItem.h"
+#include "gui/rulers/ControllerEventsRuler.h"
+#include "gui/rulers/ControlRuler.h"
+#include "gui/rulers/PropertyControlRuler.h"
+#include "RosegardenCanvasView.h"
+#include <kaction.h>
+#include <kcommand.h>
+#include <kdockwidget.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <kstddirs.h>
+#include <ktabwidget.h>
+#include <kxmlguiclient.h>
+#include <qaccel.h>
+#include <qbutton.h>
+#include <qdialog.h>
+#include <qframe.h>
+#include <qinputdialog.h>
+#include <qlabel.h>
+#include <qobjectlist.h>
+#include <qpopupmenu.h>
+#include <qsize.h>
+#include <qstring.h>
+#include <qtabwidget.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <qwmatrix.h>
+
+
+namespace Rosegarden
+{
+
+const unsigned int EditView::CONTROLS_ROW = 0;
+const unsigned int EditView::RULERS_ROW = CONTROLS_ROW + 1;
+const unsigned int EditView::TOPBARBUTTONS_ROW = RULERS_ROW + 1;
+const unsigned int EditView::CANVASVIEW_ROW = TOPBARBUTTONS_ROW + 1;
+const unsigned int EditView::CONTROLRULER_ROW = CANVASVIEW_ROW + 1;
+
+// Just some simple features we might want to show - make them bit maskable
+//
+static int FeatureShowVelocity = 0x00001; // show the velocity ruler
+
+EditView::EditView(RosegardenGUIDoc *doc,
+ std::vector<Segment *> segments,
+ unsigned int cols,
+ QWidget *parent, const char *name) :
+ EditViewBase(doc, segments, cols, parent, name),
+ m_currentEventSelection(0),
+ m_activeItem(0),
+ m_canvasView(0),
+ m_rulerBox(new QVBoxLayout), // top ruler box - added to grid later on
+ m_rulerBoxFiller(0), // On the left of m_rulerBox
+ m_controlBox(new QVBoxLayout), // top control ruler box - added to grid later on
+ m_bottomBox(new QVBox(this, "bottomframe")), // bottom box - added to bottom of canvas view by setCanvasView()
+ m_topStandardRuler(0),
+ m_bottomStandardRuler(0),
+ m_controlRuler(0),
+ m_controlRulers(new KTabWidget(getBottomWidget(), "controlrulers"))
+{
+ m_controlRulers->setHoverCloseButton(true);
+ m_controlRulers->setHoverCloseButtonDelayed(false);
+ connect(m_controlRulers, SIGNAL(closeRequest(QWidget*)),
+ this, SLOT(slotRemoveControlRuler(QWidget*)));
+
+ (dynamic_cast<QBoxLayout*>(m_bottomBox->layout()))->setDirection(QBoxLayout::BottomToTop);
+
+ // m_rulerBoxFiller is a white label used to keep m_rulerBox exactly
+ // above the scrolling part of the view (and never above the
+ // RosegardenCanvasView::m_leftWidget).
+ QGridLayout * gl = new QGridLayout(1, 2);
+ gl->setColStretch(0, 0);
+ gl->setColStretch(1, 1);
+ gl->addLayout(m_rulerBox, 0, 1);
+ m_rulerBoxFiller = new QLabel(getCentralWidget());
+ gl->addWidget(m_rulerBoxFiller, 0, 0);
+ m_rulerBoxFiller->hide();
+
+ m_grid->addLayout(gl, RULERS_ROW, m_mainCol);
+
+ m_grid->addMultiCellLayout(m_controlBox, CONTROLS_ROW, CONTROLS_ROW, 0, 1);
+ m_controlBox->setAlignment(AlignRight);
+ // m_grid->addWidget(m_controlRulers, CONTROLRULER_ROW, 2);
+
+ m_controlRulers->hide();
+ m_controlRulers->setTabPosition(QTabWidget::Bottom);
+}
+
+EditView::~EditView()
+{
+ delete m_currentEventSelection;
+ m_currentEventSelection = 0;
+}
+
+void EditView::updateBottomWidgetGeometry()
+{
+ getBottomWidget()->layout()->invalidate();
+ getBottomWidget()->updateGeometry();
+ getCanvasView()->updateBottomWidgetGeometry();
+}
+
+void EditView::paintEvent(QPaintEvent* e)
+{
+ RG_DEBUG << "EditView::paintEvent()\n";
+ EditViewBase::paintEvent(e);
+
+ if (m_needUpdate) {
+ RG_DEBUG << "EditView::paintEvent() - calling updateView\n";
+ updateView();
+ getCanvasView()->slotUpdate();
+
+ // update rulers
+ QLayoutIterator it = m_rulerBox->iterator();
+ QLayoutItem *child;
+ while ( (child = it.current()) != 0 ) {
+ if (child->widget())
+ child->widget()->update();
+ ++it;
+ }
+
+ updateControlRulers();
+
+ } else {
+
+ getCanvasView()->slotUpdate();
+ updateControlRulers();
+
+ }
+
+ m_needUpdate = false;
+}
+
+void EditView::updateControlRulers(bool updateHPos)
+{
+ for (int i = 0; i < m_controlRulers->count(); ++i) {
+ ControlRuler* ruler = dynamic_cast<ControlRuler*>(m_controlRulers->page(i));
+ if (ruler) {
+ if (updateHPos)
+ ruler->slotUpdateElementsHPos();
+ else
+ ruler->slotUpdate();
+ }
+ }
+}
+
+void EditView::setControlRulersZoom(QWMatrix zoomMatrix)
+{
+ m_currentRulerZoomMatrix = zoomMatrix;
+
+ for (int i = 0; i < m_controlRulers->count(); ++i) {
+ ControlRuler* ruler = dynamic_cast<ControlRuler*>(m_controlRulers->page(i));
+ if (ruler)
+ ruler->setWorldMatrix(zoomMatrix);
+ }
+}
+
+void EditView::setControlRulersCurrentSegment()
+{
+ RG_DEBUG << "EditView::setControlRulersCurrentSegment: visible is " << m_controlRulers->isVisible() << endl;
+
+ bool visible = m_controlRulers->isVisible();
+
+ delete m_controlRulers;
+ m_controlRulers = new KTabWidget(getBottomWidget(), "controlrulers");
+
+ bool haveTabs = setupControllerTabs();
+ setupAddControlRulerMenu();
+
+ if (haveTabs)
+ m_controlRulers->show();
+ else
+ m_controlRulers->hide();
+
+ updateBottomWidgetGeometry();
+
+ /*
+ for (int i = 0; i < m_controlRulers->count(); ++i) {
+
+ PropertyControlRuler *pcr = dynamic_cast<PropertyControlRuler *>
+ (m_controlRulers->page(i));
+
+ if (pcr) pcr->setStaff(getCurrentStaff());
+ else {
+
+ ControllerEventsRuler *cer = dynamic_cast<ControllerEventsRuler *>
+ (m_controlRulers->page(i));
+
+ if (cer) cer->setSegment(getCurrentSegment());
+ }
+ }
+ */
+}
+
+void EditView::setTopStandardRuler(StandardRuler* w, QWidget *leftBox)
+{
+ delete m_topStandardRuler;
+ m_topStandardRuler = w;
+
+ QGridLayout * gl = new QGridLayout(1, 2);
+ gl->setColStretch(0, 0);
+ gl->setColStretch(1, 1);
+
+ gl->addWidget(w, 0, 1);
+ if (leftBox) {
+ gl->addWidget(leftBox, 0, 0);
+ }
+
+ m_grid->addLayout(gl, TOPBARBUTTONS_ROW, m_mainCol);
+
+ if (m_canvasView) {
+ connect(m_canvasView->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ m_topStandardRuler, SLOT(slotScrollHoriz(int)));
+ connect(m_canvasView->horizontalScrollBar(), SIGNAL(sliderMoved(int)),
+ m_topStandardRuler, SLOT(slotScrollHoriz(int)));
+ }
+}
+
+void EditView::setBottomStandardRuler(StandardRuler* w)
+{
+ delete m_bottomStandardRuler;
+ m_bottomStandardRuler = w;
+
+ // m_bottomBox->insertWidget(0, w);
+
+ if (m_canvasView) {
+ connect(m_canvasView->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ m_bottomStandardRuler, SLOT(slotScrollHoriz(int)));
+ connect(m_canvasView->horizontalScrollBar(), SIGNAL(sliderMoved(int)),
+ m_bottomStandardRuler, SLOT(slotScrollHoriz(int)));
+ }
+}
+
+void EditView::setRewFFwdToAutoRepeat()
+{
+ QWidget* transportToolbar = factory()->container("Transport Toolbar", this);
+
+ if (transportToolbar) {
+ QObjectList *l = transportToolbar->queryList();
+ QObjectListIt it(*l); // iterate over the buttons
+ QObject *obj;
+
+ while ( (obj = it.current()) != 0 ) {
+ // for each found object...
+ ++it;
+ // RG_DEBUG << "EditView::setRewFFwdToAutoRepeat() : obj name : " << obj->name() << endl;
+ QString objName = obj->name();
+
+ if (objName.endsWith("playback_pointer_back_bar") || objName.endsWith("playback_pointer_forward_bar")) {
+ QButton* btn = dynamic_cast<QButton*>(obj);
+ if (!btn) {
+ RG_DEBUG << "Very strange - found widgets in Transport Toolbar which aren't buttons\n";
+
+ continue;
+ }
+ btn->setAutoRepeat(true);
+ }
+
+
+ }
+ delete l;
+
+ } else {
+ RG_DEBUG << "transportToolbar == 0\n";
+ }
+
+}
+
+void EditView::addRuler(QWidget* w)
+{
+ m_rulerBox->addWidget(w);
+
+ if (m_canvasView) {
+ connect(m_canvasView->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ w, SLOT(slotScrollHoriz(int)));
+ connect(m_canvasView->horizontalScrollBar(), SIGNAL(sliderMoved(int)),
+ w, SLOT(slotScrollHoriz(int)));
+ }
+}
+
+void EditView::addPropertyBox(QWidget *w)
+{
+ m_controlBox->addWidget(w);
+}
+
+void EditView::addControlRuler(ControlRuler* ruler)
+{
+ ruler->setWorldMatrix(m_currentRulerZoomMatrix);
+ m_controlRulers->addTab(ruler, KGlobal::iconLoader()->loadIconSet("fileclose", KIcon::Small),
+ ruler->getName());
+ m_controlRulers->showPage(ruler);
+
+ if (m_canvasView) {
+ connect(m_canvasView->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ ruler->horizontalScrollBar(), SIGNAL(valueChanged(int)));
+ connect(m_canvasView->horizontalScrollBar(), SIGNAL(sliderMoved(int)),
+ ruler->horizontalScrollBar(), SIGNAL(sliderMoved(int)));
+ }
+
+ connect(ruler, SIGNAL(stateChange(const QString&, bool)),
+ this, SLOT(slotStateChanged(const QString&, bool)));
+
+ stateChanged("have_control_ruler", KXMLGUIClient::StateReverse);
+}
+
+void EditView::readjustViewSize(QSize requestedSize, bool exact)
+{
+ Profiler profiler("EditView::readjustViewSize", true);
+
+ if (exact) {
+ RG_DEBUG << "EditView::readjustViewSize: exact size requested ("
+ << requestedSize.width() << ", " << requestedSize.height()
+ << ")\n";
+
+ setViewSize(requestedSize);
+ getCanvasView()->slotUpdate();
+ return ;
+ }
+
+ int requestedWidth = requestedSize.width(),
+ requestedHeight = requestedSize.height(),
+ windowWidth = width(),
+ windowHeight = height();
+
+ QSize newSize;
+
+ newSize.setWidth(((requestedWidth / windowWidth) + 1) * windowWidth);
+ newSize.setHeight(((requestedHeight / windowHeight) + 1) * windowHeight);
+
+ RG_DEBUG << "EditView::readjustViewSize: requested ("
+ << requestedSize.width() << ", " << requestedSize.height()
+ << "), getting (" << newSize.width() << ", "
+ << newSize.height() << ")" << endl;
+
+ setViewSize(newSize);
+
+ getCanvasView()->slotUpdate();
+}
+
+void EditView::setCanvasView(RosegardenCanvasView *canvasView)
+{
+ delete m_canvasView;
+ m_canvasView = canvasView;
+ m_grid->addWidget(m_canvasView, CANVASVIEW_ROW, m_mainCol);
+ m_canvasView->setBottomFixedWidget(m_bottomBox);
+
+ // TODO : connect canvas view's horiz. scrollbar to top/bottom bars and rulers
+
+ // m_horizontalScrollBar->setRange(m_canvasView->horizontalScrollBar()->minValue(),
+ // m_canvasView->horizontalScrollBar()->maxValue());
+
+ // m_horizontalScrollBar->setSteps(m_canvasView->horizontalScrollBar()->lineStep(),
+ // m_canvasView->horizontalScrollBar()->pageStep());
+
+ // connect(m_horizontalScrollBar, SIGNAL(valueChanged(int)),
+ // m_canvasView->horizontalScrollBar(), SIGNAL(valueChanged(int)));
+ // connect(m_horizontalScrollBar, SIGNAL(sliderMoved(int)),
+ // m_canvasView->horizontalScrollBar(), SIGNAL(sliderMoved(int)));
+
+}
+
+Device *
+EditView::getCurrentDevice()
+{
+ Segment *segment = getCurrentSegment();
+ if (!segment)
+ return 0;
+
+ Studio &studio = getDocument()->getStudio();
+ Instrument *instrument =
+ studio.getInstrumentById
+ (segment->getComposition()->getTrackById(segment->getTrack())->
+ getInstrument());
+ if (!instrument)
+ return 0;
+
+ return instrument->getDevice();
+}
+
+timeT
+EditView::getInsertionTime(Clef &clef,
+ Rosegarden::Key &key)
+{
+ timeT t = getInsertionTime();
+ Segment *segment = getCurrentSegment();
+
+ if (segment) {
+ clef = segment->getClefAtTime(t);
+ key = segment->getKeyAtTime(t);
+ } else {
+ clef = Clef();
+ key = ::Rosegarden::Key();
+ }
+
+ return t;
+}
+
+void EditView::slotActiveItemPressed(QMouseEvent* e,
+ QCanvasItem* item)
+{
+ if (!item)
+ return ;
+
+ // Check if it's a groupable item, if so get its group
+ //
+ QCanvasGroupableItem *gitem = dynamic_cast<QCanvasGroupableItem*>(item);
+ if (gitem)
+ item = gitem->group();
+
+ // Check if it's an active item
+ //
+ ActiveItem *activeItem = dynamic_cast<ActiveItem*>(item);
+
+ if (activeItem) {
+
+ setActiveItem(activeItem);
+ activeItem->handleMousePress(e);
+ updateView();
+
+ }
+}
+
+void
+EditView::slotStepBackward()
+{
+ Staff *staff = getCurrentStaff();
+ if (!staff)
+ return ;
+ ViewElementList *vel = staff->getViewElementList();
+
+ timeT time = getInsertionTime();
+ ViewElementList::iterator i = vel->findTime(time);
+
+ while (i != vel->begin() &&
+ (i == vel->end() || (*i)->getViewAbsoluteTime() >= time))
+ --i;
+
+ if (i != vel->end())
+ slotSetInsertCursorPosition((*i)->getViewAbsoluteTime());
+}
+
+void
+EditView::slotStepForward()
+{
+ Staff *staff = getCurrentStaff();
+ if (!staff)
+ return ;
+ ViewElementList *vel = staff->getViewElementList();
+
+ timeT time = getInsertionTime();
+ ViewElementList::iterator i = vel->findTime(time);
+
+ while (i != vel->end() &&
+ (*i)->getViewAbsoluteTime() <= time)
+ ++i;
+
+ if (i == vel->end()) {
+ slotSetInsertCursorPosition(staff->getSegment().getEndMarkerTime());
+ } else {
+ slotSetInsertCursorPosition((*i)->getViewAbsoluteTime());
+ }
+}
+
+void
+EditView::slotJumpBackward()
+{
+ Segment *segment = getCurrentSegment();
+ if (!segment)
+ return ;
+ timeT time = getInsertionTime();
+ time = segment->getBarStartForTime(time - 1);
+ slotSetInsertCursorPosition(time);
+}
+
+void
+EditView::slotJumpForward()
+{
+ Segment *segment = getCurrentSegment();
+ if (!segment)
+ return ;
+ timeT time = getInsertionTime();
+ time = segment->getBarEndForTime(time);
+ slotSetInsertCursorPosition(time);
+}
+
+void
+EditView::slotJumpToStart()
+{
+ Segment *segment = getCurrentSegment();
+ if (!segment)
+ return ;
+ timeT time = segment->getStartTime();
+ slotSetInsertCursorPosition(time);
+}
+
+void
+EditView::slotJumpToEnd()
+{
+ Segment *segment = getCurrentSegment();
+ if (!segment)
+ return ;
+ timeT time = segment->getEndMarkerTime();
+ slotSetInsertCursorPosition(time);
+}
+
+void EditView::slotExtendSelectionBackward()
+{
+ slotExtendSelectionBackward(false);
+}
+
+void EditView::slotExtendSelectionBackwardBar()
+{
+ slotExtendSelectionBackward(true);
+}
+
+void EditView::slotExtendSelectionBackward(bool bar)
+{
+ // If there is no current selection, or the selection is entirely
+ // to the right of the cursor, move the cursor left and add to the
+ // selection
+
+ timeT oldTime = getInsertionTime();
+ if (bar)
+ slotJumpBackward();
+ else
+ slotStepBackward();
+ timeT newTime = getInsertionTime();
+
+ Staff *staff = getCurrentStaff();
+ if (!staff)
+ return ;
+ Segment *segment = &staff->getSegment();
+ ViewElementList *vel = staff->getViewElementList();
+
+ EventSelection *es = new EventSelection(*segment);
+ if (m_currentEventSelection &&
+ &m_currentEventSelection->getSegment() == segment)
+ es->addFromSelection(m_currentEventSelection);
+
+ if (!m_currentEventSelection ||
+ &m_currentEventSelection->getSegment() != segment ||
+ m_currentEventSelection->getSegmentEvents().size() == 0 ||
+ m_currentEventSelection->getStartTime() >= oldTime) {
+
+ ViewElementList::iterator extendFrom = vel->findTime(oldTime);
+
+ while (extendFrom != vel->begin() &&
+ (*--extendFrom)->getViewAbsoluteTime() >= newTime) {
+ if ((*extendFrom)->event()->isa(Note::EventType)) {
+ es->addEvent((*extendFrom)->event());
+ }
+ }
+
+ } else { // remove an event
+
+ EventSelection::eventcontainer::iterator i =
+ es->getSegmentEvents().end();
+
+ std::vector<Event *> toErase;
+
+ while (i != es->getSegmentEvents().begin() &&
+ (*--i)->getAbsoluteTime() >= newTime) {
+ toErase.push_back(*i);
+ }
+
+ for (unsigned int j = 0; j < toErase.size(); ++j) {
+ es->removeEvent(toErase[j]);
+ }
+ }
+
+ setCurrentSelection(es);
+}
+
+void EditView::slotExtendSelectionForward()
+{
+ slotExtendSelectionForward(false);
+}
+
+void EditView::slotExtendSelectionForwardBar()
+{
+ slotExtendSelectionForward(true);
+}
+
+void EditView::slotExtendSelectionForward(bool bar)
+{
+ // If there is no current selection, or the selection is entirely
+ // to the left of the cursor, move the cursor right and add to the
+ // selection
+
+ timeT oldTime = getInsertionTime();
+ if (bar)
+ slotJumpForward();
+ else
+ slotStepForward();
+ timeT newTime = getInsertionTime();
+
+ Staff *staff = getCurrentStaff();
+ if (!staff)
+ return ;
+ Segment *segment = &staff->getSegment();
+ ViewElementList *vel = staff->getViewElementList();
+
+ EventSelection *es = new EventSelection(*segment);
+ if (m_currentEventSelection &&
+ &m_currentEventSelection->getSegment() == segment)
+ es->addFromSelection(m_currentEventSelection);
+
+ if (!m_currentEventSelection ||
+ &m_currentEventSelection->getSegment() != segment ||
+ m_currentEventSelection->getSegmentEvents().size() == 0 ||
+ m_currentEventSelection->getEndTime() <= oldTime) {
+
+ ViewElementList::iterator extendFrom = vel->findTime(oldTime);
+
+ while (extendFrom != vel->end() &&
+ (*extendFrom)->getViewAbsoluteTime() < newTime) {
+ if ((*extendFrom)->event()->isa(Note::EventType)) {
+ es->addEvent((*extendFrom)->event());
+ }
+ ++extendFrom;
+ }
+
+ } else { // remove an event
+
+ EventSelection::eventcontainer::iterator i =
+ es->getSegmentEvents().begin();
+
+ std::vector<Event *> toErase;
+
+ while (i != es->getSegmentEvents().end() &&
+ (*i)->getAbsoluteTime() < newTime) {
+ toErase.push_back(*i);
+ ++i;
+ }
+
+ for (unsigned int j = 0; j < toErase.size(); ++j) {
+ es->removeEvent(toErase[j]);
+ }
+ }
+
+ setCurrentSelection(es);
+}
+
+void
+EditView::setupActions()
+{
+ createInsertPitchActionMenu();
+
+ //
+ // Tempo and time signature changes
+ //
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QCanvasPixmap pixmap(pixmapDir + "/toolbar/event-insert-tempo.png");
+ QIconSet icon = QIconSet(pixmap);
+ new KAction(AddTempoChangeCommand::getGlobalName(),
+ icon, 0,
+ this, SLOT(slotAddTempo()),
+ actionCollection(), "add_tempo");
+
+ pixmap.load(pixmapDir + "/toolbar/event-insert-timesig.png");
+ icon = QIconSet(pixmap);
+ new KAction(AddTimeSignatureCommand::getGlobalName(),
+ icon, 0,
+ this, SLOT(slotAddTimeSignature()),
+ actionCollection(), "add_time_signature");
+
+ //
+ // Transforms
+ //
+ new KAction(i18n("&Halve Durations"), Key_H + CTRL, this,
+ SLOT(slotHalveDurations()), actionCollection(),
+ "halve_durations");
+
+ new KAction(i18n("&Double Durations"), Key_H + CTRL + SHIFT, this,
+ SLOT(slotDoubleDurations()), actionCollection(),
+ "double_durations");
+
+ new KAction(RescaleCommand::getGlobalName(), 0, this,
+ SLOT(slotRescale()), actionCollection(),
+ "rescale");
+
+ new KAction(TransposeCommand::getGlobalName(1), 0,
+ Key_Up, this,
+ SLOT(slotTransposeUp()), actionCollection(),
+ "transpose_up");
+
+ new KAction(TransposeCommand::getGlobalName(12), 0,
+ Key_Up + CTRL, this,
+ SLOT(slotTransposeUpOctave()), actionCollection(),
+ "transpose_up_octave");
+
+ new KAction(TransposeCommand::getGlobalName( -1), 0,
+ Key_Down, this,
+ SLOT(slotTransposeDown()), actionCollection(),
+ "transpose_down");
+
+ new KAction(TransposeCommand::getGlobalName( -12), 0,
+ Key_Down + CTRL, this,
+ SLOT(slotTransposeDownOctave()), actionCollection(),
+ "transpose_down_octave");
+
+ new KAction(TransposeCommand::getGlobalName(0), 0, this,
+ SLOT(slotTranspose()), actionCollection(),
+ "general_transpose");
+
+ new KAction(TransposeCommand::getDiatonicGlobalName(0,0), 0, this,
+ SLOT(slotDiatonicTranspose()), actionCollection(),
+ "general_diatonic_transpose");
+
+ new KAction(InvertCommand::getGlobalName(0), 0, this,
+ SLOT(slotInvert()), actionCollection(),
+ "invert");
+
+ new KAction(RetrogradeCommand::getGlobalName(0), 0, this,
+ SLOT(slotRetrograde()), actionCollection(),
+ "retrograde");
+
+ new KAction(RetrogradeInvertCommand::getGlobalName(0), 0, this,
+ SLOT(slotRetrogradeInvert()), actionCollection(),
+ "retrograde_invert");
+
+ new KAction(i18n("Jog &Left"), Key_Left + ALT, this,
+ SLOT(slotJogLeft()), actionCollection(),
+ "jog_left");
+
+ new KAction(i18n("Jog &Right"), Key_Right + ALT, this,
+ SLOT(slotJogRight()), actionCollection(),
+ "jog_right");
+
+ // Control rulers
+ //
+ new KAction(i18n("Show Velocity Property Ruler"), 0, this,
+ SLOT(slotShowVelocityControlRuler()), actionCollection(),
+ "show_velocity_control_ruler");
+
+ /*
+ new KAction(i18n("Show Controllers Events Ruler"), 0, this,
+ SLOT(slotShowControllerEventsRuler()), actionCollection(),
+ "show_controller_events_ruler");
+ */
+
+ // Disabled for now
+ //
+ // new KAction(i18n("Add Control Ruler..."), 0, this,
+ // SLOT(slotShowPropertyControlRuler()), actionCollection(),
+ // "add_control_ruler");
+
+ //
+ // Control Ruler context menu
+ //
+ new KAction(i18n("Insert item"), 0, this,
+ SLOT(slotInsertControlRulerItem()), actionCollection(),
+ "insert_control_ruler_item");
+
+ // This was on Key_Delete, but that conflicts with existing Delete commands
+ // on individual edit views
+ new KAction(i18n("Erase selected items"), 0, this,
+ SLOT(slotEraseControlRulerItem()), actionCollection(),
+ "erase_control_ruler_item");
+
+ new KAction(i18n("Clear ruler"), 0, this,
+ SLOT(slotClearControlRulerItem()), actionCollection(),
+ "clear_control_ruler_item");
+
+ new KAction(i18n("Insert line of controllers"), 0, this,
+ SLOT(slotStartControlLineItem()), actionCollection(),
+ "start_control_line_item");
+
+ new KAction(i18n("Flip forward"), Key_BracketRight, this,
+ SLOT(slotFlipForwards()), actionCollection(),
+ "flip_control_events_forward");
+
+ new KAction(i18n("Flip backwards"), Key_BracketLeft, this,
+ SLOT(slotFlipBackwards()), actionCollection(),
+ "flip_control_events_back");
+
+ new KAction(i18n("Draw property line"), 0, this,
+ SLOT(slotDrawPropertyLine()), actionCollection(),
+ "draw_property_line");
+
+ new KAction(i18n("Select all property values"), 0, this,
+ SLOT(slotSelectAllProperties()), actionCollection(),
+ "select_all_properties");
+}
+
+void
+EditView::setupAddControlRulerMenu()
+{
+ RG_DEBUG << "EditView::setupAddControlRulerMenu" << endl;
+
+ QPopupMenu* addControlRulerMenu = dynamic_cast<QPopupMenu*>
+ (factory()->container("add_control_ruler", this));
+
+ if (addControlRulerMenu) {
+
+ addControlRulerMenu->clear();
+
+ //!!! problem here with notation view -- current segment can
+ // change after construction, but this function isn't used again
+
+ Controllable *c =
+ dynamic_cast<MidiDevice *>(getCurrentDevice());
+ if (!c) {
+ c = dynamic_cast<SoftSynthDevice *>(getCurrentDevice());
+ if (!c)
+ return ;
+ }
+
+ const ControlList &list = c->getControlParameters();
+
+ int i = 0;
+ QString itemStr;
+
+ for (ControlList::const_iterator it = list.begin();
+ it != list.end(); ++it) {
+ if (it->getType() == Controller::EventType) {
+ QString hexValue;
+ hexValue.sprintf("(0x%x)", it->getControllerValue());
+
+ itemStr = i18n("%1 Controller %2 %3").arg(strtoqstr(it->getName()))
+ .arg(it->getControllerValue())
+ .arg(hexValue);
+
+ } else if (it->getType() == PitchBend::EventType)
+ itemStr = i18n("Pitch Bend");
+ else
+ itemStr = i18n("Unsupported Event Type");
+
+ addControlRulerMenu->insertItem(itemStr, i++);
+ }
+
+ connect(addControlRulerMenu, SIGNAL(activated(int)),
+ SLOT(slotAddControlRuler(int)));
+ }
+
+}
+
+bool
+EditView::setupControllerTabs()
+{
+ bool have = false;
+
+ // Setup control rulers the Segment already has some stored against it.
+ //
+ Segment *segment = getCurrentSegment();
+ Segment::EventRulerList list = segment->getEventRulerList();
+
+ RG_DEBUG << "EditView::setupControllerTabs - got " << list.size() << " EventRulers" << endl;
+
+ RG_DEBUG << "Segment view features: " << segment->getViewFeatures() << endl;
+ if (segment->getViewFeatures() & FeatureShowVelocity) {
+ showPropertyControlRuler(BaseProperties::VELOCITY);
+ have = true;
+ }
+
+ if (list.size()) {
+ Controllable *c =
+ dynamic_cast<MidiDevice *>(getCurrentDevice());
+ if (!c) {
+ c = dynamic_cast<SoftSynthDevice *>(getCurrentDevice());
+ if (!c)
+ return have;
+ }
+
+ have = true;
+
+ Segment::EventRulerListIterator it;
+
+ for (it = list.begin(); it != list.end(); ++it) {
+ // Get ControlParameter object from controller value
+ //
+ const ControlParameter *controlParameter =
+ c->getControlParameter((*it)->m_type,
+ MidiByte((*it)->m_controllerValue));
+
+ RG_DEBUG << "EditView::setupControllerTabs - "
+ << "Control Parameter type = " << (*it)->m_type << endl;
+
+ if (controlParameter) {
+ ControllerEventsRuler* controlRuler = makeControllerEventRuler(controlParameter);
+ addControlRuler(controlRuler);
+ RG_DEBUG << "EditView::setupControllerTabs - adding Ruler" << endl;
+ }
+ }
+
+ if (!m_controlRulers->isVisible())
+ m_controlRulers->show();
+
+ updateBottomWidgetGeometry();
+ }
+
+ return have;
+}
+
+void
+EditView::slotAddControlRuler(int controller)
+{
+ RG_DEBUG << "EditView::slotAddControlRuler - item = "
+ << controller << endl;
+
+ Controllable *c =
+ dynamic_cast<MidiDevice *>(getCurrentDevice());
+ if (!c) {
+ c = dynamic_cast<SoftSynthDevice *>(getCurrentDevice());
+ if (!c)
+ return ;
+ }
+
+ const ControlList &list = c->getControlParameters();
+ ControlParameter control = list[controller];
+
+ int index = 0;
+
+ ControlRuler* existingRuler = findRuler(control, index);
+
+ if (existingRuler) {
+
+ m_controlRulers->setCurrentPage(index);
+
+ } else {
+
+ // Create control ruler to a specific controller. This duplicates
+ // the control parameter in the supplied pointer.
+ ControllerEventsRuler* controlRuler = makeControllerEventRuler(&control);
+
+ addControlRuler(controlRuler);
+ }
+
+ if (!m_controlRulers->isVisible()) {
+ m_controlRulers->show();
+ }
+
+ updateBottomWidgetGeometry();
+
+ // Add the controller to the segment so the views can
+ // remember what we've opened against it.
+ //
+ Staff *staff = getCurrentStaff();
+ staff->getSegment().addEventRuler(control.getType(), control.getControllerValue());
+
+ getDocument()->slotDocumentModified();
+}
+
+void EditView::slotRemoveControlRuler(QWidget* w)
+{
+ ControllerEventsRuler* ruler = dynamic_cast<ControllerEventsRuler*>(w);
+
+ if (ruler) {
+ ControlParameter *controller = ruler->getControlParameter();
+
+ // remove the control parameter from the "showing controllers" list on the segment
+ //
+ if (controller) {
+ Staff *staff = getCurrentStaff();
+ bool value = staff->getSegment().
+ deleteEventRuler(controller->getType(), controller->getControllerValue());
+
+ if (value)
+ RG_DEBUG << "slotRemoveControlRuler : removed controller from segment\n";
+ else
+ RG_DEBUG << "slotRemoveControlRuler : couldn't remove controller from segment - "
+ << int(controller->getControllerValue())
+ << endl;
+
+ }
+ } else { // else it's probably a velocity ruler
+ PropertyControlRuler *propertyRuler = dynamic_cast<PropertyControlRuler*>(w);
+
+ if (propertyRuler) {
+ Segment &seg = getCurrentStaff()->getSegment();
+ seg.setViewFeatures(0); // for the moment we only have one view feature so
+ // we can just blank it out
+
+ RG_DEBUG << "slotRemoveControlRuler : removed velocity ruler" << endl;
+ }
+ }
+
+ delete w;
+
+ if (m_controlRulers->count() == 0) {
+ m_controlRulers->hide();
+ updateBottomWidgetGeometry();
+ }
+
+ getDocument()->slotDocumentModified();
+}
+
+void
+EditView::createInsertPitchActionMenu()
+{
+ QString notePitchNames[] = {
+ i18n("I"), i18n("II"), i18n("III"), i18n("IV"),
+ i18n("V"), i18n("VI"), i18n("VII"), i18n("VIII")
+ };
+ QString flat = i18n("%1 flat");
+ QString sharp = i18n("%1 sharp");
+
+ const Key notePitchKeys[3][7] = {
+ {
+ Key_A, Key_S, Key_D, Key_F, Key_J, Key_K, Key_L,
+ },
+ {
+ Key_Q, Key_W, Key_E, Key_R, Key_U, Key_I, Key_O,
+ },
+ {
+ Key_Z, Key_X, Key_C, Key_V, Key_B, Key_N, Key_M,
+ },
+ };
+
+ KActionMenu *insertPitchActionMenu =
+ new KActionMenu(i18n("&Insert Note"), this, "insert_note_actionmenu");
+
+ for (int octave = 0; octave <= 2; ++octave) {
+
+ KActionMenu *menu = insertPitchActionMenu;
+ if (octave == 1) {
+ menu = new KActionMenu(i18n("&Upper Octave"), this,
+ "insert_note_actionmenu_upper_octave");
+ insertPitchActionMenu->insert(new KActionSeparator(this));
+ insertPitchActionMenu->insert(menu);
+ } else if (octave == 2) {
+ menu = new KActionMenu(i18n("&Lower Octave"), this,
+ "insert_note_actionmenu_lower_octave");
+ insertPitchActionMenu->insert(menu);
+ }
+
+ for (unsigned int i = 0; i < 7; ++i) {
+
+ KAction *insertPitchAction = 0;
+
+ QString octaveSuffix;
+ if (octave == 1)
+ octaveSuffix = "_high";
+ else if (octave == 2)
+ octaveSuffix = "_low";
+
+ // do and fa lack a flat
+
+ if (i != 0 && i != 3) {
+
+ insertPitchAction =
+ new KAction
+ (flat.arg(notePitchNames[i]),
+ CTRL + SHIFT + notePitchKeys[octave][i],
+ this, SLOT(slotInsertNoteFromAction()), actionCollection(),
+ QString("insert_%1_flat%2").arg(i).arg(octaveSuffix));
+
+ menu->insert(insertPitchAction);
+ }
+
+ insertPitchAction =
+ new KAction
+ (notePitchNames[i],
+ notePitchKeys[octave][i],
+ this, SLOT(slotInsertNoteFromAction()), actionCollection(),
+ QString("insert_%1%2").arg(i).arg(octaveSuffix));
+
+ menu->insert(insertPitchAction);
+
+ // and mi and ti lack a sharp
+
+ if (i != 2 && i != 6) {
+
+ insertPitchAction =
+ new KAction
+ (sharp.arg(notePitchNames[i]),
+ SHIFT + notePitchKeys[octave][i],
+ this, SLOT(slotInsertNoteFromAction()), actionCollection(),
+ QString("insert_%1_sharp%2").arg(i).arg(octaveSuffix));
+
+ menu->insert(insertPitchAction);
+ }
+
+ if (i < 6)
+ menu->insert(new KActionSeparator(this));
+ }
+ }
+
+ actionCollection()->insert(insertPitchActionMenu);
+}
+
+int
+EditView::getPitchFromNoteInsertAction(QString name,
+ Accidental &accidental,
+ const Clef &clef,
+ const ::Rosegarden::Key &key)
+{
+ using namespace Accidentals;
+
+ accidental = NoAccidental;
+
+ if (name.left(7) == "insert_") {
+
+ name = name.right(name.length() - 7);
+
+ int modify = 0;
+ int octave = 0;
+
+ if (name.right(5) == "_high") {
+
+ octave = 1;
+ name = name.left(name.length() - 5);
+
+ } else if (name.right(4) == "_low") {
+
+ octave = -1;
+ name = name.left(name.length() - 4);
+ }
+
+ if (name.right(6) == "_sharp") {
+
+ modify = 1;
+ accidental = Sharp;
+ name = name.left(name.length() - 6);
+
+ } else if (name.right(5) == "_flat") {
+
+ modify = -1;
+ accidental = Flat;
+ name = name.left(name.length() - 5);
+ }
+
+ int scalePitch = name.toInt();
+
+ if (scalePitch < 0 || scalePitch > 7) {
+ NOTATION_DEBUG << "EditView::getPitchFromNoteInsertAction: pitch "
+ << scalePitch << " out of range, using 0" << endl;
+ scalePitch = 0;
+ }
+
+ Pitch pitch
+ (scalePitch, 4 + octave + clef.getOctave(), key, accidental);
+ return pitch.getPerformancePitch();
+
+ } else {
+
+ throw Exception("Not an insert action",
+ __FILE__, __LINE__);
+ }
+}
+
+void EditView::slotAddTempo()
+{
+ timeT insertionTime = getInsertionTime();
+
+ TempoDialog tempoDlg(this, getDocument());
+
+ connect(&tempoDlg,
+ SIGNAL(changeTempo(timeT,
+ tempoT,
+ tempoT,
+ TempoDialog::TempoDialogAction)),
+ this,
+ SIGNAL(changeTempo(timeT,
+ tempoT,
+ tempoT,
+ TempoDialog::TempoDialogAction)));
+
+ tempoDlg.setTempoPosition(insertionTime);
+ tempoDlg.exec();
+}
+
+void EditView::slotAddTimeSignature()
+{
+ Segment *segment = getCurrentSegment();
+ if (!segment)
+ return ;
+ Composition *composition = segment->getComposition();
+ timeT insertionTime = getInsertionTime();
+
+ TimeSignatureDialog *dialog = 0;
+ int timeSigNo = composition->getTimeSignatureNumberAt(insertionTime);
+
+ if (timeSigNo >= 0) {
+
+ dialog = new TimeSignatureDialog
+ (this, composition, insertionTime,
+ composition->getTimeSignatureAt(insertionTime));
+
+ } else {
+
+ timeT endTime = composition->getDuration();
+ if (composition->getTimeSignatureCount() > 0) {
+ endTime = composition->getTimeSignatureChange(0).first;
+ }
+
+ CompositionTimeSliceAdapter adapter
+ (composition, insertionTime, endTime);
+ AnalysisHelper helper;
+ TimeSignature timeSig = helper.guessTimeSignature(adapter);
+
+ dialog = new TimeSignatureDialog
+ (this, composition, insertionTime, timeSig, false,
+ i18n("Estimated time signature shown"));
+ }
+
+ if (dialog->exec() == QDialog::Accepted) {
+
+ insertionTime = dialog->getTime();
+
+ if (dialog->shouldNormalizeRests()) {
+
+ addCommandToHistory(new AddTimeSignatureAndNormalizeCommand
+ (composition, insertionTime,
+ dialog->getTimeSignature()));
+
+ } else {
+
+ addCommandToHistory(new AddTimeSignatureCommand
+ (composition, insertionTime,
+ dialog->getTimeSignature()));
+ }
+ }
+
+ delete dialog;
+}
+
+void EditView::showPropertyControlRuler(PropertyName propertyName)
+{
+ int index = 0;
+
+ ControlRuler* existingRuler = findRuler(propertyName, index);
+
+ if (existingRuler) {
+
+ m_controlRulers->setCurrentPage(index);
+
+ } else {
+
+ PropertyControlRuler* controlRuler = makePropertyControlRuler(propertyName);
+ addControlRuler(controlRuler);
+ }
+
+ if (!m_controlRulers->isVisible()) {
+ m_controlRulers->show();
+ }
+
+ updateBottomWidgetGeometry();
+}
+
+void EditView::slotShowVelocityControlRuler()
+{
+ showPropertyControlRuler(BaseProperties::VELOCITY);
+ Segment &seg = getCurrentStaff()->getSegment();
+ seg.setViewFeatures(seg.getViewFeatures() | FeatureShowVelocity);
+ getDocument()->slotDocumentModified();
+}
+
+void EditView::slotShowControllerEventsRuler()
+{
+
+ // int index = 0;
+
+ // ControlRuler* existingRuler = findRuler(propertyName, index);
+
+ // if (existingRuler) {
+
+ // m_controlRulers->setCurrentPage(index);
+
+ // } else {
+
+ // ControllerEventsRuler* controlRuler = makeControllerEventRuler();
+ // addControlRuler(controlRuler);
+ // }
+
+ // if (!m_controlRulers->isVisible()) {
+ // m_controlRulers->show();
+ // }
+
+ // updateBottomWidgetGeometry();
+}
+
+void EditView::slotShowPropertyControlRuler()
+{
+ /*
+ KDialogBase propChooserDialog(this, "propertychooserdialog", true, i18n("Select event property"),
+ KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok);
+
+ KListBox* propList = new KListBox(propChooserDialog.makeVBoxMainWidget());
+ new QListBoxRGProperty(propList, BaseProperties::VELOCITY.c_str());
+
+ int rc = propChooserDialog.exec();
+ if (rc == QDialog::Accepted) {
+ // fix for KDE 3.0
+ //QListBoxRGProperty* item = dynamic_cast<QListBoxRGProperty*>(propList->selectedItem());
+ QListBoxRGProperty* item = dynamic_cast<QListBoxRGProperty*>
+ (propList->item(propList->currentItem()));
+
+ if (item) {
+ PropertyName property = item->getPropertyName();
+ showPropertyControlRuler(property);
+ }
+ }
+ */
+}
+
+void
+EditView::slotInsertControlRulerItem()
+{
+ ControllerEventsRuler* ruler = dynamic_cast<ControllerEventsRuler*>(getCurrentControlRuler());
+ if (ruler)
+ ruler->insertControllerEvent();
+}
+
+void
+EditView::slotEraseControlRulerItem()
+{
+ ControllerEventsRuler* ruler = dynamic_cast<ControllerEventsRuler*>(getCurrentControlRuler());
+ if (ruler)
+ ruler->eraseControllerEvent();
+}
+
+void
+EditView::slotStartControlLineItem()
+{
+ ControllerEventsRuler* ruler = dynamic_cast<ControllerEventsRuler*>(getCurrentControlRuler());
+ if (ruler)
+ ruler->startControlLine();
+}
+
+void
+EditView::slotDrawPropertyLine()
+{
+ int index = 0;
+ PropertyControlRuler* ruler = dynamic_cast<PropertyControlRuler*>
+ (findRuler(BaseProperties::VELOCITY, index));
+
+ if (ruler)
+ ruler->startPropertyLine();
+}
+
+void
+EditView::slotSelectAllProperties()
+{
+ int index = 0;
+ PropertyControlRuler* ruler = dynamic_cast<PropertyControlRuler*>
+ (findRuler(BaseProperties::VELOCITY, index));
+
+ if (ruler)
+ ruler->selectAllProperties();
+}
+
+void
+EditView::slotClearControlRulerItem()
+{
+ ControllerEventsRuler* ruler = dynamic_cast<ControllerEventsRuler*>(getCurrentControlRuler());
+ if (ruler)
+ ruler->clearControllerEvents();
+}
+
+void
+EditView::slotHalveDurations()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ KTmpStatusMsg msg(i18n("Halving durations..."), this);
+
+ addCommandToHistory(
+ new RescaleCommand(*m_currentEventSelection,
+ m_currentEventSelection->getTotalDuration() / 2,
+ false));
+}
+
+void
+EditView::slotDoubleDurations()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ KTmpStatusMsg msg(i18n("Doubling durations..."), this);
+
+ addCommandToHistory(
+ new RescaleCommand(*m_currentEventSelection,
+ m_currentEventSelection->getTotalDuration() * 2,
+ false));
+}
+
+void
+EditView::slotRescale()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ RescaleDialog dialog
+ (this,
+ &getDocument()->getComposition(),
+ m_currentEventSelection->getStartTime(),
+ m_currentEventSelection->getEndTime() -
+ m_currentEventSelection->getStartTime(),
+ true,
+ true);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ KTmpStatusMsg msg(i18n("Rescaling..."), this);
+ addCommandToHistory(new RescaleCommand
+ (*m_currentEventSelection,
+ dialog.getNewDuration(),
+ dialog.shouldCloseGap()));
+ }
+}
+
+void EditView::slotTranspose()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ m_config->setGroup(EditViewConfigGroup);
+
+ int dialogDefault = m_config->readNumEntry("lasttransposition", 0);
+
+ bool ok = false;
+ int semitones = QInputDialog::getInteger
+ (i18n("Transpose"),
+ i18n("By number of semitones: "),
+ dialogDefault, -127, 127, 1, &ok, this);
+ if (!ok || semitones == 0) return;
+
+ m_config->setGroup(EditViewConfigGroup);
+ m_config->writeEntry("lasttransposition", semitones);
+
+ KTmpStatusMsg msg(i18n("Transposing..."), this);
+ addCommandToHistory(new TransposeCommand
+ (semitones, *m_currentEventSelection));
+}
+
+void EditView::slotDiatonicTranspose()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ m_config->setGroup(EditViewConfigGroup);
+
+ IntervalDialog intervalDialog(this);
+ int ok = intervalDialog.exec();
+ //int dialogDefault = m_config->readNumEntry("lasttransposition", 0);
+ int semitones = intervalDialog.getChromaticDistance();
+ int steps = intervalDialog.getDiatonicDistance();
+
+ if (!ok || (semitones == 0 && steps == 0)) return;
+
+ m_config->setGroup(EditViewConfigGroup);
+
+ KTmpStatusMsg msg(i18n("Transposing..."), this);
+ if (intervalDialog.getChangeKey())
+ {
+ std::cout << "Transposing changing keys is not currently supported on selections" << std::endl;
+ }
+ else
+ {
+ // Transpose within key
+ //std::cout << "Transposing semitones, steps: " << semitones << ", " << steps << std::endl;
+ addCommandToHistory(new TransposeCommand
+ (semitones, steps, *m_currentEventSelection));
+ }
+}
+
+void EditView::slotTransposeUp()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Transposing up one semitone..."), this);
+
+ addCommandToHistory(new TransposeCommand(1, *m_currentEventSelection));
+}
+
+void EditView::slotTransposeUpOctave()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Transposing up one octave..."), this);
+
+ addCommandToHistory(new TransposeCommand(12, *m_currentEventSelection));
+}
+
+void EditView::slotTransposeDown()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Transposing down one semitone..."), this);
+
+ addCommandToHistory(new TransposeCommand( -1, *m_currentEventSelection));
+}
+
+void EditView::slotTransposeDownOctave()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Transposing down one octave..."), this);
+
+ addCommandToHistory(new TransposeCommand( -12, *m_currentEventSelection));
+}
+
+void EditView::slotInvert()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ int semitones = 0;
+
+ KTmpStatusMsg msg(i18n("Inverting..."), this);
+ addCommandToHistory(new InvertCommand
+ (semitones, *m_currentEventSelection));
+}
+
+void EditView::slotRetrograde()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ int semitones = 0;
+
+ KTmpStatusMsg msg(i18n("Retrograding..."), this);
+ addCommandToHistory(new RetrogradeCommand
+ (semitones, *m_currentEventSelection));
+}
+
+void EditView::slotRetrogradeInvert()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ int semitones = 0;
+
+ KTmpStatusMsg msg(i18n("Retrograde inverting..."), this);
+ addCommandToHistory(new RetrogradeInvertCommand
+ (semitones, *m_currentEventSelection));
+}
+
+void EditView::slotJogLeft()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Jogging left..."), this);
+
+ RG_DEBUG << "EditView::slotJogLeft" << endl;
+
+ addCommandToHistory(
+ new MoveCommand(*getCurrentSegment(),
+ -Note(Note::Demisemiquaver).getDuration(),
+ false, // don't use notation timings
+ *m_currentEventSelection));
+}
+
+void EditView::slotJogRight()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Jogging right..."), this);
+
+ RG_DEBUG << "EditView::slotJogRight" << endl;
+
+ addCommandToHistory(
+ new MoveCommand(*getCurrentSegment(),
+ Note(Note::Demisemiquaver).getDuration(),
+ false, // don't use notation timings
+ *m_currentEventSelection));
+}
+
+void
+EditView::slotFlipForwards()
+{
+ RG_DEBUG << "EditView::slotFlipForwards" << endl;
+ ControlRuler* ruler = getCurrentControlRuler();
+ if (ruler) ruler->flipForwards();
+}
+
+void
+EditView::slotFlipBackwards()
+{
+ RG_DEBUG << "EditView::slotFlipBackwards" << endl;
+ ControlRuler* ruler = getCurrentControlRuler();
+ if (ruler) ruler->flipBackwards();
+}
+
+ControlRuler* EditView::getCurrentControlRuler()
+{
+ return dynamic_cast<ControlRuler*>(m_controlRulers->currentPage());
+}
+
+ControlRuler* EditView::findRuler(PropertyName propertyName, int &index)
+{
+ for(index = 0; index < m_controlRulers->count(); ++index) {
+ PropertyControlRuler* ruler = dynamic_cast<PropertyControlRuler*>(m_controlRulers->page(index));
+ if (ruler && ruler->getPropertyName() == propertyName) return ruler;
+ }
+
+ return 0;
+}
+
+ControlRuler* EditView::findRuler(const ControlParameter& controller, int &index)
+{
+ for(index = 0; index < m_controlRulers->count(); ++index) {
+ ControllerEventsRuler* ruler = dynamic_cast<ControllerEventsRuler*>(m_controlRulers->page(index));
+ if (ruler && *(ruler->getControlParameter()) == controller) return ruler;
+ }
+
+ return 0;
+}
+
+PropertyControlRuler* EditView::makePropertyControlRuler(PropertyName propertyName)
+{
+ QCanvas* controlRulerCanvas = new QCanvas(this);
+ QSize viewSize = getViewSize();
+ controlRulerCanvas->resize(viewSize.width(), ControlRuler::DefaultRulerHeight); // TODO - keep it in sync with main canvas size
+
+// QCanvas* controlRulerCanvas = ControlRulerCanvasRepository::getCanvas(getCurrentSegment(), propertyName,
+// getViewSize());
+
+ PropertyControlRuler* controlRuler = new PropertyControlRuler
+ (propertyName, getCurrentStaff(), getHLayout(), this,
+ controlRulerCanvas, m_controlRulers);
+
+ controlRuler->setMainHorizontalScrollBar(m_canvasView->horizontalScrollBar());
+
+ return controlRuler;
+}
+
+ControllerEventsRuler* EditView::makeControllerEventRuler(const ControlParameter *controller)
+{
+ QCanvas* controlRulerCanvas = new QCanvas(this);
+ QSize viewSize = getViewSize();
+ controlRulerCanvas->resize(viewSize.width(), ControlRuler::DefaultRulerHeight); // TODO - keep it in sync with main canvas size
+// QCanvas* controlRulerCanvas = ControlRulerCanvasRepository::getCanvas(getCurrentSegment(), controller,
+// getViewSize());
+
+
+ ControllerEventsRuler* controlRuler = new ControllerEventsRuler
+ (getCurrentSegment(), getHLayout(), this,
+ controlRulerCanvas, m_controlRulers, controller);
+
+ controlRuler->setMainHorizontalScrollBar(m_canvasView->horizontalScrollBar());
+
+ return controlRuler;
+}
+
+RosegardenCanvasView* EditView::getCanvasView()
+{
+ return m_canvasView;
+}
+
+}
+#include "EditView.moc"
diff --git a/src/gui/general/EditView.h b/src/gui/general/EditView.h
new file mode 100644
index 0000000..da18982
--- /dev/null
+++ b/src/gui/general/EditView.h
@@ -0,0 +1,405 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_EDITVIEW_H_
+#define _RG_EDITVIEW_H_
+
+#include "base/PropertyName.h"
+#include "EditViewBase.h"
+#include "gui/dialogs/TempoDialog.h"
+#include <qsize.h>
+#include <qstring.h>
+#include <qwmatrix.h>
+#include <vector>
+#include "base/Event.h"
+
+
+class QWidget;
+class QVBoxLayout;
+class QVBox;
+class QPaintEvent;
+class QMouseEvent;
+class QCanvasItem;
+class KTabWidget;
+class Accidental;
+
+
+namespace Rosegarden
+{
+
+class Staff;
+class Segment;
+class RulerScale;
+class RosegardenGUIDoc;
+class RosegardenCanvasView;
+class PropertyControlRuler;
+class Key;
+class EventSelection;
+class Device;
+class ControlRuler;
+class ControlParameter;
+class ControllerEventsRuler;
+class Clef;
+class StandardRuler;
+class ActiveItem;
+
+
+class EditView : public EditViewBase
+{
+ Q_OBJECT
+
+public:
+
+ /**
+ * Create an EditView for the segments \a segments from document \a doc.
+ *
+ * \arg cols : number of columns, main column is always rightmost
+ *
+ */
+ EditView(RosegardenGUIDoc *doc,
+ std::vector<Segment *> segments,
+ unsigned int cols,
+ QWidget *parent,
+ const char *name = 0);
+
+ virtual ~EditView();
+
+ /**
+ * "Clever" readjustment of the view size
+ * If the new size is larger, enlarge to that size plus a margin
+ * If it is smaller, only shrink if the reduction is significant
+ * (e.g. new size is less than 75% of the old one)
+ *
+ * @arg exact if true, then set to newSize exactly
+ */
+ virtual void readjustViewSize(QSize newSize, bool exact = false);
+
+ /**
+ * Return the active item
+ */
+ ActiveItem* activeItem() { return m_activeItem; }
+
+ /**
+ * Set the active item
+ */
+ void setActiveItem(ActiveItem* i) { m_activeItem = i; }
+
+ /**
+ * 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) = 0;
+
+ EventSelection* getCurrentSelection()
+ { return m_currentEventSelection; }
+
+ RosegardenCanvasView* getRawCanvasView() { return m_canvasView; }
+ virtual RosegardenCanvasView* getCanvasView();
+
+signals:
+ void changeTempo(timeT, // tempo change time
+ tempoT, // tempo value
+ tempoT, // target value
+ TempoDialog::TempoDialogAction); // tempo action
+
+public slots:
+ /**
+ * Called when a mouse press occurred on an active canvas item
+ *
+ * @see ActiveItem
+ * @see QCanvasItem#setActive
+ */
+ virtual void slotActiveItemPressed(QMouseEvent*, QCanvasItem*);
+
+ virtual void slotSetInsertCursorPosition(timeT position) = 0;
+
+ void slotExtendSelectionBackward();
+ void slotExtendSelectionForward();
+ void slotExtendSelectionBackwardBar();
+ void slotExtendSelectionForwardBar();
+ void slotExtendSelectionBackward(bool bar);
+ void slotExtendSelectionForward(bool bar);
+
+ virtual void slotStepBackward(); // default is event-by-event
+ virtual void slotStepForward(); // default is event-by-event
+ void slotJumpBackward();
+ void slotJumpForward();
+ void slotJumpToStart();
+ void slotJumpToEnd();
+
+ void slotAddTempo();
+ void slotAddTimeSignature();
+
+ virtual void slotShowVelocityControlRuler();
+ virtual void slotShowControllerEventsRuler();
+ virtual void slotShowPropertyControlRuler();
+
+ // rescale
+ void slotHalveDurations();
+ void slotDoubleDurations();
+ void slotRescale();
+
+ // transpose
+ void slotTransposeUp();
+ void slotTransposeUpOctave();
+ void slotTransposeDown();
+ void slotTransposeDownOctave();
+ void slotTranspose();
+ void slotDiatonicTranspose();
+
+ // invert
+ void slotInvert();
+ void slotRetrograde();
+ void slotRetrogradeInvert();
+
+ // jog events
+ void slotJogLeft();
+ void slotJogRight();
+
+ // Control ruler actions
+ //
+ void slotInsertControlRulerItem();
+ void slotEraseControlRulerItem();
+ void slotClearControlRulerItem();
+ void slotStartControlLineItem();
+ void slotFlipForwards();
+ void slotFlipBackwards();
+
+ // Property ruler actions
+ //
+ void slotDrawPropertyLine();
+ void slotSelectAllProperties();
+
+ // add control ruler
+ void slotAddControlRuler(int);
+ void slotRemoveControlRuler(QWidget*);
+
+protected:
+ virtual RulerScale* getHLayout() = 0;
+
+ QVBox* getBottomWidget() { return m_bottomBox; }
+
+ virtual void updateBottomWidgetGeometry();
+
+ virtual void paintEvent(QPaintEvent* e);
+
+ /**
+ * Locate the given widgets in the top bar-buttons position and
+ * connect up its scrolling signals.
+ */
+ void setTopStandardRuler(StandardRuler*, QWidget *leftBox = NULL);
+
+ /**
+ * Locate the given widget in the bottom bar-buttons position and
+ * connect up its scrolling signals.
+ */
+ void setBottomStandardRuler(StandardRuler*);
+
+ /**
+ * Set the 'Rewind' and 'Fast Forward' buttons in the transport
+ * toolbar to AutoRepeat
+ */
+ void setRewFFwdToAutoRepeat();
+
+ /**
+ * Locate the given widget right above the top bar-buttons and
+ * connect up its scrolling signals.
+ * The widget has to have a slotScrollHoriz(int) slot
+ */
+ void addRuler(QWidget*);
+
+ /**
+ * Add a ruler control box
+ */
+ void addPropertyBox(QWidget*);
+
+ /**
+ * Make a control ruler for the given property,
+ */
+ PropertyControlRuler* makePropertyControlRuler(PropertyName propertyName);
+
+ /**
+ * Make a ruler for controller events
+ */
+ ControllerEventsRuler* makeControllerEventRuler(const ControlParameter *controller = 0);
+
+ /**
+ * Add control ruler
+ */
+ void addControlRuler(ControlRuler* ruler);
+
+ /**
+ * Update all control rulers
+ */
+ void updateControlRulers(bool updateHPos=false);
+
+ /**
+ * Set zoom factor of control rulers
+ */
+ void setControlRulersZoom(QWMatrix);
+
+ /**
+ * Set current segment for control rulers
+ */
+ void setControlRulersCurrentSegment();
+
+ /**
+ * Find the control ruler for the given property name
+ * if it's already been created, return 0 otherwise
+ */
+ ControlRuler* findRuler(PropertyName propertyName, int &index);
+
+ /**
+ * Find the control ruler for the given controller
+ * if it's already been created, return 0 otherwise
+ */
+ ControlRuler* findRuler(const ControlParameter& controller, int &index);
+
+ /**
+ * Show a control ruler for the given property
+ * If the ruler already exists, activate the tab it's in,
+ * otherwise create the ruler and add it to the control rulers tab
+ * widget
+ */
+ void showPropertyControlRuler(PropertyName propertyName);
+
+ /**
+ * Return the control ruler currently displayed, or 0 if none exist
+ */
+ ControlRuler* getCurrentControlRuler();
+
+ /**
+ * Set up those actions common to any EditView (e.g. note insertion,
+ * time signatures etc)
+ */
+ void setupActions();
+
+ /**
+ * Set up the 'Add control ruler' sub-menu
+ */
+ void setupAddControlRulerMenu();
+
+ /**
+ * Do this after any other segment setup in a subordinate view.
+ * Returns true if there were any tabs to set up.
+ */
+ bool setupControllerTabs();
+
+ /**
+ * Create an action menu for inserting notes from the PC keyboard,
+ * and add it to the action collection. This is one of the methods
+ * called by setupActions().
+ */
+ void createInsertPitchActionMenu();
+
+ /**
+ * Get a note pitch from an action name (where the action is one of
+ * those created by createInsertPitchActionMenu). Can throw an
+ * Exception to mean that the action is not an insert one. Also
+ * returns any specified accidental through the reference arg.
+ */
+ int getPitchFromNoteInsertAction(QString actionName,
+ Accidental &acc,
+ const Clef &clef,
+ const ::Rosegarden::Key &key);
+
+ /**
+ * Abstract method to get the view size
+ * Typically implemented as canvas()->size().
+ */
+ virtual QSize getViewSize() = 0;
+
+ /**
+ * Abstract method to set the view size
+ * Typically implemented as canvas()->resize().
+ */
+ virtual void setViewSize(QSize) = 0;
+
+ /**
+ * Abstract method to get current insert-pointer time
+ */
+ virtual timeT getInsertionTime() = 0;
+
+ /**
+ * Return the time at which the insert cursor may be found,
+ * and the time signature, clef and key at that time. Default
+ * implementation is okay but slow.
+ */
+ virtual timeT getInsertionTime(Clef &clef, ::Rosegarden::Key &key);
+
+ /**
+ * Abstract method to get current staff (the returned staff will be
+ * that representing the segment of getCurrentSegment())
+ */
+ virtual Staff *getCurrentStaff() = 0;
+
+ /**
+ * Return the device of the current segment, if any
+ */
+ Device *getCurrentDevice();
+
+ virtual void setCanvasView(RosegardenCanvasView *cv);
+
+ //--------------- Data members ---------------------------------
+
+ /// The current selection of Events (for cut/copy/paste)
+ EventSelection* m_currentEventSelection;
+
+ ActiveItem* m_activeItem;
+
+ RosegardenCanvasView *m_canvasView;
+
+ QVBoxLayout *m_rulerBox;
+ QLabel *m_rulerBoxFiller;
+ QVBoxLayout *m_controlBox;
+ QVBox *m_bottomBox;
+ StandardRuler *m_topStandardRuler;
+ StandardRuler *m_bottomStandardRuler;
+ ControlRuler *m_controlRuler;
+ KTabWidget *m_controlRulers;
+ QWMatrix m_currentRulerZoomMatrix;
+
+ static const unsigned int RULERS_ROW;
+ static const unsigned int CONTROLS_ROW;
+ static const unsigned int TOPBARBUTTONS_ROW;
+ static const unsigned int CANVASVIEW_ROW;
+ static const unsigned int CONTROLRULER_ROW;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/general/EditViewBase.cpp b/src/gui/general/EditViewBase.cpp
new file mode 100644
index 0000000..0193beb
--- /dev/null
+++ b/src/gui/general/EditViewBase.cpp
@@ -0,0 +1,711 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "EditViewBase.h"
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include <klocale.h>
+#include <kstddirs.h>
+#include "misc/Debug.h"
+#include "base/Clipboard.h"
+#include "base/Event.h"
+#include "base/NotationTypes.h"
+#include "base/Segment.h"
+#include "commands/segment/SegmentReconfigureCommand.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/RosegardenGUIDoc.h"
+#include "EditToolBox.h"
+#include "EditTool.h"
+#include "EditView.h"
+#include "gui/dialogs/ConfigureDialog.h"
+#include "gui/dialogs/TimeDialog.h"
+#include "gui/general/EditViewTimeSigNotifier.h"
+#include "gui/kdeext/KTmpStatusMsg.h"
+#include <kaction.h>
+#include <kcommand.h>
+#include <kconfig.h>
+#include <kdockwidget.h>
+#include <kedittoolbar.h>
+#include <kglobal.h>
+#include <kkeydialog.h>
+#include <kmainwindow.h>
+#include <kstatusbar.h>
+#include <kstdaccel.h>
+#include <kstdaction.h>
+#include <kxmlguiclient.h>
+#include <qaccel.h>
+#include <qcanvas.h>
+#include <qdialog.h>
+#include <qframe.h>
+#include <qiconset.h>
+#include <qobject.h>
+#include <qpixmap.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+bool EditViewBase::m_inPaintEvent = false;
+const unsigned int EditViewBase::ID_STATUS_MSG = 1;
+const unsigned int EditViewBase::NbLayoutRows = 6;
+
+EditViewBase::EditViewBase(RosegardenGUIDoc *doc,
+ std::vector<Segment *> segments,
+ unsigned int cols,
+ QWidget *parent, const char *name) :
+ KDockMainWindow(parent, name),
+ m_viewNumber( -1),
+ m_viewLocalPropertyPrefix(makeViewLocalPropertyPrefix()),
+ m_config(kapp->config()),
+ m_doc(doc),
+ m_segments(segments),
+ m_tool(0),
+ m_toolBox(0),
+ m_mainDockWidget(0),
+ m_centralFrame(0),
+ m_grid(0),
+ m_mainCol(cols - 1),
+ m_compositionRefreshStatusId(doc->getComposition().getNewRefreshStatusId()),
+ m_needUpdate(false),
+ m_pendingPaintEvent(0),
+ m_havePendingPaintEvent(false),
+ m_accelerators(0),
+ m_configDialogPageIndex(0),
+ m_inCtor(true),
+ m_timeSigNotifier(new EditViewTimeSigNotifier(doc))
+{
+
+ QPixmap dummyPixmap; // any icon will do
+ m_mainDockWidget = createDockWidget("Rosegarden EditView DockWidget", dummyPixmap,
+ 0L, "editview_dock_widget");
+ // allow others to dock to the left and right sides only
+ m_mainDockWidget->setDockSite(KDockWidget::DockLeft | KDockWidget::DockRight);
+ // forbit docking abilities of m_mainDockWidget itself
+ m_mainDockWidget->setEnableDocking(KDockWidget::DockNone);
+ setView(m_mainDockWidget); // central widget in a KDE mainwindow
+ setMainDockWidget(m_mainDockWidget); // master dockwidget
+
+ m_centralFrame = new QFrame(m_mainDockWidget, "centralframe");
+ m_grid = new QGridLayout(m_centralFrame, NbLayoutRows, cols);
+
+ m_mainDockWidget->setWidget(m_centralFrame);
+
+ initSegmentRefreshStatusIds();
+
+ m_doc->attachEditView(this);
+
+ QObject::connect
+ (getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(update()));
+
+ QObject::connect
+ (getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(slotTestClipboard()));
+
+ // create accelerators
+ //
+ m_accelerators = new QAccel(this);
+}
+
+EditViewBase::~EditViewBase()
+{
+ delete m_timeSigNotifier;
+
+ m_doc->detachEditView(this);
+
+ getCommandHistory()->detachView(actionCollection());
+ m_viewNumberPool.erase(m_viewNumber);
+ slotSaveOptions();
+}
+
+void EditViewBase::slotSaveOptions()
+{}
+
+void EditViewBase::readOptions()
+{
+ getToggleAction("options_show_statusbar")->setChecked(!statusBar()->isHidden());
+ getToggleAction("options_show_toolbar")->setChecked(!toolBar()->isHidden());
+}
+
+void EditViewBase::setupActions(QString rcFileName, bool haveClipboard)
+{
+ setRCFileName(rcFileName);
+
+ // Actions all edit views will have
+
+ KStdAction::showToolbar(this, SLOT(slotToggleToolBar()),
+ actionCollection(), "options_show_toolbar");
+
+ KStdAction::showStatusbar(this, SLOT(slotToggleStatusBar()),
+ actionCollection(), "options_show_statusbar");
+
+ KStdAction::preferences(this,
+ SLOT(slotConfigure()),
+ actionCollection());
+
+ KStdAction::keyBindings(this,
+ SLOT(slotEditKeys()),
+ actionCollection());
+
+ KStdAction::configureToolbars(this,
+ SLOT(slotEditToolbars()),
+ actionCollection());
+
+
+ // File menu
+ KStdAction::save (this, SIGNAL(saveFile()), actionCollection());
+ KStdAction::close(this, SLOT(slotCloseWindow()), actionCollection());
+
+ if (haveClipboard) {
+ KStdAction::cut (this, SLOT(slotEditCut()), actionCollection());
+ KStdAction::copy (this, SLOT(slotEditCopy()), actionCollection());
+ KStdAction::paste (this, SLOT(slotEditPaste()), actionCollection());
+ }
+
+ 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/");
+
+ QCanvasPixmap pixmap(pixmapDir + "/toolbar/matrix.png");
+ QIconSet icon = QIconSet(pixmap);
+ new KAction(i18n("Open in Matri&x Editor"), icon, 0, this,
+ SLOT(slotOpenInMatrix()), actionCollection(),
+ "open_in_matrix");
+
+ pixmap.load(pixmapDir + "/toolbar/matrix-percussion.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Open in &Percussion Matrix Editor"), icon, 0, this,
+ SLOT(slotOpenInPercussionMatrix()), actionCollection(),
+ "open_in_percussion_matrix");
+
+ pixmap.load(pixmapDir + "/toolbar/notation.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Open in &Notation Editor"), icon, 0, this,
+ SLOT(slotOpenInNotation()), actionCollection(),
+ "open_in_notation");
+
+ pixmap.load(pixmapDir + "/toolbar/eventlist.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Open in &Event List Editor"), icon, 0, this,
+ SLOT(slotOpenInEventList()), actionCollection(),
+ "open_in_event_list");
+
+ new KAction(i18n("Set Segment Start Time..."), 0, this,
+ SLOT(slotSetSegmentStartTime()), actionCollection(),
+ "set_segment_start");
+
+ new KAction(i18n("Set Segment Duration..."), 0, this,
+ SLOT(slotSetSegmentDuration()), actionCollection(),
+ "set_segment_duration");
+
+ // add undo and redo to edit menu and toolbar
+ getCommandHistory()->attachView(actionCollection());
+
+}
+
+void EditViewBase::slotConfigure()
+{
+ ConfigureDialog *configDlg =
+ new ConfigureDialog(getDocument(), m_config, this);
+
+ configDlg->showPage(getConfigDialogPageIndex());
+ configDlg->show();
+}
+
+void EditViewBase::slotEditKeys()
+{
+ KKeyDialog::configure(actionCollection());
+}
+
+void EditViewBase::slotEditToolbars()
+{
+ KEditToolbar dlg(actionCollection(), getRCFileName());
+
+ connect(&dlg, SIGNAL(newToolbarConfig()),
+ SLOT(slotUpdateToolbars()));
+
+ dlg.exec();
+}
+
+void EditViewBase::slotUpdateToolbars()
+{
+ createGUI(getRCFileName());
+ //m_viewToolBar->setChecked(!toolBar()->isHidden());
+}
+
+void
+EditViewBase::slotOpenInNotation()
+{
+
+ emit openInNotation(m_segments);
+}
+
+void
+EditViewBase::slotOpenInMatrix()
+{
+ emit openInMatrix(m_segments);
+}
+
+void
+EditViewBase::slotOpenInPercussionMatrix()
+{
+ emit openInPercussionMatrix(m_segments);
+}
+
+void
+EditViewBase::slotOpenInEventList()
+{
+ emit openInEventList(m_segments);
+}
+
+std::set<int> EditViewBase::m_viewNumberPool;
+
+std::string
+EditViewBase::makeViewLocalPropertyPrefix()
+{
+ static char buffer[100];
+ int i = 0;
+ while (m_viewNumberPool.find(i) != m_viewNumberPool.end())
+ ++i;
+ m_viewNumber = i;
+ m_viewNumberPool.insert(i);
+ sprintf(buffer, "View%d::", i);
+ return buffer;
+}
+
+void EditViewBase::paintEvent(QPaintEvent* e)
+{
+ // It is possible for this function to be called re-entrantly,
+ // because a re-layout procedure may deliberately ask the event
+ // loop to process some more events so as to keep the GUI looking
+ // responsive. If that happens, we remember the events that came
+ // in in the middle of one paintEvent call and process their union
+ // again at the end of the call.
+ /*
+ if (m_inPaintEvent) {
+ NOTATION_DEBUG << "EditViewBase::paintEvent: in paint event already" << endl;
+ if (e) {
+ if (m_havePendingPaintEvent) {
+ if (m_pendingPaintEvent) {
+ QRect r = m_pendingPaintEvent->rect().unite(e->rect());
+ *m_pendingPaintEvent = QPaintEvent(r);
+ } else {
+ m_pendingPaintEvent = new QPaintEvent(*e);
+ }
+ } else {
+ m_pendingPaintEvent = new QPaintEvent(*e);
+ }
+ }
+ m_havePendingPaintEvent = true;
+ return;
+ }
+ */
+ //!!! m_inPaintEvent = true;
+
+ 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 ;
+ }
+ }
+ }
+
+
+ m_needUpdate = false;
+
+ // Scan all segments and check if they've been modified.
+ //
+ // If we have more than one segment modified, we need to update
+ // them all at once with the same time range, otherwise we can run
+ // into problems when the layout of one depends on the others. So
+ // we use updateStart/End to calculate a bounding range for all
+ // modifications.
+
+ timeT updateStart = 0, updateEnd = 0;
+ int segmentsToUpdate = 0;
+ Segment *singleSegment = 0;
+
+ for (unsigned int i = 0; i < m_segments.size(); ++i) {
+
+ Segment* segment = m_segments[i];
+ unsigned int refreshStatusId = m_segmentsRefreshStatusIds[i];
+ SegmentRefreshStatus &refreshStatus =
+ segment->getRefreshStatus(refreshStatusId);
+
+ if (refreshStatus.needsRefresh() && isCompositionModified()) {
+
+ // if composition is also modified, relayout everything
+ refreshSegment(0);
+ segmentsToUpdate = 0;
+ break;
+
+ } else if (m_timeSigNotifier->hasTimeSigChanged()) {
+
+ // not exactly optimal!
+ refreshSegment(0);
+ segmentsToUpdate = 0;
+ m_timeSigNotifier->reset();
+ break;
+
+ } else if (refreshStatus.needsRefresh()) {
+
+ timeT startTime = refreshStatus.from(),
+ endTime = refreshStatus.to();
+
+ if (segmentsToUpdate == 0 || startTime < updateStart) {
+ updateStart = startTime;
+ }
+ if (segmentsToUpdate == 0 || endTime > updateEnd) {
+ updateEnd = endTime;
+ }
+ singleSegment = segment;
+ ++segmentsToUpdate;
+
+ refreshStatus.setNeedsRefresh(false);
+ m_needUpdate = true;
+ }
+ }
+
+ if (segmentsToUpdate > 1) {
+ refreshSegment(0, updateStart, updateEnd);
+ } else if (segmentsToUpdate > 0) {
+ refreshSegment(singleSegment, updateStart, updateEnd);
+ }
+
+ if (e)
+ KMainWindow::paintEvent(e);
+
+ // moved this to the end of the method so that things called
+ // from this method can still test whether the composition had
+ // been modified (it's sometimes useful to know whether e.g.
+ // any time signatures have changed)
+ setCompositionModified(false);
+
+ //!!! m_inPaintEvent = false;
+ /*
+ if (m_havePendingPaintEvent) {
+ e = m_pendingPaintEvent;
+ m_havePendingPaintEvent = false;
+ m_pendingPaintEvent = 0;
+ paintEvent(e);
+ delete e;
+ }
+ */
+}
+
+void EditViewBase::closeEvent(QCloseEvent* e)
+{
+ RG_DEBUG << "EditViewBase::closeEvent()\n";
+
+ if (isInCtor()) {
+ RG_DEBUG << "EditViewBase::closeEvent() : is in ctor, ignoring close event\n";
+ e->ignore();
+ } else {
+ KMainWindow::closeEvent(e);
+ }
+}
+
+void EditViewBase::addCommandToHistory(KCommand *command)
+{
+ getCommandHistory()->addCommand(command);
+}
+
+void EditViewBase::setTool(EditTool* tool)
+{
+ if (m_tool)
+ m_tool->stow();
+
+ m_tool = tool;
+
+ if (m_tool)
+ m_tool->ready();
+
+}
+
+void EditViewBase::slotCloseWindow()
+{
+ close();
+}
+
+void EditViewBase::slotToggleToolBar()
+{
+ KTmpStatusMsg msg(i18n("Toggle the toolbar..."), this);
+
+ if (toolBar()->isVisible())
+ toolBar()->hide();
+ else
+ toolBar()->show();
+}
+
+void EditViewBase::slotToggleStatusBar()
+{
+ KTmpStatusMsg msg(i18n("Toggle the statusbar..."), this);
+
+ if (statusBar()->isVisible())
+ statusBar()->hide();
+ else
+ statusBar()->show();
+}
+
+void EditViewBase::slotStatusMsg(const QString &text)
+{
+ ///////////////////////////////////////////////////////////////////
+ // change status message permanently
+ statusBar()->clear();
+ statusBar()->changeItem(text, ID_STATUS_MSG);
+}
+
+void EditViewBase::slotStatusHelpMsg(const QString &text)
+{
+ ///////////////////////////////////////////////////////////////////
+ // change status message of whole statusbar temporary (text, msec)
+ statusBar()->message(text, 2000);
+}
+
+void EditViewBase::initSegmentRefreshStatusIds()
+{
+ for (unsigned int i = 0; i < m_segments.size(); ++i) {
+
+ unsigned int rid = m_segments[i]->getNewRefreshStatusId();
+ m_segmentsRefreshStatusIds.push_back(rid);
+ }
+}
+
+bool EditViewBase::isCompositionModified()
+{
+ return getDocument()->getComposition().getRefreshStatus
+ (m_compositionRefreshStatusId).needsRefresh();
+}
+
+void EditViewBase::setCompositionModified(bool c)
+{
+ getDocument()->getComposition().getRefreshStatus
+ (m_compositionRefreshStatusId).setNeedsRefresh(c);
+}
+
+bool EditViewBase::getSegmentsOnlyRestsAndClefs()
+{
+ using Rosegarden::Segment;
+
+ for (unsigned int i = 0; i < m_segments.size(); ++i) {
+
+ Segment* segment = m_segments[i];
+
+ for (Segment::iterator iter = segment->begin();
+ iter != segment->end(); ++iter) {
+
+ if (((*iter)->getType() != Note::EventRestType)
+ && ((*iter)->getType() != Clef::EventType))
+ return false;
+ }
+
+ }
+
+ return true;
+
+}
+
+void EditViewBase::toggleWidget(QWidget* widget,
+ const QString& toggleActionName)
+{
+ KToggleAction* toggleAction = getToggleAction(toggleActionName);
+
+ if (!toggleAction) {
+ RG_DEBUG << "!!! Unknown toggle action : " << toggleActionName << endl;
+ return ;
+ }
+
+ widget->setShown(toggleAction->isChecked());
+}
+
+void
+EditViewBase::slotTestClipboard()
+{
+ if (getDocument()->getClipboard()->isEmpty()) {
+ RG_DEBUG << "EditViewBase::slotTestClipboard(): empty" << endl;
+
+ stateChanged("have_clipboard", KXMLGUIClient::StateReverse);
+ stateChanged("have_clipboard_single_segment",
+ KXMLGUIClient::StateReverse);
+ } else {
+ RG_DEBUG << "EditViewBase::slotTestClipboard(): not empty" << endl;
+
+ stateChanged("have_clipboard", KXMLGUIClient::StateNoReverse);
+ stateChanged("have_clipboard_single_segment",
+ (getDocument()->getClipboard()->isSingleSegment() ?
+ KXMLGUIClient::StateNoReverse :
+ KXMLGUIClient::StateReverse));
+ }
+}
+
+void
+EditViewBase::slotToggleSolo()
+{
+ KToggleAction* toggleSoloAction = getToggleAction("toggle_solo");
+ if (!toggleSoloAction)
+ return ;
+
+ bool newSoloState = toggleSoloAction->isChecked();
+
+ RG_DEBUG << "EditViewBase::slotToggleSolo() : solo = " << newSoloState << endl;
+ emit toggleSolo(newSoloState);
+
+ if (newSoloState) {
+ emit selectTrack(getCurrentSegment()->getTrack());
+ }
+
+}
+
+void
+EditViewBase::slotStateChanged(const QString& s,
+ bool noReverse)
+{
+ RG_DEBUG << "EditViewBase::slotStateChanged " << s << ", " << noReverse << endl;
+ stateChanged(s, noReverse ? KXMLGUIClient::StateNoReverse : KXMLGUIClient::StateReverse);
+}
+
+void
+EditViewBase::slotSetSegmentStartTime()
+{
+ Segment *s = getCurrentSegment();
+ if (!s)
+ return ;
+
+ TimeDialog dialog(this, i18n("Segment Start Time"),
+ &getDocument()->getComposition(),
+ s->getStartTime(), false);
+
+ if (dialog.exec() == QDialog::Accepted) {
+
+ SegmentReconfigureCommand *command =
+ new SegmentReconfigureCommand(i18n("Set Segment Start Time"));
+
+ command->addSegment
+ (s, dialog.getTime(),
+ s->getEndMarkerTime() - s->getStartTime() + dialog.getTime(),
+ s->getTrack());
+
+ addCommandToHistory(command);
+ }
+}
+
+void
+EditViewBase::slotSetSegmentDuration()
+{
+ Segment *s = getCurrentSegment();
+ if (!s)
+ return ;
+
+ TimeDialog dialog(this, i18n("Segment Duration"),
+ &getDocument()->getComposition(),
+ s->getStartTime(),
+ s->getEndMarkerTime() - s->getStartTime(), false);
+
+ if (dialog.exec() == QDialog::Accepted) {
+
+ SegmentReconfigureCommand *command =
+ new SegmentReconfigureCommand(i18n("Set Segment Duration"));
+
+ command->addSegment
+ (s, s->getStartTime(),
+ s->getStartTime() + dialog.getTime(),
+ s->getTrack());
+
+ addCommandToHistory(command);
+ }
+}
+
+void EditViewBase::slotCompositionStateUpdate()
+{
+ // update state of 'solo' toggle
+ //
+ KToggleAction* toggleSolo = getToggleAction("toggle_solo");
+ if (!toggleSolo)
+ return ;
+
+ if (getDocument()->getComposition().isSolo()) {
+ bool s = m_segments[0]->getTrack() == getDocument()->getComposition().getSelectedTrack();
+ RG_DEBUG << "EditViewBase::slotCompositionStateUpdate() : set solo to " << s << endl;
+ toggleSolo->setChecked(s);
+ } else {
+ toggleSolo->setChecked(false);
+ RG_DEBUG << "EditViewBase::slotCompositionStateUpdate() : set solo to false\n";
+ }
+
+ // update the window caption
+ //
+ updateViewCaption();
+}
+
+void
+EditViewBase::windowActivationChange(bool oldState)
+{
+ if (isActiveWindow()) {
+ emit windowActivated();
+ }
+}
+
+void
+EditViewBase::handleEventRemoved(Event *event)
+{
+ if (m_tool)
+ m_tool->handleEventRemoved(event);
+}
+
+MultiViewCommandHistory* EditViewBase::getCommandHistory()
+{
+ return getDocument()->getCommandHistory();
+}
+
+KToggleAction* EditViewBase::getToggleAction(const QString& actionName)
+{
+ return dynamic_cast<KToggleAction*>(actionCollection()->action(actionName));
+}
+
+}
+#include "EditViewBase.moc"
diff --git a/src/gui/general/EditViewBase.h b/src/gui/general/EditViewBase.h
new file mode 100644
index 0000000..03784cb
--- /dev/null
+++ b/src/gui/general/EditViewBase.h
@@ -0,0 +1,396 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_EDITVIEWBASE_H_
+#define _RG_EDITVIEWBASE_H_
+
+#include <set>
+#include <string>
+#include <kdockwidget.h>
+#include <qstring.h>
+#include <vector>
+#include "base/Event.h"
+
+
+class QWidget;
+class QPaintEvent;
+class QGridLayout;
+class QFrame;
+class QCloseEvent;
+class QAccel;
+class KToggleAction;
+class KConfig;
+class KCommand;
+namespace Rosegarden { class EditViewTimeSigNotifier; }
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class RosegardenGUIDoc;
+class MultiViewCommandHistory;
+class Event;
+class EditToolBox;
+class EditTool;
+
+
+class EditViewBase : public KDockMainWindow
+{
+
+ Q_OBJECT
+
+public:
+
+ /**
+ * Create an EditViewBase for the segments \a segments from document \a doc.
+ *
+ * \arg cols : number of columns, main column is always rightmost
+ *
+ */
+ EditViewBase(RosegardenGUIDoc *doc,
+ std::vector<Segment *> segments,
+ unsigned int cols,
+ QWidget *parent,
+ const char *name = 0);
+
+ virtual ~EditViewBase();
+
+ const RosegardenGUIDoc *getDocument() const { return m_doc; }
+ RosegardenGUIDoc *getDocument() { return m_doc; }
+
+ /**
+ * Refresh part of a Segment following a modification made in this
+ * or another view. The startTime and endTime give the extents of
+ * the modified region. This method is called following a
+ * modification to any Segment; no attempt has been made to check
+ * that the given Segment is actually shown in this view, so take
+ * care.
+ *
+ * If segment is null, refresh all segments.
+ * If the startTime and endTime are equal, refresh the whole of
+ * the relevant segments.
+ */
+ virtual void refreshSegment(Segment *segment,
+ timeT startTime = 0,
+ timeT endTime = 0) = 0;
+
+ /**
+ * Get the document's global command history
+ */
+ virtual MultiViewCommandHistory *getCommandHistory();
+
+ /**
+ * Add a Command to the history
+ */
+ virtual void addCommandToHistory(KCommand *);
+
+ /**
+ * Update the view
+ */
+ virtual void updateView() = 0;
+
+ /**
+ * Return our local accelerator object
+ */
+ QAccel* getAccelerators() { return m_accelerators; }
+
+ /**
+ * Return a string unique to this view (amongst views currently
+ * extant) that can be used (e.g. as a prefix) to distinguish
+ * view-local properties. It's up to the subclass or other user
+ * of this string to manage the properties correctly, for example
+ * by deleting them from the events when the view closes.
+ */
+ std::string getViewLocalPropertyPrefix() {
+ return m_viewLocalPropertyPrefix;
+ }
+
+ /*
+ * So that other people can create tools against our view
+ *
+ */
+ EditToolBox* getToolBox() { return m_toolBox; }
+
+ /**
+ * Let tools know if their current element has gone
+ */
+ virtual void handleEventRemoved(Event *event);
+
+ static const unsigned int ID_STATUS_MSG;
+ static const unsigned int NbLayoutRows;
+
+
+signals:
+ /**
+ * Tell the app to save the file.
+ */
+ void saveFile();
+
+ /**
+ * Reopen the given segments in another sort of editor.
+ */
+ void openInNotation(std::vector<Segment *>);
+ void openInMatrix(std::vector<Segment *>);
+ void openInPercussionMatrix(std::vector<Segment *>);
+ void openInEventList(std::vector<Segment *>);
+
+ /**
+ * Tell the main view that the track being edited is the
+ * current selected track
+ * This is used by #slotToggleSolo
+ */
+ void selectTrack(int);
+
+ /**
+ * Tell the main view that the solo status has changed (the user clicked on the 'solo' toggle)
+ */
+ void toggleSolo(bool);
+
+ void windowActivated();
+
+public slots:
+ /**
+ * close window
+ */
+ virtual void slotCloseWindow();
+
+ /**
+ * put the indicationed text/object into the clipboard and remove * it
+ * from the document
+ */
+ virtual void slotEditCut() = 0;
+
+ /**
+ * put the indicationed text/object into the clipboard
+ */
+ virtual void slotEditCopy() = 0;
+
+ /**
+ * paste the clipboard into the document
+ */
+ virtual void slotEditPaste() = 0;
+
+ /**
+ * toggles the main toolbar
+ */
+ virtual void slotToggleToolBar();
+
+ /**
+ * toggles the statusbar
+ */
+ virtual void slotToggleStatusBar();
+
+ /**
+ * Changes the statusbar contents for the standard label permanently,
+ * used to indicate current actions.
+ *
+ * @param text the text that is displayed in the statusbar
+ */
+ virtual void slotStatusMsg(const QString &text);
+
+ /**
+ * Changes the status message of the whole statusbar for two
+ * seconds, then restores the last status. This is used to display
+ * statusbar messages that give information about actions for
+ * toolbar icons and menuentries.
+ *
+ * @param text the text that is displayed in the statusbar
+ */
+ virtual void slotStatusHelpMsg(const QString &text);
+
+ /**
+ * A command has happened; check the clipboard in case we
+ * need to change state
+ */
+ virtual void slotTestClipboard();
+
+ /**
+ * Connected to this view's toolbar 'solo' button
+ */
+ virtual void slotToggleSolo();
+
+ void slotStateChanged(const QString&, bool noReverse);
+
+ virtual void slotOpenInMatrix();
+ virtual void slotOpenInPercussionMatrix();
+ virtual void slotOpenInNotation();
+ virtual void slotOpenInEventList();
+
+ /**
+ * Set the start time of the current segment
+ */
+ void slotSetSegmentStartTime();
+
+ /**
+ * Set the duration of the current segment
+ */
+ void slotSetSegmentDuration();
+
+ /**
+ * Global composition updates from the main view (track selection, solo, etc...)
+ */
+ virtual void slotCompositionStateUpdate();
+
+protected:
+
+ virtual void windowActivationChange(bool);
+
+ virtual void paintEvent(QPaintEvent* e);
+
+ /**
+ * @see #setInCtor
+ */
+ virtual void closeEvent(QCloseEvent* e);
+
+ /**
+ * ignore close events while we're in ctor
+ */
+ void setOutOfCtor() { m_inCtor = false; }
+
+ /**
+ * Check if we're still in ctor
+ */
+ bool isInCtor() { return m_inCtor; }
+
+ /**
+ * Set the current Notation tool (note inserter, rest inserter, eraser...)
+ *
+ * Called when the user selects a new item on one of the notation toolbars
+ * (notes toolbars, rests toolbars...)
+ */
+ void setTool(EditTool*);
+
+ /**
+ * read general Options again and initialize all variables like the recent file list
+ */
+ virtual void readOptions();
+
+ /**
+ * create menus and toolbars
+ */
+ virtual void setupActions(QString rcFileName, bool haveClipboard = true);
+
+ /**
+ * setup status bar
+ */
+ virtual void initStatusBar() = 0;
+
+ /**
+ * Abstract method to get current segment
+ */
+ virtual Segment *getCurrentSegment() = 0;
+
+ /**
+ * Set the caption of the view's window
+ */
+ virtual void updateViewCaption() = 0;
+
+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();
+ virtual void slotConfigure();
+ virtual void slotEditKeys();
+ virtual void slotEditToolbars();
+ virtual void slotUpdateToolbars();
+
+protected:
+ QWidget* getCentralWidget() { return m_centralFrame; }
+
+ void initSegmentRefreshStatusIds();
+
+ bool isCompositionModified();
+ void setCompositionModified(bool);
+
+ /**
+ * Returns true if all of the segments contain
+ * only rests and clefs events
+ */
+ bool getSegmentsOnlyRestsAndClefs();
+
+ /// Convenience function around actionCollection()->action()
+ KToggleAction* getToggleAction(const QString& actionName);
+
+ /**
+ * Make a widget visible depending on the state of a
+ * KToggleAction
+ */
+ virtual void toggleWidget(QWidget* widget, const QString& toggleActionName);
+
+ void setRCFileName(QString s) { m_rcFileName = s; }
+ QString getRCFileName() { return m_rcFileName; }
+
+ /**
+ * Set the page index of the config dialog which corresponds to
+ * this editview
+ */
+ void setConfigDialogPageIndex(int p) { m_configDialogPageIndex = p; }
+ int getConfigDialogPageIndex() { return m_configDialogPageIndex; }
+
+ //--------------- Data members ---------------------------------
+ QString m_rcFileName;
+
+ static std::set<int> m_viewNumberPool;
+ std::string makeViewLocalPropertyPrefix();
+ int m_viewNumber;
+ std::string m_viewLocalPropertyPrefix;
+
+ KConfig* m_config;
+
+ RosegardenGUIDoc* m_doc;
+ std::vector<Segment *> m_segments;
+ std::vector<unsigned int> m_segmentsRefreshStatusIds;
+
+ EditTool* m_tool;
+ EditToolBox* m_toolBox;
+
+ KDockWidget *m_mainDockWidget;
+ QFrame *m_centralFrame;
+ QGridLayout *m_grid;
+
+ unsigned int m_mainCol;
+ unsigned int m_compositionRefreshStatusId;
+ bool m_needUpdate;
+
+ QPaintEvent *m_pendingPaintEvent;
+ bool m_havePendingPaintEvent;
+ static bool m_inPaintEvent; // true if _any_ edit view is in a paint event
+
+ QAccel *m_accelerators;
+
+ int m_configDialogPageIndex;
+
+ bool m_inCtor;
+
+ EditViewTimeSigNotifier *m_timeSigNotifier;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/general/EditViewTimeSigNotifier.h b/src/gui/general/EditViewTimeSigNotifier.h
new file mode 100644
index 0000000..679494d
--- /dev/null
+++ b/src/gui/general/EditViewTimeSigNotifier.h
@@ -0,0 +1,56 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_EDITVIEWTIMESIGNOTIFIER_H_
+#define _RG_EDITVIEWTIMESIGNOTIFIER_H_
+
+namespace Rosegarden {
+
+class EditViewTimeSigNotifier : public Rosegarden::CompositionObserver
+{
+public:
+ EditViewTimeSigNotifier(RosegardenGUIDoc *doc) :
+ m_composition(&doc->getComposition()),
+ m_timeSigChanged(false) {
+ m_composition->addObserver(this);
+ }
+ virtual ~EditViewTimeSigNotifier() {
+ if (!isCompositionDeleted()) m_composition->removeObserver(this);
+ }
+ virtual void timeSignatureChanged(const Rosegarden::Composition *c) {
+ if (c == m_composition) m_timeSigChanged = true;
+ }
+
+ bool hasTimeSigChanged() const { return m_timeSigChanged; }
+ void reset() { m_timeSigChanged = false; }
+
+protected:
+ Rosegarden::Composition *m_composition;
+ bool m_timeSigChanged;
+};
+
+}
+
+#endif /*EDITVIEWTIMESIGNOTIFIER_H_*/
diff --git a/src/gui/general/GUIPalette.cpp b/src/gui/general/GUIPalette.cpp
new file mode 100644
index 0000000..4705c21
--- /dev/null
+++ b/src/gui/general/GUIPalette.cpp
@@ -0,0 +1,311 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "GUIPalette.h"
+#include <kapplication.h>
+
+#include "base/Colour.h"
+#include "document/ConfigGroups.h"
+#include <kconfig.h>
+#include <qcolor.h>
+
+
+namespace Rosegarden
+{
+
+QColor GUIPalette::getColour(const char* const colourName)
+{
+ KConfig* config = kapp->config();
+ config->setGroup(ColoursConfigGroup);
+
+ QColor res = getInstance()->m_defaultsMap[colourName];
+ config->readColorEntry(colourName, &res);
+ return res;
+}
+
+Colour GUIPalette::convertColour(const QColor &input)
+{
+ int r, g, b;
+ input.rgb(&r, &g, &b);
+ return Colour(r, g, b);
+}
+
+QColor GUIPalette::convertColour(const Colour& input)
+{
+ return QColor(input.getRed(), input.getGreen(), input.getBlue());
+}
+
+GUIPalette::GUIPalette()
+{
+ m_defaultsMap[ActiveRecordTrack] = Qt::red;
+
+ m_defaultsMap[SegmentCanvas] = QColor(230, 230, 230);
+ m_defaultsMap[SegmentBorder] = Qt::black;
+
+ // 1.0 colors
+ // m_defaultsMap[RecordingInternalSegmentBlock] = QColor(255, 182, 193);
+ // m_defaultsMap[RecordingAudioSegmentBlock] = QColor(182, 222, 255);
+
+ // MIDI recording preview (pale yellow)
+ m_defaultsMap[RecordingInternalSegmentBlock] = QColor(255, 234, 182);
+
+ // audio recording preview (pale red)
+ m_defaultsMap[RecordingAudioSegmentBlock] = QColor(255, 182, 193);
+
+ m_defaultsMap[RecordingSegmentBorder] = Qt::black;
+
+ m_defaultsMap[RepeatSegmentBorder] = QColor(130, 133, 170);
+
+ m_defaultsMap[SegmentAudioPreview] = QColor(39, 71, 22);
+ m_defaultsMap[SegmentInternalPreview] = Qt::white;
+ m_defaultsMap[SegmentLabel] = Qt::black;
+ m_defaultsMap[SegmentSplitLine] = Qt::black;
+
+ m_defaultsMap[MatrixElementBorder] = Qt::black;
+ m_defaultsMap[MatrixElementBlock] = QColor(98, 128, 232);
+ m_defaultsMap[MatrixOverlapBlock] = Qt::black;
+
+ m_defaultsMap[LoopRulerBackground] = QColor(120, 120, 120);
+ m_defaultsMap[LoopRulerForeground] = Qt::white;
+ m_defaultsMap[LoopHighlight] = Qt::white;
+
+ m_defaultsMap[TempoBase] = QColor(197, 211, 125);
+
+ //m_defaultsMap[TextRulerBackground] = QColor(60, 205, 230, QColor::Hsv);
+ // m_defaultsMap[TextRulerBackground] = QColor(120, 90, 238, QColor::Hsv);
+ // m_defaultsMap[TextRulerBackground] = QColor(210, 220, 140);
+ m_defaultsMap[TextRulerBackground] = QColor(226, 232, 187);
+ m_defaultsMap[TextRulerForeground] = Qt::white;
+
+ m_defaultsMap[ChordNameRulerBackground] = QColor(230, 230, 230);
+ m_defaultsMap[ChordNameRulerForeground] = Qt::black;
+
+ m_defaultsMap[RawNoteRulerBackground] = QColor(240, 240, 240);
+ m_defaultsMap[RawNoteRulerForeground] = Qt::black;
+
+ m_defaultsMap[LevelMeterGreen] = QColor(0, 200, 0);
+ m_defaultsMap[LevelMeterOrange] = QColor(255, 165, 0);
+ m_defaultsMap[LevelMeterRed] = QColor(200, 0, 0);
+
+ // m_defaultsMap[LevelMeterSolidGreen] = QColor(0, 140, 0);
+ m_defaultsMap[LevelMeterSolidGreen] = QColor(84, 177, 248); // blue!
+ // m_defaultsMap[LevelMeterSolidOrange] = QColor(220, 120, 0);
+ m_defaultsMap[LevelMeterSolidOrange] = QColor(255, 225, 0);
+ // m_defaultsMap[LevelMeterSolidRed] = QColor(255, 50, 50);
+ m_defaultsMap[LevelMeterSolidRed] = QColor(255, 0, 0);
+
+ m_defaultsMap[BarLine] = Qt::black;
+ m_defaultsMap[BarLineIncorrect] = QColor(211, 0, 31);
+ m_defaultsMap[BeatLine] = QColor(100, 100, 100);
+ m_defaultsMap[SubBeatLine] = QColor(212, 212, 212);
+ m_defaultsMap[StaffConnectingLine] = QColor(192, 192, 192);
+ m_defaultsMap[StaffConnectingTerminatingLine] = QColor(128, 128, 128);
+
+ m_defaultsMap[Pointer] = Qt::darkBlue;
+ m_defaultsMap[PointerRuler] = QColor(100, 100, 100);
+
+ m_defaultsMap[InsertCursor] = QColor(160, 104, 186);
+ m_defaultsMap[InsertCursorRuler] = QColor(160, 136, 170);
+
+ m_defaultsMap[TrackDivider] = QColor(145, 145, 145);
+ //m_defaultsMap[MovementGuide] = QColor(172, 230, 139);
+ m_defaultsMap[MovementGuide] = QColor(62, 161, 194);
+ //m_defaultsMap[MovementGuide] = QColor(255, 189, 89);
+ m_defaultsMap[SelectionRectangle] = QColor(103, 128, 211);
+ m_defaultsMap[SelectedElement] = QColor(0, 54, 232);
+
+ const int SelectedElementHue = 225;
+ const int SelectedElementMinValue = 220;
+ const int HighlightedElementHue = 25;
+ const int HighlightedElementMinValue = 220;
+ const int QuantizedNoteHue = 69;
+ const int QuantizedNoteMinValue = 140;
+ const int TriggerNoteHue = 4;
+ const int TriggerNoteMinValue = 140;
+ const int OutRangeNoteHue = 0;
+ const int OutRangeNoteMinValue = 200;
+
+ m_defaultsMap[TextAnnotationBackground] = QColor(255, 255, 180);
+
+ m_defaultsMap[TextLilyPondDirectiveBackground] = QColor(95, 157, 87);
+
+ m_defaultsMap[AudioCountdownBackground] = Qt::darkGray;
+ m_defaultsMap[AudioCountdownForeground] = Qt::red;
+
+// m_defaultsMap[RotaryFloatBackground] = Qt::cyan;
+ m_defaultsMap[RotaryFloatBackground] = QColor(182, 222, 255);
+ m_defaultsMap[RotaryFloatForeground] = Qt::black;
+
+ m_defaultsMap[RotaryPastelBlue] = QColor(205, 212, 255);
+ m_defaultsMap[RotaryPastelRed] = QColor(255, 168, 169);
+ m_defaultsMap[RotaryPastelGreen] = QColor(231, 255, 223);
+ m_defaultsMap[RotaryPastelOrange] = QColor(255, 233, 208);
+ m_defaultsMap[RotaryPastelYellow] = QColor(249, 255, 208);
+
+ m_defaultsMap[MatrixKeyboardFocus] = QColor(224, 112, 8);
+
+ // m_defaultsMap[RotaryPlugin] = QColor(185, 255, 248);
+ m_defaultsMap[RotaryPlugin] = QColor(185, 200, 248);
+ // m_defaultsMap[RotaryPlugin] = QColor(185, 185, 185);
+
+ m_defaultsMap[RotaryMeter] = QColor(255, 100, 0);
+
+ m_defaultsMap[MarkerBackground] = QColor(185, 255, 248);
+
+ m_defaultsMap[QuickMarker] = Qt::red;
+
+ // m_defaultsMap[MuteTrackLED] = QColor(218, 190, 230, QColor::Hsv);
+ m_defaultsMap[MuteTrackLED] = QColor(211, 194, 238, QColor::Hsv);
+ m_defaultsMap[RecordMIDITrackLED] = QColor(45, 250, 225, QColor::Hsv);
+ m_defaultsMap[RecordAudioTrackLED] = QColor(0, 250, 225, QColor::Hsv);
+
+ m_defaultsMap[PlaybackFaderOutline] = QColor(211, 194, 238, QColor::Hsv);
+ m_defaultsMap[RecordFaderOutline] = QColor(0, 250, 225, QColor::Hsv);
+}
+
+GUIPalette* GUIPalette::getInstance()
+{
+ if (!m_instance) m_instance = new GUIPalette();
+ return m_instance;
+}
+
+const char* const GUIPalette::ActiveRecordTrack = "activerecordtrack";
+
+
+const char* const GUIPalette::SegmentCanvas = "segmentcanvas";
+const char* const GUIPalette::SegmentBorder = "segmentborder";
+const char* const GUIPalette::RecordingInternalSegmentBlock = "recordinginternalsegmentblock";
+const char* const GUIPalette::RecordingAudioSegmentBlock = "recordingaudiosegmentblock";
+const char* const GUIPalette::RecordingSegmentBorder = "recordingsegmentborder";
+
+const char* const GUIPalette::RepeatSegmentBorder = "repeatsegmentborder";
+
+const char* const GUIPalette::SegmentAudioPreview = "segmentaudiopreview";
+const char* const GUIPalette::SegmentInternalPreview = "segmentinternalpreview";
+const char* const GUIPalette::SegmentLabel = "segmentlabel";
+const char* const GUIPalette::SegmentSplitLine = "segmentsplitline";
+
+const char* const GUIPalette::MatrixElementBorder = "matrixelementborder";
+const char* const GUIPalette::MatrixElementBlock = "matrixelementblock";
+const char* const GUIPalette::MatrixOverlapBlock = "matrixoverlapblock";
+
+const char* const GUIPalette::LoopRulerBackground = "looprulerbackground";
+const char* const GUIPalette::LoopRulerForeground = "looprulerforeground";
+const char* const GUIPalette::LoopHighlight = "loophighlight";
+
+const char* const GUIPalette::TempoBase = "tempobase";
+
+const char* const GUIPalette::TextRulerBackground = "textrulerbackground";
+const char* const GUIPalette::TextRulerForeground = "textrulerforeground";
+
+const char* const GUIPalette::ChordNameRulerBackground = "chordnamerulerbackground";
+const char* const GUIPalette::ChordNameRulerForeground = "chordnamerulerforeground";
+
+const char* const GUIPalette::RawNoteRulerBackground = "rawnoterulerbackground";
+const char* const GUIPalette::RawNoteRulerForeground = "rawnoterulerforeground";
+
+const char* const GUIPalette::LevelMeterGreen = "levelmetergreen";
+const char* const GUIPalette::LevelMeterOrange = "levelmeterorange";
+const char* const GUIPalette::LevelMeterRed = "levelmeterred";
+
+const char* const GUIPalette::LevelMeterSolidGreen = "levelmetersolidgreen";
+const char* const GUIPalette::LevelMeterSolidOrange = "levelmetersolidorange";
+const char* const GUIPalette::LevelMeterSolidRed = "levelmetersolidred";
+
+const char* const GUIPalette::BarLine = "barline";
+const char* const GUIPalette::BarLineIncorrect = "barlineincorrect";
+const char* const GUIPalette::BeatLine = "beatline";
+const char* const GUIPalette::SubBeatLine = "subbeatline";
+const char* const GUIPalette::StaffConnectingLine = "staffconnectingline";
+const char* const GUIPalette::StaffConnectingTerminatingLine = "staffconnectingterminatingline";
+
+const char* const GUIPalette::Pointer = "pointer";
+const char* const GUIPalette::PointerRuler = "pointerruler";
+
+const char* const GUIPalette::InsertCursor = "insertcursor";
+const char* const GUIPalette::InsertCursorRuler = "insertcursorruler";
+
+const char* const GUIPalette::TrackDivider = "trackdivider";
+const char* const GUIPalette::MovementGuide = "movementguide";
+const char* const GUIPalette::SelectionRectangle = "selectionrectangle";
+const char* const GUIPalette::SelectedElement = "selectedelement";
+
+const int GUIPalette::SelectedElementHue = 225;
+const int GUIPalette::SelectedElementMinValue = 220;
+const int GUIPalette::HighlightedElementHue = 25;
+const int GUIPalette::HighlightedElementMinValue = 220;
+const int GUIPalette::QuantizedNoteHue = 69;
+const int GUIPalette::QuantizedNoteMinValue = 140;
+const int GUIPalette::TriggerNoteHue = 4;
+const int GUIPalette::TriggerNoteMinValue = 140;
+const int GUIPalette::OutRangeNoteHue = 0;
+const int GUIPalette::OutRangeNoteMinValue = 200;
+
+const int GUIPalette::CollisionHaloHue = 42;
+const int GUIPalette::CollisionHaloSaturation = 200;
+
+const char* const GUIPalette::TextAnnotationBackground = "textannotationbackground";
+
+const char* const GUIPalette::TextLilyPondDirectiveBackground = "textlilyponddirectivebackground";
+
+const char* const GUIPalette::AudioCountdownBackground = "audiocountdownbackground";
+const char* const GUIPalette::AudioCountdownForeground = "audiocountdownforeground";
+
+const char* const GUIPalette::RotaryFloatBackground = "rotaryfloatbackground";
+const char* const GUIPalette::RotaryFloatForeground = "rotaryfloatforeground";
+
+const char* const GUIPalette::RotaryPastelBlue = "rotarypastelblue";
+const char* const GUIPalette::RotaryPastelRed = "rotarypastelred";
+const char* const GUIPalette::RotaryPastelGreen = "rotarypastelgreen";
+const char* const GUIPalette::RotaryPastelOrange = "rotarypastelorange";
+const char* const GUIPalette::RotaryPastelYellow = "rotarypastelyellow";
+
+const char* const GUIPalette::MatrixKeyboardFocus = "matrixkeyboardfocus";
+
+const char* const GUIPalette::RotaryPlugin = "rotaryplugin";
+
+const char* const GUIPalette::RotaryMeter = "rotarymeter";
+
+const char* const GUIPalette::MarkerBackground = "markerbackground";
+
+const char* const GUIPalette::QuickMarker = "quickmarker";
+
+const char* const GUIPalette::MuteTrackLED = "mutetrackled";
+const char* const GUIPalette::RecordMIDITrackLED = "recordmiditrackled";
+const char* const GUIPalette::RecordAudioTrackLED = "recordaudiotrackled";
+
+const char* const GUIPalette::PlaybackFaderOutline = "playbackfaderoutline";
+const char* const GUIPalette::RecordFaderOutline = "recordfaderoutline";
+
+
+GUIPalette* GUIPalette::m_instance = 0;
+
+// defines which index in the document's colourmap should be used as the color
+// when creating new audio segments from recordings, or inserting from the
+// audio file manager (presumes a file derived from the updated autoload.rg
+// that shipped along with this change)
+const int GUIPalette::AudioDefaultIndex = 1;
+
+}
diff --git a/src/gui/general/GUIPalette.h b/src/gui/general/GUIPalette.h
new file mode 100644
index 0000000..c8760fb
--- /dev/null
+++ b/src/gui/general/GUIPalette.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_GUIPALETTE_H_
+#define _RG_GUIPALETTE_H_
+
+#include "base/Colour.h"
+#include <map>
+#include <qcolor.h>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * Definitions of colours to be used throughout the Rosegarden GUI.
+ *
+ * They're in this file not so much to allow them to be easily
+ * changed, as to ensure a certain amount of consistency between
+ * colours for different functions that might end up being seen
+ * at the same time.
+ */
+
+class GUIPalette
+{
+public:
+ static QColor getColour(const char* const colourName);
+
+ static Colour convertColour(const QColor &input);
+ static QColor convertColour(const Colour &input);
+
+ static const char* const ActiveRecordTrack;
+
+ static const char* const SegmentCanvas;
+ static const char* const SegmentBorder;
+
+ static const char* const RecordingInternalSegmentBlock;
+ static const char* const RecordingAudioSegmentBlock;
+ static const char* const RecordingSegmentBorder;
+
+ static const char* const RepeatSegmentBorder;
+
+ static const char* const SegmentAudioPreview;
+ static const char* const SegmentInternalPreview;
+ static const char* const SegmentLabel;
+ static const char* const SegmentSplitLine;
+
+ static const char* const MatrixElementBorder;
+ static const char* const MatrixElementBlock;
+ static const char* const MatrixOverlapBlock;
+
+ static const char* const LoopRulerBackground;
+ static const char* const LoopRulerForeground;
+ static const char* const LoopHighlight;
+
+ static const char* const TempoBase;
+
+ static const char* const TextRulerBackground;
+ static const char* const TextRulerForeground;
+
+ static const char* const ChordNameRulerBackground;
+ static const char* const ChordNameRulerForeground;
+
+ static const char* const RawNoteRulerBackground;
+ static const char* const RawNoteRulerForeground;
+
+ static const char* const LevelMeterGreen;
+ static const char* const LevelMeterOrange;
+ static const char* const LevelMeterRed;
+
+ static const char* const LevelMeterSolidGreen;
+ static const char* const LevelMeterSolidOrange;
+ static const char* const LevelMeterSolidRed;
+
+ static const char* const BarLine;
+ static const char* const BarLineIncorrect;
+ static const char* const BeatLine;
+ static const char* const SubBeatLine;
+ static const char* const StaffConnectingLine;
+ static const char* const StaffConnectingTerminatingLine;
+
+ static const char* const Pointer;
+ static const char* const PointerRuler;
+
+ static const char* const InsertCursor;
+ static const char* const InsertCursorRuler;
+
+ static const char* const TrackDivider;
+ static const char* const MovementGuide;
+ static const char* const SelectionRectangle;
+ static const char* const SelectedElement;
+
+ static const int SelectedElementHue;
+ static const int SelectedElementMinValue;
+ static const int HighlightedElementHue;
+ static const int HighlightedElementMinValue;
+ static const int QuantizedNoteHue;
+ static const int QuantizedNoteMinValue;
+ static const int TriggerNoteHue;
+ static const int TriggerNoteMinValue;
+ static const int OutRangeNoteHue;
+ static const int OutRangeNoteMinValue;
+
+ static const int CollisionHaloHue;
+ static const int CollisionHaloSaturation;
+
+ static const char* const TextAnnotationBackground;
+
+ static const char* const TextLilyPondDirectiveBackground;
+
+ static const char* const AudioCountdownBackground;
+ static const char* const AudioCountdownForeground;
+
+ static const char* const RotaryFloatBackground;
+ static const char* const RotaryFloatForeground;
+
+ static const char* const RotaryPastelBlue;
+ static const char* const RotaryPastelRed;
+ static const char* const RotaryPastelGreen;
+ static const char* const RotaryPastelOrange;
+ static const char* const RotaryPastelYellow;
+
+ static const char* const MatrixKeyboardFocus;
+
+ static const char* const RotaryPlugin;
+
+ static const char* const RotaryMeter;
+
+ static const char* const MarkerBackground;
+
+ static const char* const QuickMarker;
+
+ static const char* const MuteTrackLED;
+ static const char* const RecordMIDITrackLED;
+ static const char* const RecordAudioTrackLED;
+
+ static const char* const PlaybackFaderOutline;
+ static const char* const RecordFaderOutline;
+
+ static const int AudioDefaultIndex;
+
+protected:
+ GUIPalette();
+ QColor getDefaultColour(const char* const colourName);
+
+ //--------------- Data members ---------------------------------
+ static GUIPalette* getInstance();
+ static GUIPalette* m_instance;
+
+ typedef std::map<const char* const, QColor> colourmap;
+
+ colourmap m_defaultsMap;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/general/HZoomable.cpp b/src/gui/general/HZoomable.cpp
new file mode 100644
index 0000000..ff81f6c
--- /dev/null
+++ b/src/gui/general/HZoomable.cpp
@@ -0,0 +1,32 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "HZoomable.h"
+
+
+
+namespace Rosegarden
+{
+}
diff --git a/src/gui/general/HZoomable.h b/src/gui/general/HZoomable.h
new file mode 100644
index 0000000..82e9aa9
--- /dev/null
+++ b/src/gui/general/HZoomable.h
@@ -0,0 +1,53 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_HZOOMABLE_H_
+#define _RG_HZOOMABLE_H_
+
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+class HZoomable
+{
+public:
+ HZoomable() : m_hScaleFactor(1.0) {}
+
+ void setHScaleFactor(double dy) { m_hScaleFactor = dy; }
+ double getHScaleFactor() const { return m_hScaleFactor; }
+
+protected:
+ double m_hScaleFactor;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/general/LinedStaff.cpp b/src/gui/general/LinedStaff.cpp
new file mode 100644
index 0000000..e2e5d12
--- /dev/null
+++ b/src/gui/general/LinedStaff.cpp
@@ -0,0 +1,1217 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "LinedStaff.h"
+
+#include "misc/Debug.h"
+#include "base/Event.h"
+#include "base/LayoutEngine.h"
+#include "base/NotationTypes.h"
+#include "base/Profiler.h"
+#include "base/Segment.h"
+#include "base/SnapGrid.h"
+#include "base/Staff.h"
+#include "base/ViewElement.h"
+#include "GUIPalette.h"
+#include "BarLine.h"
+#include <qcanvas.h>
+#include <qcolor.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qpen.h>
+#include <qrect.h>
+#include <qstring.h>
+#include <algorithm>
+
+
+namespace Rosegarden
+{
+
+// width of pointer
+//
+const int pointerWidth = 3;
+
+
+LinedStaff::LinedStaff(QCanvas *canvas, Segment *segment,
+ SnapGrid *snapGrid, int id,
+ int resolution, int lineThickness) :
+ Staff(*segment),
+ m_canvas(canvas),
+ m_snapGrid(snapGrid),
+ m_id(id),
+ m_x(0.0),
+ m_y(0),
+ m_margin(0.0),
+ m_titleHeight(0),
+ m_resolution(resolution),
+ m_lineThickness(lineThickness),
+ m_pageMode(LinearMode),
+ m_pageWidth(2000.0), // fairly arbitrary, but we need something non-zero
+ m_rowsPerPage(0),
+ m_rowSpacing(0),
+ m_connectingLineLength(0),
+ m_startLayoutX(0),
+ m_endLayoutX(0),
+ m_current(false),
+ m_pointer(new QCanvasLine(canvas)),
+ m_insertCursor(new QCanvasLine(canvas)),
+ m_insertCursorTime(segment->getStartTime()),
+ m_insertCursorTimeValid(false)
+{
+ initCursors();
+}
+
+LinedStaff::LinedStaff(QCanvas *canvas, Segment *segment,
+ SnapGrid *snapGrid,
+ int id, int resolution, int lineThickness,
+ double pageWidth, int rowsPerPage, int rowSpacing) :
+ Staff(*segment),
+ m_canvas(canvas),
+ m_snapGrid(snapGrid),
+ m_id(id),
+ m_x(0.0),
+ m_y(0),
+ m_margin(0.0),
+ m_titleHeight(0),
+ m_resolution(resolution),
+ m_lineThickness(lineThickness),
+ m_pageMode(rowsPerPage ? MultiPageMode : ContinuousPageMode),
+ m_pageWidth(pageWidth),
+ m_rowsPerPage(rowsPerPage),
+ m_rowSpacing(rowSpacing),
+ m_connectingLineLength(0),
+ m_startLayoutX(0),
+ m_endLayoutX(0),
+ m_current(false),
+ m_pointer(new QCanvasLine(canvas)),
+ m_insertCursor(new QCanvasLine(canvas)),
+ m_insertCursorTime(segment->getStartTime()),
+ m_insertCursorTimeValid(false)
+{
+ initCursors();
+}
+
+LinedStaff::LinedStaff(QCanvas *canvas, Segment *segment,
+ SnapGrid *snapGrid,
+ int id, int resolution, int lineThickness,
+ PageMode pageMode, double pageWidth, int rowsPerPage,
+ int rowSpacing) :
+ Staff(*segment),
+ m_canvas(canvas),
+ m_snapGrid(snapGrid),
+ m_id(id),
+ m_x(0.0),
+ m_y(0),
+ m_margin(0.0),
+ m_titleHeight(0),
+ m_resolution(resolution),
+ m_lineThickness(lineThickness),
+ m_pageMode(pageMode),
+ m_pageWidth(pageWidth),
+ m_rowsPerPage(rowsPerPage),
+ m_rowSpacing(rowSpacing),
+ m_connectingLineLength(0),
+ m_startLayoutX(0),
+ m_endLayoutX(0),
+ m_current(false),
+ m_pointer(new QCanvasLine(canvas)),
+ m_insertCursor(new QCanvasLine(canvas)),
+ m_insertCursorTime(segment->getStartTime()),
+ m_insertCursorTimeValid(false)
+{
+ initCursors();
+}
+
+LinedStaff::~LinedStaff()
+{
+ /*!!! No, the canvas items are all deleted by the canvas on destruction.
+
+ deleteBars();
+ for (int i = 0; i < (int)m_staffLines.size(); ++i) clearStaffLineRow(i);
+ */
+}
+
+void
+LinedStaff::initCursors()
+{
+ QPen pen(GUIPalette::getColour(GUIPalette::Pointer));
+ pen.setWidth(pointerWidth);
+
+ m_pointer->setPen(pen);
+ m_pointer->setBrush(GUIPalette::getColour(GUIPalette::Pointer));
+
+ pen.setColor(GUIPalette::getColour(GUIPalette::InsertCursor));
+
+ m_insertCursor->setPen(pen);
+ m_insertCursor->setBrush(GUIPalette::getColour(GUIPalette::InsertCursor));
+}
+
+void
+LinedStaff::setResolution(int resolution)
+{
+ m_resolution = resolution;
+}
+
+void
+LinedStaff::setLineThickness(int lineThickness)
+{
+ m_lineThickness = lineThickness;
+}
+
+void
+LinedStaff::setPageMode(PageMode pageMode)
+{
+ m_pageMode = pageMode;
+}
+
+void
+LinedStaff::setPageWidth(double pageWidth)
+{
+ m_pageWidth = pageWidth;
+}
+
+void
+LinedStaff::setRowsPerPage(int rowsPerPage)
+{
+ m_rowsPerPage = rowsPerPage;
+}
+
+void
+LinedStaff::setRowSpacing(int rowSpacing)
+{
+ m_rowSpacing = rowSpacing;
+}
+
+void
+LinedStaff::setConnectingLineLength(int connectingLineLength)
+{
+ m_connectingLineLength = connectingLineLength;
+}
+
+int
+LinedStaff::getId() const
+{
+ return m_id;
+}
+
+void
+LinedStaff::setX(double x)
+{
+ m_x = x;
+}
+
+double
+LinedStaff::getX() const
+{
+ return m_x;
+}
+
+void
+LinedStaff::setY(int y)
+{
+ m_y = y;
+}
+
+int
+LinedStaff::getY() const
+{
+ return m_y;
+}
+
+void
+LinedStaff::setMargin(double margin)
+{
+ m_margin = margin;
+}
+
+double
+LinedStaff::getMargin() const
+{
+ if (m_pageMode != MultiPageMode)
+ return 0;
+ return m_margin;
+}
+
+void
+LinedStaff::setTitleHeight(int titleHeight)
+{
+ m_titleHeight = titleHeight;
+}
+
+int
+LinedStaff::getTitleHeight() const
+{
+ return m_titleHeight;
+}
+
+double
+LinedStaff::getTotalWidth() const
+{
+ switch (m_pageMode) {
+
+ case ContinuousPageMode:
+ return getCanvasXForRightOfRow(getRowForLayoutX(m_endLayoutX)) - m_x;
+
+ case MultiPageMode:
+ return getCanvasXForRightOfRow(getRowForLayoutX(m_endLayoutX)) + m_margin - m_x;
+
+ case LinearMode:
+ default:
+ return getCanvasXForLayoutX(m_endLayoutX) - m_x;
+ }
+}
+
+int
+LinedStaff::getTotalHeight() const
+{
+ switch (m_pageMode) {
+
+ case ContinuousPageMode:
+ return getCanvasYForTopOfStaff(getRowForLayoutX(m_endLayoutX)) +
+ getHeightOfRow() - m_y;
+
+ case MultiPageMode:
+ return getCanvasYForTopOfStaff(m_rowsPerPage - 1) +
+ getHeightOfRow() - m_y;
+
+ case LinearMode:
+ default:
+ return getCanvasYForTopOfStaff(0) + getHeightOfRow() - m_y;
+ }
+}
+
+int
+LinedStaff::getHeightOfRow() const
+{
+ return getTopLineOffset() + getLegerLineCount() * getLineSpacing()
+ + getBarLineHeight() + m_lineThickness;
+}
+
+bool
+LinedStaff::containsCanvasCoords(double x, int y) const
+{
+ switch (m_pageMode) {
+
+ case ContinuousPageMode:
+
+ for (int row = getRowForLayoutX(m_startLayoutX);
+ row <= getRowForLayoutX(m_endLayoutX); ++row) {
+ if (y >= getCanvasYForTopOfStaff(row) &&
+ y < getCanvasYForTopOfStaff(row) + getHeightOfRow()) {
+ return true;
+ }
+ }
+
+ return false;
+
+ case MultiPageMode:
+
+ for (int row = getRowForLayoutX(m_startLayoutX);
+ row <= getRowForLayoutX(m_endLayoutX); ++row) {
+ if (y >= getCanvasYForTopOfStaff(row) &&
+ y < getCanvasYForTopOfStaff(row) + getHeightOfRow() &&
+ x >= getCanvasXForLeftOfRow(row) &&
+ x <= getCanvasXForRightOfRow(row)) {
+ return true;
+ }
+ }
+
+ return false;
+
+ case LinearMode:
+ default:
+
+ return (y >= getCanvasYForTopOfStaff() &&
+ y < getCanvasYForTopOfStaff() + getHeightOfRow());
+ }
+}
+
+int
+LinedStaff::getCanvasYForHeight(int h, double baseX, int baseY) const
+{
+ int y;
+
+ // NOTATION_DEBUG << "LinedStaff::getCanvasYForHeight(" << h << "," << baseY
+ // << ")" << endl;
+
+ if (baseX < 0)
+ baseX = getX() + getMargin();
+
+ if (baseY >= 0) {
+ y = getCanvasYForTopLine(getRowForCanvasCoords(baseX, baseY));
+ } else {
+ y = getCanvasYForTopLine();
+ }
+
+ y += getLayoutYForHeight(h);
+
+ return y;
+}
+
+int
+LinedStaff::getLayoutYForHeight(int h) const
+{
+ int y = ((getTopLineHeight() - h) * getLineSpacing()) / getHeightPerLine();
+ if (h < getTopLineHeight() && (h % getHeightPerLine() != 0))
+ ++y;
+
+ return y;
+}
+
+int
+LinedStaff::getHeightAtCanvasCoords(double x, int y) const
+{
+ //!!! the lazy route: approximate, then get the right value
+ // by calling getCanvasYForHeight a few times... ugh
+
+ // RG_DEBUG << "\nNotationStaff::heightOfYCoord: y = " << y
+ // << ", getTopLineOffset() = " << getTopLineOffset()
+ // << ", getLineSpacing() = " << m_npf->getLineSpacing()
+ // << endl;
+
+ if (x < 0)
+ x = getX() + getMargin();
+
+ int row = getRowForCanvasCoords(x, y);
+ int ph = (y - getCanvasYForTopLine(row)) * getHeightPerLine() /
+ getLineSpacing();
+ ph = getTopLineHeight() - ph;
+
+ int i;
+ int mi = -2;
+ int md = getLineSpacing() * 2;
+
+ int testi = -2;
+ int testMd = 1000;
+
+ for (i = -1; i <= 1; ++i) {
+ int d = y - getCanvasYForHeight(ph + i, x, y);
+ if (d < 0)
+ d = -d;
+ if (d < md) {
+ md = d;
+ mi = i;
+ }
+ if (d < testMd) {
+ testMd = d;
+ testi = i;
+ }
+ }
+
+ if (mi > -2) {
+ // RG_DEBUG << "LinedStaff::getHeightAtCanvasCoords: " << y
+ // << " -> " << (ph + mi) << " (mi is " << mi << ", distance "
+ // << md << ")" << endl;
+ // if (mi == 0) {
+ // RG_DEBUG << "GOOD APPROXIMATION" << endl;
+ // } else {
+ // RG_DEBUG << "BAD APPROXIMATION" << endl;
+ // }
+ return ph + mi;
+ } else {
+ RG_DEBUG << "LinedStaff::getHeightAtCanvasCoords: heuristic got " << ph << ", nothing within range (closest was " << (ph + testi) << " which is " << testMd << " away)" << endl;
+ return 0;
+ }
+}
+
+QRect
+LinedStaff::getBarExtents(double x, int y) const
+{
+ int row = getRowForCanvasCoords(x, y);
+
+ for (int i = 1; i < m_barLines.size(); ++i) {
+
+ double layoutX = m_barLines[i]->getLayoutX();
+ int barRow = getRowForLayoutX(layoutX);
+
+ if (m_pageMode != LinearMode && (barRow < row))
+ continue;
+
+ BarLine *line = m_barLines[i];
+
+ if (line) {
+ if (line->x() <= x)
+ continue;
+
+ return QRect(int(m_barLines[i -1]->x()),
+ getCanvasYForTopOfStaff(barRow),
+ int(line->x() - m_barLines[i - 1]->x()),
+ getHeightOfRow());
+ }
+ }
+
+ // failure
+ return QRect(int(getX() + getMargin()), getCanvasYForTopOfStaff(), 4, getHeightOfRow());
+}
+
+double
+LinedStaff::getCanvasXForLayoutX(double x) const
+{
+ switch (m_pageMode) {
+
+ case ContinuousPageMode:
+ return m_x + x - (m_pageWidth * getRowForLayoutX(x));
+
+ case MultiPageMode: {
+ int pageNo = getRowForLayoutX(x) / getRowsPerPage();
+ double cx = m_x + x - (m_pageWidth * getRowForLayoutX(x));
+ cx += m_margin + (m_margin * 2 + m_pageWidth) * pageNo;
+ return cx;
+ }
+
+ case LinearMode:
+ default:
+ return m_x + x;
+ }
+}
+
+LinedStaff::LinedStaffCoords
+
+LinedStaff::getLayoutCoordsForCanvasCoords(double x, int y) const
+{
+ int row = getRowForCanvasCoords(x, y);
+ return LinedStaffCoords
+ ((row * m_pageWidth) + x - getCanvasXForLeftOfRow(row),
+ y - getCanvasYForTopOfStaff(row));
+}
+
+LinedStaff::LinedStaffCoords
+
+LinedStaff::getCanvasCoordsForLayoutCoords(double x, int y) const
+{
+ int row = getRowForLayoutX(x);
+ return LinedStaffCoords
+ (getCanvasXForLayoutX(x), getCanvasYForTopLine(row) + y);
+}
+
+int
+LinedStaff::getRowForCanvasCoords(double x, int y) const
+{
+ switch (m_pageMode) {
+
+ case ContinuousPageMode:
+ return ((y - m_y) / m_rowSpacing);
+
+ case MultiPageMode: {
+ int px = int(x - m_x - m_margin);
+ int pw = int(m_margin * 2 + m_pageWidth);
+ if (px < pw)
+ y -= m_titleHeight;
+ return (getRowsPerPage() * (px / pw)) + ((y - m_y) / m_rowSpacing);
+ }
+
+ case LinearMode:
+ default:
+ return (int)((x - m_x) / m_pageWidth);
+ }
+}
+
+int
+LinedStaff::getCanvasYForTopOfStaff(int row) const
+{
+ switch (m_pageMode) {
+
+ case ContinuousPageMode:
+ if (row <= 0)
+ return m_y;
+ else
+ return m_y + (row * m_rowSpacing);
+
+ case MultiPageMode:
+ if (row <= 0)
+ return m_y + m_titleHeight;
+ else if (row < getRowsPerPage())
+ return m_y + ((row % getRowsPerPage()) * m_rowSpacing) + m_titleHeight;
+ else
+ return m_y + ((row % getRowsPerPage()) * m_rowSpacing);
+
+ case LinearMode:
+ default:
+ return m_y;
+ }
+}
+
+double
+LinedStaff::getCanvasXForLeftOfRow(int row) const
+{
+ switch (m_pageMode) {
+
+ case ContinuousPageMode:
+ return m_x;
+
+ case MultiPageMode:
+ return m_x + m_margin +
+ (m_margin*2 + m_pageWidth) * (row / getRowsPerPage());
+
+ case LinearMode:
+ default:
+ return m_x + (row * m_pageWidth);
+ }
+}
+
+void
+LinedStaff::sizeStaff(HorizontalLayoutEngine &layout)
+{
+ Profiler profiler("LinedStaff::sizeStaff", true);
+
+ deleteBars();
+ deleteRepeatedClefsAndKeys();
+ deleteTimeSignatures();
+
+ // RG_DEBUG << "LinedStaff::sizeStaff" << endl;
+
+ int lastBar = layout.getLastVisibleBarOnStaff(*this);
+
+ double xleft = 0, xright = 0;
+ bool haveXLeft = false;
+
+ xright = layout.getBarPosition(lastBar) - 1;
+
+ TimeSignature currentTimeSignature;
+
+ for (int barNo = layout.getFirstVisibleBarOnStaff(*this);
+ barNo <= lastBar; ++barNo) {
+
+ double x = layout.getBarPosition(barNo);
+
+ if (!haveXLeft) {
+ xleft = x;
+ haveXLeft = true;
+ }
+
+ double timeSigX = 0;
+ TimeSignature timeSig;
+ bool isNew = layout.getTimeSignaturePosition(*this, barNo, timeSig, timeSigX);
+
+ if (isNew && barNo < lastBar) {
+ currentTimeSignature = timeSig;
+ insertTimeSignature(timeSigX, currentTimeSignature);
+ RG_DEBUG << "LinedStaff[" << this << "]::sizeStaff: bar no " << barNo << " has time signature at " << timeSigX << endl;
+ }
+
+ RG_DEBUG << "LinedStaff::sizeStaff: inserting bar at " << x << " on staff " << this << " (isNew " << isNew << ", timeSigX " << timeSigX << ")" << endl;
+
+ bool showBarNo =
+ (showBarNumbersEvery() > 0 &&
+ ((barNo + 1) % showBarNumbersEvery()) == 0);
+
+ insertBar(x,
+ ((barNo == lastBar) ? 0 :
+ (layout.getBarPosition(barNo + 1) - x)),
+ layout.isBarCorrectOnStaff(*this, barNo - 1),
+ currentTimeSignature,
+ barNo,
+ showBarNo);
+ }
+
+ m_startLayoutX = xleft;
+ m_endLayoutX = xright;
+
+ drawStaffName();
+ resizeStaffLines();
+}
+
+void
+LinedStaff::deleteBars()
+{
+ for (BarLineList::iterator i = m_barLines.begin();
+ i != m_barLines.end(); ++i) {
+ (*i)->hide();
+ delete *i;
+ }
+
+ for (LineRecList::iterator i = m_beatLines.begin();
+ i != m_beatLines.end(); ++i) {
+ i->second->hide();
+ delete i->second;
+ }
+
+ for (LineRecList::iterator i = m_barConnectingLines.begin();
+ i != m_barConnectingLines.end(); ++i) {
+ i->second->hide();
+ delete i->second;
+ }
+
+ for (ItemList::iterator i = m_barNumbers.begin();
+ i != m_barNumbers.end(); ++i) {
+ (*i)->hide();
+ delete *i;
+ }
+
+ m_barLines.clear();
+ m_beatLines.clear();
+ m_barConnectingLines.clear();
+ m_barNumbers.clear();
+}
+
+void
+LinedStaff::insertBar(double layoutX, double width, bool isCorrect,
+ const TimeSignature &timeSig,
+ int barNo, bool showBarNo)
+{
+ // RG_DEBUG << "insertBar: " << layoutX << ", " << width
+ // << ", " << isCorrect << endl;
+
+ int barThickness = m_lineThickness * 5 / 4;
+
+ // hack to ensure the bar line appears on the correct row in
+ // notation page layouts, with a conditional to prevent us from
+ // moving the bar and beat lines in the matrix
+ if (!showBeatLines()) {
+ if (width > 0.01) { // not final bar in staff
+ layoutX += 1;
+ } else {
+ layoutX -= 1;
+ }
+ }
+
+ int row = getRowForLayoutX(layoutX);
+ double x = getCanvasXForLayoutX(layoutX);
+ int y = getCanvasYForTopLine(row);
+
+ bool firstBarInRow = false, lastBarInRow = false;
+
+ if (m_pageMode != LinearMode &&
+ (getRowForLayoutX(layoutX) >
+ getRowForLayoutX(layoutX - getMargin() - 2)))
+ firstBarInRow = true;
+
+ if (m_pageMode != LinearMode &&
+ width > 0.01 && // width == 0 for final bar in staff
+ (getRowForLayoutX(layoutX) <
+ getRowForLayoutX(layoutX + width + getMargin() + 2)))
+ lastBarInRow = true;
+
+ BarStyle style = getBarStyle(barNo);
+
+ if (style == RepeatBothBar && firstBarInRow)
+ style = RepeatStartBar;
+
+ if (firstBarInRow)
+ insertRepeatedClefAndKey(layoutX, barNo);
+
+ // If we're supposed to be hiding bar lines, we do just that --
+ // create them as normal, then hide them. We can't simply not
+ // create them because we rely on this to find bar extents for
+ // things like double-click selection in notation.
+ bool hidden = false;
+ if (style == PlainBar && timeSig.hasHiddenBars())
+ hidden = true;
+
+ double inset = 0.0;
+ if (style == RepeatStartBar || style == RepeatBothBar) {
+ inset = getBarInset(barNo, firstBarInRow);
+ }
+
+ BarLine *line = new BarLine(m_canvas, layoutX,
+ getBarLineHeight(), barThickness, getLineSpacing(),
+ (int)inset, style);
+
+ line->moveBy(x, y);
+
+ if (isCorrect) {
+ line->setPen(GUIPalette::getColour(GUIPalette::BarLine));
+ line->setBrush(GUIPalette::getColour(GUIPalette::BarLine));
+ } else {
+ line->setPen(GUIPalette::getColour(GUIPalette::BarLineIncorrect));
+ line->setBrush(GUIPalette::getColour(GUIPalette::BarLineIncorrect));
+ }
+
+ line->setZ( -1);
+ if (hidden)
+ line->hide();
+ else
+ line->show();
+
+ // The bar lines have to be in order of layout-x (there's no
+ // such interesting stipulation for beat or connecting lines)
+ BarLineList::iterator insertPoint = lower_bound
+ (m_barLines.begin(), m_barLines.end(), line, compareBars);
+ m_barLines.insert(insertPoint, line);
+
+ if (lastBarInRow) {
+
+ double xe = x + width - barThickness;
+ style = getBarStyle(barNo + 1);
+ if (style == RepeatBothBar)
+ style = RepeatEndBar;
+
+ BarLine *eline = new BarLine(m_canvas, layoutX,
+ getBarLineHeight(), barThickness, getLineSpacing(),
+ 0, style);
+ eline->moveBy(xe, y);
+
+ eline->setPen(GUIPalette::getColour(GUIPalette::BarLine));
+ eline->setBrush(GUIPalette::getColour(GUIPalette::BarLine));
+
+ eline->setZ( -1);
+ if (hidden)
+ eline->hide();
+ else
+ eline->show();
+
+ BarLineList::iterator insertPoint = lower_bound
+ (m_barLines.begin(), m_barLines.end(), eline, compareBars);
+ m_barLines.insert(insertPoint, eline);
+ }
+
+ if (showBarNo) {
+
+ QFont font;
+ font.setPixelSize(m_resolution * 3 / 2);
+ QFontMetrics metrics(font);
+ QString text = QString("%1").arg(barNo + 1);
+
+ QCanvasItem *barNoText = new QCanvasText(text, font, m_canvas);
+ barNoText->setX(x);
+ barNoText->setY(y - metrics.height() - m_resolution * 2);
+ barNoText->setZ( -1);
+ if (hidden)
+ barNoText->hide();
+ else
+ barNoText->show();
+
+ m_barNumbers.push_back(barNoText);
+ }
+
+ QCanvasRectangle *rect = 0;
+
+ if (showBeatLines()) {
+
+ double gridLines; // number of grid lines per bar may be fractional
+
+ // If the snap time is zero we default to beat markers
+ //
+ if (m_snapGrid && m_snapGrid->getSnapTime(x))
+ gridLines = double(timeSig.getBarDuration()) /
+ double(m_snapGrid->getSnapTime(x));
+ else
+ gridLines = timeSig.getBeatsPerBar();
+
+ double dx = width / gridLines;
+
+ for (int gridLine = hidden ? 0 : 1; gridLine < gridLines; ++gridLine) {
+
+ rect = new QCanvasRectangle
+ (0, 0, barThickness, getBarLineHeight(), m_canvas);
+
+ rect->moveBy(x + gridLine * dx, y);
+
+ double currentGrid = gridLines / double(timeSig.getBeatsPerBar());
+
+ rect->setPen(GUIPalette::getColour(GUIPalette::BeatLine));
+ rect->setBrush(GUIPalette::getColour(GUIPalette::BeatLine));
+
+ // Reset to SubBeatLine colour if we're not a beat line - avoid div by zero!
+ //
+ if (currentGrid > 1.0 && double(gridLine) / currentGrid != gridLine / int(currentGrid)) {
+ rect->setPen(GUIPalette::getColour(GUIPalette::SubBeatLine));
+ rect->setBrush(GUIPalette::getColour(GUIPalette::SubBeatLine));
+ }
+
+ rect->setZ( -1);
+ rect->show(); // show beat lines even if the bar lines are hidden
+
+ LineRec beatLine(layoutX + gridLine * dx, rect);
+ m_beatLines.push_back(beatLine);
+ }
+ }
+
+ if (m_connectingLineLength > 0) {
+
+ rect = new QCanvasRectangle
+ (0, 0, barThickness, m_connectingLineLength, m_canvas);
+
+ rect->moveBy(x, y);
+
+ rect->setPen(GUIPalette::getColour(GUIPalette::StaffConnectingLine));
+ rect->setBrush(GUIPalette::getColour(GUIPalette::StaffConnectingLine));
+ rect->setZ( -3);
+ if (hidden)
+ rect->hide();
+ else
+ rect->show();
+
+ LineRec connectingLine(layoutX, rect);
+ m_barConnectingLines.push_back(connectingLine);
+ }
+}
+
+bool
+LinedStaff::compareBars(const BarLine *barLine1, const BarLine *barLine2)
+{
+ return (barLine1->getLayoutX() < barLine2->getLayoutX());
+}
+
+bool
+LinedStaff::compareBarToLayoutX(const BarLine *barLine1, int x)
+{
+ return (barLine1->getLayoutX() < x);
+}
+
+void
+LinedStaff::deleteTimeSignatures()
+{
+ // default implementation is empty
+}
+
+void
+LinedStaff::insertTimeSignature(double, const TimeSignature &)
+{
+ // default implementation is empty
+}
+
+void
+LinedStaff::deleteRepeatedClefsAndKeys()
+{
+ // default implementation is empty
+}
+
+void
+LinedStaff::insertRepeatedClefAndKey(double, int)
+{
+ // default implementation is empty
+}
+
+void
+LinedStaff::drawStaffName()
+{
+ // default implementation is empty
+}
+
+void
+LinedStaff::resizeStaffLines()
+{
+ int firstRow = getRowForLayoutX(m_startLayoutX);
+ int lastRow = getRowForLayoutX(m_endLayoutX);
+
+ RG_DEBUG << "LinedStaff::resizeStaffLines: firstRow "
+ << firstRow << ", lastRow " << lastRow
+ << " (startLayoutX " << m_startLayoutX
+ << ", endLayoutX " << m_endLayoutX << ")" << endl;
+
+ assert(lastRow >= firstRow);
+
+ int i;
+ while ((int)m_staffLines.size() <= lastRow) {
+ m_staffLines.push_back(ItemList());
+ m_staffConnectingLines.push_back(0);
+ }
+
+ // Remove all the staff lines that precede the start of the staff
+
+ for (i = 0; i < firstRow; ++i)
+ clearStaffLineRow(i);
+
+ // now i == firstRow
+
+ while (i <= lastRow) {
+
+ double x0;
+ double x1;
+
+ if (i == firstRow) {
+ x0 = getCanvasXForLayoutX(m_startLayoutX);
+ } else {
+ x0 = getCanvasXForLeftOfRow(i);
+ }
+
+ if (i == lastRow) {
+ x1 = getCanvasXForLayoutX(m_endLayoutX);
+ } else {
+ x1 = getCanvasXForRightOfRow(i);
+ }
+
+ resizeStaffLineRow(i, x0, x1 - x0);
+
+ ++i;
+ }
+
+ // now i == lastRow + 1
+
+ while (i < (int)m_staffLines.size())
+ clearStaffLineRow(i++);
+}
+
+void
+LinedStaff::clearStaffLineRow(int row)
+{
+ for (int h = 0; h < (int)m_staffLines[row].size(); ++h) {
+ delete m_staffLines[row][h];
+ }
+ m_staffLines[row].clear();
+
+ delete m_staffConnectingLines[row];
+ m_staffConnectingLines[row] = 0;
+}
+
+void
+LinedStaff::resizeStaffLineRow(int row, double x, double length)
+{
+ // RG_DEBUG << "LinedStaff::resizeStaffLineRow: row "
+ // << row << ", x " << x << ", length "
+ // << length << endl;
+
+
+ // If the resolution is 8 or less, we want to reduce the blackness
+ // of the staff lines somewhat to make them less intrusive
+
+ int level = 0;
+ int z = 2;
+ if (m_resolution < 6) {
+ z = -1;
+ level = (9 - m_resolution) * 32;
+ if (level > 200)
+ level = 200;
+ }
+
+ QColor lineColour(level, level, level);
+
+ int h;
+
+ /*!!! No longer really good enough. But we could potentially use the
+ bar positions to sort this out
+
+ if (m_pageMode && row > 0 && offset == 0.0) {
+ offset = (double)m_npf->getBarMargin() / 2;
+ length -= offset;
+ }
+ */
+
+ int y;
+
+ delete m_staffConnectingLines[row];
+
+ if (m_pageMode != LinearMode && m_connectingLineLength > 0.1) {
+
+ // rather arbitrary (dup in insertBar)
+ int barThickness = m_resolution / 12 + 1;
+ y = getCanvasYForTopLine(row);
+ QCanvasRectangle *line = new QCanvasRectangle
+ (int(x + length), y, barThickness, m_connectingLineLength, m_canvas);
+ line->setPen(GUIPalette::getColour(GUIPalette::StaffConnectingTerminatingLine));
+ line->setBrush(GUIPalette::getColour(GUIPalette::StaffConnectingTerminatingLine));
+ line->setZ( -2);
+ line->show();
+ m_staffConnectingLines[row] = line;
+
+ } else {
+ m_staffConnectingLines[row] = 0;
+ }
+
+ while ((int)m_staffLines[row].size() <= getLineCount() * m_lineThickness) {
+ m_staffLines[row].push_back(0);
+ }
+
+ int lineIndex = 0;
+
+ for (h = 0; h < getLineCount(); ++h) {
+
+ y = getCanvasYForHeight
+ (getBottomLineHeight() + getHeightPerLine() * h,
+ x, getCanvasYForTopLine(row));
+
+ if (elementsInSpaces()) {
+ y -= getLineSpacing() / 2 + 1;
+ }
+
+ // RG_DEBUG << "LinedStaff: drawing line from ("
+ // << x << "," << y << ") to (" << (x+length-1)
+ // << "," << y << ")" << endl;
+
+ QCanvasItem *line;
+ delete m_staffLines[row][lineIndex];
+ m_staffLines[row][lineIndex] = 0;
+
+ if (m_lineThickness > 1) {
+ QCanvasRectangle *rline = new QCanvasRectangle
+ (int(x), y, int(length), m_lineThickness, m_canvas);
+ rline->setPen(lineColour);
+ rline->setBrush(lineColour);
+ line = rline;
+ } else {
+ QCanvasLine *lline = new QCanvasLine(m_canvas);
+ lline->setPoints(int(x), y, int(x + length), y);
+ lline->setPen(lineColour);
+ line = lline;
+ }
+
+ // if (j > 0) line->setSignificant(false);
+
+ line->setZ(z);
+ m_staffLines[row][lineIndex] = line;
+ line->show();
+
+ ++lineIndex;
+ }
+
+ while (lineIndex < (int)m_staffLines[row].size()) {
+ delete m_staffLines[row][lineIndex];
+ m_staffLines[row][lineIndex] = 0;
+ ++lineIndex;
+ }
+}
+
+void
+LinedStaff::setCurrent(bool current)
+{
+ m_current = current;
+ if (m_current) {
+ m_insertCursor->show();
+ } else {
+ m_insertCursor->hide();
+ }
+}
+
+double
+LinedStaff::getLayoutXOfPointer() const
+{
+ double x = m_pointer->x();
+ int row = getRowForCanvasCoords(x, int(m_pointer->y()));
+ return getLayoutCoordsForCanvasCoords(x, getCanvasYForTopLine(row)).first;
+}
+
+void
+LinedStaff::getPointerPosition(double &cx, int &cy) const
+{
+ cx = m_pointer->x();
+ cy = getCanvasYForTopOfStaff(getRowForCanvasCoords(cx, int(m_pointer->y())));
+}
+
+double
+LinedStaff::getLayoutXOfInsertCursor() const
+{
+ if (!m_current) return -1;
+ double x = m_insertCursor->x();
+ int row = getRowForCanvasCoords(x, int(m_insertCursor->y()));
+ return getLayoutCoordsForCanvasCoords(x, getCanvasYForTopLine(row)).first;
+}
+
+timeT
+LinedStaff::getInsertCursorTime(HorizontalLayoutEngine &layout) const
+{
+ if (m_insertCursorTimeValid) return m_insertCursorTime;
+ return layout.getTimeForX(getLayoutXOfInsertCursor());
+}
+
+void
+LinedStaff::getInsertCursorPosition(double &cx, int &cy) const
+{
+ if (!m_current) {
+ cx = -1;
+ cy = -1;
+ return ;
+ }
+ cx = m_insertCursor->x();
+ cy = getCanvasYForTopOfStaff(getRowForCanvasCoords(cx, int(m_insertCursor->y())));
+}
+
+void
+LinedStaff::setPointerPosition(double canvasX, int canvasY)
+{
+ int row = getRowForCanvasCoords(canvasX, canvasY);
+ canvasY = getCanvasYForTopOfStaff(row);
+ m_pointer->setX(int(canvasX));
+ m_pointer->setY(int(canvasY));
+ m_pointer->setZ( -30); // behind everything else
+ m_pointer->setPoints(0, 0, 0, getHeightOfRow() /* - 1 */);
+ m_pointer->show();
+}
+
+void
+LinedStaff::setPointerPosition(HorizontalLayoutEngine &layout,
+ timeT time)
+{
+ setPointerPosition(layout.getXForTime(time));
+}
+
+void
+LinedStaff::setPointerPosition(double layoutX)
+{
+ LinedStaffCoords coords = getCanvasCoordsForLayoutCoords(layoutX, 0);
+ setPointerPosition(coords.first, coords.second);
+}
+
+void
+LinedStaff::hidePointer()
+{
+ m_pointer->hide();
+}
+
+void
+LinedStaff::setInsertCursorPosition(double canvasX, int canvasY)
+{
+ if (!m_current) return;
+
+ int row = getRowForCanvasCoords(canvasX, canvasY);
+ canvasY = getCanvasYForTopOfStaff(row);
+ m_insertCursor->setX(canvasX);
+ m_insertCursor->setY(canvasY);
+ m_insertCursor->setZ( -28); // behind everything else except playback pointer
+ m_insertCursor->setPoints(0, 0, 0, getHeightOfRow() - 1);
+ m_insertCursor->show();
+ m_insertCursorTimeValid = false;
+}
+
+void
+LinedStaff::setInsertCursorPosition(HorizontalLayoutEngine &layout,
+ timeT time)
+{
+ double x = layout.getXForTime(time);
+ LinedStaffCoords coords = getCanvasCoordsForLayoutCoords(x, 0);
+ setInsertCursorPosition(coords.first, coords.second);
+ m_insertCursorTime = time;
+ m_insertCursorTimeValid = true;
+}
+
+void
+LinedStaff::hideInsertCursor()
+{
+ m_insertCursor->hide();
+}
+
+void
+LinedStaff::renderElements(ViewElementList::iterator,
+ ViewElementList::iterator)
+{
+ // nothing -- we assume rendering will be done by the implementation
+ // of positionElements
+}
+
+void
+LinedStaff::renderAllElements()
+{
+ renderElements(getViewElementList()->begin(),
+ getViewElementList()->end());
+}
+
+void
+LinedStaff::positionAllElements()
+{
+ positionElements(getSegment().getStartTime(),
+ getSegment().getEndTime());
+}
+
+}
diff --git a/src/gui/general/LinedStaff.h b/src/gui/general/LinedStaff.h
new file mode 100644
index 0000000..1444bd2
--- /dev/null
+++ b/src/gui/general/LinedStaff.h
@@ -0,0 +1,759 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_LINEDSTAFF_H_
+#define _RG_LINEDSTAFF_H_
+
+#include "base/Event.h"
+#include "base/FastVector.h"
+#include "base/Staff.h"
+#include "base/ViewElement.h"
+#include <qrect.h>
+#include <utility>
+#include <vector>
+
+
+class QCanvasLine;
+class QCanvasItem;
+class QCanvas;
+class isFirstBarInRow;
+class barNo;
+
+
+namespace Rosegarden
+{
+
+class BarLine;
+class TimeSignature;
+class SnapGrid;
+class Segment;
+class HorizontalLayoutEngine;
+class Event;
+
+
+/**
+ * LinedStaff is a base class for implementations of Staff that
+ * display the contents of a Segment on a set of horizontal lines
+ * with optional vertical bar lines.
+ * Likely subclasses include the notation and piano-roll staffs.
+ *
+ * In general, this class handles x coordinates in floating-point,
+ * but y-coordinates as integers because of the requirement that
+ * staff lines be a precise integral distance apart.
+ */
+
+class LinedStaff : public Staff
+{
+public:
+ typedef std::pair<double, int> LinedStaffCoords;
+
+ enum PageMode {
+ LinearMode = 0,
+ ContinuousPageMode,
+ MultiPageMode
+ };
+
+ enum BarStyle {
+ PlainBar = 0,
+ DoubleBar,
+ HeavyDoubleBar,
+ RepeatEndBar,
+ RepeatStartBar,
+ RepeatBothBar,
+ NoVisibleBar
+ };
+
+protected:
+ /**
+ * Create a new LinedStaff for the given Segment, with a
+ * linear layout.
+ *
+ * \a id is an arbitrary id for the staff in its view,
+ * not used within the LinedStaff implementation but
+ * queryable via getId
+ *
+ * \a resolution is the number of blank pixels between
+ * staff lines
+ *
+ * \a lineThickness is the number of pixels thick a
+ * staff line should be
+ */
+ LinedStaff(QCanvas *, Segment *, SnapGrid *,
+ int id, int resolution, int lineThickness);
+
+ /**
+ * Create a new LinedStaff for the given Segment, with a
+ * page layout.
+ *
+ * \a id is an arbitrary id for the staff in its view,
+ * not used within the LinedStaff implementation but
+ * queryable via getId
+ *
+ * \a resolution is the number of blank pixels between
+ * staff lines
+ *
+ * \a lineThickness is the number of pixels thick a
+ * staff line should be
+ *
+ * \a pageWidth is the width of a page, to determine
+ * when to break lines for page layout
+ *
+ * \a rowsPerPage is the number of rows to a page, or zero
+ * for a single continuous page
+ *
+ * \a rowSpacing is the distance in pixels between
+ * the tops of consecutive rows on this staff
+ */
+ LinedStaff(QCanvas *, Segment *, SnapGrid *,
+ int id, int resolution, int lineThickness,
+ double pageWidth, int rowsPerPage, int rowSpacing);
+
+ /**
+ * Create a new LinedStaff for the given Segment, with
+ * either page or linear layout.
+ */
+ LinedStaff(QCanvas *, Segment *, SnapGrid *,
+ int id, int resolution, int lineThickness, PageMode pageMode,
+ double pageWidth, int rowsPerPage, int rowSpacing);
+
+public:
+ virtual ~LinedStaff();
+
+protected:
+ // Methods required to define the type of staff this is
+
+ /**
+ * Returns the number of visible staff lines
+ */
+ virtual int getLineCount() const = 0;
+
+ /**
+ * Returns the number of invisible staff lines
+ * to leave space for above (and below) the visible staff
+ */
+ virtual int getLegerLineCount() const = 0;
+
+ /**
+ * Returns the height-on-staff value for
+ * the bottom visible staff line (a shorthand means for
+ * referring to staff lines)
+ */
+ virtual int getBottomLineHeight() const = 0;
+
+ /**
+ * Returns the difference between the height-on-
+ * staff value of one visible staff line and the next one
+ * above it
+ */
+ virtual int getHeightPerLine() const = 0;
+
+ /**
+ * Returns the height-on-staff value for the top visible
+ * staff line. This is deliberately not virtual.
+ */
+ int getTopLineHeight() const {
+ return getBottomLineHeight() +
+ (getLineCount() - 1) * getHeightPerLine();
+ }
+
+ /**
+ * Returns true if elements fill the spaces between lines,
+ * false if elements can fall on lines. If true, the lines
+ * will be displaced vertically by half a line spacing.
+ */
+ virtual bool elementsInSpaces() const {
+ return false;
+ }
+
+ /**
+ * Returns true if the staff should draw a faint vertical line at
+ * each beat, in between the (darker) bar lines.
+ */
+ virtual bool showBeatLines() const {
+ return false;
+ }
+
+ /**
+ * Returns the number of bars between bar-line numbers, or zero if
+ * bar lines should not be numbered. For example, if this
+ * function returns 5, every 5th bar (starting at bar 5) will be
+ * numbered.
+ */
+ virtual int showBarNumbersEvery() const {
+ return 0;
+ }
+
+ /**
+ * Returns the bar line / repeat style for the start of the given bar.
+ */
+ virtual BarStyle getBarStyle(int /* barNo */) const {
+ return PlainBar;
+ }
+
+ /**
+ * Returns the distance the opening (repeat) bar is inset from the
+ * nominal barline position. This is to accommodate the situation
+ * where a repeat bar has to appear after the clef and key.
+ */
+ virtual double getBarInset(int /* barNo */, bool /* isFirstBarInRow */) const {
+ return 0;
+ }
+
+protected:
+ /// Subclass may wish to expose this
+ virtual void setResolution(int resolution);
+
+ /// Subclass may wish to expose this
+ virtual void setLineThickness(int lineThickness);
+
+ /// Subclass may wish to expose this
+ virtual void setPageMode(PageMode pageMode);
+
+ /// Subclass may wish to expose this
+ virtual void setPageWidth(double pageWidth);
+
+ /// Subclass may wish to expose this
+ virtual void setRowsPerPage(int rowsPerPage);
+
+ /// Subclass may wish to expose this
+ virtual void setRowSpacing(int rowSpacing);
+
+ /// Subclass may wish to expose this. Default is zero
+ virtual void setConnectingLineLength(int length);
+
+public:
+ /**
+ * Return the id of the staff. This is only useful to external
+ * agents, it isn't used by the LinedStaff itself.
+ */
+ virtual int getId() const;
+
+ /**
+ * Set the canvas x-coordinate of the left-hand end of the staff.
+ * This does not move any canvas items that have already been
+ * created; it should be called before the sizeStaff/positionElements
+ * procedure begins.
+ */
+ virtual void setX(double x);
+
+ /**
+ * Get the canvas x-coordinate of the left-hand end of the staff.
+ */
+ virtual double getX() const;
+
+ /**
+ * Set the canvas y-coordinate of the top of the first staff row.
+ * This does not move any canvas items that have already been
+ * created; it should be called before the sizeStaff/positionElements
+ * procedure begins.
+ */
+ virtual void setY(int y);
+
+ /**
+ * Get the canvas y-coordinate of the top of the first staff row.
+ */
+ virtual int getY() const;
+
+ /**
+ * Set the canvas width of the margin to left and right of the
+ * staff on each page (used only in MultiPageMode). Each staff
+ * row will still be pageWidth wide (that is, the margin is in
+ * addition to the pageWidth, not included in it). This does not
+ * move any canvas items that have already been created; it should
+ * be called before the sizeStaff/positionElements procedure
+ * begins.
+ */
+ virtual void setMargin(double m);
+
+ /**
+ * Get the canvas width of the left and right margins.
+ */
+ virtual double getMargin() const;
+
+ /**
+ * Set the canvas height of the area at the top of the first page
+ * reserved for the composition title and composer's name (used
+ * only in MultiPageMode).
+ */
+ virtual void setTitleHeight(int h);
+
+ /**
+ * Get the canvas height of the title area.
+ */
+ virtual int getTitleHeight() const;
+
+ /**
+ * Returns the width of the entire staff after layout. Call
+ * this only after you've done the full sizeStaff/positionElements
+ * procedure.
+ */
+ virtual double getTotalWidth() const;
+
+ /**
+ * Returns the height of the entire staff after layout. Call
+ * this only after you've done the full sizeStaff/positionElements
+ * procedure. If there are multiple rows, this will be the
+ * height of all rows, including any space between rows that
+ * is used to display other staffs.
+ */
+ virtual int getTotalHeight() const;
+
+ /**
+ * Returns the total number of pages used by the staff.
+ */
+ int getPageCount() const {
+ if (m_pageMode != MultiPageMode) return 1;
+ else return 1 + (getRowForLayoutX(m_endLayoutX) / getRowsPerPage());
+ }
+
+ /**
+ * Returns the difference between the y coordinates of
+ * neighbouring visible staff lines. Deliberately non-virtual
+ */
+ int getLineSpacing() const {
+ return m_resolution + m_lineThickness;
+ }
+
+ /**
+ * Returns the total height of a single staff row, including ruler
+ */
+ virtual int getHeightOfRow() const;
+
+ /**
+ * Returns true if the given canvas coordinates fall within
+ * (any of the rows of) this staff. False if they fall in the
+ * gap between two rows.
+ */
+ virtual bool containsCanvasCoords(double canvasX, int canvasY) const;
+
+ /**
+ * Returns the canvas y coordinate of the specified line on the
+ * staff. baseX/baseY are a canvas coordinates somewhere on the
+ * correct row, or -1 for the default row.
+ */
+ virtual int getCanvasYForHeight(int height, double baseX = -1, int baseY = -1) const;
+
+ /**
+ * Returns the y coordinate of the specified line on the
+ * staff, relative to the top of the row.
+ */
+ virtual int getLayoutYForHeight(int height) const;
+
+ /**
+ * Returns the height-on-staff value nearest to the given
+ * canvas coordinates.
+ */
+ virtual int getHeightAtCanvasCoords(double x, int y) const;
+
+ /**
+ * Return the full width, height and origin of the bar containing
+ * the given canvas cooordinates.
+ */
+ virtual QRect getBarExtents(double x, int y) const;
+
+ /**
+ * Set whether this is the current staff or not. A staff that is
+ * current will differ visually from non-current staffs.
+ *
+ * The owner of the staffs should normally ensure that one staff
+ * is current (the default is non-current, even if there only is
+ * one staff) and that only one staff is current at once.
+ */
+ virtual void setCurrent(bool current);
+
+ /**
+ * Move the playback pointer to the layout-X coordinate
+ * corresponding to the given time, and show it.
+ */
+ virtual void setPointerPosition
+ (HorizontalLayoutEngine&, timeT);
+
+ /**
+ * Move the playback pointer to the layout-X coordinate
+ * corresponding to the given canvas coordinates, and show it.
+ */
+ virtual void setPointerPosition(double x, int y);
+
+ /**
+ * Move the playback pointer to the given layout-X
+ * coordinate, and show it.
+ */
+ virtual void setPointerPosition(double x);
+
+ /**
+ * Returns the layout-X coordinate corresponding to the current
+ * position of the playback pointer.
+ */
+ virtual double getLayoutXOfPointer() const;
+
+ /**
+ * Returns the canvas coordinates of the top of the playback
+ * pointer.
+ */
+ virtual void getPointerPosition(double &x, int &y) const;
+
+ /**
+ * Hide the playback pointer.
+ */
+ virtual void hidePointer();
+
+ /**
+ * Move the insertion cursor to the layout-X coordinate
+ * corresponding to the given time, and show it.
+ */
+ virtual void setInsertCursorPosition(HorizontalLayoutEngine&, timeT);
+
+ /**
+ * Move the insertion cursor to the layout-X coordinate
+ * corresponding to the given canvas coordinates, and show it.
+ */
+ virtual void setInsertCursorPosition(double x, int y);
+
+ /**
+ * Returns the layout-X coordinate corresponding to the current
+ * position of the insertion cursor. Returns -1 if this staff
+ * is not current or there is some other problem.
+ */
+ virtual double getLayoutXOfInsertCursor() const;
+
+ /**
+ * Return the time of the insert cursor.
+ */
+ virtual timeT getInsertCursorTime(HorizontalLayoutEngine&) const;
+
+ /**
+ * Return the canvas coordinates of the top of the insert
+ * cursor.
+ */
+ virtual void getInsertCursorPosition(double &x, int &y) const;
+
+ /**
+ * Hide the insert cursor.
+ */
+ virtual void hideInsertCursor();
+
+ /**
+ * Query the given horizontal layout object (which is assumed to
+ * have just completed its layout procedure) to determine the
+ * required extents of the staff and the positions of the bars,
+ * and create the bars and staff lines accordingly. It may be
+ * called either before or after renderElements and/or
+ * positionElements.
+ *
+ * No bars or staff lines will appear unless this method has
+ * been called.
+ */
+ virtual void sizeStaff(HorizontalLayoutEngine& layout);
+
+ /**
+ * Generate or re-generate sprites for all the elements between
+ * from and to. See subclasses for specific detailed comments.
+ *
+ * A very simplistic staff subclass may choose not to
+ * implement this (the default implementation is empty) and to
+ * do all the rendering work in positionElements. If rendering
+ * elements is slow, however, it makes sense to do it here
+ * because this method may be called less often.
+ */
+ virtual void renderElements(ViewElementList::iterator from,
+ ViewElementList::iterator to);
+
+ /**
+ * Call renderElements(from, to) on the whole staff.
+ */
+ virtual void renderAllElements();
+
+ /**
+ * Assign suitable coordinates to the elements on the staff
+ * between the start and end times, based entirely on the layout
+ * X and Y coordinates they were given by the horizontal and
+ * vertical layout processes.
+ *
+ * The implementation is free to render any elements it
+ * chooses in this method as well.
+ */
+ virtual void positionElements(timeT from,
+ timeT to) = 0;
+
+ /**
+ * Call positionElements(from, to) on the whole staff.
+ */
+ virtual void positionAllElements();
+
+
+ /* Some optional methods for the subclass. */
+
+
+ /**
+ * Return an iterator pointing to the nearest view element to the
+ * given canvas coordinates.
+ *
+ * If notesAndRestsOnly is true, do not return any view element
+ * other than a note or rest.
+ *
+ * If the closest view element is further away than
+ * proximityThreshold pixels in either x or y axis, return end().
+ * If proximityThreshold is less than zero, treat it as infinite.
+ *
+ * Also return the clef and key in force at these coordinates.
+ *
+ * The default implementation should suit for subclasses that only
+ * show a single element per layout X coordinate.
+ */
+ virtual ViewElementList::iterator getClosestElementToCanvasCoords
+ (double x, int y,
+ Event *&clef, Event *&key,
+ bool notesAndRestsOnly = false, int proximityThreshold = 10) {
+ LinedStaffCoords layoutCoords = getLayoutCoordsForCanvasCoords(x, y);
+ return getClosestElementToLayoutX
+ (layoutCoords.first, clef, key,
+ notesAndRestsOnly, proximityThreshold);
+ }
+
+ /**
+ * Return an iterator pointing to the nearest view element to the
+ * given layout x-coordinate.
+ *
+ * If notesAndRestsOnly is true, do not return any view element
+ * other than a note or rest.
+ *
+ * If the closest view element is further away than
+ * proximityThreshold pixels in either x or y axis, return end().
+ * If proximityThreshold is less than zero, treat it as infinite.
+ *
+ * Also return the clef and key in force at these coordinates.
+ *
+ * The subclass may decide whether to implement this method or not
+ * based on the semantics and intended usage of the class.
+ */
+ virtual ViewElementList::iterator getClosestElementToLayoutX
+ (double x,
+ Event *&clef, Event *&key,
+ bool notesAndRestsOnly = false, int proximityThreshold = 10) {
+ return getViewElementList()->end();
+ }
+
+ /**
+ * Return an iterator pointing to the element "under" the given
+ * canvas coordinates.
+ *
+ * Return end() if there is no such element.
+ *
+ * Also return the clef and key in force at these coordinates.
+ *
+ *
+ * The default implementation should suit for subclasses that only
+ * show a single element per layout X coordinate.
+ */
+ virtual ViewElementList::iterator getElementUnderCanvasCoords
+ (double x, int y, Event *&clef, Event *&key) {
+ LinedStaffCoords layoutCoords = getLayoutCoordsForCanvasCoords(x, y);
+ return getElementUnderLayoutX(layoutCoords.first, clef, key);
+ }
+
+ /**
+ * Return an iterator pointing to the element "under" the given
+ * canvas coordinates.
+ *
+ * Return end() if there is no such element.
+ *
+ * Also return the clef and key in force at these coordinates.
+ *
+ * The subclass may decide whether to implement this method or not
+ * based on the semantics and intended usage of the class.
+ */
+ virtual ViewElementList::iterator getElementUnderLayoutX
+ (double x, Event *&clef, Event *&key) {
+ return getViewElementList()->end();
+ }
+
+ // The default implementation of the following is empty. The
+ // subclass is presumed to know what the staff's name is and
+ // where to put it; this is simply called at some point during
+ // the staff-drawing process.
+ virtual void drawStaffName();
+
+
+public:
+ // This should not really be public -- it should be one of the
+ // protected methods below -- but we have some code that needs
+ // it and hasn't been supplied with a proper way to do without.
+ // Please try to avoid calling this method.
+ //!!! fix NotationView::doDeferredCursorMove
+
+ // This should not really be public -- it should be one of the
+ // protected methods below -- but we have some code that needs
+ // it and hasn't been supplied with a proper way to do without.
+ // Please try to avoid calling this method.
+ //!!! fix NotationView::getStaffForCanvasCoords
+ LinedStaffCoords
+ getLayoutCoordsForCanvasCoords(double x, int y) const;
+
+ // This should not really be public -- it should be one of the
+ // protected methods below -- but we have some code that needs
+ // it and hasn't been supplied with a proper way to do without.
+ // Please try to avoid calling this method.
+ //!!! fix NotationView::scrollToTime
+ LinedStaffCoords
+ getCanvasCoordsForLayoutCoords(double x, int y) const;//!!!
+
+ // This should not really be public -- it should be one of the
+ // protected methods below -- but we have some code that needs
+ // it and hasn't been supplied with a proper way to do without.
+ // Please try to avoid calling this method.
+ //!!! fix NotationView::print etc
+ int getRowSpacing() { return m_rowSpacing; }
+
+protected:
+ // Methods that the subclass may (indeed, should) use to convert
+ // between the layout coordinates of elements and their canvas
+ // coordinates. These are deliberately not virtual.
+
+ // Note that even linear-layout staffs have multiple rows; their
+ // rows all have the same y coordinate but increasing x
+ // coordinates, instead of the other way around. (The only reason
+ // for this is that it seems to be more efficient from the QCanvas
+ // perspective to create and manipulate many relatively short
+ // canvas lines rather than a smaller number of very long ones.)
+
+ int getTopLineOffset() const {
+ return getLineSpacing() * getLegerLineCount();
+ }
+
+ int getBarLineHeight() const {
+ return getLineSpacing() * (getLineCount() - 1) + m_lineThickness;
+ }
+
+ int getRowForLayoutX(double x) const {
+ return (int)(x / m_pageWidth);
+ }
+
+ int getRowForCanvasCoords(double x, int y) const;
+
+ int getCanvasYForTopOfStaff(int row = -1) const;
+
+ int getCanvasYForTopLine(int row = -1) const {
+ return getCanvasYForTopOfStaff(row) + getTopLineOffset();
+ }
+
+ double getCanvasXForLeftOfRow(int row) const;
+
+ double getCanvasXForRightOfRow(int row) const {
+ return getCanvasXForLeftOfRow(row) + m_pageWidth;
+ }
+
+ LinedStaffCoords
+ getCanvasOffsetsForLayoutCoords(double x, int y) const {
+ LinedStaffCoords cc = getCanvasCoordsForLayoutCoords(x, y);
+ return LinedStaffCoords(cc.first - x, cc.second - y);
+ }
+
+ double getCanvasXForLayoutX(double x) const;
+
+ int getRowsPerPage() const {
+ return m_rowsPerPage;
+ }
+
+protected:
+ // Actual implementation methods. The default implementation
+ // shows staff lines, connecting lines (where appropriate) and bar
+ // lines, but does not show time signatures. To see time
+ // signatures, override the deleteTimeSignatures and
+ // insertTimeSignature methods. For repeated clefs and keys at
+ // the start of each row, override deleteRepeatedClefsAndKeys
+ // and insertRepeatedClefAndKey, but note that your layout class
+ // will need to allot the space for them separately.
+
+ virtual void resizeStaffLines();
+ virtual void clearStaffLineRow(int row);
+ virtual void resizeStaffLineRow(int row, double offset, double length);
+
+ virtual void deleteBars();
+ virtual void insertBar(double layoutX, double width, bool isCorrect,
+ const TimeSignature &,
+ int barNo, bool showBarNo);
+
+ // The default implementations of the following two are empty.
+ virtual void deleteTimeSignatures();
+ virtual void insertTimeSignature(double layoutX,
+ const TimeSignature &);
+
+ // The default implementations of the following two are empty.
+ virtual void deleteRepeatedClefsAndKeys();
+ virtual void insertRepeatedClefAndKey(double layoutX, int barNo);
+
+ void initCursors();
+
+protected:
+
+ //--------------- Data members ---------------------------------
+
+ QCanvas *m_canvas;
+ SnapGrid *m_snapGrid;
+
+ int m_id;
+
+ double m_x;
+ int m_y;
+ double m_margin;
+ int m_titleHeight;
+ int m_resolution;
+ int m_lineThickness;
+
+ PageMode m_pageMode;
+ double m_pageWidth;
+ int m_rowsPerPage;
+ int m_rowSpacing;
+ int m_connectingLineLength;
+
+ double m_startLayoutX;
+ double m_endLayoutX;
+
+ bool m_current;
+
+ typedef std::vector<QCanvasItem *> ItemList;
+ typedef std::vector<ItemList> ItemMatrix;
+ ItemMatrix m_staffLines;
+ ItemList m_staffConnectingLines;
+
+ typedef std::pair<double, QCanvasItem *> LineRec; // layout-x, line
+ typedef FastVector<LineRec> LineRecList;
+ typedef FastVector<BarLine *> BarLineList;//!!! should be multiset I reckon
+ static bool compareBars(const BarLine *, const BarLine *);
+ static bool compareBarToLayoutX(const BarLine *, int);
+ BarLineList m_barLines;
+ LineRecList m_beatLines;
+ LineRecList m_barConnectingLines;
+ ItemList m_barNumbers;
+
+ QCanvasLine *m_pointer;
+ QCanvasLine *m_insertCursor;
+ timeT m_insertCursorTime;
+ bool m_insertCursorTimeValid;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/general/LinedStaffManager.cpp b/src/gui/general/LinedStaffManager.cpp
new file mode 100644
index 0000000..b1b92d2
--- /dev/null
+++ b/src/gui/general/LinedStaffManager.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "LinedStaffManager.h"
+
+#include "LinedStaff.h"
+
+
+namespace Rosegarden
+{
+}
diff --git a/src/gui/general/LinedStaffManager.h b/src/gui/general/LinedStaffManager.h
new file mode 100644
index 0000000..44338f1
--- /dev/null
+++ b/src/gui/general/LinedStaffManager.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_LINEDSTAFFMANAGER_H_
+#define _RG_LINEDSTAFFMANAGER_H_
+
+
+
+
+
+namespace Rosegarden
+{
+
+class LinedStaff;
+
+
+/**
+ * LinedStaffManager is a trivial abstract base for classes that own
+ * and position sets of LinedStaffs, as a convenient API to permit
+ * clients (such as canvas implementations) to discover which staff
+ * lies where.
+ *
+ * LinedStaffManager is not used by LinedStaff.
+ */
+
+class LinedStaff;
+
+class LinedStaffManager
+{
+public:
+ virtual ~LinedStaffManager() {}
+ virtual LinedStaff *getStaffForCanvasCoords(int x, int y) const = 0;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/general/MidiPitchLabel.cpp b/src/gui/general/MidiPitchLabel.cpp
new file mode 100644
index 0000000..47a748b
--- /dev/null
+++ b/src/gui/general/MidiPitchLabel.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "MidiPitchLabel.h"
+#include <kapplication.h>
+
+#include "document/ConfigGroups.h"
+#include <kconfig.h>
+#include <klocale.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+static QString notes[] = {
+ i18n("C%1"), i18n("C#%1"), i18n("D%1"), i18n("D#%1"),
+ i18n("E%1"), i18n("F%1"), i18n("F#%1"), i18n("G%1"),
+ i18n("G#%1"), i18n("A%1"), i18n("A#%1"), i18n("B%1")
+};
+
+
+MidiPitchLabel::MidiPitchLabel(int pitch)
+{
+ if (pitch < 0 || pitch > 127) {
+
+ m_midiNote = "";
+
+ } else {
+
+ KConfig *config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+ int baseOctave = config->readNumEntry("midipitchoctave", -2);
+
+ int octave = (int)(((float)pitch) / 12.0) + baseOctave;
+ m_midiNote = notes[pitch % 12].arg(octave);
+ }
+}
+
+std::string
+MidiPitchLabel::getString() const
+{
+ return std::string(m_midiNote.utf8().data());
+}
+
+QString
+MidiPitchLabel::getQString() const
+{
+ return m_midiNote;
+}
+
+}
diff --git a/src/gui/general/MidiPitchLabel.h b/src/gui/general/MidiPitchLabel.h
new file mode 100644
index 0000000..9abcc11
--- /dev/null
+++ b/src/gui/general/MidiPitchLabel.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_MIDIPITCHLABEL_H_
+#define _RG_MIDIPITCHLABEL_H_
+
+#include <string>
+#include <qstring.h>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+class MidiPitchLabel
+{
+public:
+ MidiPitchLabel(int pitch);
+
+ std::string getString() const;
+ QString getQString() const;
+
+private:
+ QString m_midiNote;
+
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/general/PixmapFunctions.cpp b/src/gui/general/PixmapFunctions.cpp
new file mode 100644
index 0000000..d297dad
--- /dev/null
+++ b/src/gui/general/PixmapFunctions.cpp
@@ -0,0 +1,271 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "PixmapFunctions.h"
+
+#include <qbitmap.h>
+#include <qcolor.h>
+#include <qimage.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+
+#include <iostream>
+
+namespace Rosegarden
+{
+
+QBitmap
+PixmapFunctions::generateMask(const QPixmap &map, const QRgb &px)
+{
+ QImage i(map.convertToImage());
+ QImage im(i.width(), i.height(), 1, 2, QImage::LittleEndian);
+
+ for (int y = 0; y < i.height(); ++y) {
+ for (int x = 0; x < i.width(); ++x) {
+ if (i.pixel(x, y) != px) {
+ im.setPixel(x, y, 1);
+ } else {
+ im.setPixel(x, y, 0);
+ }
+ }
+ }
+
+ QBitmap m;
+ m.convertFromImage(im);
+ return m;
+}
+
+QBitmap
+PixmapFunctions::generateMask(const QPixmap &map)
+{
+ QImage i(map.convertToImage());
+ QImage im(i.width(), i.height(), 1, 2, QImage::LittleEndian);
+
+ QRgb px0(i.pixel(0, 0));
+ QRgb px1(i.pixel(i.width() - 1, 0));
+ QRgb px2(i.pixel(i.width() - 1, i.height() - 1));
+ QRgb px3(i.pixel(0, i.height() - 1));
+
+ QRgb px(px0);
+ if (px0 != px2 && px1 == px3)
+ px = px1;
+
+ for (int y = 0; y < i.height(); ++y) {
+ for (int x = 0; x < i.width(); ++x) {
+ if (i.pixel(x, y) != px) {
+ im.setPixel(x, y, 1);
+ } else {
+ im.setPixel(x, y, 0);
+ }
+ }
+ }
+
+ QBitmap m;
+ m.convertFromImage(im);
+ return m;
+}
+
+QPixmap
+PixmapFunctions::colourPixmap(const QPixmap &map, int hue, int minValue)
+{
+ // assumes pixmap is currently in shades of grey; maps black ->
+ // solid colour and greys -> shades of colour
+
+ QImage image = map.convertToImage();
+
+ int s, v;
+
+ bool warned = false;
+
+ for (int y = 0; y < image.height(); ++y) {
+ for (int x = 0; x < image.width(); ++x) {
+
+ QColor pixel(image.pixel(x, y));
+
+ int oldHue;
+ pixel.hsv(&oldHue, &s, &v);
+
+ if (oldHue >= 0) {
+ if (!warned) {
+ std::cerr << "PixmapFunctions::recolour: Not a greyscale pixmap "
+ << "(found rgb value " << pixel.red() << ","
+ << pixel.green() << "," << pixel.blue()
+ << "), hoping for the best" << std::endl;
+ warned = true;
+ }
+ }
+
+ image.setPixel
+ (x, y, QColor(hue,
+ 255 - v,
+ v > minValue ? v : minValue,
+ QColor::Hsv).rgb());
+ }
+ }
+
+ QPixmap rmap;
+ rmap.convertFromImage(image);
+ if (map.mask())
+ rmap.setMask(*map.mask());
+ return rmap;
+}
+
+QPixmap
+PixmapFunctions::shadePixmap(const QPixmap &map)
+{
+ QImage image = map.convertToImage();
+
+ int h, s, v;
+
+ for (int y = 0; y < image.height(); ++y) {
+ for (int x = 0; x < image.width(); ++x) {
+
+ QColor pixel(image.pixel(x, y));
+
+ pixel.hsv(&h, &s, &v);
+
+ image.setPixel
+ (x, y, QColor(h,
+ s,
+ 255 - ((255 - v) / 2),
+ QColor::Hsv).rgb());
+ }
+ }
+
+ QPixmap rmap;
+ rmap.convertFromImage(image);
+ if (map.mask())
+ rmap.setMask(*map.mask());
+ return rmap;
+}
+
+QPixmap
+PixmapFunctions::flipVertical(const QPixmap &map)
+{
+ QPixmap rmap;
+ QImage i(map.convertToImage());
+ rmap.convertFromImage(i.mirror(false, true));
+
+ if (map.mask()) {
+ QImage im(map.mask()->convertToImage());
+ QBitmap newMask;
+ newMask.convertFromImage(im.mirror(false, true));
+ rmap.setMask(newMask);
+ }
+
+ return rmap;
+}
+
+QPixmap
+PixmapFunctions::flipHorizontal(const QPixmap &map)
+{
+ QPixmap rmap;
+ QImage i(map.convertToImage());
+ rmap.convertFromImage(i.mirror(true, false));
+
+ if (map.mask()) {
+ QImage im(map.mask()->convertToImage());
+ QBitmap newMask;
+ newMask.convertFromImage(im.mirror(true, false));
+ rmap.setMask(newMask);
+ }
+
+ return rmap;
+}
+
+std::pair<QPixmap, QPixmap>
+PixmapFunctions::splitPixmap(const QPixmap &pixmap, int x)
+{
+ QPixmap left(x, pixmap.height(), pixmap.depth());
+ QBitmap leftMask(left.width(), left.height());
+
+ QPixmap right(pixmap.width() - x, pixmap.height(), pixmap.depth());
+ QBitmap rightMask(right.width(), right.height());
+
+ QPainter paint;
+
+ paint.begin(&left);
+ paint.drawPixmap(0, 0, pixmap, 0, 0, left.width(), left.height());
+ paint.end();
+
+ paint.begin(&leftMask);
+ paint.drawPixmap(0, 0, *pixmap.mask(), 0, 0, left.width(), left.height());
+ paint.end();
+
+ left.setMask(leftMask);
+
+ paint.begin(&right);
+ paint.drawPixmap(0, 0, pixmap, left.width(), 0, right.width(), right.height());
+ paint.end();
+
+ paint.begin(&rightMask);
+ paint.drawPixmap(0, 0, *pixmap.mask(), left.width(), 0, right.width(), right.height());
+ paint.end();
+
+ right.setMask(rightMask);
+
+ return std::pair<QPixmap, QPixmap>(left, right);
+}
+
+void
+PixmapFunctions::drawPixmapMasked(QPixmap &dest, QBitmap &destMask,
+ int x0, int y0,
+ const QPixmap &src)
+{
+ QImage idp(dest.convertToImage());
+ QImage idm(destMask.convertToImage());
+ QImage isp(src.convertToImage());
+ QImage ism(src.mask()->convertToImage());
+
+ for (int y = 0; y < isp.height(); ++y) {
+ for (int x = 0; x < isp.width(); ++x) {
+
+ if (x >= ism.width())
+ continue;
+ if (y >= ism.height())
+ continue;
+
+ if (ism.depth() == 1 && ism.pixel(x, y) == 0)
+ continue;
+ if (ism.pixel(x, y) == Qt::white.rgb())
+ continue;
+
+ int x1 = x + x0;
+ int y1 = y + y0;
+ if (x1 < 0 || x1 >= idp.width())
+ continue;
+ if (y1 < 0 || y1 >= idp.height())
+ continue;
+
+ idp.setPixel(x1, y1, isp.pixel(x, y));
+ idm.setPixel(x1, y1, 1);
+ }
+ }
+
+ dest.convertFromImage(idp);
+ destMask.convertFromImage(idm);
+}
+
+}
diff --git a/src/gui/general/PixmapFunctions.h b/src/gui/general/PixmapFunctions.h
new file mode 100644
index 0000000..22da0f0
--- /dev/null
+++ b/src/gui/general/PixmapFunctions.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_PIXMAPFUNCTIONS_H_
+#define _RG_PIXMAPFUNCTIONS_H_
+
+#include <qbitmap.h>
+#include <qpixmap.h>
+#include <utility>
+
+
+namespace Rosegarden
+{
+
+
+
+class PixmapFunctions
+{
+public:
+ /**
+ * Generate a heuristic mask for the given pixmap. Unlike
+ * QPixmap::createHeuristicMask, this removes from the mask all
+ * pixels that are apparently "background" even if they appear in
+ * holes in the middle of the image. This is more usually what we
+ * want than the default behaviour of createHeuristicMask.
+ *
+ * The rgb value specifies the colour to treat as background.
+ *
+ * This function is slow.
+ */
+ static QBitmap generateMask(const QPixmap &map, const QRgb &rgb);
+
+ /**
+ * Generate a heuristic mask for the given pixmap. Unlike
+ * QPixmap::createHeuristicMask, this removes from the mask all
+ * pixels that are apparently "background" even if they appear in
+ * holes in the middle of the image. This is more usually what we
+ * want than the default behaviour of createHeuristicMask.
+ *
+ * This function calculates its own estimated colour to match as
+ * background.
+ *
+ * This function is slow.
+ */
+ static QBitmap generateMask(const QPixmap &map);
+
+ /**
+ * Colour a greyscale pixmap with the given hue.
+ * minValue specifies the minimum value (in the HSV sense) that
+ * will be used for any recoloured pixel.
+ */
+ static QPixmap colourPixmap(const QPixmap &map, int hue, int minValue);
+
+ /**
+ * Make a pixmap grey, or otherwise reduce its intensity.
+ */
+ static QPixmap shadePixmap(const QPixmap &map);
+
+ /// Return a QPixmap that is a mirror image of map (including mask)
+ static QPixmap flipVertical(const QPixmap &map);
+
+ /// Return a QPixmap that is a mirror image of map (including mask)
+ static QPixmap flipHorizontal(const QPixmap &map);
+
+ /// Return left and right parts of the QPixmap
+ static std::pair<QPixmap, QPixmap> splitPixmap(const QPixmap &original, int x);
+
+ /**
+ * Using QPainter::drawPixmap to draw one pixmap on another does
+ * not appear to take the mask into account properly. Background
+ * pixels in the second pixmap erase foreground pixels in the
+ * first one, regardless of whether they're masked or not. This
+ * function does what I expect.
+ *
+ * Note that the source pixmap _must_ have a mask.
+ */
+ static void drawPixmapMasked(QPixmap &dest, QBitmap &destMask,
+ int x, int y,
+ const QPixmap &source);
+};
+
+
+}
+
+#endif
diff --git a/src/gui/general/PresetElement.cpp b/src/gui/general/PresetElement.cpp
new file mode 100644
index 0000000..4158d69
--- /dev/null
+++ b/src/gui/general/PresetElement.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ This file is Copyright 2006
+ D. Michael McIntyre <dmmcintyr@users.sourceforge.net>
+
+ 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 "PresetElement.h"
+
+#include "misc/Debug.h"
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+PresetElement::PresetElement(QString name,
+ int clef,
+ int transpose,
+ int highAm,
+ int lowAm,
+ int highPro,
+ int lowPro) :
+ m_name (name),
+ m_clef (clef),
+ m_transpose (transpose),
+ m_highAm (highAm),
+ m_lowAm (lowAm),
+ m_highPro (highPro),
+ m_lowPro (lowPro)
+{
+ RG_DEBUG << "PresetElement::PresetElement(" << endl
+ << " name = " << name << endl
+ << " clef = " << clef << endl
+ << " trns.= " << transpose << endl
+ << " higH = " << highAm << endl
+ << " lowA = " << lowAm << endl
+ << " higP = " << highPro << endl
+ << " lowP = " << lowPro << ")" << endl;
+}
+
+PresetElement::~PresetElement()
+{
+ // nothing to do
+}
+
+}
diff --git a/src/gui/general/PresetElement.h b/src/gui/general/PresetElement.h
new file mode 100644
index 0000000..24d3ee4
--- /dev/null
+++ b/src/gui/general/PresetElement.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ This file is Copyright 2006
+ D. Michael McIntyre <dmmcintyr@users.sourceforge.net>
+
+ 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_PRESETELEMENT_H_
+#define _RG_PRESETELEMENT_H_
+
+#include <qstring.h>
+
+#include <vector>
+
+
+
+namespace Rosegarden
+{
+
+/*
+ * A container class for storing a set of data describing a real world
+ * instrument for which one is writing musical notation
+ */
+class PresetElement
+{
+public:
+
+ PresetElement(QString name,
+ int clef,
+ int transpose,
+ int highAm,
+ int lowAm,
+ int highPro,
+ int lowPro);
+
+ ~PresetElement();
+
+ // accessors
+ QString getName() { return m_name; }
+ int getClef() { return m_clef; }
+ int getTranspose() { return m_transpose; }
+ int getHighAm() { return m_highAm; }
+ int getLowAm() { return m_lowAm; }
+ int getHighPro() { return m_highPro; }
+ int getLowPro() { return m_lowPro; }
+
+private:
+ QString m_name;
+ int m_clef;
+ int m_transpose;
+ int m_highAm;
+ int m_lowAm;
+ int m_highPro;
+ int m_lowPro;
+}; // PresetElement
+
+typedef std::vector<PresetElement> ElementContainer;
+
+}
+
+#endif
diff --git a/src/gui/general/PresetGroup.cpp b/src/gui/general/PresetGroup.cpp
new file mode 100644
index 0000000..4a457a9
--- /dev/null
+++ b/src/gui/general/PresetGroup.cpp
@@ -0,0 +1,269 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ This file is Copyright 2006
+ D. Michael McIntyre <dmmcintyr@users.sourceforge.net>
+
+ 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 "PresetGroup.h"
+
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "gui/general/ClefIndex.h"
+#include "base/Exception.h"
+#include "CategoryElement.h"
+#include <klocale.h>
+#include <kstddirs.h>
+#include <kglobal.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qregexp.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+PresetGroup::PresetGroup() :
+ m_errorString(i18n("unknown error")),
+ m_elCategoryName(""),
+ m_elInstrumentName(""),
+ m_elClef(0),
+ m_elTranspose(0),
+ m_elLowAm(0),
+ m_elHighAm(0),
+ m_elLowPro(0),
+ m_elHighPro(0),
+ m_lastCategory( -1),
+ m_currentCategory( -1),
+ m_lastInstrument( -1),
+ m_currentInstrument( -1),
+ m_name(false),
+ m_clef(false),
+ m_transpose(false),
+ m_amateur(false),
+ m_pro(false)
+{
+ m_presetDirectory = KGlobal::dirs()->findResource("appdata", "presets/");
+
+ QString language = KGlobal::locale()->language();
+
+ QString presetFileName = QString("%1/presets-%2.xml")
+ .arg(m_presetDirectory).arg(language);
+
+ if (!QFileInfo(presetFileName).isReadable()) {
+
+ RG_DEBUG << "Failed to open " << presetFileName << endl;
+
+ language.replace(QRegExp("_.*$"), "");
+ presetFileName = QString("%1/presets-%2.xml")
+ .arg(m_presetDirectory).arg(language);
+
+ if (!QFileInfo(presetFileName).isReadable()) {
+
+ RG_DEBUG << "Failed to open " << presetFileName << endl;
+
+ presetFileName = QString("%1/presets.xml")
+ .arg(m_presetDirectory);
+
+ if (!QFileInfo(presetFileName).isReadable()) {
+
+ RG_DEBUG << "Failed to open " << presetFileName << endl;
+
+ throw PresetFileReadFailed
+ (qstrtostr(i18n("Can't open preset file %1").
+ arg(presetFileName)));
+ }
+ }
+ }
+
+ QFile presetFile(presetFileName);
+
+ QXmlInputSource source(presetFile);
+ QXmlSimpleReader reader;
+ reader.setContentHandler(this);
+ reader.setErrorHandler(this);
+ bool ok = reader.parse(source);
+ presetFile.close();
+
+ if (!ok) {
+ throw PresetFileReadFailed(qstrtostr(m_errorString));
+ }
+}
+
+PresetGroup::~PresetGroup()
+{
+ //!!! do I have anything to do here?
+}
+
+bool
+PresetGroup::startElement(const QString &, const QString &,
+ const QString &qName,
+ const QXmlAttributes &attributes)
+{
+ QString lcName = qName.lower();
+
+ // RG_DEBUG << "PresetGroup::startElement: processing starting element: " << lcName << endl;
+
+ if (lcName == "category") {
+
+ QString s = attributes.value("name");
+ if (s) {
+ m_elCategoryName = s;
+ // increment the current category number
+ m_lastCategory = m_currentCategory;
+ m_currentCategory++;
+
+ // reset the instrument counter going into the new category
+ m_lastInstrument = -1;
+ m_currentInstrument = -1;
+
+ RG_DEBUG << "PresetGroup::startElement: adding category " << m_elCategoryName << " last: "
+ << m_lastCategory << " curr: " << m_currentCategory << endl;
+
+ // add new CategoryElement to m_categories, in order to contain
+ // subsequent PresetElements
+ CategoryElement ce(m_elCategoryName);
+ m_categories.push_back(ce);
+ }
+
+ } else if (lcName == "instrument") {
+
+ QString s = attributes.value("name");
+ if (s) {
+ m_elInstrumentName = s;
+ m_name = true;
+
+ // increment the current instrument number
+ m_lastInstrument = m_currentInstrument;
+ m_currentInstrument++;
+ }
+
+ } else if (lcName == "clef") {
+ QString s = attributes.value("type");
+ if (s) {
+ m_elClef = clefNameToClefIndex(s);
+ m_clef = true;
+ }
+ } else if (lcName == "transpose") {
+ QString s = attributes.value("value");
+ if (s) {
+ m_elTranspose = s.toInt();
+ m_transpose = true;
+ }
+
+ } else if (lcName == "range") {
+ QString s = attributes.value("class");
+
+ if (s == "amateur") {
+ s = attributes.value("low");
+ if (s) {
+ m_elLowAm = s.toInt();
+ m_amateur = true;
+ }
+
+ s = attributes.value("high");
+ if (s && m_amateur) {
+ m_elHighAm = s.toInt();
+ } else {
+ return false;
+ }
+
+ } else if (s == "professional") {
+ s = attributes.value("low");
+ if (s) {
+ m_pro = true;
+ m_elLowPro = s.toInt();
+ }
+
+ s = attributes.value("high");
+ if (s && m_pro) {
+ m_elHighPro = s.toInt();
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ // RG_DEBUG << "PresetGroup::startElement(): accumulating flags:" << endl
+ // << " name: " << (m_name ? "true" : "false") << endl
+ // << " clef: " << (m_clef ? "true" : "false") << endl
+ // << "transpose: " << (m_transpose ? "true" : "false") << endl
+ // << " am. rng: " << (m_amateur ? "true" : "false") << endl
+ // << " pro rng: " << (m_pro ? "true" : "false") << endl;
+
+ // once we have assembled all the bits, create a new PresetElement
+ if (m_name && m_clef && m_transpose && m_amateur && m_pro) {
+ m_categories[m_currentCategory].addPreset(m_elInstrumentName,
+ m_elClef,
+ m_elTranspose,
+ m_elHighAm,
+ m_elLowAm,
+ m_elHighPro,
+ m_elLowPro);
+ // increment the current instrument
+ //!!! (is this ever going to be needed?)
+ m_lastInstrument = m_currentInstrument;
+ m_currentInstrument++;
+
+ // reset the "do we have a whole preset yet?" flags
+ m_name = false;
+ m_clef = false;
+ m_transpose = false;
+ m_amateur = false;
+ m_pro = false;
+ }
+
+ return true;
+
+} // startElement
+
+bool
+PresetGroup::error(const QXmlParseException& exception)
+{
+ RG_DEBUG << "PresetGroup::error(): jubilation and glee, we have an error, whee!" << endl;
+
+ 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
+PresetGroup::fatalError(const QXmlParseException& exception)
+{
+ RG_DEBUG << "PresetGroup::fatalError(): double your jubilation, and triple your glee, a fatal error doth it be!" << endl;
+ 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);
+}
+
+}
diff --git a/src/gui/general/PresetGroup.h b/src/gui/general/PresetGroup.h
new file mode 100644
index 0000000..476a878
--- /dev/null
+++ b/src/gui/general/PresetGroup.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ This file is Copyright 2006
+ D. Michael McIntyre <dmmcintyr@users.sourceforge.net>
+
+ 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_PRESETGROUP_H_
+#define _RG_PRESETGROUP_H_
+
+#include "base/Exception.h"
+#include "CategoryElement.h"
+#include <qstring.h>
+#include <qxml.h>
+
+
+class QXmlParseException;
+class QXmlAttributes;
+
+
+namespace Rosegarden
+{
+
+/*
+ * Read presets.xml from disk and store a collection of PresetElement objects
+ * which can then be used to populate and run the chooser GUI
+ */
+class PresetGroup : public QXmlDefaultHandler
+{
+public:
+ typedef Exception PresetFileReadFailed;
+
+ PresetGroup(); // load and parse the XML mapping file
+ ~PresetGroup();
+
+ CategoriesContainer getCategories() { return m_categories; }
+ //CategoryElement getCategoryByIndex(int index) { return m_categories [index]; }
+
+ // Xml handler methods:
+
+ virtual bool startElement (const QString& namespaceURI, const QString& localName,
+ const QString& qName, const QXmlAttributes& atts);
+
+ bool error(const QXmlParseException& exception);
+ bool fatalError(const QXmlParseException& exception);
+
+ // I don't think I have anything to do with this, but it must return true?
+// bool characters(const QString &) { return true; }
+
+private:
+
+ //--------------- Data members ---------------------------------
+ CategoriesContainer m_categories;
+
+ // For use when reading the XML file:
+ QString m_errorString;
+ QString m_presetDirectory;
+
+ QString m_elCategoryName;
+ QString m_elInstrumentName;
+ int m_elClef;
+ int m_elTranspose;
+ int m_elLowAm;
+ int m_elHighAm;
+ int m_elLowPro;
+ int m_elHighPro;
+
+ int m_lastCategory;
+ int m_currentCategory;
+ int m_lastInstrument;
+ int m_currentInstrument;
+
+ bool m_name;
+ bool m_clef;
+ bool m_transpose;
+ bool m_amateur;
+ bool m_pro;
+
+}; // PresetGroup
+
+
+}
+
+#endif
diff --git a/src/gui/general/PresetHandlerDialog.cpp b/src/gui/general/PresetHandlerDialog.cpp
new file mode 100644
index 0000000..6081f85
--- /dev/null
+++ b/src/gui/general/PresetHandlerDialog.cpp
@@ -0,0 +1,281 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ This file is Copyright 2006
+ D. Michael McIntyre <dmmcintyr@users.sourceforge.net>
+
+ 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 "PresetHandlerDialog.h"
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "document/ConfigGroups.h"
+#include "CategoryElement.h"
+#include "PresetElement.h"
+#include "PresetGroup.h"
+#include <kcombobox.h>
+#include <kconfig.h>
+#include <kdialogbase.h>
+#include <qbuttongroup.h>
+#include <qdialog.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+PresetHandlerDialog::PresetHandlerDialog(QWidget *parent, bool fromNotation)
+ : KDialogBase(parent, "presethandlerdialog", true, i18n("Load track parameters preset"), Ok | Cancel, Ok),
+ m_config(kapp->config()),
+ m_fromNotation(fromNotation)
+{
+ m_presets = new PresetGroup();
+ m_categories = m_presets->getCategories();
+ if (m_fromNotation) setCaption(i18n("Convert notation for..."));
+
+ initDialog();
+}
+
+PresetHandlerDialog::~PresetHandlerDialog()
+{
+ // delete m_presets
+ if (m_presets != NULL) {
+ delete m_presets;
+ }
+}
+
+void
+PresetHandlerDialog::initDialog()
+{
+ RG_DEBUG << "PresetHandlerDialog::initDialog()" << endl;
+
+ QVBox *vBox = makeVBoxMainWidget();
+
+ QFrame *frame = new QFrame(vBox);
+
+ QGridLayout *layout = new QGridLayout(frame, 6, 5, 10, 5);
+
+ QLabel *title = new QLabel(i18n("Select preset track parameters for:"), frame);
+ if (m_fromNotation) title->setText(i18n("Create appropriate notation for:"));
+
+ QLabel *catlabel = new QLabel(i18n("Category"), frame);
+ m_categoryCombo = new KComboBox(frame);
+
+ QLabel *inslabel = new QLabel(i18n("Instrument"), frame);
+ m_instrumentCombo = new KComboBox(frame);
+
+ QLabel *plylabel = new QLabel(i18n("Player Ability"), frame);
+ m_playerCombo = new KComboBox(frame);
+ m_playerCombo->insertItem(i18n("Amateur"));
+ m_playerCombo->insertItem(i18n("Professional"));
+
+ QGroupBox *scopeBox = new QButtonGroup
+ (1, Horizontal, i18n("Scope"), frame);
+ if (m_fromNotation) {
+ QRadioButton *onlySelectedSegments = new
+ QRadioButton(i18n("Only selected segments"), scopeBox);
+ m_convertAllSegments = new
+ QRadioButton(i18n("All segments in this track"), scopeBox);
+ onlySelectedSegments->setChecked(true);
+ }
+ else {
+ QRadioButton *onlyNewSegments = new
+ QRadioButton(i18n("Only for new segments"), scopeBox);
+ m_convertSegments = new
+ QRadioButton(i18n("Convert existing segments"), scopeBox);
+ onlyNewSegments->setChecked(true);
+ }
+
+ layout->addMultiCellWidget(title, 0, 0, 0, 1, AlignLeft);
+ layout->addWidget(catlabel, 1, 0, AlignRight);
+ layout->addWidget(m_categoryCombo, 1, 1);
+ layout->addWidget(inslabel, 2, 0, AlignRight);
+ layout->addWidget(m_instrumentCombo, 2, 1);
+ layout->addWidget(plylabel, 3, 0, AlignRight);
+ layout->addWidget(m_playerCombo, 3, 1);
+ layout->addMultiCellWidget(scopeBox, 4, 4, 0, 1, AlignLeft);
+
+ populateCategoryCombo();
+ // try to set to same category used previously
+ m_config->setGroup(GeneralOptionsConfigGroup);
+ m_categoryCombo->setCurrentItem(m_config->readNumEntry("category_combo_index", 0));
+
+ // populate the instrument combo
+ slotCategoryIndexChanged(m_categoryCombo->currentItem());
+
+ // try to set to same instrument used previously
+ m_config->setGroup(GeneralOptionsConfigGroup);
+ m_instrumentCombo->setCurrentItem(m_config->readNumEntry("instrument_combo_index", 0));
+
+ // set to same player used previously (this one can't fail, unlike the
+ // others, because the contents of this combo are static)
+ m_playerCombo->setCurrentItem(m_config->readNumEntry("player_combo_index", 0));
+
+ if (m_fromNotation){
+ m_convertAllSegments->setChecked(m_config->readBoolEntry("convert_all_segments", 0));
+ }
+ else {
+ m_convertSegments->setChecked(m_config->readBoolEntry("convert_segments", 0));
+ }
+
+
+ connect(m_categoryCombo, SIGNAL(activated(int)),
+ SLOT(slotCategoryIndexChanged(int)));
+}
+
+QString
+PresetHandlerDialog::getName()
+{
+ return m_instrumentCombo->currentText();
+}
+
+int
+PresetHandlerDialog::getClef()
+{
+ PresetElement p = m_categories[m_categoryCombo->currentItem()].
+ getPresetByIndex(m_instrumentCombo->currentItem());
+
+ return p.getClef();
+}
+
+int
+PresetHandlerDialog::getTranspose()
+{
+ PresetElement p = m_categories[m_categoryCombo->currentItem()].
+ getPresetByIndex(m_instrumentCombo->currentItem());
+
+ return p.getTranspose();
+}
+
+int
+PresetHandlerDialog::getLowRange()
+{
+ PresetElement p = m_categories[m_categoryCombo->currentItem()].
+ getPresetByIndex(m_instrumentCombo->currentItem());
+ // 0 == amateur
+ // 1 == pro
+ if (m_playerCombo->currentItem() == 0) {
+ return p.getLowAm();
+ } else {
+ return p.getLowPro();
+ }
+}
+
+int
+PresetHandlerDialog::getHighRange()
+{
+ PresetElement p = m_categories[m_categoryCombo->currentItem()].
+ getPresetByIndex(m_instrumentCombo->currentItem());
+ // 0 == amateur
+ // 1 == pro
+ if (m_playerCombo->currentItem() == 0) {
+ return p.getHighAm();
+ } else {
+ return p.getHighPro();
+ }
+}
+
+bool
+PresetHandlerDialog::getConvertAllSegments()
+{
+ if (m_fromNotation) {
+ return m_convertAllSegments && m_convertAllSegments->isChecked();
+ }
+ else {
+ return m_convertSegments && m_convertSegments->isChecked();
+ }
+}
+
+bool
+PresetHandlerDialog::getConvertOnlySelectedSegments()
+{
+ if (m_fromNotation) {
+ return m_convertAllSegments && !m_convertAllSegments->isChecked();
+ }
+ else {
+ return false;
+ }
+}
+
+void
+PresetHandlerDialog::populateCategoryCombo()
+{
+ RG_DEBUG << "PresetHandlerDialog::populateCategoryCombo()" << endl;
+
+ for (CategoriesContainer::iterator i = m_categories.begin();
+ i != m_categories.end(); ++i) {
+
+ RG_DEBUG << " adding category: " << (*i).getName() << endl;
+
+ m_categoryCombo->insertItem((*i).getName());
+ }
+}
+
+void
+PresetHandlerDialog::slotCategoryIndexChanged(int index)
+{
+ RG_DEBUG << "PresetHandlerDialog::slotCategoryIndexChanged(" << index << ")" << endl;
+
+ CategoryElement e = m_categories[index];
+ ElementContainer c = e.getPresets();
+
+ m_instrumentCombo->clear();
+
+ for (ElementContainer::iterator i = c.begin();
+ i != c.end(); ++i) {
+
+ RG_DEBUG << " adding instrument: " << (*i).getName() << endl;
+
+ m_instrumentCombo->insertItem((*i).getName());
+ }
+
+}
+
+void
+PresetHandlerDialog::slotOk()
+{
+ m_config->setGroup(GeneralOptionsConfigGroup);
+ m_config->writeEntry("category_combo_index", m_categoryCombo->currentItem());
+ m_config->writeEntry("instrument_combo_index", m_instrumentCombo->currentItem());
+ m_config->writeEntry("player_combo_index", m_playerCombo->currentItem());
+
+ if (m_fromNotation) {
+ m_config->writeEntry("convert_all_segments", m_convertAllSegments->isChecked());
+ }
+ else {
+ m_config->writeEntry("convert_segments", m_convertSegments->isChecked());
+ }
+
+ QDialog::accept();
+}
+
+}
+#include "PresetHandlerDialog.moc"
diff --git a/src/gui/general/PresetHandlerDialog.h b/src/gui/general/PresetHandlerDialog.h
new file mode 100644
index 0000000..879ddca
--- /dev/null
+++ b/src/gui/general/PresetHandlerDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ This file is Copyright 2006
+ D. Michael McIntyre <dmmcintyr@users.sourceforge.net>
+
+ 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_PRESETHANDLERDIALOG_H_
+#define _RG_PRESETHANDLERDIALOG_H_
+
+#include <kdialogbase.h>
+#include <qradiobutton.h>
+#include <qstring.h>
+#include "CategoryElement.h"
+
+class QWidget;
+class KConfig;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class PresetGroup;
+
+
+class PresetHandlerDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+
+ PresetHandlerDialog(QWidget* parent, bool fromNotation = false);
+ ~PresetHandlerDialog();
+
+ PresetGroup *m_presets;
+ CategoriesContainer m_categories;
+
+ KConfig *m_config;
+ bool m_fromNotation;
+
+ //-------[ accessor functions ]------------------------
+
+ QString getName();
+
+ int getClef();
+ int getTranspose();
+ int getLowRange();
+ int getHighRange();
+ bool getConvertAllSegments();
+ bool getConvertOnlySelectedSegments();
+
+protected:
+
+ //--------[ member functions ]-------------------------
+
+ // initialize the dialog
+ void initDialog();
+
+ // populate the category combo
+ void populateCategoryCombo();
+
+
+ //---------[ data members ]-----------------------------
+
+ KComboBox *m_categoryCombo;
+ KComboBox *m_instrumentCombo;
+ KComboBox *m_playerCombo;
+ QRadioButton *m_convertSegments;
+ QRadioButton *m_convertAllSegments;
+
+protected slots:
+
+ // de-populate and re-populate the Instrument combo when the category
+ // changes.
+ void slotCategoryIndexChanged(int index);
+
+ // write out settings to kconfig data for next time and call accept()
+ void slotOk();
+
+}; // PresetHandlerDialog
+
+
+}
+
+#endif
diff --git a/src/gui/general/ProgressReporter.cpp b/src/gui/general/ProgressReporter.cpp
new file mode 100644
index 0000000..0d9e896
--- /dev/null
+++ b/src/gui/general/ProgressReporter.cpp
@@ -0,0 +1,53 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ProgressReporter.h"
+
+#include <qobject.h>
+
+
+namespace Rosegarden
+{
+
+ProgressReporter::ProgressReporter(QObject* parent, const char* name)
+ : QObject(parent, name), m_isCancelled(false)
+{}
+
+
+void ProgressReporter::throwIfCancelled()
+{
+ if (m_isCancelled) {
+ m_isCancelled = false;
+ throw Cancelled();
+ }
+}
+
+void ProgressReporter::slotCancel()
+{
+ m_isCancelled = true;
+};
+
+}
+#include "ProgressReporter.moc"
diff --git a/src/gui/general/ProgressReporter.h b/src/gui/general/ProgressReporter.h
new file mode 100644
index 0000000..d8aa306
--- /dev/null
+++ b/src/gui/general/ProgressReporter.h
@@ -0,0 +1,80 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_PROGRESSREPORTER_H_
+#define _RG_PROGRESSREPORTER_H_
+
+#include <qobject.h>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+class ProgressReporter : public QObject
+{
+ Q_OBJECT
+public:
+ ProgressReporter(QObject* parent, const char* name = 0);
+
+ // exception class for cancellations
+ class Cancelled { };
+
+protected:
+ /**
+ * Call this at appropriate times if you know Qt isn't in the stack
+ */
+ void throwIfCancelled();
+
+ /*
+ We have to use these accessors rather than throwing directly
+ from slotCancel() because Qt is generally compiled without
+ exception support, so we can't throw from a slot.
+ */
+ bool isOperationCancelled() const { return m_isCancelled; }
+// void resetOperationCancelledState() { m_isCancelled = false; }
+
+protected slots:
+ virtual void slotCancel();
+
+signals:
+ /// Report progress
+ void setProgress(int);
+ void incrementProgress(int);
+ void setOperationName(QString);
+
+protected:
+ //--------------- Data members ---------------------------------
+ bool m_isCancelled;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/general/RosegardenCanvasView.cpp b/src/gui/general/RosegardenCanvasView.cpp
new file mode 100644
index 0000000..a829aac
--- /dev/null
+++ b/src/gui/general/RosegardenCanvasView.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "RosegardenCanvasView.h"
+
+#include "misc/Debug.h"
+#include "gui/general/CanvasItemGC.h"
+#include <qcanvas.h>
+#include <qcursor.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qscrollbar.h>
+#include <qsize.h>
+#include <qsizepolicy.h>
+#include <qtimer.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+RosegardenCanvasView::RosegardenCanvasView(QCanvas* canvas,
+ QWidget* parent,
+ const char* name, WFlags f)
+ : QCanvasView(canvas, parent, name, f),
+ m_bottomWidget(0),
+ m_currentBottomWidgetHeight( -1),
+ m_leftWidget(0),
+ m_smoothScroll(true),
+ m_smoothScrollTimeInterval(DefaultSmoothScrollTimeInterval),
+ m_minDeltaScroll(DefaultMinDeltaScroll),
+ m_autoScrollTime(InitialScrollTime),
+ m_autoScrollAccel(InitialScrollAccel),
+ m_autoScrollXMargin(0),
+ m_autoScrollYMargin(0),
+ m_currentScrollDirection(None),
+ m_scrollDirectionConstraint(NoFollow),
+ m_autoScrolling(false)
+{
+ setDragAutoScroll(true);
+ connect( &m_autoScrollTimer, SIGNAL( timeout() ),
+ this, SLOT( doAutoScroll() ) );
+}
+
+void RosegardenCanvasView::fitWidthToContents()
+{
+ QRect allItemsBoundingRect;
+
+ QCanvasItemList items = canvas()->allItems();
+
+ QCanvasItemList::Iterator it;
+
+ for (it = items.begin(); it != items.end(); ++it) {
+ allItemsBoundingRect |= (*it)->boundingRect();
+ }
+
+ QSize currentSize = canvas()->size();
+ resizeContents(allItemsBoundingRect.width(), currentSize.height());
+}
+
+void RosegardenCanvasView::setBottomFixedWidget(QWidget* w)
+{
+ m_bottomWidget = w;
+ if (m_bottomWidget) {
+ int lww = m_leftWidget ? m_leftWidget->sizeHint().width() : 0;
+ m_bottomWidget->reparent(this, 0, QPoint(0, 0));
+ m_bottomWidget->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
+ setMargins(lww, 0, 0, m_bottomWidget->sizeHint().height());
+ }
+}
+
+void RosegardenCanvasView::slotUpdate()
+{
+ CanvasItemGC::gc();
+ canvas()->update();
+}
+
+// Smooth scroll checks
+//
+
+const int RosegardenCanvasView::AutoscrollMargin = 16;
+const int RosegardenCanvasView::InitialScrollTime = 30;
+const int RosegardenCanvasView::InitialScrollAccel = 5;
+const int RosegardenCanvasView::MaxScrollDelta = 100; // max a.scroll speed
+const double RosegardenCanvasView::ScrollAccelValue = 1.04;// acceleration rate
+
+const int RosegardenCanvasView::DefaultSmoothScrollTimeInterval = 10;
+const double RosegardenCanvasView::DefaultMinDeltaScroll = 1.2;
+
+void RosegardenCanvasView::startAutoScroll()
+{
+ // RG_DEBUG << "RosegardenCanvasView::startAutoScroll()\n";
+
+ if ( !m_autoScrollTimer.isActive() ) {
+ m_autoScrollTime = InitialScrollTime;
+ m_autoScrollAccel = InitialScrollAccel;
+ m_autoScrollTimer.start( m_autoScrollTime );
+ }
+
+ QPoint autoScrollStartPoint = viewport()->mapFromGlobal( QCursor::pos() );
+ m_autoScrollYMargin = autoScrollStartPoint.y() / 10;
+ m_autoScrollXMargin = autoScrollStartPoint.x() / 10;
+
+ m_autoScrolling = true;
+}
+
+void RosegardenCanvasView::startAutoScroll(int directionConstraint)
+{
+ setScrollDirectionConstraint(directionConstraint);
+ startAutoScroll();
+}
+
+void RosegardenCanvasView::stopAutoScroll()
+{
+ // RG_DEBUG << "RosegardenCanvasView::stopAutoScroll()\n";
+
+ m_autoScrollTimer.stop();
+ m_minDeltaScroll = DefaultMinDeltaScroll;
+ m_currentScrollDirection = None;
+
+ m_autoScrolling = false;
+}
+
+void RosegardenCanvasView::doAutoScroll()
+{
+ // RG_DEBUG << "RosegardenCanvasView::doAutoScroll()\n";
+
+ QPoint p = viewport()->mapFromGlobal( QCursor::pos() );
+ QPoint dp = p - m_previousP;
+ m_previousP = p;
+
+ m_autoScrollTimer.start( m_autoScrollTime );
+ ScrollDirection scrollDirection = None;
+
+ int dx = 0, dy = 0;
+ if (m_scrollDirectionConstraint & FollowVertical) {
+ if ( p.y() < m_autoScrollYMargin ) {
+ dy = -(int(m_minDeltaScroll));
+ scrollDirection = Top;
+ } else if ( p.y() > visibleHeight() - m_autoScrollYMargin ) {
+ dy = + (int(m_minDeltaScroll));
+ scrollDirection = Bottom;
+ }
+ }
+ bool startDecelerating = false;
+ if (m_scrollDirectionConstraint & FollowHorizontal) {
+
+ // RG_DEBUG << "p.x() : " << p.x() << " - visibleWidth : " << visibleWidth() << " - autoScrollXMargin : " << m_autoScrollXMargin << endl;
+
+ if ( p.x() < m_autoScrollXMargin ) {
+ if ( dp.x() > 0 ) {
+ startDecelerating = true;
+ m_minDeltaScroll /= ScrollAccelValue;
+ }
+ dx = -(int(m_minDeltaScroll));
+ scrollDirection = Left;
+ } else if ( p.x() > visibleWidth() - m_autoScrollXMargin ) {
+ if ( dp.x() < 0 ) {
+ startDecelerating = true;
+ m_minDeltaScroll /= ScrollAccelValue;
+ }
+ dx = + (int(m_minDeltaScroll));
+ scrollDirection = Right;
+ }
+ }
+
+ // RG_DEBUG << "dx: " << dx << ", dy: " << dy << endl;
+
+ if ( (dx || dy) &&
+ ((scrollDirection == m_currentScrollDirection) || (m_currentScrollDirection == None)) ) {
+ scrollBy(dx, dy);
+ if ( startDecelerating )
+ m_minDeltaScroll /= ScrollAccelValue;
+ else
+ m_minDeltaScroll *= ScrollAccelValue;
+ if (m_minDeltaScroll > MaxScrollDelta )
+ m_minDeltaScroll = MaxScrollDelta;
+ m_currentScrollDirection = scrollDirection;
+
+ } else {
+ // Don't automatically stopAutoScroll() here, the mouse button
+ // is presumably still pressed.
+ m_minDeltaScroll = DefaultMinDeltaScroll;
+ m_currentScrollDirection = None;
+ }
+
+}
+
+bool RosegardenCanvasView::isTimeForSmoothScroll()
+{
+ if (m_smoothScroll) {
+ int ta = m_scrollAccelerationTimer.elapsed();
+ int t = m_scrollTimer.elapsed();
+
+ // RG_DEBUG << "t = " << t << ", ta = " << ta << ", int " << m_smoothScrollTimeInterval << ", delta " << m_minDeltaScroll << endl;
+
+ if (t < m_smoothScrollTimeInterval) {
+
+ return false;
+
+ } else {
+
+ if (ta > 300) {
+ // reset smoothScrollTimeInterval
+ m_smoothScrollTimeInterval = DefaultSmoothScrollTimeInterval;
+ m_minDeltaScroll = DefaultMinDeltaScroll;
+ m_scrollAccelerationTimer.restart();
+ } else if (ta > 50) {
+ // m_smoothScrollTimeInterval /= 2;
+ m_minDeltaScroll *= 1.08;
+ m_scrollAccelerationTimer.restart();
+ }
+
+ m_scrollTimer.restart();
+ return true;
+ }
+ }
+
+ return true;
+}
+
+void RosegardenCanvasView::slotScrollHoriz(int hpos)
+{
+ QScrollBar* hbar = getMainHorizontalScrollBar();
+ int currentContentYPos = contentsY();
+
+ /* Lots of performance hitting debug
+ RG_DEBUG << "RosegardenCanvasView::slotScrollHoriz: hpos is " << hpos
+ << ", contentsX is " << contentsX() << ", visibleWidth is "
+ << visibleWidth() << endl;
+ */
+
+ if (hpos == 0) {
+
+ // returning to zero
+ // hbar->setValue(0);
+ setContentsPos(0, currentContentYPos);
+
+ } else if (hpos > (contentsX() +
+ visibleWidth() * 1.6) ||
+ hpos < (contentsX() -
+ visibleWidth() * 0.7)) {
+
+ // miles off one side or the other
+ // hbar->setValue(hpos - int(visibleWidth() * 0.4));
+ setContentsPos(hpos - int(visibleWidth() * 0.4), currentContentYPos);
+
+ } else if (hpos > (contentsX() +
+ visibleWidth() * 0.9)) {
+
+ // moving off the right hand side of the view
+ // hbar->setValue(hbar->value() + int(visibleWidth() * 0.6));
+ setContentsPos(hbar->value() + int(visibleWidth() * 0.6), currentContentYPos);
+
+ } else if (hpos < (contentsX() +
+ visibleWidth() * 0.1)) {
+
+ // moving off the left
+ // hbar->setValue(hbar->value() - int(visibleWidth() * 0.6));
+ setContentsPos(hbar->value() - int(visibleWidth() * 0.6), currentContentYPos);
+ }
+}
+
+void RosegardenCanvasView::slotScrollHorizSmallSteps(int hpos)
+{
+ QScrollBar* hbar = getMainHorizontalScrollBar();
+ int currentContentYPos = contentsY();
+
+ int diff = 0;
+
+ if (hpos == 0) {
+
+ // returning to zero
+ // hbar->setValue(0);
+ setContentsPos(0, currentContentYPos);
+
+ } else if ((diff = int(hpos - (contentsX() +
+ visibleWidth() * 0.90))) > 0) {
+
+ // moving off the right hand side of the view
+
+ int delta = diff / 6;
+ int diff10 = std::min(diff, (int)m_minDeltaScroll);
+ delta = std::max(delta, diff10);
+
+ // hbar->setValue(hbar->value() + delta);
+ setContentsPos(hbar->value() + delta, currentContentYPos);
+
+ } else if ((diff = int(hpos - (contentsX() +
+ visibleWidth() * 0.10))) < 0) {
+ // moving off the left
+
+ int delta = -diff / 6;
+ int diff10 = std::min( -diff, (int)m_minDeltaScroll);
+ delta = std::max(delta, diff10);
+
+ // hbar->setValue(hbar->value() - delta);
+ setContentsPos(hbar->value() - delta, currentContentYPos);
+
+ }
+}
+
+void RosegardenCanvasView::slotScrollVertSmallSteps(int vpos)
+{
+ QScrollBar* vbar = verticalScrollBar();
+
+ // RG_DEBUG << "RosegardenCanvasView::slotScrollVertSmallSteps: vpos is " << vpos << ", contentsY is " << contentsY() << ", visibleHeight is " << visibleHeight() << endl;
+
+ // As a special case (or hack), ignore any request made before we've
+ // actually been rendered and sized
+ if (visibleHeight() <= 1)
+ return ;
+
+ int diff = 0;
+
+ if (vpos == 0) {
+
+ // returning to zero
+ vbar->setValue(0);
+
+ } else if ((diff = int(vpos - (contentsY() +
+ visibleHeight() * 0.90))) > 0) {
+
+ // moving off up
+
+ int delta = diff / 6;
+ int diff10 = std::min(diff, (int)m_minDeltaScroll);
+ delta = std::max(delta, diff10);
+
+ vbar->setValue(vbar->value() + diff);
+
+ } else if ((diff = int(vpos - (contentsY() +
+ visibleHeight() * 0.10))) < 0) {
+
+ // moving off down
+
+ int delta = -diff / 6;
+ int diff10 = std::min( -diff, (int)m_minDeltaScroll);
+ delta = std::max(delta, diff10);
+
+ vbar->setValue(vbar->value() - delta);
+
+ }
+}
+
+void RosegardenCanvasView::slotScrollVertToTop(int vpos)
+{
+ QScrollBar* vbar = verticalScrollBar();
+ if (vpos < visibleHeight() / 3)
+ vbar->setValue(0);
+ else
+ vbar->setValue(vpos - visibleHeight() / 5);
+}
+
+void RosegardenCanvasView::slotSetScrollPos(const QPoint &pos)
+{
+ getMainHorizontalScrollBar()->setValue(pos.x());
+ verticalScrollBar()->setValue(pos.y());
+}
+
+void RosegardenCanvasView::resizeEvent(QResizeEvent* e)
+{
+ QCanvasView::resizeEvent(e);
+ if (!horizontalScrollBar()->isVisible())
+ updateBottomWidgetGeometry();
+ updateLeftWidgetGeometry();
+}
+
+void RosegardenCanvasView::setHBarGeometry(QScrollBar &hbar, int x, int y, int w, int h)
+{
+ QCanvasView::setHBarGeometry(hbar, x, y, w, h);
+ updateBottomWidgetGeometry();
+}
+
+void RosegardenCanvasView::updateBottomWidgetGeometry()
+{
+ if (!m_bottomWidget)
+ return ;
+
+ int bottomWidgetHeight = m_bottomWidget->sizeHint().height();
+
+ int leftWidgetWidth = 0;
+ if (m_leftWidget && m_leftWidget->isVisible()) {
+ QScrollView * qsv = dynamic_cast<QScrollView *>(m_leftWidget);
+ leftWidgetWidth = qsv->contentsWidth()+2;
+ qsv->setFixedWidth(leftWidgetWidth);
+ }
+
+ setMargins(leftWidgetWidth, 0, 0, bottomWidgetHeight);
+
+ QRect r = frameRect();
+ int hScrollBarHeight = 0;
+ if (horizontalScrollBar()->isVisible())
+ hScrollBarHeight = horizontalScrollBar()->height() + 2;
+ // + 2 offset : needed to preserve border shadow
+
+ int vScrollBarWidth = 0;
+ if (verticalScrollBar()->isVisible())
+ vScrollBarWidth = verticalScrollBar()->width();
+
+ m_bottomWidget->setGeometry(r.x() + leftWidgetWidth,
+ r.y() + r.height() - bottomWidgetHeight - hScrollBarHeight,
+ r.width() - vScrollBarWidth - leftWidgetWidth,
+ bottomWidgetHeight);
+
+ if (bottomWidgetHeight != m_currentBottomWidgetHeight) {
+ emit bottomWidgetHeightChanged(bottomWidgetHeight);
+ m_currentBottomWidgetHeight = bottomWidgetHeight;
+ }
+}
+
+void RosegardenCanvasView::wheelEvent(QWheelEvent *e)
+{
+ if (e->state() & ControlButton) {
+ if (e->delta() > 0)
+ emit zoomIn();
+ else if (e->delta() < 0)
+ emit zoomOut();
+ return ;
+ }
+ QCanvasView::wheelEvent(e);
+}
+
+void RosegardenCanvasView::setLeftFixedWidget(QWidget* w)
+{
+ m_leftWidget = w;
+ if (m_leftWidget) {
+ int bwh = m_bottomWidget ? m_bottomWidget->sizeHint().height() : 0;
+ m_leftWidget->reparent(this, 0, QPoint(0, 0));
+ m_leftWidget->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
+ setMargins(m_leftWidget->sizeHint().width(), 0, 0, bwh);
+ }
+}
+
+void RosegardenCanvasView::updateLeftWidgetGeometry()
+{
+ if (!m_leftWidget)
+ return ;
+
+ int leftWidgetWidth = 0;
+ if (m_leftWidget->isVisible()) {
+ QScrollView * qsv = dynamic_cast<QScrollView *>(m_leftWidget);
+ leftWidgetWidth = qsv->contentsWidth() + 2;
+ }
+ m_leftWidget->setFixedWidth(leftWidgetWidth);
+
+ int bottomWidgetHeight = m_bottomWidget ?
+ m_bottomWidget->sizeHint().height() : 0;
+
+ setMargins(leftWidgetWidth, 0, 0, bottomWidgetHeight);
+
+ QRect r = frameRect();
+ int hScrollBarHeight = 0;
+ if (horizontalScrollBar()->isVisible())
+ hScrollBarHeight = horizontalScrollBar()->height() + 2;
+ // + 2 offset : needed to preserve border shadow
+
+ m_leftWidget->setFixedHeight(r.height() - bottomWidgetHeight - hScrollBarHeight);
+}
+
+
+}
+#include "RosegardenCanvasView.moc"
diff --git a/src/gui/general/RosegardenCanvasView.h b/src/gui/general/RosegardenCanvasView.h
new file mode 100644
index 0000000..509c1aa
--- /dev/null
+++ b/src/gui/general/RosegardenCanvasView.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ROSEGARDENCANVASVIEW_H_
+#define _RG_ROSEGARDENCANVASVIEW_H_
+
+#include <qpoint.h>
+#include <qtimer.h>
+#include <qcanvas.h>
+#include <qdatetime.h>
+#include <qwmatrix.h>
+
+class QWidget;
+class QWheelEvent;
+class QScrollBar;
+class QResizeEvent;
+
+
+namespace Rosegarden
+{
+
+/**
+ * A QCanvasView with an auxiliary horiz. scrollbar
+ * That scrollbar should be provided by the parent widget
+ * (typically an EditView). The RosegardenCanvasView keeps
+ * the auxilliary horiz. scrollbar range in sync with the
+ * one of its own scrollbar with slotUpdate().
+ */
+
+class RosegardenCanvasView : public QCanvasView
+{
+ Q_OBJECT
+public:
+ RosegardenCanvasView(QCanvas*,
+ QWidget* parent=0, const char* name=0, WFlags f=0);
+
+ /**
+ * EditTool::handleMouseMove() returns a OR-ed combination of these
+ * to indicate which direction to scroll to
+ */
+ enum {
+ NoFollow = 0x0,
+ FollowHorizontal = 0x1,
+ FollowVertical = 0x2
+ };
+
+ /**
+ * Sets the canvas width to be exactly the width needed to show
+ * all the items
+ */
+ void fitWidthToContents();
+
+ /**
+ * Sets the widget which will be between the scrollable part of the view
+ * and the horizontal scrollbar
+ */
+ void setBottomFixedWidget(QWidget*);
+
+ void updateBottomWidgetGeometry();
+
+ /**
+ * Sets the widget which will be between the scrollable part of the view
+ * and the left edge of the view.
+ */
+ void setLeftFixedWidget(QWidget*);
+
+ void updateLeftWidgetGeometry();
+
+ /// Map a point with the inverse world matrix
+ QPoint inverseMapPoint(const QPoint& p) { return inverseWorldMatrix().map(p); }
+
+ void setSmoothScroll(bool s) { m_smoothScroll = s; }
+
+ bool isTimeForSmoothScroll();
+
+ void setScrollDirectionConstraint(int d) { m_scrollDirectionConstraint = d; }
+
+ bool isAutoScrolling() const { return m_autoScrolling; }
+
+ virtual void wheelEvent(QWheelEvent *);
+
+public slots:
+ /// Update the RosegardenCanvasView after a change of content
+ virtual void slotUpdate();
+
+ /**
+ * Scroll horizontally to make the given position visible,
+ * paging to as to get some visibility of the next screenful
+ * (for playback etc)
+ */
+ void slotScrollHoriz(int hpos);
+
+ /**
+ * Scroll horizontally to make the given position somewhat
+ * nearer to visible, scrolling by only "a small distance"
+ * at a time
+ */
+ void slotScrollHorizSmallSteps(int hpos);
+
+ /**
+ * Scroll vertically to make the given position somewhat
+ * nearer to visible, scrolling by only "a small distance"
+ * at a time
+ */
+ void slotScrollVertSmallSteps(int vpos);
+
+ /**
+ * Scroll vertically so as to place the given position
+ * somewhere near the top of the viewport.
+ */
+ void slotScrollVertToTop(int vpos);
+
+ /**
+ * Set the x and y scrollbars to a particular position
+ */
+ void slotSetScrollPos(const QPoint &);
+
+ void startAutoScroll();
+ void startAutoScroll(int directionConstraint);
+ void stopAutoScroll();
+ void doAutoScroll();
+
+signals:
+ void bottomWidgetHeightChanged(int);
+
+ void zoomIn();
+ void zoomOut();
+
+protected:
+
+ virtual void resizeEvent(QResizeEvent*);
+ virtual void setHBarGeometry(QScrollBar &hbar, int x, int y, int w, int h);
+
+ virtual QScrollBar* getMainHorizontalScrollBar() { return horizontalScrollBar(); }
+
+ //--------------- Data members ---------------------------------
+ enum ScrollDirection { None, Top, Bottom, Left, Right };
+
+
+ QWidget* m_bottomWidget;
+ int m_currentBottomWidgetHeight;
+
+ QWidget* m_leftWidget;
+
+ bool m_smoothScroll;
+ int m_smoothScrollTimeInterval;
+ float m_minDeltaScroll;
+ QTime m_scrollTimer;
+ QTime m_scrollAccelerationTimer;
+
+ QTimer m_autoScrollTimer;
+ int m_autoScrollTime;
+ int m_autoScrollAccel;
+ QPoint m_previousP;
+ int m_autoScrollXMargin;
+ int m_autoScrollYMargin;
+ ScrollDirection m_currentScrollDirection;
+ int m_scrollDirectionConstraint;
+ bool m_autoScrolling;
+
+ static const int DefaultSmoothScrollTimeInterval;
+ static const double DefaultMinDeltaScroll;
+
+ static const int AutoscrollMargin;
+ static const int InitialScrollTime;
+ static const int InitialScrollAccel;
+ static const int MaxScrollDelta;
+ static const double ScrollAccelValue;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/general/RosegardenScrollView.cpp b/src/gui/general/RosegardenScrollView.cpp
new file mode 100644
index 0000000..fbcaf79
--- /dev/null
+++ b/src/gui/general/RosegardenScrollView.cpp
@@ -0,0 +1,416 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "RosegardenScrollView.h"
+
+#include "misc/Debug.h"
+#include <qapplication.h>
+#include <qcursor.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qscrollbar.h>
+#include <qscrollview.h>
+#include <qsizepolicy.h>
+#include <qtimer.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+// Smooth scroll checks
+//
+
+const int RosegardenScrollView::AutoscrollMargin = 16;
+const int RosegardenScrollView::InitialScrollTime = 30;
+const int RosegardenScrollView::InitialScrollAccel = 5;
+const int RosegardenScrollView::MaxScrollDelta = 100; // max a.scroll speed
+const double RosegardenScrollView::ScrollAccelValue = 1.04;// acceleration rate
+
+RosegardenScrollView::RosegardenScrollView(QWidget* parent,
+ const char* name, WFlags f)
+ : QScrollView(parent, name, f),
+ m_bottomWidget(0),
+ m_currentBottomWidgetHeight( -1),
+ m_smoothScroll(true),
+ m_smoothScrollTimeInterval(DefaultSmoothScrollTimeInterval),
+ m_minDeltaScroll(DefaultMinDeltaScroll),
+ m_autoScrollTime(InitialScrollTime),
+ m_autoScrollAccel(InitialScrollAccel),
+ m_autoScrollXMargin(0),
+ m_autoScrollYMargin(0),
+ m_currentScrollDirection(None),
+ m_scrollDirectionConstraint(NoFollow),
+ m_autoScrolling(false)
+{
+ setDragAutoScroll(true);
+ connect( &m_autoScrollTimer, SIGNAL( timeout() ),
+ this, SLOT( doAutoScroll() ) );
+}
+
+void RosegardenScrollView::setBottomFixedWidget(QWidget* w)
+{
+ m_bottomWidget = w;
+ if (m_bottomWidget) {
+ m_bottomWidget->reparent(this, 0, QPoint(0, 0));
+ m_bottomWidget->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
+ setMargins(0, 0, 0, m_bottomWidget->sizeHint().height());
+ }
+}
+
+void RosegardenScrollView::startAutoScroll()
+{
+ // RG_DEBUG << "RosegardenScrollView::startAutoScroll()\n";
+
+ if ( !m_autoScrollTimer.isActive() ) {
+ m_autoScrollTime = InitialScrollTime;
+ m_autoScrollAccel = InitialScrollAccel;
+ m_autoScrollTimer.start( m_autoScrollTime );
+ }
+
+ QPoint autoScrollStartPoint = viewport()->mapFromGlobal( QCursor::pos() );
+ m_autoScrollYMargin = autoScrollStartPoint.y() / 10;
+ m_autoScrollXMargin = autoScrollStartPoint.x() / 10;
+
+ m_autoScrolling = true;
+}
+
+void RosegardenScrollView::startAutoScroll(int directionConstraint)
+{
+ setScrollDirectionConstraint(directionConstraint);
+ startAutoScroll();
+}
+
+void RosegardenScrollView::stopAutoScroll()
+{
+ // RG_DEBUG << "RosegardenScrollView::stopAutoScroll()\n";
+
+ m_autoScrollTimer.stop();
+ m_minDeltaScroll = DefaultMinDeltaScroll;
+ m_currentScrollDirection = None;
+
+ m_autoScrolling = false;
+}
+
+void RosegardenScrollView::doAutoScroll()
+{
+ // RG_DEBUG << "RosegardenScrollView::doAutoScroll()\n";
+
+ QPoint p = viewport()->mapFromGlobal( QCursor::pos() );
+ QPoint dp = p - m_previousP;
+ m_previousP = p;
+
+ m_autoScrollTimer.start( m_autoScrollTime );
+ ScrollDirection scrollDirection = None;
+
+ int dx = 0, dy = 0;
+ if (m_scrollDirectionConstraint & FollowVertical) {
+ if ( p.y() < m_autoScrollYMargin ) {
+ dy = -(int(m_minDeltaScroll));
+ scrollDirection = Top;
+ } else if ( p.y() > visibleHeight() - m_autoScrollYMargin ) {
+ dy = + (int(m_minDeltaScroll));
+ scrollDirection = Bottom;
+ }
+ }
+ bool startDecelerating = false;
+ if (m_scrollDirectionConstraint & FollowHorizontal) {
+
+ // RG_DEBUG << "p.x() : " << p.x() << " - visibleWidth : " << visibleWidth() << " - autoScrollXMargin : " << m_autoScrollXMargin << endl;
+
+ if ( p.x() < m_autoScrollXMargin ) {
+ if ( dp.x() > 0 ) {
+ startDecelerating = true;
+ m_minDeltaScroll /= ScrollAccelValue;
+ }
+ dx = -(int(m_minDeltaScroll));
+ scrollDirection = Left;
+ } else if ( p.x() > visibleWidth() - m_autoScrollXMargin ) {
+ if ( dp.x() < 0 ) {
+ startDecelerating = true;
+ m_minDeltaScroll /= ScrollAccelValue;
+ }
+ dx = + (int(m_minDeltaScroll));
+ scrollDirection = Right;
+ }
+ }
+
+ // RG_DEBUG << "dx: " << dx << ", dy: " << dy << endl;
+
+ if ( (dx || dy) &&
+ ((scrollDirection == m_currentScrollDirection) || (m_currentScrollDirection == None)) ) {
+ scrollBy(dx, dy);
+ if ( startDecelerating )
+ m_minDeltaScroll /= ScrollAccelValue;
+ else
+ m_minDeltaScroll *= ScrollAccelValue;
+ if (m_minDeltaScroll > MaxScrollDelta )
+ m_minDeltaScroll = MaxScrollDelta;
+ m_currentScrollDirection = scrollDirection;
+
+ } else {
+ // Don't automatically stopAutoScroll() here, the mouse button
+ // is presumably still pressed.
+ m_minDeltaScroll = DefaultMinDeltaScroll;
+ m_currentScrollDirection = None;
+ }
+
+}
+
+const int RosegardenScrollView::DefaultSmoothScrollTimeInterval = 10;
+const double RosegardenScrollView::DefaultMinDeltaScroll = 1.2;
+
+bool RosegardenScrollView::isTimeForSmoothScroll()
+{
+ static int desktopWidth = QApplication::desktop()->width(),
+ desktopHeight = QApplication::desktop()->height();
+
+ if (m_smoothScroll) {
+ int ta = m_scrollAccelerationTimer.elapsed();
+ int t = m_scrollTimer.elapsed();
+
+ RG_DEBUG << "t = " << t << ", ta = " << ta << ", int " << m_smoothScrollTimeInterval << ", delta " << m_minDeltaScroll << endl;
+
+ if (t < m_smoothScrollTimeInterval) {
+
+ return false;
+
+ } else {
+
+ if (ta > 300) {
+ // reset smoothScrollTimeInterval
+ m_smoothScrollTimeInterval = DefaultSmoothScrollTimeInterval;
+ m_minDeltaScroll = DefaultMinDeltaScroll;
+ m_scrollAccelerationTimer.restart();
+ } else if (ta > 50) {
+ // m_smoothScrollTimeInterval /= 2;
+ m_minDeltaScroll *= 1.08;
+ m_scrollAccelerationTimer.restart();
+ }
+
+ m_scrollTimer.restart();
+ return true;
+ }
+ }
+
+ return true;
+}
+
+void RosegardenScrollView::slotScrollHoriz(int hpos)
+{
+ QScrollBar* hbar = getMainHorizontalScrollBar();
+ int currentContentYPos = contentsY();
+
+ /* Lots of performance hitting debug
+ RG_DEBUG << "RosegardenCanvasView::slotScrollHoriz: hpos is " << hpos
+ << ", contentsX is " << contentsX() << ", visibleWidth is "
+ << visibleWidth() << endl;
+ */
+
+ if (hpos == 0) {
+
+ // returning to zero
+ // hbar->setValue(0);
+ setContentsPos(0, currentContentYPos);
+
+ } else if (hpos > (contentsX() +
+ visibleWidth() * 1.6) ||
+ hpos < (contentsX() -
+ visibleWidth() * 0.7)) {
+
+ // miles off one side or the other
+ // hbar->setValue(hpos - int(visibleWidth() * 0.4));
+ setContentsPos(hpos - int(visibleWidth() * 0.4), currentContentYPos);
+
+ } else if (hpos > (contentsX() +
+ visibleWidth() * 0.9)) {
+
+ // moving off the right hand side of the view
+ // hbar->setValue(hbar->value() + int(visibleWidth() * 0.6));
+ setContentsPos(hbar->value() + int(visibleWidth() * 0.6), currentContentYPos);
+
+ } else if (hpos < (contentsX() +
+ visibleWidth() * 0.1)) {
+
+ // moving off the left
+ // hbar->setValue(hbar->value() - int(visibleWidth() * 0.6));
+ setContentsPos(hbar->value() - int(visibleWidth() * 0.6), currentContentYPos);
+ }
+}
+
+void RosegardenScrollView::slotScrollHorizSmallSteps(int hpos)
+{
+ QScrollBar* hbar = getMainHorizontalScrollBar();
+ int currentContentYPos = contentsY();
+
+ int diff = 0;
+
+ if (hpos == 0) {
+
+ // returning to zero
+ // hbar->setValue(0);
+ setContentsPos(0, currentContentYPos);
+
+ } else if ((diff = int(hpos - (contentsX() +
+ visibleWidth() * 0.90))) > 0) {
+
+ // moving off the right hand side of the view
+
+ int delta = diff / 6;
+ int diff10 = std::min(diff, (int)m_minDeltaScroll);
+ delta = std::max(delta, diff10);
+
+ // hbar->setValue(hbar->value() + delta);
+ setContentsPos(hbar->value() + delta, currentContentYPos);
+
+ } else if ((diff = int(hpos - (contentsX() +
+ visibleWidth() * 0.10))) < 0) {
+ // moving off the left
+
+ int delta = -diff / 6;
+ int diff10 = std::min( -diff, (int)m_minDeltaScroll);
+ delta = std::max(delta, diff10);
+
+ // hbar->setValue(hbar->value() - delta);
+ setContentsPos(hbar->value() - delta, currentContentYPos);
+
+ }
+}
+
+void RosegardenScrollView::slotScrollVertSmallSteps(int vpos)
+{
+ QScrollBar* vbar = verticalScrollBar();
+
+ // RG_DEBUG << "RosegardenCanvasView::slotScrollVertSmallSteps: vpos is " << vpos << ", contentsY is " << contentsY() << ", visibleHeight is " << visibleHeight() << endl;
+
+ // As a special case (or hack), ignore any request made before we've
+ // actually been rendered and sized
+ if (visibleHeight() <= 1)
+ return ;
+
+ int diff = 0;
+
+ if (vpos == 0) {
+
+ // returning to zero
+ vbar->setValue(0);
+
+ } else if ((diff = int(vpos - (contentsY() +
+ visibleHeight() * 0.90))) > 0) {
+
+ // moving off up
+
+ int delta = diff / 6;
+ int diff10 = std::min(diff, (int)m_minDeltaScroll);
+ delta = std::max(delta, diff10);
+
+ vbar->setValue(vbar->value() + diff);
+
+ } else if ((diff = int(vpos - (contentsY() +
+ visibleHeight() * 0.10))) < 0) {
+
+ // moving off down
+
+ int delta = -diff / 6;
+ int diff10 = std::min( -diff, (int)m_minDeltaScroll);
+ delta = std::max(delta, diff10);
+
+ vbar->setValue(vbar->value() - delta);
+
+ }
+}
+
+void RosegardenScrollView::slotScrollVertToTop(int vpos)
+{
+ QScrollBar* vbar = verticalScrollBar();
+ if (vpos < visibleHeight() / 3)
+ vbar->setValue(0);
+ else
+ vbar->setValue(vpos - visibleHeight() / 5);
+}
+
+void RosegardenScrollView::slotSetScrollPos(const QPoint &pos)
+{
+ horizontalScrollBar()->setValue(pos.x());
+ verticalScrollBar()->setValue(pos.y());
+}
+
+void RosegardenScrollView::resizeEvent(QResizeEvent* e)
+{
+ QScrollView::resizeEvent(e);
+ if (!horizontalScrollBar()->isVisible())
+ updateBottomWidgetGeometry();
+
+}
+
+void RosegardenScrollView::setHBarGeometry(QScrollBar &hbar, int x, int y, int w, int h)
+{
+ QScrollView::setHBarGeometry(hbar, x, y, w, h);
+ updateBottomWidgetGeometry();
+}
+
+void RosegardenScrollView::updateBottomWidgetGeometry()
+{
+ if (!m_bottomWidget)
+ return ;
+
+ int bottomWidgetHeight = m_bottomWidget->sizeHint().height();
+
+ setMargins(0, 0, 0, bottomWidgetHeight);
+ QRect r = frameRect();
+ int hScrollBarHeight = 0;
+ if (horizontalScrollBar()->isVisible())
+ hScrollBarHeight = horizontalScrollBar()->height() + 2; // + 2 offset needed to preserve border shadow
+
+ int vScrollBarWidth = 0;
+ if (verticalScrollBar()->isVisible())
+ vScrollBarWidth = verticalScrollBar()->width();
+
+ m_bottomWidget->setGeometry(r.x(),
+ r.y() + r.height() - bottomWidgetHeight - hScrollBarHeight,
+ r.width() - vScrollBarWidth,
+ bottomWidgetHeight);
+
+ if (bottomWidgetHeight != m_currentBottomWidgetHeight) {
+ emit bottomWidgetHeightChanged(bottomWidgetHeight);
+ m_currentBottomWidgetHeight = bottomWidgetHeight;
+ }
+
+}
+
+void RosegardenScrollView::wheelEvent(QWheelEvent *e)
+{
+ if (e->state() & ControlButton) {
+ if (e->delta() > 0)
+ emit zoomIn();
+ else if (e->delta() < 0)
+ emit zoomOut();
+ return ;
+ }
+ QScrollView::wheelEvent(e);
+}
+
+}
+#include "RosegardenScrollView.moc"
diff --git a/src/gui/general/RosegardenScrollView.h b/src/gui/general/RosegardenScrollView.h
new file mode 100644
index 0000000..6a0dab7
--- /dev/null
+++ b/src/gui/general/RosegardenScrollView.h
@@ -0,0 +1,183 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ROSEGARDENSCROLLVIEW_H_
+#define _RG_ROSEGARDENSCROLLVIEW_H_
+
+#include <qpoint.h>
+#include <qscrollview.h>
+#include <qdatetime.h>
+#include <qtimer.h>
+
+
+class QWidget;
+class QWheelEvent;
+class QScrollBar;
+class QResizeEvent;
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * A QScrollView with more elaborate auto-scrolling capabilities
+ * and the ability to have a "fixed" (non-scrolling) widget at its bottom,
+ * just above the bottom scrollbar.
+ */
+class RosegardenScrollView : public QScrollView
+{
+ Q_OBJECT
+public:
+ RosegardenScrollView(QWidget* parent=0, const char* name=0, WFlags f=0);
+
+ /**
+ * EditTool::handleMouseMove() returns a OR-ed combination of these
+ * to indicate which direction to scroll to
+ */
+ enum {
+ NoFollow = 0x0,
+ FollowHorizontal = 0x1,
+ FollowVertical = 0x2
+ };
+
+ /**
+ * Sets the canvas width to be exactly the width needed to show
+ * all the items
+ */
+// void fitWidthToContents();
+
+ /**
+ * Sets the widget which will be between the scrollable part of the view
+ * and the horizontal scrollbar
+ */
+ void setBottomFixedWidget(QWidget*);
+
+ void updateBottomWidgetGeometry();
+
+ /// Map a point with the inverse world matrix
+// QPoint inverseMapPoint(const QPoint& p) { return inverseWorldMatrix().map(p); }
+
+ void setSmoothScroll(bool s) { m_smoothScroll = s; }
+
+ bool isTimeForSmoothScroll();
+
+ void setScrollDirectionConstraint(int d) { m_scrollDirectionConstraint = d; }
+
+ int getDeltaScroll() { return m_minDeltaScroll; }
+
+ virtual void wheelEvent(QWheelEvent *);
+
+public slots:
+ /**
+ * Scroll horizontally to make the given position visible,
+ * paging to as to get some visibility of the next screenful
+ * (for playback etc)
+ */
+ void slotScrollHoriz(int hpos);
+
+ /**
+ * Scroll horizontally to make the given position somewhat
+ * nearer to visible, scrolling by only "a small distance"
+ * at a time
+ */
+ void slotScrollHorizSmallSteps(int hpos);
+
+ /**
+ * Scroll vertically to make the given position somewhat
+ * nearer to visible, scrolling by only "a small distance"
+ * at a time
+ */
+ void slotScrollVertSmallSteps(int vpos);
+
+ /**
+ * Scroll vertically so as to place the given position
+ * somewhere near the top of the viewport.
+ */
+ void slotScrollVertToTop(int vpos);
+
+ /**
+ * Set the x and y scrollbars to a particular position
+ */
+ void slotSetScrollPos(const QPoint &);
+
+ void startAutoScroll();
+ void startAutoScroll(int directionConstraint);
+ void stopAutoScroll();
+ void doAutoScroll();
+
+ bool isAutoScrolling() const { return m_autoScrolling; }
+
+signals:
+ void bottomWidgetHeightChanged(int);
+
+ void zoomIn();
+ void zoomOut();
+
+protected:
+
+ virtual void resizeEvent(QResizeEvent*);
+ virtual void setHBarGeometry(QScrollBar &hbar, int x, int y, int w, int h);
+
+ virtual QScrollBar* getMainHorizontalScrollBar() { return horizontalScrollBar(); }
+
+ //--------------- Data members ---------------------------------
+ enum ScrollDirection { None, Top, Bottom, Left, Right };
+
+ QWidget* m_bottomWidget;
+ int m_currentBottomWidgetHeight;
+
+ bool m_smoothScroll;
+ int m_smoothScrollTimeInterval;
+ float m_minDeltaScroll;
+ QTime m_scrollTimer;
+ QTime m_scrollAccelerationTimer;
+
+ QTimer m_autoScrollTimer;
+ int m_autoScrollTime;
+ int m_autoScrollAccel;
+ QPoint m_previousP;
+ int m_autoScrollXMargin;
+ int m_autoScrollYMargin;
+ ScrollDirection m_currentScrollDirection;
+ int m_scrollDirectionConstraint;
+ bool m_autoScrolling;
+
+ static const int DefaultSmoothScrollTimeInterval;
+ static const double DefaultMinDeltaScroll;
+
+ static const int AutoscrollMargin;
+ static const int InitialScrollTime;
+ static const int InitialScrollAccel;
+ static const int MaxScrollDelta;
+ static const double ScrollAccelValue;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/general/Spline.cpp b/src/gui/general/Spline.cpp
new file mode 100644
index 0000000..455cca5
--- /dev/null
+++ b/src/gui/general/Spline.cpp
@@ -0,0 +1,130 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "Spline.h"
+
+#include <qpoint.h>
+
+
+namespace Rosegarden
+{
+
+Spline::PointList *
+
+Spline::calculate(const QPoint &s, const QPoint &f, const PointList &cp,
+ QPoint &topLeft, QPoint &bottomRight)
+{
+ if (cp.size() < 2)
+ return 0;
+
+ int i;
+ PointList *acc = new PointList();
+ QPoint p(s);
+
+ topLeft = bottomRight = QPoint(0, 0);
+
+ for (i = 1; i < cp.size(); ++i) {
+
+ QPoint c(cp[i - 1]);
+
+ int x = (c.x() + cp[i].x()) / 2;
+ int y = (c.y() + cp[i].y()) / 2;
+ QPoint n(x, y);
+
+ calculateSegment(acc, p, n, c, topLeft, bottomRight);
+
+ p = n;
+ }
+
+ calculateSegment(acc, p, f, cp[i - 1], topLeft, bottomRight);
+
+ return acc;
+}
+
+void
+Spline::calculateSegment(PointList *acc,
+ const QPoint &s, const QPoint &f, const QPoint &c,
+ QPoint &topLeft, QPoint &bottomRight)
+{
+ int x, y, n;
+
+ x = c.x() - s.x();
+ y = c.y() - s.y();
+
+ if (x < 0)
+ x = -x;
+ if (y < 0)
+ y = -y;
+ if (x > y)
+ n = x;
+ else
+ n = y;
+
+ x = f.x() - c.x();
+ y = f.y() - c.y();
+
+ if (x < 0)
+ x = -x;
+ if (y < 0)
+ y = -y;
+ if (x > y)
+ n += x;
+ else
+ n += y;
+
+ calculateSegmentSub(acc, s, f, c, n, topLeft, bottomRight);
+}
+
+void
+Spline::calculateSegmentSub(PointList *acc,
+ const QPoint &s, const QPoint &f, const QPoint &c,
+ int n, QPoint &topLeft, QPoint &bottomRight)
+{
+ double ax = (double)(f.x() + s.x() - 2 * c.x()) / (double)n;
+ double ay = (double)(f.y() + s.y() - 2 * c.y()) / (double)n;
+
+ double bx = 2.0 * (double)(c.x() - s.x());
+ double by = 2.0 * (double)(c.y() - s.y());
+
+ for (int m = 0; m <= n; ++m) {
+
+ int x = s.x() + (int)((m * ((double)m * ax + bx)) / n);
+ int y = s.y() + (int)((m * ((double)m * ay + by)) / n);
+
+ if (x < topLeft.x())
+ topLeft.setX(x);
+ if (y < topLeft.y())
+ topLeft.setY(y);
+
+ if (x > bottomRight.x())
+ bottomRight.setX(x);
+ if (y > bottomRight.y())
+ bottomRight.setY(y);
+
+ acc->push_back(QPoint(x, y));
+ }
+}
+
+}
diff --git a/src/gui/general/Spline.h b/src/gui/general/Spline.h
new file mode 100644
index 0000000..63946a5
--- /dev/null
+++ b/src/gui/general/Spline.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_SPLINE_H_
+#define _RG_SPLINE_H_
+
+#include "base/FastVector.h"
+
+
+class QPoint;
+class PointList;
+
+
+namespace Rosegarden
+{
+
+
+
+class Spline
+{
+public:
+ typedef FastVector<QPoint> PointList;
+
+ /**
+ * Calculate a set of polyline points to approximate
+ * a Bezier spline. Caller takes ownership of returned
+ * heap-allocated container.
+ */
+ static PointList *calculate(const QPoint &start, const QPoint &finish,
+ const PointList &controlPoints,
+ QPoint &topLeft, QPoint &bottomRight);
+
+private:
+ static void calculateSegment
+ (PointList *acc,
+ const QPoint &start, const QPoint &finish, const QPoint &control,
+ QPoint &topLeft, QPoint &bottomRight);
+
+ static void calculateSegmentSub
+ (PointList *acc,
+ const QPoint &start, const QPoint &finish, const QPoint &control, int n,
+ QPoint &topLeft, QPoint &bottomRight);
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/general/StaffLine.cpp b/src/gui/general/StaffLine.cpp
new file mode 100644
index 0000000..ab5d5ff
--- /dev/null
+++ b/src/gui/general/StaffLine.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "StaffLine.h"
+
+#include "misc/Debug.h"
+#include <qcanvas.h>
+#include <qpen.h>
+
+
+namespace Rosegarden
+{
+
+StaffLine::StaffLine(QCanvas *c, QCanvasItemGroup *g, int height) :
+ QCanvasLineGroupable(c, g),
+ m_height(height),
+ m_significant(true)
+{
+ setZ(1);
+}
+
+void
+StaffLine::setHighlighted(bool highlighted)
+{
+ // RG_DEBUG << "StaffLine::setHighlighted("
+ // << highlighted << ")\n";
+
+ if (highlighted) {
+
+ m_normalPen = pen();
+ QPen newPen = m_normalPen;
+ newPen.setColor(red);
+ setPen(newPen);
+
+ } else {
+
+ setPen(m_normalPen);
+
+ }
+}
+
+}
diff --git a/src/gui/general/StaffLine.h b/src/gui/general/StaffLine.h
new file mode 100644
index 0000000..7d01ff4
--- /dev/null
+++ b/src/gui/general/StaffLine.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_STAFFLINE_H_
+#define _RG_STAFFLINE_H_
+
+#include "gui/kdeext/QCanvasGroupableItem.h"
+#include <qpen.h>
+
+
+class QCanvasItemGroup;
+class QCanvas;
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * A staff line
+ *
+ * A groupable line which can be "highlighted"
+ * (drawn with a different color)
+ */
+class StaffLine : public QCanvasLineGroupable
+{
+public:
+ StaffLine(QCanvas *c, QCanvasItemGroup *g, int height);
+
+ enum { NoHeight = -150 };
+
+ void setHeight(int h) { m_height = h; }
+ int getHeight() const { return m_height; }
+
+ void setSignificant(bool s) { m_significant = s; }
+ bool isSignificant() const { return m_significant; }
+
+ /**
+ * "highlight" the line (set its pen to red)
+ */
+ void setHighlighted(bool);
+
+protected:
+ //--------------- Data members ---------------------------------
+
+ int m_height;
+ bool m_significant;
+
+ QPen m_normalPen;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/kdeext/KLedButton.cpp b/src/gui/kdeext/KLedButton.cpp
new file mode 100644
index 0000000..f4e2a95
--- /dev/null
+++ b/src/gui/kdeext/KLedButton.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ This file taken from KMix
+ Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>.
+
+ 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 "KLedButton.h"
+
+#include <kled.h>
+#include <qcolor.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+KLedButton::KLedButton(const QColor &col, QWidget *parent, const char *name)
+ : KLed( col, parent, name )
+{}
+
+KLedButton::KLedButton(const QColor& col, KLed::State st, KLed::Look look,
+ KLed::Shape shape, QWidget *parent, const char *name)
+ : KLed( col, st, look, shape, parent, name )
+{}
+
+KLedButton::~KLedButton()
+{}
+
+void KLedButton::mousePressEvent( QMouseEvent *e )
+{
+ if (e->button() == LeftButton) {
+ toggle();
+ emit stateChanged( state() );
+ }
+}
+
+}
+#include "KLedButton.moc"
diff --git a/src/gui/kdeext/KLedButton.h b/src/gui/kdeext/KLedButton.h
new file mode 100644
index 0000000..e17ecdb
--- /dev/null
+++ b/src/gui/kdeext/KLedButton.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ This file taken from KMix
+ Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>.
+
+ 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_KLEDBUTTON_H_
+#define _RG_KLEDBUTTON_H_
+
+#include <kled.h>
+
+
+class QWidget;
+class QMouseEvent;
+class QColor;
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * @author Stefan Schimanski
+ * Taken from KMix code,
+ * Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de>
+ */
+class KLedButton : public KLed {
+ Q_OBJECT
+ public:
+ KLedButton(const QColor &col=Qt::green, QWidget *parent=0, const char *name=0);
+ KLedButton(const QColor& col, KLed::State st, KLed::Look look, KLed::Shape shape,
+ QWidget *parent=0, const char *name=0);
+ ~KLedButton();
+
+ signals:
+ void stateChanged( bool newState );
+
+ protected:
+ void mousePressEvent ( QMouseEvent *e );
+};
+
+
+// This class creates a list of mute and record buttons
+// based on the rosegarden document and a specialisation
+// of the Vertical Box widget.
+//
+//
+//
+
+
+}
+
+#endif
diff --git a/src/gui/kdeext/KStartupLogo.cpp b/src/gui/kdeext/KStartupLogo.cpp
new file mode 100644
index 0000000..9a04d8f
--- /dev/null
+++ b/src/gui/kdeext/KStartupLogo.cpp
@@ -0,0 +1,159 @@
+// -*- c-basic-offset: 4 -*-
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <bownie@bownie.com>
+
+ This file contains code borrowed from KDevelop 2.0
+ Copyright (c) The KDevelop Development Team.
+
+ 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 <unistd.h>
+#include <kapplication.h>
+
+#include <qpainter.h>
+#include <qfontmetrics.h>
+
+#include <kapp.h>
+#include <kstddirs.h>
+#include <kconfig.h>
+#include <ktip.h>
+
+#include "KStartupLogo.h"
+#include "misc/Debug.h"
+
+KStartupLogo::KStartupLogo(QWidget * parent, const char *name)
+ : QWidget(parent, name,
+ WStyle_Customize |
+#if QT_VERSION >= 0x030100
+ WStyle_Splash
+#else
+ WStyle_NoBorder | WStyle_StaysOnTop | WStyle_Tool | WX11BypassWM | WWinOwnDC
+#endif
+ ),
+ m_readyToHide(false),
+ m_showTip(true)
+{
+ QString pixmapFile = locate("appdata", "pixmaps/splash.png");
+ if (!pixmapFile)
+ return ;
+ m_pixmap.load(pixmapFile);
+ setBackgroundPixmap(m_pixmap);
+ setGeometry(QApplication::desktop()->width() / 2 - m_pixmap.width() / 2,
+ QApplication::desktop()->height() / 2 - m_pixmap.height() / 2,
+ m_pixmap.width(), m_pixmap.height());
+}
+
+KStartupLogo::~KStartupLogo()
+{
+ m_wasClosed = true;
+ m_instance = 0;
+}
+
+void KStartupLogo::paintEvent(QPaintEvent*)
+{
+ // Print version number
+ QPainter paint(this);
+
+ QFont defaultFont;
+ defaultFont.setPixelSize(12);
+ paint.setFont(defaultFont);
+
+ QFontMetrics metrics(defaultFont);
+ int width = metrics.width(m_statusMessage) + 6;
+ if (width > 200)
+ width = 200;
+
+ int y = m_pixmap.height() - 12;
+
+ // grep me: splash color
+ // QColor bg(49, 94, 19); // color for 2006 splash
+ QColor bg(19, 19, 19); // color for the 2008 splash
+ paint.setPen(bg);
+ paint.setBrush(bg);
+ paint.drawRect(QRect(m_pixmap.width() - 220, m_pixmap.height() - 43,
+ 220, (y + 8) - (m_pixmap.height() - 43)));
+
+ // paint.setPen(Qt::black);
+ // paint.setBrush(Qt::black);
+ paint.setPen(Qt::white);
+ paint.setBrush(Qt::white);
+
+ //QString version(VERSION);
+ //int sepIdx = version.find("-");
+ QString versionLabel(VERSION);
+ //QString("R%1 v%2").arg(version.left(sepIdx)).arg(version.mid(sepIdx + 1));
+ int versionWidth = metrics.width(versionLabel);
+
+ paint.drawText(m_pixmap.width() - versionWidth - 18,
+ m_pixmap.height() - 28,
+ versionLabel);
+
+ paint.drawText(m_pixmap.width() - (width + 10), y, m_statusMessage);
+}
+
+void KStartupLogo::slotShowStatusMessage(QString message)
+{
+ m_statusMessage = message;
+ paintEvent(0);
+ QApplication::flushX();
+}
+
+void KStartupLogo::close()
+{
+ if (!m_wasClosed && isVisible()) {
+
+ if (m_showTip) {
+ RG_DEBUG << "KStartupLogo::close: Showing Tips\n";
+ KTipDialog::showTip(locate("data", "rosegarden/tips"));
+ }
+ }
+
+ QWidget::close();
+}
+
+
+void KStartupLogo::mousePressEvent(QMouseEvent*)
+{
+ // for the haters of raising startlogos
+ if (m_readyToHide)
+ hide(); // don't close, main() sets up a QTimer for that
+}
+
+KStartupLogo* KStartupLogo::getInstance()
+{
+ if (m_wasClosed)
+ return 0;
+
+ if (!m_instance)
+ m_instance = new KStartupLogo;
+
+ return m_instance;
+}
+
+void KStartupLogo::hideIfStillThere()
+{
+ if (m_instance)
+ m_instance->hide();
+ // don't close, main() sets up a QTimer for that
+}
+
+
+KStartupLogo* KStartupLogo::m_instance = 0;
+bool KStartupLogo::m_wasClosed = false;
+
+#include "KStartupLogo.moc"
diff --git a/src/gui/kdeext/KStartupLogo.h b/src/gui/kdeext/KStartupLogo.h
new file mode 100644
index 0000000..1af80fa
--- /dev/null
+++ b/src/gui/kdeext/KStartupLogo.h
@@ -0,0 +1,70 @@
+// -*- c-basic-offset: 4 -*-
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <bownie@bownie.com>
+
+ This file contains code borrowed from KDevelop 2.0
+ Copyright (c) The KDevelop Development Team.
+
+ 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 KSTARTUPLOGO_H
+#define KSTARTUPLOGO_H
+
+#include <qwidget.h>
+#include <qpixmap.h>
+
+class KStartupLogo : public QWidget
+{
+ Q_OBJECT
+
+public:
+ static KStartupLogo* getInstance();
+
+ static void hideIfStillThere();
+
+ void setHideEnabled(bool enabled) { m_readyToHide = enabled; };
+ void setShowTip(bool showTip) { m_showTip = showTip; };
+
+public slots:
+ void slotShowStatusMessage(QString);
+ virtual void close();
+
+protected:
+
+ KStartupLogo(QWidget *parent=0, const char *name=0);
+ ~KStartupLogo();
+
+ virtual void paintEvent(QPaintEvent*);
+ virtual void mousePressEvent( QMouseEvent*);
+
+ bool m_readyToHide;
+ bool m_showTip;
+
+ QPixmap m_pixmap;
+
+ static KStartupLogo* m_instance;
+ static bool m_wasClosed;
+ QString m_statusMessage;
+};
+
+#endif
+
+
+
+
+
diff --git a/src/gui/kdeext/KTmpStatusMsg.cpp b/src/gui/kdeext/KTmpStatusMsg.cpp
new file mode 100644
index 0000000..81142d2
--- /dev/null
+++ b/src/gui/kdeext/KTmpStatusMsg.cpp
@@ -0,0 +1,70 @@
+// -*- c-basic-offset: 4 -*-
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <bownie@bownie.com>
+
+ 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 <qapplication.h>
+#include <qeventloop.h>
+
+#include <kmainwindow.h>
+#include <kstatusbar.h>
+#include <klocale.h>
+
+#include "KTmpStatusMsg.h"
+#include "gui/application/RosegardenApplication.h"
+
+KTmpStatusMsg::KTmpStatusMsg(const QString& msg, KMainWindow* window, int id)
+ : m_mainWindow(window),
+ m_id(id)
+{
+ m_mainWindow->statusBar()->changeItem(QString(" %1").arg(msg), m_id);
+ Rosegarden::rgapp->refreshGUI(50);
+}
+
+KTmpStatusMsg::~KTmpStatusMsg()
+{
+ m_mainWindow->statusBar()->clear();
+ m_mainWindow->statusBar()->changeItem(m_defaultMsg, m_id);
+ Rosegarden::rgapp->refreshGUI(50);
+}
+
+
+void KTmpStatusMsg::setDefaultMsg(const QString& m)
+{
+ m_defaultMsg = m;
+}
+
+const QString& KTmpStatusMsg::getDefaultMsg()
+{
+ return m_defaultMsg;
+}
+
+void KTmpStatusMsg::setDefaultId(int id)
+{
+ m_defaultId = id;
+}
+
+int KTmpStatusMsg::getDefaultId()
+{
+ return m_defaultId;
+}
+
+
+int KTmpStatusMsg::m_defaultId = 1;
+QString KTmpStatusMsg::m_defaultMsg = "";
diff --git a/src/gui/kdeext/KTmpStatusMsg.h b/src/gui/kdeext/KTmpStatusMsg.h
new file mode 100644
index 0000000..6fd512c
--- /dev/null
+++ b/src/gui/kdeext/KTmpStatusMsg.h
@@ -0,0 +1,88 @@
+// -*- c-basic-offset: 4 -*-
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <bownie@bownie.com>
+
+ 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 KTMPSTATUSMSG_H
+#define KTMPSTATUSMSG_H
+
+class KMainWindow;
+
+/**
+ * A class to create a temporary message on KMainWindow's status bar
+ *
+ * Use as follows :
+ * { // some block of code starts here
+ * KTmpStatusMsg tmpMsg("doing something...", mainWindow);
+ *
+ * // do something
+ *
+ * } // the message goes away
+ *
+ */
+class KTmpStatusMsg
+{
+public:
+
+ /**
+ * Creates a new temporary status message on the status bar
+ * of the specified KMainWindow.
+ * The id of the text widget in the status bar can be specified
+ */
+ KTmpStatusMsg(const QString& msg, KMainWindow*, int id = m_defaultId);
+
+ ~KTmpStatusMsg();
+
+ /**
+ * Sets the message which will replace the temporary one in the
+ * status bar
+ */
+ static void setDefaultMsg(const QString&);
+
+ /**
+ * Returns the default message which will replace the temporary
+ * one in the status bar
+ */
+ static const QString& getDefaultMsg();
+
+ /**
+ * Sets the default id which will be used as the id of the text
+ * widget in the status bar
+ */
+ static void setDefaultId(int);
+
+ /**
+ * Returns the default id which will be used as id of the text
+ * widget in the status bar
+ */
+ static int getDefaultId();
+
+protected:
+
+ //--------------- Data members ---------------------------------
+
+ KMainWindow* m_mainWindow;
+ int m_id;
+
+ static int m_defaultId;
+ static QString m_defaultMsg;
+};
+
+#endif
+
diff --git a/src/gui/kdeext/QCanvasGroupableItem.cpp b/src/gui/kdeext/QCanvasGroupableItem.cpp
new file mode 100644
index 0000000..1fc2f2d
--- /dev/null
+++ b/src/gui/kdeext/QCanvasGroupableItem.cpp
@@ -0,0 +1,279 @@
+// -*- c-basic-offset: 4 -*-
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <bownie@bownie.com>
+
+ 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 <qcanvas.h>
+#include "misc/Debug.h"
+
+#include "QCanvasGroupableItem.h"
+
+QCanvasGroupableItem::QCanvasGroupableItem(QCanvasItem *i,
+ QCanvasItemGroup *g,
+ bool withRelativeCoords)
+ : m_group(g),
+ m_item(i)
+{
+ // RG_DEBUG << "QCanvasGroupableItem() - this : " << this
+ // << " - group : " << g
+ // << " - item : " << i << endl;
+
+ if (withRelativeCoords)
+ group()->addItemWithRelativeCoords(item());
+ else
+ group()->addItem(item());
+}
+
+QCanvasGroupableItem::~QCanvasGroupableItem()
+{
+ // RG_DEBUG << "~QCanvasGroupableItem() - this : " << this
+ // << " - group : " << group()
+ // << " - item : " << item() << endl;
+
+ // remove from the item group if we're still attached to one
+ if (group())
+ group()->removeItem(item());
+}
+
+void
+QCanvasGroupableItem::relativeMoveBy(double dx, double dy)
+{
+ m_item->moveBy(dx + m_group->x(),
+ dy + m_group->y());
+}
+
+void
+QCanvasGroupableItem::detach()
+{
+ m_group = 0;
+}
+
+//////////////////////////////////////////////////////////////////////
+
+QCanvasItemGroup::QCanvasItemGroup(QCanvas *c)
+ : QCanvasItem(c)
+{
+ // RG_DEBUG << "QCanvasItemGroup() - this : " << this << endl;
+}
+
+QCanvasItemGroup::~QCanvasItemGroup()
+{
+ // RG_DEBUG << "~QCanvasItemGroup() - this : " << this << endl;
+
+ // Tell all our items that we're being destroyed
+ QCanvasItemList::Iterator i;
+ for (i = m_items.begin(); i != m_items.end(); ++i) {
+ QCanvasGroupableItem *t = dynamic_cast<QCanvasGroupableItem*>(*i);
+ if (t)
+ t->detach();
+ }
+}
+
+void
+QCanvasItemGroup::moveBy(double dx, double dy)
+{
+ QCanvasItem::moveBy(dx, dy); // move ourselves
+
+ QCanvasItemList::Iterator i; // move group items
+ for (i = m_items.begin(); i != m_items.end(); ++i)
+ (*i)->moveBy(dx, dy);
+}
+
+void
+QCanvasItemGroup::advance(int stage)
+{
+ QCanvasItemList::Iterator i;
+ for (i = m_items.begin(); i != m_items.end(); ++i)
+ (*i)->advance(stage);
+}
+
+bool
+QCanvasItemGroup::collidesWith(const QCanvasItem *item) const
+{
+ QCanvasItemList::ConstIterator i;
+ for (i = m_items.begin(); i != m_items.end(); ++i)
+ if ((*i)->collidesWith(item))
+ return true;
+
+ return false;
+}
+
+void
+QCanvasItemGroup::draw(QPainter&)
+{
+ // There isn't anything to do - all the items will be drawn
+ // seperately by the canvas anyway. However the function has to be
+ // implemented because it's an abstract virtual in QCanvasItem.
+
+ // QCanvasItemList::Iterator i;
+ // for(i = m_items.begin(); i != m_items.end(); ++i)
+ // (*i)->draw(p);
+}
+
+void
+QCanvasItemGroup::setVisible(bool yes)
+{
+ QCanvasItemList::Iterator i;
+ for (i = m_items.begin(); i != m_items.end(); ++i)
+ (*i)->setVisible(yes);
+}
+
+void
+QCanvasItemGroup::setSelected(bool yes)
+{
+ QCanvasItem::setSelected(yes);
+
+ QCanvasItemList::Iterator i;
+ for (i = m_items.begin(); i != m_items.end(); ++i)
+ (*i)->setVisible(yes);
+}
+
+void
+QCanvasItemGroup::setEnabled(bool yes)
+{
+ QCanvasItem::setEnabled(yes);
+
+ QCanvasItemList::Iterator i;
+ for (i = m_items.begin(); i != m_items.end(); ++i)
+ (*i)->setEnabled(yes);
+}
+
+void
+QCanvasItemGroup::setActive(bool yes)
+{
+ QCanvasItem::setActive(yes);
+
+ QCanvasItemList::Iterator i;
+ for (i = m_items.begin(); i != m_items.end(); ++i)
+ (*i)->setActive(yes);
+}
+
+int
+QCanvasItemGroup::rtti() const
+{
+ return 10002;
+}
+
+QRect
+QCanvasItemGroup::boundingRect() const
+{
+ QRect r;
+
+ QCanvasItemList::ConstIterator i;
+ for (i = m_items.begin(); i != m_items.end(); ++i)
+ r.unite((*i)->boundingRect());
+
+ return r;
+}
+
+QRect
+QCanvasItemGroup::boundingRectAdvanced() const
+{
+ QRect r;
+
+ QCanvasItemList::ConstIterator i;
+ for (i = m_items.begin(); i != m_items.end(); ++i)
+ r.unite((*i)->boundingRectAdvanced());
+
+ return r;
+}
+
+bool
+QCanvasItemGroup::collidesWith(const QCanvasSprite *s,
+ const QCanvasPolygonalItem *p,
+ const QCanvasRectangle *r,
+ const QCanvasEllipse *e,
+ const QCanvasText *t) const
+{
+ if (s)
+ return collidesWith(s);
+ else if (p)
+ return collidesWith(p);
+ else if (r)
+ return collidesWith(r);
+ else if (e)
+ return collidesWith(e);
+ else if (t)
+ return collidesWith(t);
+
+ return false;
+
+}
+
+void
+QCanvasItemGroup::addItem(QCanvasItem *i)
+{
+ m_items.append(i);
+}
+
+void
+QCanvasItemGroup::addItemWithRelativeCoords(QCanvasItem *i)
+{
+ i->moveBy(x(), y());
+ addItem(i);
+}
+
+void
+QCanvasItemGroup::removeItem(QCanvasItem *i)
+{
+ // RG_DEBUG << "QCanvasItemGroup::removeItem() - this : "
+ // << this << " - item : " << i << endl;
+ m_items.remove(i);
+}
+
+//////////////////////////////////////////////////////////////////////
+
+
+QCanvasLineGroupable::QCanvasLineGroupable(QCanvas *c,
+ QCanvasItemGroup *g)
+ : QCanvasLine(c),
+ QCanvasGroupableItem(this, g)
+{}
+
+//////////////////////////////////////////////////////////////////////
+
+QCanvasRectangleGroupable::QCanvasRectangleGroupable(QCanvas *c,
+ QCanvasItemGroup *g)
+ : QCanvasRectangle(c),
+ QCanvasGroupableItem(this, g)
+{}
+
+//////////////////////////////////////////////////////////////////////
+
+QCanvasTextGroupable::QCanvasTextGroupable(const QString& label,
+ QCanvas *c,
+ QCanvasItemGroup *g)
+ : QCanvasText(label, c),
+ QCanvasGroupableItem(this, g)
+{}
+
+QCanvasTextGroupable::QCanvasTextGroupable(QCanvas *c,
+ QCanvasItemGroup *g)
+ : QCanvasText(c),
+ QCanvasGroupableItem(this, g)
+{}
+
+//////////////////////////////////////////////////////////////////////
+
+QCanvasSpriteGroupable::QCanvasSpriteGroupable(QCanvasPixmapArray *pa,
+ QCanvas *c,
+ QCanvasItemGroup *g)
+ : QCanvasSprite(pa, c),
+ QCanvasGroupableItem(this, g)
+{}
diff --git a/src/gui/kdeext/QCanvasGroupableItem.h b/src/gui/kdeext/QCanvasGroupableItem.h
new file mode 100644
index 0000000..97d1917
--- /dev/null
+++ b/src/gui/kdeext/QCanvasGroupableItem.h
@@ -0,0 +1,201 @@
+// -*- c-basic-offset: 4 -*-
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <bownie@bownie.com>
+
+ 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 QCANVASGROUPABLEITEM_H
+#define QCANVASGROUPABLEITEM_H
+
+#include <qcanvas.h>
+
+class QCanvasItemGroup;
+
+/**
+ * This class is meant to be inherited by QCanvasItem children to make
+ * them groupable.
+ *
+ * On destruction, the item will remove itself from the group it's
+ * attached to.
+ *
+ * @see QCanvasSpriteGroupable
+ * @see QCanvasLineGroupable
+ */
+class QCanvasGroupableItem
+{
+ friend class QCanvasItemGroup;
+
+public:
+
+ /**
+ * Create a groupable item, e.g. put the item in the specified
+ * QCanvasItemGroup. If withRelativeCoords is true, the item's
+ * position will be translated so that it's coordinates are
+ * relative to those of the item group.
+ *
+ * @see QCanvasItemGroup#addItemWithRelativeCoords()
+ */
+ QCanvasGroupableItem(QCanvasItem*, QCanvasItemGroup*,
+ bool withRelativeCoords = false);
+
+ virtual ~QCanvasGroupableItem();
+
+ /// Returns the QCanvasItemGroup this groupable item belongs to
+ QCanvasItemGroup* group() { return m_group; }
+
+ /// Returns the QCanvasItemGroup this groupable item belongs to
+ const QCanvasItemGroup* group() const { return m_group; }
+
+ /// Returns the QCanvasItem which this groupable item wraps
+ QCanvasItem *item() { return m_item; }
+
+ /**
+ * Same as moveBy(), except that the move is done relative to the
+ * item group's coordinates
+ */
+ virtual void relativeMoveBy(double dx, double dy);
+
+protected:
+ /**
+ * Detach item from the item group - called by QCanvasItemGroup only
+ *
+ * Set m_group to 0, so that on destruction the item won't try to
+ * remove itself from the group
+ */
+ void detach();
+
+private:
+ //--------------- Data members ---------------------------------
+
+ QCanvasItemGroup* m_group;
+ QCanvasItem* m_item;
+
+};
+
+
+/**
+ * This class implements QCanvasItem groups
+ *
+ * An item group will keep its items in a fixed relative position when
+ * moved, just like in a drawing program where you can "bind" several
+ * items together so that they'll behave as a single item.
+ *
+ * Proper behavior requires collaboration from the QCanvasView,
+ * though. When about to move an item, the QCanvasView object should
+ * first check if it's not a groupable item, and if so fetch its
+ * QCanvasItemGroup and move it instead.
+ */
+class QCanvasItemGroup : public QCanvasItem
+{
+public:
+ QCanvasItemGroup(QCanvas *);
+ virtual ~QCanvasItemGroup();
+
+ virtual void moveBy(double dx, double dy);
+ virtual void advance(int stage);
+ virtual bool collidesWith(const QCanvasItem*) const;
+ virtual void draw(QPainter&);
+ virtual void setVisible(bool yes);
+ virtual void setSelected(bool yes);
+ virtual void setEnabled(bool yes);
+ virtual void setActive(bool yes);
+ virtual int rtti() const;
+ virtual QRect boundingRect() const;
+ virtual QRect boundingRectAdvanced() const;
+
+ /**
+ * Add a new item to this group.
+ *
+ * The item's coordinates are kept as is.
+ *
+ * @see addItemWithRelativeCoords()
+ */
+ virtual void addItem(QCanvasItem *);
+
+ /**
+ * Add a new item to this group.
+ *
+ * The item's coordinates are considered relative to the group.
+ * For example, suppose you have a QCanvasItemGroup whose coords
+ * are 10,10. If you call addItemWithRelativeCoords() with an item
+ * whose coords are 5,5, the item is moved so that its coords
+ * will be 5,5 relative to the group (e.g. 15,15).
+ *
+ * @see addItem()
+ */
+ virtual void addItemWithRelativeCoords(QCanvasItem *);
+
+ /**
+ * Remove the specified item from the group
+ */
+ virtual void removeItem(QCanvasItem*);
+
+private:
+ virtual bool collidesWith(const QCanvasSprite*,
+ const QCanvasPolygonalItem*,
+ const QCanvasRectangle*,
+ const QCanvasEllipse*,
+ const QCanvasText* ) const;
+
+protected:
+ //--------------- Data members ---------------------------------
+
+ QCanvasItemList m_items;
+};
+
+
+/**
+ * A QCanvasLine which can be put in a QCanvasGroup
+ */
+class QCanvasLineGroupable : public QCanvasLine, public QCanvasGroupableItem
+{
+public:
+ QCanvasLineGroupable(QCanvas *c, QCanvasItemGroup *g);
+};
+
+/**
+ * A QCanvasRectangle which can be put in a QCanvasGroup
+ */
+class QCanvasRectangleGroupable : public QCanvasRectangle, public QCanvasGroupableItem
+{
+public:
+ QCanvasRectangleGroupable(QCanvas *c, QCanvasItemGroup *g);
+};
+
+/**
+ * A QCanvasText which can be put in a QCanvasGroup
+ */
+class QCanvasTextGroupable : public QCanvasText, public QCanvasGroupableItem
+{
+public:
+ QCanvasTextGroupable(QCanvas *c, QCanvasItemGroup *g);
+ QCanvasTextGroupable(const QString&, QCanvas *c, QCanvasItemGroup *g);
+};
+
+/**
+ * A QCanvasSprite that can be put in a QCanvasGroup
+ */
+class QCanvasSpriteGroupable : public QCanvasSprite, public QCanvasGroupableItem
+{
+public:
+ QCanvasSpriteGroupable(QCanvasPixmapArray*,
+ QCanvas*,
+ QCanvasItemGroup*);
+};
+
+#endif
diff --git a/src/gui/kdeext/QCanvasSimpleSprite.cpp b/src/gui/kdeext/QCanvasSimpleSprite.cpp
new file mode 100644
index 0000000..537cc62
--- /dev/null
+++ b/src/gui/kdeext/QCanvasSimpleSprite.cpp
@@ -0,0 +1,217 @@
+// -*- c-basic-offset: 4 -*-
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <bownie@bownie.com>
+
+ 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 <vector>
+#include "misc/Debug.h"
+
+#include <qpainter.h>
+
+#include "QCanvasSimpleSprite.h"
+
+namespace Rosegarden {
+
+QCanvasSimpleSprite::QCanvasSimpleSprite(QPixmap *pixmap, QCanvas *canvas)
+ : QCanvasSprite(0, canvas),
+ m_pixmapArray(0)
+{
+ m_pixmapArray = makePixmapArray(pixmap);
+ setSequence(m_pixmapArray);
+}
+
+QCanvasSimpleSprite::QCanvasSimpleSprite(QCanvasPixmap *pixmap, QCanvas *canvas)
+ : QCanvasSprite(0, canvas),
+ m_pixmapArray(0)
+{
+ m_pixmapArray = makePixmapArray(pixmap);
+ setSequence(m_pixmapArray);
+}
+
+QCanvasSimpleSprite::QCanvasSimpleSprite(const QString &pixmapfile,
+ QCanvas *canvas)
+ : QCanvasSprite(0, canvas),
+ m_pixmapArray(0)
+{
+ m_pixmapArray = makePixmapArray(pixmapfile);
+ setSequence(m_pixmapArray);
+}
+
+QCanvasSimpleSprite::QCanvasSimpleSprite(QCanvas *canvas)
+ : QCanvasSprite(0, canvas),
+ m_pixmapArray(0)
+{
+ QCanvasPixmapArray* tmpArray = makePixmapArray(new QPixmap());
+ setSequence(tmpArray);
+}
+
+
+QCanvasSimpleSprite::~QCanvasSimpleSprite()
+{
+ PixmapArrayGC::registerForDeletion(m_pixmapArray);
+ m_pixmapArray = 0;
+
+ // We can't delete m_pixmapArray or we get a core dump.
+ //
+ // The reason I think is that after the QCanvasSprite is deleted,
+ // it is removed from the QCanvas, which therefore needs the
+ // pixmaps to know how to update itself (the crash is in
+ // QCanvas::removeChunks(), usually).
+ //
+ // So instead we have to do this GCish
+ // thingy. PixmapArrayGC::deleteAll() is called by
+ // NotationView::redoLayout
+}
+
+QCanvasPixmapArray*
+QCanvasSimpleSprite::makePixmapArray(QPixmap *pixmap)
+{
+ QList<QPixmap> pixlist;
+ pixlist.setAutoDelete(true); // the QCanvasPixmapArray creates its
+ // own copies of the pixmaps, so we
+ // can delete the one we're passed
+ pixlist.append(pixmap);
+
+ QList<QPoint> spotlist;
+ spotlist.setAutoDelete(true);
+ spotlist.append(new QPoint(0, 0));
+
+ return new QCanvasPixmapArray(pixlist, spotlist);
+}
+
+QCanvasPixmapArray*
+QCanvasSimpleSprite::makePixmapArray(QCanvasPixmap *pixmap)
+{
+ QList<QPixmap> pixlist;
+ pixlist.setAutoDelete(true); // the QCanvasPixmapArray creates its
+ // own copies of the pixmaps, so we
+ // can delete the one we're passed
+ pixlist.append(pixmap);
+
+ QList<QPoint> spotlist;
+ spotlist.setAutoDelete(true);
+ spotlist.append(new QPoint(pixmap->offsetX(), pixmap->offsetY()));
+
+ return new QCanvasPixmapArray(pixlist, spotlist);
+}
+
+QCanvasPixmapArray*
+QCanvasSimpleSprite::makePixmapArray(const QString &pixmapfile)
+{
+ return new QCanvasPixmapArray(pixmapfile);
+}
+
+//////////////////////////////////////////////////////////////////////
+
+QCanvasNotationSprite::QCanvasNotationSprite(NotationElement& n,
+ QPixmap* pixmap,
+ QCanvas* canvas)
+ : QCanvasSimpleSprite(pixmap, canvas),
+ m_notationElement(n)
+{}
+
+QCanvasNotationSprite::QCanvasNotationSprite(NotationElement& n,
+ QCanvasPixmap* pixmap,
+ QCanvas* canvas)
+ : QCanvasSimpleSprite(pixmap, canvas),
+ m_notationElement(n)
+
+{}
+
+QCanvasNotationSprite::~QCanvasNotationSprite()
+{}
+
+
+QCanvasNonElementSprite::QCanvasNonElementSprite(QPixmap *pixmap,
+ QCanvas *canvas) :
+ QCanvasSimpleSprite(pixmap, canvas)
+{}
+
+QCanvasNonElementSprite::QCanvasNonElementSprite(QCanvasPixmap *pixmap,
+ QCanvas *canvas) :
+ QCanvasSimpleSprite(pixmap, canvas)
+{}
+
+QCanvasNonElementSprite::~QCanvasNonElementSprite()
+{}
+
+QCanvasTimeSigSprite::QCanvasTimeSigSprite(double layoutX,
+ QPixmap *pixmap,
+ QCanvas *canvas) :
+ QCanvasNonElementSprite(pixmap, canvas),
+ m_layoutX(layoutX)
+{}
+
+QCanvasTimeSigSprite::QCanvasTimeSigSprite(double layoutX,
+ QCanvasPixmap *pixmap,
+ QCanvas *canvas) :
+ QCanvasNonElementSprite(pixmap, canvas),
+ m_layoutX(layoutX)
+{}
+
+QCanvasTimeSigSprite::~QCanvasTimeSigSprite()
+{}
+
+
+QCanvasStaffNameSprite::QCanvasStaffNameSprite(QPixmap *pixmap,
+ QCanvas *canvas) :
+ QCanvasNonElementSprite(pixmap, canvas)
+{}
+
+QCanvasStaffNameSprite::QCanvasStaffNameSprite(QCanvasPixmap *pixmap,
+ QCanvas *canvas) :
+ QCanvasNonElementSprite(pixmap, canvas)
+{}
+
+QCanvasStaffNameSprite::~QCanvasStaffNameSprite()
+{}
+
+
+//////////////////////////////////////////////////////////////////////
+
+void PixmapArrayGC::registerForDeletion(QCanvasPixmapArray* array)
+{
+ m_pixmapArrays.push_back(array);
+}
+
+void PixmapArrayGC::deleteAll()
+{
+ RG_DEBUG << "PixmapArrayGC::deleteAll() : "
+ << m_pixmapArrays.size() << " pixmap arrays to delete\n";
+
+ static unsigned long total = 0;
+
+ for (unsigned int i = 0; i < m_pixmapArrays.size(); ++i) {
+ QCanvasPixmapArray *array = m_pixmapArrays[i];
+ QPixmap *pixmap = array->image(0);
+ if (pixmap) {
+ total += pixmap->width() * pixmap->height();
+ // NOTATION_DEBUG << "PixmapArrayGC::deleteAll(): " << pixmap->width() << "x" << pixmap->height() << " (" << (pixmap->width()*pixmap->height()) << " px, " << total << " total)" << endl;
+ }
+ delete m_pixmapArrays[i];
+ }
+
+ m_pixmapArrays.clear();
+}
+
+std::vector<QCanvasPixmapArray*> PixmapArrayGC::m_pixmapArrays;
+
+}
+
+//////////////////////////////////////////////////////////////////////
diff --git a/src/gui/kdeext/QCanvasSimpleSprite.h b/src/gui/kdeext/QCanvasSimpleSprite.h
new file mode 100644
index 0000000..15a02f9
--- /dev/null
+++ b/src/gui/kdeext/QCanvasSimpleSprite.h
@@ -0,0 +1,133 @@
+// -*- c-basic-offset: 4 -*-
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <bownie@bownie.com>
+
+ 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 QCANVASSIMPLESPRITE_H
+#define QCANVASSIMPLESPRITE_H
+
+#include <qwmatrix.h>
+#include <qcanvas.h>
+
+#include "gui/editors/notation/NotePixmapFactory.h"
+
+namespace Rosegarden {
+
+/**
+ * A QCanvasSprite with 1 frame only
+ */
+class QCanvasSimpleSprite : public QCanvasSprite
+{
+public:
+ QCanvasSimpleSprite(QPixmap*, QCanvas*);
+ QCanvasSimpleSprite(QCanvasPixmap*, QCanvas*);
+ QCanvasSimpleSprite(const QString &pixmapfile, QCanvas*);
+
+ // For lazy pixmap rendering, when we get around looking at it
+ QCanvasSimpleSprite(QCanvas*);
+
+ virtual ~QCanvasSimpleSprite();
+
+protected:
+ static QCanvasPixmapArray* makePixmapArray(QPixmap *pixmap);
+
+ static QCanvasPixmapArray* makePixmapArray(QCanvasPixmap *pixmap);
+
+ static QCanvasPixmapArray* makePixmapArray(const QString &pixmapfile);
+
+ //--------------- Data members ---------------------------------
+
+ QCanvasPixmapArray* m_pixmapArray;
+};
+
+class NotationElement;
+
+/**
+ * A QCanvasSprite referencing a NotationElement
+ */
+class QCanvasNotationSprite : public QCanvasSimpleSprite
+{
+public:
+ QCanvasNotationSprite(Rosegarden::NotationElement&, QPixmap*, QCanvas*);
+ QCanvasNotationSprite(Rosegarden::NotationElement&, QCanvasPixmap*, QCanvas*);
+
+ virtual ~QCanvasNotationSprite();
+
+ Rosegarden::NotationElement& getNotationElement() { return m_notationElement; }
+
+protected:
+ //--------------- Data members ---------------------------------
+
+ Rosegarden::NotationElement& m_notationElement;
+};
+
+class QCanvasNonElementSprite : public QCanvasSimpleSprite
+{
+public:
+ QCanvasNonElementSprite(QPixmap *, QCanvas *);
+ QCanvasNonElementSprite(QCanvasPixmap *, QCanvas *);
+ virtual ~QCanvasNonElementSprite();
+};
+
+/**
+ * A QCanvasSprite used for a time signature
+ */
+class QCanvasTimeSigSprite : public QCanvasNonElementSprite
+{
+public:
+ QCanvasTimeSigSprite(double layoutX, QPixmap *, QCanvas *);
+ QCanvasTimeSigSprite(double layoutX, QCanvasPixmap *, QCanvas *);
+ virtual ~QCanvasTimeSigSprite();
+
+ void setLayoutX(double layoutX) { m_layoutX = layoutX; }
+ double getLayoutX() const { return m_layoutX; }
+
+protected:
+ double m_layoutX;
+};
+
+/**
+ * A QCanvasSprite used for a staff name
+ */
+class QCanvasStaffNameSprite : public QCanvasNonElementSprite
+{
+public:
+ QCanvasStaffNameSprite(QPixmap *, QCanvas *);
+ QCanvasStaffNameSprite(QCanvasPixmap *, QCanvas *);
+ virtual ~QCanvasStaffNameSprite();
+};
+
+/**
+ * A GC for QCanvasPixmapArray which have to be deleted seperatly
+ */
+class PixmapArrayGC
+{
+public:
+ static void registerForDeletion(QCanvasPixmapArray*);
+ static void deleteAll();
+
+protected:
+ //--------------- Data members ---------------------------------
+
+ static std::vector<QCanvasPixmapArray*> m_pixmapArrays;
+};
+
+}
+
+#endif
diff --git a/src/gui/kdeext/RGLed.cpp b/src/gui/kdeext/RGLed.cpp
new file mode 100644
index 0000000..54b91b2
--- /dev/null
+++ b/src/gui/kdeext/RGLed.cpp
@@ -0,0 +1,729 @@
+// -*- c-basic-offset: 2 -*-
+
+/* This file is a modified version of kled.cpp, which is part of the
+ KDE libraries. The modifications (for "brute-force" antialiasing)
+ were carried out by Chris Cannam, April 2004.
+
+ Copyright (C) 1998 Jörg Habenicht (j.habenicht@europemail.com)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+/*************************************************************************
+ * $Id: rgled.cpp 6694 2005-07-17 11:42:36Z cannam $
+ *
+ * $Log$
+ * Revision 1.2 2005/07/17 11:42:36 cannam
+ * * fixes from stg branch:
+ * - fix failure to recolour led when switching track from audio to midi or vice versa
+ * - fix colour number / menu index confusion in spb colour menu
+ *
+ * Revision 1.1.10.1 2005/07/17 00:00:30 cannam
+ *
+ * * merge across from HEAD to 17/07/2005
+ * * appearance fixes
+ *
+ * Revision 1.1 2004/04/05 09:54:05 cannam
+ *
+ * * antialiased LEDs
+ *
+ * Revision 1.24 2002/12/16 15:10:03 mkretz
+ * don't use a gray background but rather take the color from the current
+ * colorscheme
+ *
+ * Revision 1.23 2002/03/04 00:51:49 lunakl
+ * Keep BC changes (the patch is almost 100KiB of boring stuff
+ * ... anybody willing to review? ;) ).
+ *
+ * Revision 1.22 2002/02/27 23:15:45 pfeiffer
+ * kapp--
+ *
+ * Revision 1.21 2002/01/22 10:23:55 hausmann
+ * - minor API fix (don't return a const QColor object)
+ *
+ * Revision 1.20 2001/10/10 17:40:39 mueller
+ * CVS_SILENT: fixincludes
+ *
+ * Revision 1.19 2001/08/08 14:35:12 hausmann
+ * - removed empty KActionCollection::childEvent
+ * - added sizeHint() and minimumSizeHint() methods to KLed, as advised by
+ * Rik in the comment in kled.h
+ * - removed unused mousePressEvent, mouseMoveEvent and mouseReleaseEvent
+ * handlers from KPassDlg
+ * - merged KToolBar::insertSeparator() and KToolBar::insertLineSeparator()
+ * with their overloaded variants
+ *
+ * Revision 1.18 2001/04/16 22:08:43 pfeiffer
+ * don't assume that the first item of an enum is 0
+ *
+ * Revision 1.17 2000/09/12 19:15:53 pfeiffer
+ * Stefan Hellwig in cooperation with Joerg Habenicht:
+ * Draw nicer LEDs, especially sunken ones.
+ *
+ * Revision 1.16 2000/08/24 12:44:48 porten
+ * "friend class" patches from Thomas Kunert <kunert@physik.tu-dresden.de>
+ *
+ * Revision 1.15 2000/08/17 16:44:44 reggie
+ * Don't crash
+ *
+ * Revision 1.14 2000/06/03 01:04:42 gehrmab
+ * * Made drawing routines available for overriding
+ * * Added a parent/name constructor
+ * * Propertyfication
+ *
+ * Revision 1.13 2000/05/08 19:38:49 sschiman
+ * Calling setColor before setting up the private data is a bad idea ;-)
+ *
+ * Revision 1.12 2000/05/07 09:49:57 habenich
+ * provided method to set the factor to dark the LED
+ * precalculated the dark color, is just retrieved at paint events
+ *
+ * Revision 1.11 2000/04/09 16:08:33 habenich
+ * fixed nasty bug #70 disappearing led
+ * reenabled flat and raised led painting
+ *
+ * Revision 1.10 1999/12/25 17:12:18 mirko
+ * Modified Look "round" to "raised", as the others are flat and
+ * sunken. All enums start with uppercase letters now to fit the overall
+ * KDE style.
+ * Implemented raised rectangluar look.
+ * --Mirko.
+ *
+ * Revision 1.9 1999/11/12 21:17:09 antlarr
+ * Fixed some bugs.
+ * Added the possibility to draw a sunk rectangle as the "old" KLedLamp did.
+ *
+ * Revision 1.9 1999/11/11 16:08:15 antlarr
+ * Fixed some bugs.
+ * Added the possibility to draw a sunk rectangle as the "old" KLedLamp did.
+ *
+ * Revision 1.8 1999/11/01 22:03:15 dmuell
+ * fixing all kinds of compile warnings
+ * (unused var, unused argument etc)
+ *
+ * Revision 1.7 1999/10/10 13:34:14 mirko
+ * First merge with KLedLamp that shows a rectangular LED.
+ * It does not yet work reliably.
+ *
+ * Revision 1.6 1999/03/01 23:34:49 kulow
+ * CVS_SILENT ported to Qt 2.0
+ *
+ * Revision 1.5 1999/02/19 08:52:42 habenich
+ * ID und LOG tags included
+ *
+ *
+ *************************************************************************/
+
+#define PAINT_BENCH
+#undef PAINT_BENCH
+
+#ifdef PAINT_BENCH
+#include <qdatetime.h>
+#include <stdio.h>
+#endif
+
+
+#include <qpainter.h>
+#include <qimage.h>
+#include <qcolor.h>
+#include <kapplication.h>
+#include <kpixmapeffect.h>
+#include "kled.h"
+
+
+class KLed::KLedPrivate
+{
+ friend class KLed;
+
+ int dark_factor;
+ QColor offcolor;
+ QPixmap *off_map;
+ QPixmap *on_map;
+};
+
+
+
+KLed::KLed(QWidget *parent, const char *name)
+ : QWidget( parent, name),
+ led_state(On),
+ led_look(Raised),
+ led_shape(Circular)
+{
+ QColor col(green);
+ d = new KLed::KLedPrivate;
+ d->dark_factor = 300;
+ d->offcolor = col.dark(300);
+ d->off_map = 0;
+ d->on_map = 0;
+
+ setColor(col);
+}
+
+
+KLed::KLed(const QColor& col, QWidget *parent, const char *name)
+ : QWidget( parent, name),
+ led_state(On),
+ led_look(Raised),
+ led_shape(Circular)
+{
+ d = new KLed::KLedPrivate;
+ d->dark_factor = 300;
+ d->offcolor = col.dark(300);
+ d->off_map = 0;
+ d->on_map = 0;
+
+ setColor(col);
+ //setShape(Circular);
+}
+
+KLed::KLed(const QColor& col, KLed::State state,
+ KLed::Look look, KLed::Shape shape, QWidget *parent, const char *name )
+ : QWidget(parent, name),
+ led_state(state),
+ led_look(look),
+ led_shape(shape)
+{
+ d = new KLed::KLedPrivate;
+ d->dark_factor = 300;
+ d->offcolor = col.dark(300);
+ d->off_map = 0;
+ d->on_map = 0;
+
+ //setShape(shape);
+ setColor(col);
+}
+
+
+KLed::~KLed()
+{
+ delete d->off_map;
+ delete d->on_map;
+ delete d;
+}
+
+void
+KLed::paintEvent(QPaintEvent *)
+{
+#ifdef PAINT_BENCH
+ const int rounds = 1000;
+ QTime t;
+ t.start();
+ for (int i = 0; i < rounds; i++) {
+#endif
+ switch (led_shape) {
+ case Rectangular:
+ switch (led_look) {
+ case Sunken :
+ paintRectFrame(false);
+ break;
+ case Raised :
+ paintRectFrame(true);
+ break;
+ case Flat :
+ paintRect();
+ break;
+ default :
+ qWarning("%s: in class KLed: no KLed::Look set", qApp->argv()[0]);
+ }
+ break;
+ case Circular:
+ switch (led_look) {
+ case Flat :
+ paintFlat();
+ break;
+ case Raised :
+ paintRound();
+ break;
+ case Sunken :
+ paintSunken();
+ break;
+ default:
+ qWarning("%s: in class KLed: no KLed::Look set", qApp->argv()[0]);
+ }
+ break;
+ default:
+ qWarning("%s: in class KLed: no KLed::Shape set", qApp->argv()[0]);
+ break;
+ }
+#ifdef PAINT_BENCH
+
+ }
+ int ready = t.elapsed();
+ qWarning("elapsed: %d msec. for %d rounds", ready, rounds);
+#endif
+}
+
+void
+KLed::paintFlat() // paint a ROUND FLAT led lamp
+{
+ QPainter paint;
+ QColor color;
+ QBrush brush;
+ QPen pen;
+
+ // Initialize coordinates, width, and height of the LED
+ //
+ int width = this->width();
+ // Make sure the LED is round!
+ if (width > this->height())
+ width = this->height();
+ width -= 2; // leave one pixel border
+ if (width < 0)
+ width = 0;
+
+
+ // start painting widget
+ //
+ paint.begin( this );
+
+ // Set the color of the LED according to given parameters
+ color = ( led_state ) ? led_color : d->offcolor;
+
+ // Set the brush to SolidPattern, this fills the entire area
+ // of the ellipse which is drawn with a thin grey "border" (pen)
+ brush.setStyle( QBrush::SolidPattern );
+ brush.setColor( color );
+
+ pen.setWidth( 1 );
+ color = colorGroup().dark();
+ pen.setColor( color ); // Set the pen accordingly
+
+ paint.setPen( pen ); // Select pen for drawing
+ paint.setBrush( brush ); // Assign the brush to the painter
+
+ // Draws a "flat" LED with the given color:
+ paint.drawEllipse( 1, 1, width, width );
+
+ paint.end();
+ //
+ // painting done
+}
+
+void
+KLed::paintRound() // paint a ROUND RAISED led lamp
+{
+ QPainter paint;
+ QColor color;
+ QBrush brush;
+ QPen pen;
+
+ // Initialize coordinates, width, and height of the LED
+ int width = this->width();
+
+ // Make sure the LED is round!
+ if (width > this->height())
+ width = this->height();
+ width -= 2; // leave one pixel border
+ if (width < 0)
+ width = 0;
+
+ // start painting widget
+ //
+ paint.begin( this );
+
+ // Set the color of the LED according to given parameters
+ color = ( led_state ) ? led_color : d->offcolor;
+
+ // Set the brush to SolidPattern, this fills the entire area
+ // of the ellipse which is drawn first
+ brush.setStyle( QBrush::SolidPattern );
+ brush.setColor( color );
+ paint.setBrush( brush ); // Assign the brush to the painter
+
+ // Draws a "flat" LED with the given color:
+ paint.drawEllipse( 1, 1, width, width );
+
+ // Draw the bright light spot of the LED now, using modified "old"
+ // painter routine taken from KDEUI´s KLed widget:
+
+ // Setting the new width of the pen is essential to avoid "pixelized"
+ // shadow like it can be observed with the old LED code
+ pen.setWidth( 2 );
+
+ // shrink the light on the LED to a size about 2/3 of the complete LED
+ int pos = width / 5 + 1;
+ int light_width = width;
+ light_width *= 2;
+ light_width /= 3;
+
+ // Calculate the LED´s "light factor":
+ int light_quote = (130 * 2 / (light_width ? light_width : 1)) + 100;
+
+ // Now draw the bright spot on the LED:
+ while (light_width)
+ {
+ color = color.light( light_quote ); // make color lighter
+ pen.setColor( color ); // set color as pen color
+ paint.setPen( pen ); // select the pen for drawing
+ paint.drawEllipse( pos, pos, light_width, light_width ); // draw the ellipse (circle)
+ light_width--;
+ if (!light_width)
+ break;
+ paint.drawEllipse( pos, pos, light_width, light_width );
+ light_width--;
+ if (!light_width)
+ break;
+ paint.drawEllipse( pos, pos, light_width, light_width );
+ pos++;
+ light_width--;
+ }
+
+ // Drawing of bright spot finished, now draw a thin grey border
+ // around the LED; it looks nicer that way. We do this here to
+ // avoid that the border can be erased by the bright spot of the LED
+
+ pen.setWidth( 1 );
+ color = colorGroup().dark();
+ pen.setColor( color ); // Set the pen accordingly
+ paint.setPen( pen ); // Select pen for drawing
+ brush.setStyle( QBrush::NoBrush ); // Switch off the brush
+ paint.setBrush( brush ); // This avoids filling of the ellipse
+
+ paint.drawEllipse( 1, 1, width, width );
+
+ paint.end();
+ //
+ // painting done
+}
+
+void
+KLed::paintSunken() // paint a ROUND SUNKEN led lamp
+{
+ QPainter paint;
+ QColor color;
+ QBrush brush;
+ QPen pen;
+
+ // First of all we want to know what area should be updated
+ // Initialize coordinates, width, and height of the LED
+ int width = this->width();
+
+ // Make sure the LED is round!
+ if (width > this->height())
+ width = this->height();
+ width -= 2; // leave one pixel border
+ if (width < 0)
+ width = 0;
+
+ // maybe we could stop HERE, if width <=0 ?
+
+ int scale = 1;
+ QPixmap *tmpMap = 0;
+ bool smooth = true;
+
+ if (smooth)
+ {
+ if (led_state) {
+ if (d->on_map) {
+ paint.begin(this);
+ paint.drawPixmap(0, 0, *d->on_map);
+ paint.end();
+ return ;
+ }
+ } else {
+ if (d->off_map) {
+ paint.begin(this);
+ paint.drawPixmap(0, 0, *d->off_map);
+ paint.end();
+ return ;
+ }
+ }
+
+ scale = 3;
+ width *= scale;
+
+ tmpMap = new QPixmap(width, width);
+ tmpMap->fill(paletteBackgroundColor());
+ paint.begin(tmpMap);
+
+ } else
+ {
+ paint.begin(this);
+ }
+
+ // Set the color of the LED according to given parameters
+ color = ( led_state ) ? led_color : d->offcolor;
+
+ // Set the brush to SolidPattern, this fills the entire area
+ // of the ellipse which is drawn first
+ brush.setStyle( QBrush::SolidPattern );
+ brush.setColor( color );
+ paint.setBrush( brush ); // Assign the brush to the painter
+
+ // Draws a "flat" LED with the given color:
+ paint.drawEllipse( scale, scale, width - scale*2, width - scale*2 );
+
+ // Draw the bright light spot of the LED now, using modified "old"
+ // painter routine taken from KDEUI´s KLed widget:
+
+ // Setting the new width of the pen is essential to avoid "pixelized"
+ // shadow like it can be observed with the old LED code
+ pen.setWidth( 2 * scale );
+
+ // shrink the light on the LED to a size about 2/3 of the complete LED
+ int pos = width / 5 + 1;
+ int light_width = width;
+ light_width *= 2;
+ light_width /= 3;
+
+ // Calculate the LED´s "light factor":
+ int light_quote = (130 * 2 / (light_width ? light_width : 1)) + 100;
+
+ // Now draw the bright spot on the LED:
+ while (light_width)
+ {
+ color = color.light( light_quote ); // make color lighter
+ pen.setColor( color ); // set color as pen color
+ paint.setPen( pen ); // select the pen for drawing
+ paint.drawEllipse( pos, pos, light_width, light_width ); // draw the ellipse (circle)
+ light_width--;
+ if (!light_width)
+ break;
+ paint.drawEllipse( pos, pos, light_width, light_width );
+ light_width--;
+ if (!light_width)
+ break;
+ paint.drawEllipse( pos, pos, light_width, light_width );
+ pos++;
+ light_width--;
+ }
+
+ // Drawing of bright spot finished, now draw a thin border
+ // around the LED which resembles a shadow with light coming
+ // from the upper left.
+
+ pen.setWidth( 2 * scale + 1 ); // ### shouldn't this value be smaller for smaller LEDs?
+ brush.setStyle( QBrush::NoBrush ); // Switch off the brush
+ paint.setBrush( brush ); // This avoids filling of the ellipse
+
+ // Set the initial color value to colorGroup().light() (bright) and start
+ // drawing the shadow border at 45° (45*16 = 720).
+
+ int angle = -720;
+ color = colorGroup().light();
+
+ for ( int arc = 120; arc < 2880; arc += 240 )
+ {
+ pen.setColor( color );
+ paint.setPen( pen );
+ int w = width - pen.width() / 2 - scale + 1;
+ paint.drawArc( pen.width() / 2, pen.width() / 2, w, w, angle + arc, 240 );
+ paint.drawArc( pen.width() / 2, pen.width() / 2, w, w, angle - arc, 240 );
+ color = color.dark( 110 ); //FIXME: this should somehow use the contrast value
+ } // end for ( angle = 720; angle < 6480; angle += 160 )
+
+ paint.end();
+ //
+ // painting done
+
+ if (smooth)
+ {
+ QPixmap *&dest = led_state ? d->on_map : d->off_map;
+ QImage i = tmpMap->convertToImage();
+ width /= 3;
+ i = i.smoothScale(width, width);
+ delete tmpMap;
+ dest = new QPixmap(i);
+ paint.begin(this);
+ paint.drawPixmap(0, 0, *dest);
+ paint.end();
+ }
+}
+
+void
+KLed::paintRect()
+{
+ QPainter painter(this);
+ QBrush lightBrush(led_color);
+ QBrush darkBrush(d->offcolor);
+ QPen pen(led_color.dark(300));
+ int w = width();
+ int h = height();
+ // -----
+ switch (led_state) {
+ case On:
+ painter.setBrush(lightBrush);
+ painter.drawRect(0, 0, w, h);
+ break;
+ case Off:
+ painter.setBrush(darkBrush);
+ painter.drawRect(0, 0, w, h);
+ painter.setPen(pen);
+ painter.drawLine(0, 0, w, 0);
+ painter.drawLine(0, h - 1, w, h - 1);
+ // Draw verticals
+ int i;
+ for (i = 0; i < w; i += 4 /* dx */)
+ painter.drawLine(i, 1, i, h - 1);
+ break;
+ default:
+ break;
+ }
+}
+
+void
+KLed::paintRectFrame(bool raised)
+{
+ QPainter painter(this);
+ QBrush lightBrush(led_color);
+ QBrush darkBrush(d->offcolor);
+ int w = width();
+ int h = height();
+ QColor black = Qt::black;
+ QColor white = Qt::white;
+ // -----
+ if (raised) {
+ painter.setPen(white);
+ painter.drawLine(0, 0, 0, h - 1);
+ painter.drawLine(1, 0, w - 1, 0);
+ painter.setPen(black);
+ painter.drawLine(1, h - 1, w - 1, h - 1);
+ painter.drawLine(w - 1, 1, w - 1, h - 1);
+ painter.fillRect(1, 1, w - 2, h - 2,
+ (led_state == On) ? lightBrush : darkBrush);
+ } else {
+ painter.setPen(black);
+ painter.drawRect(0, 0, w, h);
+ painter.drawRect(0, 0, w - 1, h - 1);
+ painter.setPen(white);
+ painter.drawRect(1, 1, w - 1, h - 1);
+ painter.fillRect(2, 2, w - 4, h - 4,
+ (led_state == On) ? lightBrush : darkBrush);
+ }
+}
+
+KLed::State
+KLed::state() const
+{
+ return led_state;
+}
+
+KLed::Shape
+KLed::shape() const
+{
+ return led_shape;
+}
+
+QColor
+KLed::color() const
+{
+ return led_color;
+}
+
+KLed::Look
+KLed::look() const
+{
+ return led_look;
+}
+
+void
+KLed::setState( State state )
+{
+ if (led_state != state) {
+ led_state = state;
+ update();
+ }
+}
+
+void
+KLed::toggleState()
+{
+ led_state = (led_state == On) ? Off : On;
+ // setColor(led_color);
+ update();
+}
+
+void
+KLed::setShape(KLed::Shape s)
+{
+ if (led_shape != s) {
+ led_shape = s;
+ update();
+ }
+}
+
+void
+KLed::setColor(const QColor& col)
+{
+ if (led_color != col) {
+ led_color = col;
+ d->offcolor = col.dark(d->dark_factor);
+ delete d->on_map;
+ d->on_map = 0;
+ delete d->off_map;
+ d->off_map = 0;
+ update();
+ }
+}
+
+void
+KLed::setDarkFactor(int darkfactor)
+{
+ if (d->dark_factor != darkfactor) {
+ d->dark_factor = darkfactor;
+ d->offcolor = led_color.dark(darkfactor);
+ update();
+ }
+}
+
+int
+KLed::darkFactor() const
+{
+ return d->dark_factor;
+}
+
+void
+KLed::setLook( Look look )
+{
+ if (led_look != look) {
+ led_look = look;
+ update();
+ }
+}
+
+void
+KLed::toggle()
+{
+ toggleState();
+}
+
+void
+KLed::on()
+{
+ setState(On);
+}
+
+void
+KLed::off()
+{
+ setState(Off);
+}
+
+QSize
+KLed::sizeHint() const
+{
+ return QSize(16, 16);
+}
+
+QSize
+KLed::minimumSizeHint() const
+{
+ return QSize(16, 16 );
+}
+
+void KLed::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/
+}
diff --git a/src/gui/kdeext/klearlook.cpp b/src/gui/kdeext/klearlook.cpp
new file mode 100644
index 0000000..3e5b986
--- /dev/null
+++ b/src/gui/kdeext/klearlook.cpp
@@ -0,0 +1,4095 @@
+/* $Id: klearlook.cpp,v 1.25 2006/04/26 18:55:41 jck Exp $
+
+Klearlook (C) Joerg C. Koenig, 2005 jck@gmx.org
+
+----
+
+Based upon QtCurve (C) Craig Drummond, 2003 Craig.Drummond@lycos.co.uk
+ Bernhard Rosenkr�zer <bero@r?dh?t.com>
+ Preston Brown <pbrown@r?dh?t.com>
+ Than Ngo <than@r?dh?t.com>
+
+Released under the GNU General Public License (GPL) v2.
+
+----
+
+B???Curve is based on the KDE Light style, 2nd revision:
+Copyright(c)2000-2001 Trolltech AS (info@trolltech.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files(the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#include <kdeversion.h>
+#include <qmenubar.h>
+#include <qapplication.h>
+#include <qpainter.h>
+#include <qpalette.h>
+#include <qframe.h>
+#include <qpushbutton.h>
+#include <qdrawutil.h>
+#include <qscrollbar.h>
+#include <qtabbar.h>
+#include <qtabwidget.h>
+#include <qguardedptr.h>
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qlistview.h>
+#include <qbitmap.h>
+#include <qcleanuphandler.h>
+#include <qimage.h>
+#include <qcombobox.h>
+#include <qspinbox.h>
+#include <qslider.h>
+#include <qstylefactory.h>
+#include <qcleanuphandler.h>
+#include <qcheckbox.h>
+#include <qradiobutton.h>
+#include <qtoolbutton.h>
+#include <qtoolbar.h>
+#include <qprogressbar.h>
+#include <qcursor.h>
+#include <qheader.h>
+#include <qwidgetstack.h>
+#include <qsplitter.h>
+#include <math.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include "klearlook.h"
+#include <qsettings.h>
+
+#if KDE_VERSION >= 0x30200
+#include <qfile.h>
+#include <qdir.h>
+#endif
+
+// Uncomment the following to enable gradients in toolbars and menubars
+// NOTE: Not yet complete!!!
+//#define QTC_GRADIENT_TOOLBARS_AND_MENUBARS
+
+//static int HIGH_LIGHT_FACTORS[]={ 100, 100, 100, 101, 102, 103, 104, 105 /*def*/, 106, 107, 108 };
+//#define HIGHLIGHT_FACTOR(X) (X<0||X>10) ? 105 : HIGH_LIGHT_FACTORS[X]
+#define QTC_HIGHLIGHT_FACTOR 105
+#define QTC_BORDERED_FRAME_WIDTH 2
+#define QTC_DEF_FRAME_WIDTH 1
+//#define QTC_MIN_BTN_SIZE 10
+#define QTC_NO_SECT -1
+
+#define MENU_POPUP_ITEM_HIGH_HI 7
+#define MENU_POPUP_ITEM_HIGH_LO 5
+//#define MENU_POPUP_SHADOW
+
+#define POS_DIV(a, b) ( (a)/(b) + ( ( (a) % (b) >= (b)/2 ) ? 1 : 0 ) )
+
+static const int itemHMargin = 6;
+static const int itemFrame = 2;
+static const int arrowHMargin = 6;
+static const int rightBorder = 12;
+
+#if KDE_VERSION >= 0x30200
+// Try to read $KDEHOME/share/config/kickerrc to find out if kicker is transparent...
+
+static QString readEnvPath( const char *env ) {
+ QCString path = getenv( env );
+
+ return path.isEmpty()
+ ? QString::null
+ : QFile::decodeName( path );
+}
+
+static bool kickerIsTrans() {
+ QString kdeHome( readEnvPath( getuid() ? "KDEHOME" : "KDEROOTHOME" ) ),
+ cfgFileName;
+ bool trans = false;
+
+ if ( kdeHome.isEmpty() )
+ cfgFileName = QDir::homeDirPath() + "/.kde/share/config/kickerrc";
+ else
+ cfgFileName = QString( kdeHome ) + "/share/config/kickerrc";
+
+ QFile cfgFile( cfgFileName );
+
+ if ( cfgFile.open( IO_ReadOnly ) ) {
+ QTextStream stream( &cfgFile );
+ QString line;
+ bool stop = false,
+ inGen = false;
+
+ while ( !stream.atEnd() && !stop ) {
+ line = stream.readLine();
+
+ if ( inGen ) {
+ if ( 0 == line.find( "Transparent=" ) ) // Found it!
+ {
+ if ( -1 != line.find( "true" ) )
+ trans = true;
+ stop = true;
+ } else if ( line[ 0 ] == QChar( '[' ) ) // Then wasn't in General section...
+ stop = true;
+ } else if ( 0 == line.find( "[General]" ) )
+ inGen = true;
+ }
+ cfgFile.close();
+ }
+
+ return trans;
+}
+#endif
+
+inline int limit( double c ) {
+ return c < 0.0
+ ? 0
+ : c > 255.0
+ ? 255
+ : ( int ) c;
+}
+
+inline QColor midColor( const QColor &a, const QColor &b, double factor = 1.0 ) {
+ return QColor( ( a.red() + limit( b.red() * factor ) ) >> 1,
+ ( a.green() + limit( b.green() * factor ) ) >> 1,
+ ( a.blue() + limit( b.blue() * factor ) ) >> 1 );
+}
+
+// Copied from Keramik...
+static bool isFormWidget( const QWidget *widget ) {
+ //Form widgets are in the KHTMLView, but that has 2 further inner levels
+ //of widgets - QClipperWidget, and outside of that, QViewportWidget
+ QWidget * potentialClipPort = widget->parentWidget();
+
+ if ( !potentialClipPort || potentialClipPort->isTopLevel() )
+ return false;
+
+ QWidget *potentialViewPort = potentialClipPort->parentWidget();
+
+ if ( !potentialViewPort || potentialViewPort->isTopLevel() || qstrcmp( potentialViewPort->name(), "qt_viewport" ) )
+ return false;
+
+ QWidget *potentialKHTML = potentialViewPort->parentWidget();
+
+ if ( !potentialKHTML || potentialKHTML->isTopLevel() || qstrcmp( potentialKHTML->className(), "KHTMLView" ) )
+ return false;
+
+ return true;
+}
+
+static void rgb2hls( double *r, double *g, double *b )
+{
+ double min;
+ double max;
+ double red;
+ double green;
+ double blue;
+ double h, l, s;
+ double delta;
+
+ red = *r;
+ green = *g;
+ blue = *b;
+
+ if (red > green)
+ {
+ if (red > blue)
+ max = red;
+ else
+ max = blue;
+
+ if (green < blue)
+ min = green;
+ else
+ min = blue;
+ }
+ else
+ {
+ if (green > blue)
+ max = green;
+ else
+ max = blue;
+
+ if (red < blue)
+ min = red;
+ else
+ min = blue;
+ }
+
+ l = (max + min) / 2;
+ s = 0;
+ h = 0;
+
+ if (max != min)
+ {
+ if (l <= 0.5)
+ s = (max - min) / (max + min);
+ else
+ s = (max - min) / (2 - max - min);
+
+ delta = max -min;
+ if (red == max)
+ h = (green - blue) / delta;
+ else if (green == max)
+ h = 2 + (blue - red) / delta;
+ else if (blue == max)
+ h = 4 + (red - green) / delta;
+
+ h *= 60;
+ if (h < 0.0)
+ h += 360;
+ }
+
+ *r = h;
+ *g = l;
+ *b = s;
+}
+
+
+static void hls2rgb( double *h, double *l, double *s ) {
+ double hue;
+ double lightness;
+ double saturation;
+ double m1, m2;
+ double r, g, b;
+ lightness = *l;
+ saturation = *s;
+
+ if (lightness <= 0.5)
+ m2 = lightness * (1 + saturation);
+ else
+ m2 = lightness + saturation - lightness * saturation;
+
+ m1 = 2 * lightness - m2;
+
+ if (saturation == 0)
+ {
+ *h = lightness;
+ *l = lightness;
+ *s = lightness;
+ }
+ else
+ {
+ hue = *h + 120;
+ while (hue > 360)
+ hue -= 360;
+ while (hue < 0)
+ hue += 360;
+
+ if (hue < 60)
+ r = m1 + (m2 - m1) * hue / 60;
+ else if (hue < 180)
+ r = m2;
+ else if (hue < 240)
+ r = m1 + (m2 - m1) * (240 - hue) / 60;
+ else
+ r = m1;
+
+ hue = *h;
+ while (hue > 360)
+ hue -= 360;
+ while (hue < 0)
+ hue += 360;
+
+ if (hue < 60)
+ g = m1 + (m2 - m1) * hue / 60;
+ else if (hue < 180)
+ g = m2;
+ else if (hue < 240)
+ g = m1 + (m2 - m1) * (240 - hue) / 60;
+ else
+ g = m1;
+
+ hue = *h - 120;
+ while (hue > 360)
+ hue -= 360;
+ while (hue < 0)
+ hue += 360;
+
+ if (hue < 60)
+ b = m1 + (m2 - m1) * hue / 60;
+ else if (hue < 180)
+ b = m2;
+ else if (hue < 240)
+ b = m1 + (m2 - m1) * (240 - hue) / 60;
+ else
+ b = m1;
+
+ *h = r;
+ *l = g;
+ *s = b;
+ }
+
+}
+
+static bool equal( double d1, double d2 ) {
+ return ( fabs( d1 - d2 ) < 0.0001 );
+}
+
+static void shade( const QColor &a, QColor *b, float k ) {
+
+ if ( equal( k, 1.0 ) )
+ * b = a;
+ else {
+ double red = a.red() / 256.0,
+ green = a.green() / 256.0,
+ blue = a.blue() / 256.0;
+
+ rgb2hls( &red, &green, &blue );
+
+ green *= k;
+ if ( green > 1.0 )
+ green = 1.0;
+ else if ( green < 0.0 )
+ green = 0.0;
+
+ blue *= k;
+ if ( blue > 1.0 )
+ blue = 1.0;
+ else if ( blue < 0.0 )
+ blue = 0.0;
+
+ hls2rgb( &red, &green, &blue );
+
+ b->setRgb( limit( red * 256 ), limit( green * 256 ), limit( blue * 256 ) );
+ }
+}
+
+static void shadeGradient( const QColor &base, QColor *vals ) {
+ vals[ KlearlookStyle::GRADIENT_BASE ] = base;
+
+ shade( vals[ KlearlookStyle::GRADIENT_BASE ],
+ &( vals[ KlearlookStyle::GRADIENT_TOP ] ), SHADE_GRADIENT_TOP );
+
+ shade( vals[ KlearlookStyle::GRADIENT_BASE ],
+ &( vals[ KlearlookStyle::GRADIENT_BOTTOM ] ), SHADE_GRADIENT_BOTTOM );
+
+ shade( vals[ KlearlookStyle::GRADIENT_BASE ],
+ &( vals[ KlearlookStyle::GRADIENT_LIGHT ] ), SHADE_GRADIENT_LIGHT );
+ shade( vals[ KlearlookStyle::GRADIENT_BASE ],
+ &( vals[ KlearlookStyle::GRADIENT_DARK ] ), SHADE_GRADIENT_DARK );
+}
+
+static void drawLines( QPainter *p, const QRect &r, bool horiz, int nLines, int offset, const QColor *cols,
+ int startOffset, bool etched, bool lightGradient ) {
+ int space = ( nLines * 2 ) + ( nLines - 1 ),
+ x = horiz ? r.x() : r.x() + ( ( r.width() - space ) >> 1 ),
+ y = horiz ? r.y() + ( ( r.height() - space ) >> 1 ) : r.y(),
+ x2 = r.x() + r.width() - 1,
+ y2 = r.y() + r.height() - 1,
+ i,
+ displacement = etched ? 1 : 0;
+
+ if ( horiz ) {
+ if ( startOffset && y + startOffset > 0 )
+ y += startOffset;
+
+ p->setPen( cols[ etched ? lightGradient ? 3 : 4 : 0 ] );
+ for ( i = 0; i < space; i += 3 )
+ p->drawLine( x + offset, y + i, x2 - ( offset + displacement ), y + i );
+
+ p->setPen( cols[ etched ? 0 : lightGradient ? 3 : 4 ] );
+ for ( i = 1; i < space; i += 3 )
+ p->drawLine( x + offset + displacement, y + i - 2, x2 - offset, y + i - 2);
+ } else {
+ if ( startOffset && x + startOffset > 0 )
+ x += startOffset;
+
+ p->setPen( cols[ etched ? lightGradient ? 3 : 4 : 0 ] );
+ for ( i = 0; i < space; i += 3 )
+ p->drawLine( x + i, y + offset, x + i, y2 - ( offset + displacement ) );
+
+ p->setPen( cols[ etched ? 0 : lightGradient ? 3 : 4 ] );
+ for ( i = 1; i < space; i += 3 )
+ p->drawLine( x + i -2, y + offset + displacement, x + i -2, y2 - offset );
+ }
+}
+
+inline QColor getFill( QStyle::SFlags flags, const QColor *use ) {
+ return !( flags & QStyle::Style_Enabled )
+ ? use[ 1 ]
+ : flags & QStyle::Style_Down
+ ? use[ 3 ]
+ : flags & QStyle::Style_MouseOver
+ ? flags & ( QStyle::Style_On | QStyle::Style_Sunken )
+ ? use[ 3 ].light( QTC_HIGHLIGHT_FACTOR )
+ : use[ NUM_SHADES ].light( QTC_HIGHLIGHT_FACTOR )
+ : flags & ( QStyle::Style_On | QStyle::Style_Sunken )
+ ? use[ 3 ]
+ : use[ NUM_SHADES ];
+}
+
+#ifdef USE_SINGLE_STYLE
+KlearlookStyle::KlearlookStyle()
+#else
+KlearlookStyle::KlearlookStyle(
+ bool gpm, bool bb, bool bf, bool round, EGroove st, h,
+ bool ge, bool va, bool bdt, bool crlh, EDefBtnIndicator dbi, ETBarBorder tbb,
+ ELvExpander lve, ELvLines lvl, bool lvd, bool ico, int popuplvl ) )
+#endif
+: KStyle( AllowMenuTransparency, WindowsStyleScrollBar ),
+themedApp( APP_OTHER ),
+#ifndef USE_SINGLE_STYLE
+borderButton( bb ), borderFrame( bf ), rounded( round ), etchedSlider( etched ), appearance( ge ? APPEARANCE_GRADIENT : APPEARANCE_FLAT ),
+pmProfile( PROFILE_SUNKEN ), vArrow( va ), boldDefText( bdt ), crLabelHighlight( crlh ), lvDark( lvd ),
+defBtnIndicator( dbi ), sliderThumbs( st ), handles( h ), toolbarBorders( tbb ), lvExpander( lve ), lvLines( lvl ), menuIcons( ico ), borderSplitter( true ), popupmenuHighlightLevel(popuplvl)
+#endif
+#if KDE_VERSION >= 0x30200
+isTransKicker( false ),
+#endif
+hover( HOVER_NONE ),
+oldCursor( -1, -1 ),
+formMode( false ),
+hoverWidget( NULL ),
+hoverSect( QTC_NO_SECT ) {
+ QSettings s;
+
+ contrast = s.readNumEntry( "/Qt/KDE/contrast", 7 );
+ if ( contrast < 0 || contrast > 10 )
+ contrast = 7;
+#ifdef USE_SINGLE_STYLE
+
+ borderButton = borderFrame = s.readBoolEntry( "/klearlookstyle/Settings/border", true );
+ rounded = borderButton ? s.readBoolEntry( "/klearlookstyle/Settings/round", true ) : false;
+ menuIcons = s.readBoolEntry( "/klearlookstyle/Settings/icons", true );
+ darkMenubar = s.readBoolEntry( "/klearlookstyle/Settings/darkMenubar", true );
+ popupmenuHighlightLevel = s.readNumEntry( "/klearlookstyle/Settings/popupmenuHighlightLevel", 3);
+
+ QString tmp = s.readEntry( "/klearlookstyle/Settings/toolbarBorders", QString::null );
+ toolbarBorders = tmp.isEmpty()
+ ? TB_LIGHT
+ : qtc_to_tbar_border( tmp.latin1() );
+
+ bool etched = s.readBoolEntry( "/klearlookstyle/Settings/etched", true );
+
+ tmp = s.readEntry( "/klearlookstyle/Settings/sliderThumbs", QString::null );
+ sliderThumbs = tmp.isEmpty()
+ ? etched ? GROOVE_SUNKEN : GROOVE_RAISED
+ : qtc_to_groove( tmp.latin1() );
+
+ tmp = s.readEntry( "/klearlookstyle/Settings/lvExpander", QString::null );
+ lvExpander = tmp.isEmpty()
+ ? LV_EXP_ARR
+ : qtc_to_lv_expander( tmp.latin1() );
+
+ tmp = s.readEntry( "/klearlookstyle/Settings/lvLines", QString::null );
+ lvLines = tmp.isEmpty()
+ ? LV_LINES_SOLID
+ : qtc_to_lv_lines( tmp.latin1() );
+
+
+ lvDark = s.readBoolEntry( "/klearlookstyle/Settings/lvDark", false );
+ handles = qtc_to_groove( s.readEntry( "/klearlookstyle/Settings/sliderThumbs", DEF_HANDLE_STR ).latin1() );
+
+ if ( GROOVE_NONE == handles )
+ handles = GROOVE_RAISED;
+
+ appearance = qtc_to_appearance(
+ s.readEntry( "/klearlookstyle/Settings/appearance", DEF_APPEARANCE_STR ).latin1() );
+ pmProfile = qtc_to_profile( s.readEntry( "/klearlookstyle/Settings/pm", DEF_PROFILE_STR ).latin1() );
+ vArrow = s.readBoolEntry( "/klearlookstyle/Settings/vArrow", false );
+ boldDefText = s.readBoolEntry( "/klearlookstyle/Settings/embolden", false );
+ crLabelHighlight = s.readBoolEntry( "/klearlookstyle/Settings/crLabelHighlight", false );
+ defBtnIndicator = qtc_to_ind(
+ s.readEntry( "/klearlookstyle/Settings/defBtnIndicator", DEF_IND_STR ).latin1() );
+
+ //if(!boldDefText && IND_NONE==defBtnIndicator)
+ // defBtnIndicator=IND_CORNER;
+
+ borderSplitter = s.readBoolEntry( "/klearlookstyle/Settings/borderSplitter", false );
+#endif
+
+ if ( PROFILE_RAISED != pmProfile )
+ shadeColors( qApp->palette().active().highlight(), menuPbar );
+ else
+ shadeGradient( qApp->palette().active().highlight(), menuPbar );
+ shadeColors( qApp->palette().active().background(), gray );
+ shadeColors( qApp->palette().active().button(), button );
+}
+
+void KlearlookStyle::polish( QApplication *app ) {
+ if ( !qstrcmp( app->argv() [ 0 ], "kicker" ) || !qstrcmp( app->argv() [ 0 ], "appletproxy" ) ) {
+ themedApp = APP_KICKER;
+#if KDE_VERSION >= 0x30200
+
+ isTransKicker = rounded && kickerIsTrans();
+#endif
+
+ } else if ( !qstrcmp( app->argv() [ 0 ], "korn" ) ) {
+ themedApp = APP_KORN;
+#if KDE_VERSION >= 0x30200
+
+ isTransKicker = rounded && kickerIsTrans();
+#endif
+
+ } else
+ themedApp = qstrcmp( qApp->argv() [ 0 ], "soffice.bin" ) ? APP_OTHER : APP_OPENOFFICE;
+}
+
+void KlearlookStyle::polish( QPalette &pal ) {
+ int c = QSettings().readNumEntry( "/Qt/KDE/contrast", 7 );
+ bool newContrast = false;
+
+ if ( c < 0 || c > 10 )
+ c = 7;
+
+ if ( c != contrast ) {
+ contrast = c;
+ newContrast = true;
+ }
+
+ if ( newContrast || gray[ NUM_SHADES ] != qApp->palette().active().background() )
+ shadeColors( qApp->palette().active().background(), gray );
+
+ if ( newContrast || button[ NUM_SHADES ] != qApp->palette().active().button() )
+ shadeColors( qApp->palette().active().button(), button );
+
+ if ( PROFILE_RAISED == pmProfile ) {
+ if ( newContrast || menuPbar[ NUM_SHADES ] != qApp->palette().active().highlight() )
+ shadeColors( qApp->palette().active().highlight(), menuPbar );
+ } else
+ if ( qApp->palette().active().highlight() != menuPbar[ GRADIENT_BASE ] )
+ shadeGradient( qApp->palette().active().highlight(), menuPbar );
+
+ const QColorGroup &actGroup = pal.active(),
+ &inactGroup = pal.inactive();
+ const QColor *use = backgroundColors( actGroup );
+ QColorGroup newAct( actGroup.foreground(), actGroup.button(),
+ use[ 0 ], use[ 5 ], actGroup.mid(), actGroup.text(),
+ actGroup.brightText(), actGroup.base(), actGroup.background() );
+
+ newAct.setColor( QColorGroup::Highlight, actGroup.color( QColorGroup::Highlight ) );
+ pal.setActive( newAct );
+
+ use = backgroundColors( inactGroup );
+
+ QColorGroup newInact( inactGroup.foreground(), inactGroup.button(),
+ use[ 0 ], use[ 5 ], inactGroup.mid(), inactGroup.text(),
+ inactGroup.brightText(), inactGroup.base(), inactGroup.background() );
+
+ newInact.setColor( QColorGroup::Highlight, inactGroup.color( QColorGroup::Highlight ) );
+ pal.setInactive( newInact );
+}
+
+static const char * kdeToolbarWidget = "kde toolbar widget";
+
+void KlearlookStyle::polish( QWidget *widget ) {
+ if ( ::qt_cast<QRadioButton *>( widget )
+ || ::qt_cast<QCheckBox *>( widget )
+ || ::qt_cast<QSpinWidget *>( widget )
+ || widget->inherits( "QSplitterHandle" ) ) {
+#if QT_VERSION >= 0x030200
+ widget->setMouseTracking( true );
+#endif
+
+ widget->installEventFilter( this );
+ } else if ( ::qt_cast<QButton *>( widget ) || ::qt_cast<QComboBox *>( widget ) ||
+ widget->inherits( "QToolBarExtensionWidget" ) ) {
+ widget->setBackgroundMode( QWidget::PaletteBackground );
+ widget->installEventFilter( this );
+
+ } else if ( ::qt_cast<QMenuBar *>( widget )
+ || ::qt_cast<QToolBar *>( widget )
+ || ::qt_cast<QPopupMenu *>( widget ) )
+ widget->setBackgroundMode( QWidget::PaletteBackground );
+
+ else if ( widget->inherits( "KToolBarSeparator" ) ) {
+ widget->setBackgroundMode( QWidget::NoBackground );
+ widget->installEventFilter( this );
+
+ } else if ( ::qt_cast<QScrollBar *>( widget ) ) {
+ widget->setMouseTracking( true );
+ widget->installEventFilter( this );
+ widget->setBackgroundMode( QWidget::NoBackground );
+
+ } else if ( ::qt_cast<QSlider *>( widget ) || ::qt_cast<QHeader *>( widget ) ) {
+ widget->setMouseTracking( true );
+ widget->installEventFilter( this );
+
+ } else if ( 0 == qstrcmp( widget->name(), kdeToolbarWidget ) ) {
+ widget->installEventFilter( this );
+ widget->setBackgroundMode( QWidget::NoBackground ); // We paint the whole background.
+ }
+
+ KStyle::polish( widget );
+}
+
+void KlearlookStyle::unPolish( QWidget *widget ) {
+ if ( ::qt_cast<QRadioButton *>( widget ) ||
+ ::qt_cast<QCheckBox *>( widget ) ||
+ ::qt_cast<QSpinWidget *>( widget ) ||
+ widget->inherits( "QSplitterHandle" ) ) {
+#if QT_VERSION >= 0x030200
+ widget->setMouseTracking( false );
+#endif
+
+ widget->removeEventFilter( this );
+ } else if ( ::qt_cast<QButton *>( widget ) || ::qt_cast<QComboBox *>( widget ) ||
+ widget->inherits( "QToolBarExtensionWidget" ) ) {
+ widget->setBackgroundMode( QWidget::PaletteButton );
+ widget->removeEventFilter( this );
+
+ } else if ( ::qt_cast<QMenuBar *>( widget ) ||
+ ::qt_cast<QToolBar *>( widget ) ||
+ ::qt_cast<QPopupMenu *>( widget ) )
+ widget->setBackgroundMode( QWidget::PaletteBackground );
+
+ else if ( widget->inherits( "KToolBarSeparator" ) ) {
+ widget->setBackgroundMode( PaletteBackground );
+ widget->removeEventFilter( this );
+
+ } else if ( ::qt_cast<QScrollBar *>( widget ) ) {
+ widget->setMouseTracking( false );
+ widget->removeEventFilter( this );
+ widget->setBackgroundMode( QWidget::PaletteButton );
+
+ } else if ( ::qt_cast<QSlider *>( widget ) ||
+ ::qt_cast<QHeader *>( widget ) ) {
+ widget->setMouseTracking( false );
+ widget->removeEventFilter( this );
+
+ } else if ( 0 == qstrcmp( widget->name(), kdeToolbarWidget ) ) {
+ widget->removeEventFilter( this );
+ widget->setBackgroundMode( PaletteBackground );
+ }
+
+ KStyle::unPolish( widget );
+}
+
+bool KlearlookStyle::eventFilter( QObject *object, QEvent *event ) {
+ if ( object->parent() && 0 == qstrcmp( object->name(), kdeToolbarWidget ) ) {
+ // Draw background for custom widgets in the toolbar that have specified a "kde toolbar widget" name.
+ if ( QEvent::Paint == event->type() ) {
+ QWidget * widget = static_cast<QWidget*>( object ),
+ *parent = static_cast<QWidget*>( object->parent() );
+#ifdef QTC_GRADIENT_TOOLBARS_AND_MENUBARS
+ // Find the top-level toolbar of this widget, since it may be nested in other
+ // widgets that are on the toolbar.
+ int x_offset = widget->x(),
+ y_offset = widget->y();
+
+ while ( parent && parent->parent() && !qstrcmp( parent->name(), kdeToolbarWidget ) ) {
+ x_offset += parent->x();
+ y_offset += parent->y();
+ parent = static_cast<QWidget*>( parent->parent() );
+ }
+
+ QRect pr( parent->rect() );
+ bool horiz_grad = pr.width() < pr.height();
+
+ // Check if the parent is a QToolbar, and use its orientation, else guess.
+ QToolBar *toolbar = dynamic_cast<QToolBar*>( parent );
+
+ if ( toolbar )
+ horiz_grad = toolbar->orientation() == Qt::Vertical;
+
+ drawBevelGradient( parent->colorGroup().background(), true, 1, &QPainter( widget ),
+ QRect( x_offset, y_offset, pr.width(), pr.height() ),
+ horiz_grad, SHADE_BAR_LIGHT, SHADE_BAR_DARK );
+#else
+
+ QPainter( widget ).fillRect( widget->rect(), parent->colorGroup().background() );
+#endif
+
+ return false; // Now draw the contents
+ }
+ } else if ( object->inherits( "KToolBarSeparator" ) && QEvent::Paint == event->type() ) {
+ QFrame * frame = dynamic_cast<QFrame*>( object );
+
+ if ( frame && QFrame::NoFrame != frame->frameShape() ) {
+ QPainter painter( frame );
+ if ( QFrame::VLine == frame->frameShape() )
+ drawPrimitive( PE_DockWindowSeparator, &painter,
+ frame->rect(), frame->colorGroup(), Style_Horizontal );
+ else if ( QFrame::HLine == frame->frameShape() )
+ drawPrimitive( PE_DockWindowSeparator, &painter,
+ frame->rect(), frame->colorGroup() );
+ else
+ return false;
+ return true; // been drawn!
+ }
+ }
+ switch ( event->type() ) {
+ case QEvent::Enter:
+ if ( object->isWidgetType() ) {
+ hoverWidget = ( QWidget * ) object;
+ if ( hoverWidget && hoverWidget->isEnabled() ) {
+ if ( redrawHoverWidget() ) {
+ hoverWidget->repaint( false );
+ if ( APP_KICKER == themedApp )
+ hover = HOVER_NONE;
+ }
+ oldCursor = QCursor::pos();
+ } else
+ hoverWidget = NULL;
+ }
+ break;
+ case QEvent::Leave:
+ if ( hoverWidget && object == hoverWidget ) {
+ oldCursor.setX( -1 );
+ oldCursor.setY( -1 );
+ hoverWidget = NULL;
+ ( ( QWidget * ) object ) ->repaint( false );
+ }
+ break;
+ case QEvent::MouseMove:
+ if ( hoverWidget && object->isWidgetType() ) {
+ if ( redrawHoverWidget() ) {
+ hoverWidget->repaint( false );
+ if ( APP_KICKER == themedApp )
+ hover = HOVER_NONE;
+ }
+ oldCursor = QCursor::pos();
+ }
+ break;
+ default:
+ break;
+ }
+
+ return KStyle::eventFilter( object, event );
+}
+
+void KlearlookStyle::drawLightBevelButton(
+ QPainter *p,
+ const QRect &r,
+ const QColorGroup &cg,
+ QStyle::SFlags flags,
+ bool useGrad,
+ ERound round,
+ const QColor &fill,
+ const QColor *custom,
+ bool light ) const
+{
+ QRect br( r );
+ bool sunken = ( flags & ( QStyle::Style_Down | QStyle::Style_On | QStyle::Style_Sunken ) );
+ int dark = borderButton ? 4 : 5,
+ c1 = sunken ? dark : light ? 6 : 0;
+
+ p->save();
+
+ if ( !borderButton )
+ br.addCoords( -1, -1, 1, 1 );
+
+ if ( ( sunken && !borderButton ) || ( !sunken && flags & QStyle::Style_Raised ) ) {
+ p->setPen( custom ? custom[ c1 ] : gray[ c1 ] );
+ if ( APPEARANCE_LIGHT_GRADIENT != appearance ) {
+ int c2 = sunken ? 0 : dark;
+
+
+ p->drawLine( br.x() + 1, br.y() + 2, br.x() + 1, br.y() + br.height() - 3 ); // left
+ p->drawLine( br.x() + 1, br.y() + 1, br.x() + br.width() - 2, br.y() + 1 ); // top
+
+ p->setPen( custom ? custom[ c2 ] : gray[ c2 ] );
+ p->drawLine( br.x() + br.width() - 2, br.y() + 1,
+ br.x() + br.width() - 2, br.y() + br.height() - 3 ); // right
+ p->drawLine( br.x() + 1, br.y() + br.height() - 2,
+ br.x() + br.width() - 2, br.y() + br.height() - 2 ); // bottom
+
+ br.addCoords( 2, 2, -2, -2 );
+ } else {
+ p->drawLine( br.x() + 1, br.y() + 2, br.x() + 1, br.y() + br.height() - 2 ); // left
+ p->drawLine( br.x() + 1, br.y() + 1, br.x() + br.width() - 2, br.y() + 1 ); // top
+
+ br.addCoords( 2, 2, -1, -1 );
+ }
+ } else
+ br.addCoords( 1, 1, -1, -1 );
+
+ // fill
+ if ( useGrad && APPEARANCE_FLAT != appearance ) {
+ drawBevelGradient( fill.dark( 100 ), !sunken, 0, p,
+ QRect( br.left() - 1, br.top() - 1, br.width() + 2, br.height() + 2 ), flags & Style_Horizontal,
+ sunken ?
+ SHADE_BEVEL_BUTTON_GRAD_LIGHT( appearance ) :
+ SHADE_BEVEL_BUTTON_GRAD_LIGHT( appearance ),
+ sunken ?
+ SHADE_BEVEL_BUTTON_GRAD_DARK( appearance ) :
+ SHADE_BEVEL_BUTTON_GRAD_DARK( appearance ) );
+ } else
+ p->fillRect( br, fill );
+
+ if ( borderButton )
+ if ( rounded && ROUNDED_NONE != round ) {
+ bool wide = r.width() >= QTC_MIN_BTN_SIZE,
+ tall = r.height() >= QTC_MIN_BTN_SIZE;
+ QColor border( flags & Style_ButtonDefault && IND_FONT_COLOUR == defBtnIndicator & flags & Style_Enabled
+ ? cg.text() : custom ? custom[ 5 ] : gray[ 5 ] );
+
+ p->setPen( border.light(80) );
+ switch ( round ) {
+ case ROUNDED_ALL:
+ p->drawLine( r.x() + 2, r.y(), r.x() + r.width() - 3, r.y() );
+ p->drawLine( r.x() + 2, r.y() + r.height() - 1, r.x() + r.width() - 3,
+ r.y() + r.height() - 1 );
+ p->drawLine( r.x(), r.y() + 2, r.x(), r.y() + r.height() - 3 );
+ p->drawLine( r.x() + r.width() - 1, r.y() + 2, r.x() + r.width() - 1,
+ r.y() + r.height() - 3 );
+ if ( tall && wide ) {
+ p->drawPoint( r.x() + r.width() - 2, r.y() + 1 );
+ p->drawPoint( r.x() + 1, r.y() + r.height() - 2 );
+ p->drawPoint( r.x() + r.width() - 2, r.y() + r.height() - 2 );
+ p->drawPoint( r.x() + 1, r.y() + 1 );
+ p->setPen( midColor( border, cg.background() ) );
+ }
+ if ( !formMode || !( tall && wide ) ) {
+ p->drawLine( r.x(), r.y() + 1, r.x() + 1, r.y() );
+ p->drawLine( r.x() + r.width() - 2, r.y(), r.x() + r.width() - 1, r.y() + 1 );
+ p->drawLine( r.x(), r.y() + r.height() - 2, r.x() + 1, r.y() + r.height() - 1 );
+ p->drawLine( r.x() + r.width() - 2, r.y() + r.height() - 1,
+ r.x() + r.width() - 1, r.y() + r.height() - 2 );
+ }
+ if ( !formMode ) {
+ if ( tall && wide )
+ p->setPen( cg.background() );
+ else
+ p->setPen( midColor( custom ? custom[ 3 ] : gray[ 3 ], cg.background() ) );
+ p->drawPoint( r.x(), r.y() );
+ p->drawPoint( r.x() + r.width() - 1, r.y() );
+ p->drawPoint( r.x(), r.y() + r.height() - 1 );
+ p->drawPoint( r.x() + r.width() - 1, r.y() + r.height() - 1 );
+ }
+ break;
+ case ROUNDED_TOP:
+ p->drawLine( r.x() + 2, r.y(), r.x() + r.width() - 3, r.y() );
+ p->drawLine( r.x() + 1, r.y() + r.height() - 1,
+ r.x() + r.width() - 2, r.y() + r.height() - 1 );
+ p->drawLine( r.x(), r.y() + 2, r.x(), r.y() + r.height() - 1 );
+ p->drawLine( r.x() + r.width() - 1, r.y() + 2,
+ r.x() + r.width() - 1, r.y() + r.height() - 1 );
+ if ( wide ) {
+ p->drawPoint( r.x() + r.width() - 2, r.y() + 1 );
+ p->drawPoint( r.x() + 1, r.y() + 1 );
+ p->setPen( midColor( border, cg.background() ) );
+ }
+ if ( !formMode || !wide ) {
+ p->drawLine( r.x(), r.y() + 1, r.x() + 1, r.y() );
+ p->drawLine( r.x() + r.width() - 2, r.y(), r.x() + r.width() - 1, r.y() + 1 );
+ }
+ if ( !formMode ) {
+ if ( wide )
+ p->setPen( cg.background() );
+ else
+ p->setPen( midColor( custom ? custom[ 3 ] : gray[ 3 ], cg.background() ) );
+ p->drawPoint( r.x(), r.y() );
+ p->drawPoint( r.x() + r.width() - 1, r.y() );
+ }
+ break;
+ case ROUNDED_BOTTOM:
+ p->drawLine( r.x() + 1, r.y(), r.x() + r.width() - 2, r.y() );
+ p->drawLine( r.x() + 2, r.y() + r.height() - 1,
+ r.x() + r.width() - 3, r.y() + r.height() - 1 );
+ p->drawLine( r.x(), r.y(), r.x(), r.y() + r.height() - 3 );
+ p->drawLine( r.x() + r.width() - 1, r.y(),
+ r.x() + r.width() - 1, r.y() + r.height() - 3 );
+ if ( wide ) {
+ p->drawPoint( r.x() + 1, r.y() + r.height() - 2 );
+ p->drawPoint( r.x() + r.width() - 2, r.y() + r.height() - 2 );
+ p->setPen( midColor( border, cg.background() ) );
+ }
+ if ( !formMode || !wide ) {
+ p->drawLine( r.x(), r.y() + r.height() - 2, r.x() + 1, r.y() + r.height() - 1 );
+ p->drawLine( r.x() + r.width() - 2, r.y() + r.height() - 1,
+ r.x() + r.width() - 1, r.y() + r.height() - 2 );
+ }
+ if ( !formMode ) {
+ if ( wide )
+ p->setPen( cg.background() );
+ else
+ p->setPen( midColor( custom ? custom[ 3 ] : gray[ 3 ], cg.background() ) );
+ p->drawPoint( r.x(), r.y() + r.height() - 1 );
+ p->drawPoint( r.x() + r.width() - 1, r.y() + r.height() - 1 );
+ }
+ break;
+ case ROUNDED_LEFT:
+ p->drawLine( r.x() + 2, r.y(), r.x() + r.width() - 2, r.y() );
+ p->drawLine( r.x() + 2, r.y() + r.height() - 1,
+ r.x() + r.width() - 2, r.y() + r.height() - 1 );
+ p->drawLine( r.x(), r.y() + 2, r.x(), r.y() + r.height() - 3 );
+ p->drawLine( r.x() + r.width() - 1, r.y(), r.x() + r.width() - 1,
+ r.y() + r.height() - 1 );
+ if ( tall ) {
+ p->drawPoint( r.x() + 1, r.y() + r.height() - 2 );
+ p->drawPoint( r.x() + 1, r.y() + 1 );
+ p->setPen( midColor( border, cg.background() ) );
+ }
+ if ( !formMode || !tall ) {
+ p->drawLine( r.x(), r.y() + 1, r.x() + 1, r.y() );
+ p->drawLine( r.x(), r.y() + r.height() - 2,
+ r.x() + 1, r.y() + r.height() - 1 );
+ }
+ if ( !formMode ) {
+ if ( tall )
+ p->setPen( cg.background() );
+ else
+ p->setPen( midColor( custom ? custom[ 3 ] : gray[ 3 ], cg.background() ) );
+ p->drawPoint( r.x(), r.y() );
+ p->drawPoint( r.x(), r.y() + r.height() - 1 );
+ }
+ break;
+ case ROUNDED_RIGHT:
+ p->drawLine( r.x() + 1, r.y(), r.x() + r.width() - 3, r.y() );
+ p->drawLine( r.x() + 1, r.y() + r.height() - 1,
+ r.x() + r.width() - 3, r.y() + r.height() - 1 );
+ p->drawLine( r.x(), r.y(), r.x(), r.y() + r.height() - 1 );
+ p->drawLine( r.x() + r.width() - 1, r.y() + 2,
+ r.x() + r.width() - 1, r.y() + r.height() - 3 );
+ if ( tall ) {
+ p->drawPoint( r.x() + r.width() - 2, r.y() + 1 );
+ p->drawPoint( r.x() + r.width() - 2, r.y() + r.height() - 2 );
+ p->setPen( midColor( border, cg.background() ) );
+ }
+ if ( !formMode || !tall ) {
+ p->drawLine( r.x() + r.width() - 2, r.y(), r.x() + r.width() - 1, r.y() + 1 );
+ p->drawLine( r.x() + r.width() - 2, r.y() + r.height() - 1,
+ r.x() + r.width() - 1, r.y() + r.height() - 2 );
+ }
+ if ( !formMode ) {
+ if ( tall )
+ p->setPen( cg.background() );
+ else
+ p->setPen( midColor( custom ? custom[ 3 ] : gray[ 3 ], cg.background() ) );
+ p->drawPoint( r.x() + r.width() - 1, r.y() );
+ p->drawPoint( r.x() + r.width() - 1, r.y() + r.height() - 1 );
+ }
+ break;
+ default:
+ break;
+ }
+ } else {
+ p->setPen( flags & Style_ButtonDefault &&
+ IND_FONT_COLOUR == defBtnIndicator ?
+ cg.text() : custom ? custom[ 5 ] : gray[ 5 ] );
+ p->setBrush( NoBrush );
+ p->drawRect( r );
+ }
+
+ p->restore();
+}
+void KlearlookStyle::drawLightBevel(
+ QPainter *p,
+ const QRect &r,
+ const QColorGroup &cg,
+ QStyle::SFlags flags,
+ bool useGrad,
+ ERound round,
+ const QColor &fill,
+ const QColor *custom,
+ bool light ) const
+{
+ QRect br( r );
+ bool sunken = ( flags & ( QStyle::Style_Down | QStyle::Style_On | QStyle::Style_Sunken ) );
+ int dark = borderButton ? 4 : 5, c1 = sunken ? dark : light ? 6 : 0;
+
+ p->save();
+
+ if ( !borderButton )
+ br.addCoords( -1, -1, 1, 1 );
+
+ if ( ( sunken && !borderButton ) || ( !sunken && flags & QStyle::Style_Raised ) ) {
+ p->setPen( custom ? custom[ c1 ] : gray[ c1 ] );
+ if ( APPEARANCE_LIGHT_GRADIENT != appearance ) {
+ int c2 = sunken ? 0 : dark;
+
+ p->drawLine( br.x() + 1, br.y() + 2, br.x() + 1, br.y() + br.height() - 3 ); // left
+ p->drawLine( br.x() + 1, br.y() + 1, br.x() + br.width() - 2, br.y() + 1 ); // top
+
+ p->setPen( custom ? custom[ c2 ] : gray[ c2 ] );
+ p->drawLine( br.x() + br.width() - 2, br.y() + 1,
+ br.x() + br.width() - 2, br.y() + br.height() - 3 ); // right
+ p->drawLine( br.x() + 1, br.y() + br.height() - 2,
+ br.x() + br.width() - 2, br.y() + br.height() - 2 ); // bottom
+
+ br.addCoords( 2, 2, -2, -2 );
+ } else {
+ p->drawLine( br.x() + 1, br.y() + 2, br.x() + 1, br.y() + br.height() - 2 ); // left
+ p->drawLine( br.x() + 1, br.y() + 1, br.x() + br.width() - 2, br.y() + 1 ); // top
+
+ br.addCoords( 2, 2, -1, -1 );
+ }
+ } else
+ br.addCoords( 1, 1, -1, -1 );
+
+ // fill
+ if ( useGrad && APPEARANCE_FLAT != appearance ) {
+ drawBevelGradient( fill, !sunken, 0, p,
+ QRect( br.left() - 1, br.top() - 1, br.width() + 2, br.height() + 2 ),
+ flags & Style_Horizontal,
+ sunken ?
+ SHADE_BEVEL_GRAD_SEL_LIGHT( appearance ) :
+ SHADE_BEVEL_GRAD_LIGHT( appearance ),
+ sunken ?
+ SHADE_BEVEL_GRAD_SEL_DARK( appearance ) :
+ SHADE_BEVEL_GRAD_DARK( appearance ) );
+ } else {
+ p->fillRect( br, fill );
+ }
+
+ if ( borderButton )
+ if ( rounded && ROUNDED_NONE != round ) {
+ bool wide = r.width() >= QTC_MIN_BTN_SIZE,
+ tall = r.height() >= QTC_MIN_BTN_SIZE;
+
+ QColor border = menuPbar[ GRADIENT_BASE ].dark( 130 );
+
+ p->setPen( border );
+
+ switch ( round ) {
+ case ROUNDED_ALL:
+ p->drawLine( r.x() + 1, r.y(), r.x() + r.width() - 2, r.y() ); // top
+ p->drawLine( r.x() + 1, r.y() + r.height() - 1, r.x() + r.width() - 2, r.y() + r.height() - 1 ); // bottom
+ p->drawLine( r.x(),r.y() + 1, r.x(),r.y() + r.height() - 2 ); // left
+ p->drawLine( r.x() + r.width() - 1, r.y() + 1, r.x() + r.width() - 1, r.y() + r.height() - 2 ); // right
+
+ //p->drawLine( r.x() + 2, r.y() + r.height() - 1, r.x() + r.width() - 3, r.y() + r.height() - 1 );
+
+ //p->drawLine( r.x(), r.y() + 2, r.x(), r.y() + r.height() - 3 );
+ //p->drawLine( r.x() + r.width() - 1, r.y() + 2, r.x() + r.width() - 1, r.y() + r.height() - 3 );
+
+ if ( tall && wide ) {
+ //p->drawPoint( r.x() + r.width() - 2, r.y() + 1 );
+ //p->drawPoint( r.x() + 1, r.y() + r.height() - 2 );
+ //p->drawPoint( r.x() + r.width() - 2, r.y() + r.height() - 2 );
+ //p->drawPoint( r.x() + 1, r.y() + 1 );
+ p->setPen( midColor( border, cg.background() ) );
+ }
+ if ( !formMode || !( tall && wide ) ) {
+ //p->drawLine( r.x(), r.y() + 1, r.x() + 1, r.y() );
+ //p->drawLine( r.x() + r.width() - 2, r.y(), r.x() + r.width() - 1, r.y() + 1 );
+ //p->drawLine( r.x(), r.y() + r.height() - 2, r.x() + 1, r.y() + r.height() - 1 );
+ //p->drawLine( r.x() + r.width() - 2, r.y() + r.height() - 1, r.x() + r.width() - 1, r.y() + r.height() - 2 );
+ }
+ if ( !formMode ) {
+ if ( tall && wide )
+ p->setPen( cg.background() );
+ else
+ p->setPen( midColor( custom ? custom[ 3 ] : gray[ 3 ], cg.background() ) );
+
+ //p->drawPoint( r.x(), r.y() );
+ //p->drawPoint( r.x() + r.width() - 1, r.y() );
+ //p->drawPoint( r.x(), r.y() + r.height() - 1 );
+ //p->drawPoint( r.x() + r.width() - 1, r.y() + r.height() - 1 );
+ }
+ break;
+
+ case ROUNDED_TOP:
+ p->drawLine( r.x() + 2, r.y(), r.x() + r.width() - 3, r.y() );
+ p->drawLine( r.x() + 1, r.y() + r.height() - 1, r.x() + r.width() - 2, r.y() + r.height() - 1 );
+ p->drawLine( r.x(), r.y() + 2, r.x(), r.y() + r.height() - 1 );
+ p->drawLine( r.x() + r.width() - 1, r.y() + 2, r.x() + r.width() - 1, r.y() + r.height() - 1 );
+
+ if ( wide ) {
+ p->drawPoint( r.x() + r.width() - 2, r.y() + 1 );
+ p->drawPoint( r.x() + 1, r.y() + 1 );
+ p->setPen( midColor( border, cg.background() ) );
+ }
+ if ( !formMode || !wide ) {
+ p->drawLine( r.x(), r.y() + 1, r.x() + 1, r.y() );
+ p->drawLine( r.x() + r.width() - 2, r.y(), r.x() + r.width() - 1, r.y() + 1 );
+ }
+ if ( !formMode ) {
+ if ( wide )
+ p->setPen( cg.background() );
+ else
+ p->setPen( midColor( custom ? custom[ 3 ] : gray[ 3 ], cg.background() ) );
+ p->drawPoint( r.x(), r.y() );
+ p->drawPoint( r.x() + r.width() - 1, r.y() );
+ }
+ break;
+ case ROUNDED_BOTTOM:
+ p->drawLine( r.x() + 1, r.y(), r.x() + r.width() - 2, r.y() );
+ p->drawLine( r.x() + 2, r.y() + r.height() - 1, r.x() + r.width() - 3, r.y() + r.height() - 1 );
+ p->drawLine( r.x(), r.y(), r.x(), r.y() + r.height() - 3 );
+ p->drawLine( r.x() + r.width() - 1, r.y(), r.x() + r.width() - 1, r.y() + r.height() - 3 );
+ if ( wide ) {
+ p->drawPoint( r.x() + 1, r.y() + r.height() - 2 );
+ p->drawPoint( r.x() + r.width() - 2, r.y() + r.height() - 2 );
+ p->setPen( midColor( border, cg.background() ) );
+ }
+ if ( !formMode || !wide ) {
+ p->drawLine( r.x(), r.y() + r.height() - 2, r.x() + 1, r.y() + r.height() - 1 );
+ p->drawLine( r.x() + r.width() - 2, r.y() + r.height() - 1,
+ r.x() + r.width() - 1, r.y() + r.height() - 2 );
+ }
+ if ( !formMode ) {
+ if ( wide )
+ p->setPen( cg.background() );
+ else
+ p->setPen( midColor( custom ? custom[ 3 ] : gray[ 3 ], cg.background() ) );
+ p->drawPoint( r.x(), r.y() + r.height() - 1 );
+ p->drawPoint( r.x() + r.width() - 1, r.y() + r.height() - 1 );
+ }
+ break;
+ case ROUNDED_LEFT:
+ p->drawLine( r.x() + 2, r.y(), r.x() + r.width() - 2, r.y() );
+ p->drawLine( r.x() + 2, r.y() + r.height() - 1,
+ r.x() + r.width() - 2, r.y() + r.height() - 1 );
+ p->drawLine( r.x(), r.y() + 2, r.x(), r.y() + r.height() - 3 );
+ p->drawLine( r.x() + r.width() - 1, r.y(), r.x() + r.width() - 1, r.y() + r.height() - 1 );
+ if ( tall ) {
+ p->drawPoint( r.x() + 1, r.y() + r.height() - 2 );
+ p->drawPoint( r.x() + 1, r.y() + 1 );
+ p->setPen( midColor( border, cg.background() ) );
+ }
+ if ( !formMode || !tall ) {
+ p->drawLine( r.x(), r.y() + 1, r.x() + 1, r.y() );
+ p->drawLine( r.x(), r.y() + r.height() - 2, r.x() + 1, r.y() + r.height() - 1 );
+ }
+ if ( !formMode ) {
+ if ( tall )
+ p->setPen( cg.background() );
+ else
+ p->setPen( midColor( custom ? custom[ 3 ] : gray[ 3 ], cg.background() ) );
+ p->drawPoint( r.x(), r.y() );
+ p->drawPoint( r.x(), r.y() + r.height() - 1 );
+ }
+ break;
+ case ROUNDED_RIGHT:
+ p->drawLine( r.x() + 1, r.y(), r.x() + r.width() - 3, r.y() );
+ p->drawLine( r.x() + 1, r.y() + r.height() - 1,
+ r.x() + r.width() - 3, r.y() + r.height() - 1 );
+ p->drawLine( r.x(), r.y(), r.x(), r.y() + r.height() - 1 );
+ p->drawLine( r.x() + r.width() - 1, r.y() + 2, r.x() + r.width() - 1,
+ r.y() + r.height() - 3 );
+ if ( tall ) {
+ p->drawPoint( r.x() + r.width() - 2, r.y() + 1 );
+ p->drawPoint( r.x() + r.width() - 2, r.y() + r.height() - 2 );
+ p->setPen( midColor( border, cg.background() ) );
+ }
+ if ( !formMode || !tall ) {
+ p->drawLine( r.x() + r.width() - 2, r.y(), r.x() + r.width() - 1, r.y() + 1 );
+ p->drawLine( r.x() + r.width() - 2, r.y() + r.height() - 1,
+ r.x() + r.width() - 1, r.y() + r.height() - 2 );
+ }
+ if ( !formMode ) {
+ if ( tall )
+ p->setPen( cg.background() );
+ else
+ p->setPen( midColor( custom ? custom[ 3 ] : gray[ 3 ], cg.background() ) );
+ p->drawPoint( r.x() + r.width() - 1, r.y() );
+ p->drawPoint( r.x() + r.width() - 1, r.y() + r.height() - 1 );
+ }
+ break;
+ default:
+ break;
+ }
+ } else {
+ p->setPen( flags & Style_ButtonDefault &&
+ IND_FONT_COLOUR == defBtnIndicator ? cg.text() : custom ? custom[ 5 ] : gray[ 5 ] );
+ p->setBrush( NoBrush );
+ p->drawRect( r );
+ }
+
+ p->restore();
+}
+
+void KlearlookStyle::drawArrow( QPainter *p, const QRect &r, const QColorGroup &cg, QStyle::SFlags flags,
+ QStyle::PrimitiveElement pe, bool small, bool checkActive ) const {
+ QPointArray a;
+ const QColor &col = flags & Style_Enabled
+ ? checkActive && flags & Style_Active
+ ? cg.highlightedText()
+ : cg.text()
+ : cg.mid();
+
+ if ( vArrow )
+ if ( small )
+ switch ( pe ) {
+ case QStyle::PE_ArrowUp:
+ a.setPoints( 7, 2, 1, 2, 0, 0, -2, -2, 0, -2, 1, -2, 0, 2, 0 );
+ break;
+ case QStyle::PE_ArrowDown:
+ a.setPoints( 7, 2, -1, 2, 0, 0, 2, -2, 0, -2, -1, -2, 0, 2, 0 );
+ break;
+ case QStyle::PE_ArrowRight:
+ a.setPoints( 7, 1, -2, 0, -2, -2, 0, 0, 2, 1, 2, 0, 2, 0, -2 );
+ break;
+ case QStyle::PE_ArrowLeft:
+ a.setPoints( 7, -1, -2, 0, -2, 2, 0, 0, 2, -1, 2, 0, 2, 0, -2 );
+ break;
+ default:
+ return ;
+ }
+ else
+ switch ( pe ) {
+ case QStyle::PE_ArrowUp:
+ a.setPoints( 7, 3, 1, 0, -2, -3, 1, -2, 2, -1, 1, 1, 1, 2, 2 );
+ break;
+ case QStyle::PE_ArrowDown:
+ a.setPoints( 7, 3, -1, 0, 2, -3, -1, -2, -2, -1, -1, 1, -1, 2, -2 );
+ break;
+ case QStyle::PE_ArrowRight:
+ a.setPoints( 7, -1, -3, 2, 0, -1, 3, -2, 2, -1, 1, -1, -1, -2, -2 );
+ break;
+ case QStyle::PE_ArrowLeft:
+ a.setPoints( 7, 1, -3, -2, 0, 1, 3, 2, 2, 1, 1, 1, -1, 2, -2 );
+ break;
+ default:
+ return ;
+ }
+ else
+ if ( small )
+ switch ( pe ) {
+ case QStyle::PE_ArrowUp:
+ a.setPoints( 4, 2, 0, 0, -2, -2, 0, 2, 0 );
+ break;
+ case QStyle::PE_ArrowDown:
+ a.setPoints( 4, 2, 0, 0, 2, -2, 0, 2, 0 );
+ break;
+ case QStyle::PE_ArrowRight:
+ a.setPoints( 4, 0, -2, -2, 0, 0, 2, 0, -2 );
+ break;
+ case QStyle::PE_ArrowLeft:
+ a.setPoints( 4, 0, -2, 2, 0, 0, 2, 0, -2 );
+ break;
+ default:
+ return ;
+ }
+ else
+ switch ( pe ) {
+ case QStyle::PE_ArrowUp:
+ a.setPoints( 4, 3, 1, 0, -2, -3, 1, 3, 1 );
+ break;
+ case QStyle::PE_ArrowDown:
+ a.setPoints( 4, 3, -1, 0, 2, -3, -1, 3, -1 );
+ break;
+ case QStyle::PE_ArrowRight:
+ a.setPoints( 4, -1, -3, 2, 0, -1, 3, -1, -3 );
+ break;
+ case QStyle::PE_ArrowLeft:
+ a.setPoints( 4, 1, -3, -2, 0, 1, 3, 1, -3 );
+ break;
+ default:
+ return ;
+ }
+
+ if ( a.isNull() )
+ return ;
+
+ p->save();
+ a.translate( ( r.x() + ( r.width() >> 1 ) ), ( r.y() + ( r.height() >> 1 ) ) );
+ p->setBrush( col );
+ p->setPen( col );
+ p->drawPolygon( a );
+ p->restore();
+}
+
+void KlearlookStyle::drawPrimitiveMenu( PrimitiveElement pe, QPainter *p, const QRect &r, const QColorGroup &cg,
+ SFlags flags, const QStyleOption &data ) const {
+ switch ( pe ) {
+ case PE_CheckMark:
+ if ( flags & Style_On || !( flags & Style_Off ) ) // !(flags&Style_Off) is for tri-state
+ {
+ QPointArray check;
+ int x = r.center().x() - 3,
+ y = r.center().y() - 3;
+
+ check.setPoints( 6,
+ x, y + 2,
+ x + 2, y + 4,
+ x + 6, y,
+ x + 6, y + 2,
+ x + 2, y + 6,
+ x, y + 4 );
+
+ if ( flags & Style_On ) {
+ if ( flags & Style_Active ) {
+ p->setBrush( cg.highlightedText() );
+ p->setPen( cg.highlightedText() );
+ } else {
+ p->setBrush( cg.text() );
+ p->setPen( cg.text() );
+ }
+ } else {
+ p->setBrush( cg.text() );
+ p->setPen( cg.text() );
+ }
+ p->drawPolygon( check );
+ }
+ break;
+
+ default:
+ KStyle::drawPrimitive( pe, p, r, cg, flags, data );
+ }
+}
+
+void KlearlookStyle::drawPrimitive( PrimitiveElement pe, QPainter *p, const QRect &r, const QColorGroup &cg,
+ SFlags flags, const QStyleOption &data ) const {
+ int x, y, w, h;
+
+ r.rect(&x, &y, &w, &h);
+
+ switch ( pe ) {
+ case PE_HeaderSection: {
+ const QColor * use = buttonColors( cg );
+
+
+ if ( APP_KICKER == themedApp ) {
+ if ( flags & Style_Down )
+ flags = ( ( flags | Style_Down ) ^ Style_Down ) | Style_Sunken;
+ flags |= Style_Enabled;
+#if KDE_VERSION >= 0x30200
+#if KDE_VERSION >= 0x30400
+
+ if ( HOVER_KICKER == hover && hoverWidget ) // && hoverWidget==p->device())
+ flags |= Style_MouseOver;
+#endif
+
+ formMode = isTransKicker;
+#endif
+
+ drawLightBevelButton( p, r, cg, flags | Style_Horizontal,
+ true, ROUNDED_ALL, getFill( flags, use ), use );
+#if KDE_VERSION >= 0x30200
+
+ formMode = false;
+#endif
+
+ } else {
+ flags = ( ( flags | Style_Sunken ) ^ Style_Sunken ) | Style_Raised;
+
+ if ( QTC_NO_SECT != hoverSect && HOVER_HEADER == hover && hoverWidget ) {
+ QHeader * hd = dynamic_cast<QHeader *>( hoverWidget );
+
+ if ( hd && hd->isClickEnabled( hoverSect ) && r == hd->sectionRect( hoverSect ) )
+ flags |= Style_MouseOver;
+ }
+ drawLightBevelButton( p, r, cg, flags | Style_Horizontal,
+ true, ROUNDED_NONE, getFill( flags, use ), use );
+ }
+ break;
+ }
+ case PE_HeaderArrow:
+ drawArrow( p, r, cg, flags, flags & Style_Up ? PE_ArrowUp : PE_ArrowDown );
+ break;
+ case PE_ButtonCommand:
+ case PE_ButtonBevel:
+ case PE_ButtonTool:
+ case PE_ButtonDropDown: {
+ const QColor *use = buttonColors( cg );
+
+ if ( !( flags & QStyle::Style_Sunken ) ) // If its not sunken, its raised-don't want flat buttons.
+ flags |= QStyle::Style_Raised;
+
+ drawLightBevelButton( p, r, cg, flags | Style_Horizontal, true,
+ r.width() < 16 || r.height() < 16
+#if KDE_VERSION >= 0x30200
+ || ( APP_KORN == themedApp && isTransKicker && PE_ButtonTool == pe )
+#endif
+ ? ROUNDED_NONE : ROUNDED_ALL,
+ getFill( flags, use ), use );
+ break;
+ }
+ case PE_ButtonDefault:
+ switch ( defBtnIndicator ) {
+ case IND_BORDER:
+ p->setBrush( NoBrush );
+ if ( rounded ) // borderButton) CPD Only use color[4] for rounded def buttons!
+ {
+ const QColor * use = buttonColors( cg );
+
+ p->setPen( use[ 4 ] );
+ int offset = r.width() >= QTC_MIN_BTN_SIZE && r.height() >= QTC_MIN_BTN_SIZE ? 4 : 3;
+
+ p->drawLine( r.x() + offset, r.y(), r.x() + r.width() - ( 1 + offset ), r.y() );
+ p->drawLine( r.x() + offset, r.y() + r.height() - 1,
+ r.x() + r.width() - ( 1 + offset ), r.y() + r.height() - 1 );
+ p->drawLine( r.x(), r.y() + offset, r.x(), r.y() + r.height() - ( 1 + offset ) );
+ p->drawLine( r.x() + r.width() - 1, r.y() + offset,
+ r.x() + r.width() - 1, r.y() + r.height() - ( 1 + offset ) );
+ } else {
+ p->setPen( cg.text() );
+ p->drawRect( r );
+ }
+ break;
+ case IND_CORNER: {
+ const QColor *use = buttonColors( cg );
+ QPointArray points;
+ bool sunken = flags & Style_Down || flags & QStyle::Style_Sunken;
+ int offset = sunken ? 4 : 3;
+
+ points.setPoints( 3, r.x() + offset, r.y() + offset, r.x() + offset + 6, r.y() + offset,
+ r.x() + offset, r.y() + offset + 6 );
+
+ p->setBrush( use[ sunken ? 0 : borderButton ? 4 : 5 ] );
+ p->setPen( use[ sunken ? 0 : borderButton ? 4 : 5 ] );
+ p->drawPolygon( points );
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ case PE_IndicatorMask:
+ if ( rounded ) {
+ p->fillRect( r, color0 );
+ p->fillRect( r.x() + 1, r.y() + 1, r.width() - 2, r.height() - 2, color1 );
+ p->setPen( color1 );
+ p->drawLine( r.x() + 1, r.y(), r.x() + r.width() - 2, r.y() );
+ p->drawLine( r.x() + 1, r.y() + r.height() - 1, r.x() + r.width() - 2, r.y() + r.height() - 1 );
+ p->drawLine( r.x(), r.y() + 1, r.x(), r.y() + r.height() - 2 );
+ p->drawLine( r.x() + r.width() - 1, r.y() + 1, r.x() + r.width() - 1, r.y() + r.height() - 2 );
+ } else
+ p->fillRect( r, color1 );
+ break;
+ case PE_CheckMark:
+ if ( flags & Style_On || !( flags & Style_Off ) ) // !(flags&Style_Off) is for tri-state
+ {
+ QPointArray check;
+ int x = r.center().x() - 3,
+ y = r.center().y() - 3;
+
+ check.setPoints( 6,
+ x, y + 2,
+ x + 2, y + 4,
+ x + 6, y,
+ x + 6, y + 2,
+ x + 2, y + 6,
+ x, y + 4 );
+ p->setBrush( flags & Style_On
+ ? flags & Style_Enabled
+ ? flags & Style_Selected
+ ? cg.highlightedText()
+ : cg.text()
+ : cg.mid()
+ : cg.light() );
+ p->setPen( flags & Style_Enabled
+ ? flags & Style_Selected
+ ? cg.highlightedText()
+ : cg.text()
+ : cg.mid() );
+ p->drawPolygon( check );
+ }
+ break;
+ case PE_CheckListController: {
+ QCheckListItem *item = data.checkListItem();
+
+ if ( item ) {
+ QListView * lv = item->listView();
+ int x = r.x(), y = r.y(), w = r.width(), h = r.height(), marg = lv->itemMargin();
+
+ p->setPen( QPen( flags & Style_Enabled ? cg.text()
+ : lv->palette().color( QPalette::Disabled, QColorGroup::Text ) ) );
+
+ if ( flags & Style_Selected && !lv->rootIsDecorated() &&
+ !( ( item->parent() && 1 == item->parent() ->rtti() &&
+ QCheckListItem::Controller == ( ( QCheckListItem* ) item->parent() ) ->type() ) )) {
+ p->fillRect( 0, 0, x + marg + w + 4, item->height(),
+ cg.brush( QColorGroup::Highlight ) );
+ if ( item->isEnabled() )
+ p->setPen( QPen( cg.highlightedText() ) );
+ }
+
+ if ( flags & Style_NoChange )
+ p->setBrush( cg.brush( QColorGroup::Button ) );
+ p->drawRect( x + marg + 2, y + 4 + 2, w - 7, h - 8 );
+ p->drawRect( x + marg, y + 4, w - 7, h - 8 );
+ }
+ break;
+ }
+ case PE_CheckListIndicator: {
+ QCheckListItem *item = data.checkListItem();
+
+ if ( item ) {
+ QListView * lv = item->listView();
+
+ p->setPen( QPen( flags & Style_Enabled ? cg.text()
+ : lv->palette().color( QPalette::Disabled, QColorGroup::Text ), 2 ) );
+ if ( flags & Style_Selected ) {
+ flags -= Style_Selected;
+ if ( !lv->rootIsDecorated() &&
+ !( ( item->parent() && 1 == item->parent() ->rtti() &&
+ QCheckListItem::Controller ==
+ ( ( QCheckListItem* ) item->parent() ) ->type() ) ) ) {
+ p->fillRect( 0, 0, r.x() + lv->itemMargin() + r.width() + 4, item->height(),
+ cg.brush( QColorGroup::Highlight ) );
+ if ( item->isEnabled() ) {
+ p->setPen( QPen( cg.highlightedText(), 2 ) );
+ flags += Style_Selected;
+ }
+ }
+ }
+
+ if ( flags & Style_NoChange )
+ p->setBrush( cg.brush( QColorGroup::Button ) );
+ p->drawRect( r.x() + lv->itemMargin(), r.y() + 2, r.width() - 4, r.width() - 4 );
+ if ( flags & QStyle::Style_On || !( flags & Style_Off ) )
+ drawPrimitive( PE_CheckMark, p, QRect( r.x() + lv->itemMargin(),
+ r.y() + 2, r.width() - 4, r.width() - 4 ), cg, flags );
+ }
+ break;
+ }
+ case PE_Indicator: {
+ const QColor *use = buttonColors( cg );
+ bool on = flags & QStyle::Style_On || !( flags & Style_Off );
+
+ if ( APPEARANCE_FLAT != appearance )
+ drawPrimitive( PE_ButtonTool, p, r, cg, flags );
+ else {
+ p->fillRect( r.x() + 1, r.y() + 2, QTC_CHECK_SIZE - 2, QTC_CHECK_SIZE - 2,
+ flags & Style_Enabled ? cg.base() : cg.background() );
+ p->setPen( use[ 4 ] );
+ p->drawLine( r.x() + 1, r.y() + QTC_CHECK_SIZE - 1, r.x() + 1, r.y() + 1 );
+ p->drawLine( r.x() + 1, r.y() + 1, r.x() + QTC_CHECK_SIZE - 2, r.y() + 1 );
+ }
+ p->setPen( use[ 5 ] );
+ p->setBrush( NoBrush );
+ if ( rounded ) {
+ p->drawLine( r.x() + 1, r.y(), r.x() + r.width() - 2, r.y() );
+ p->drawLine( r.x() + 1, r.y() + r.height() - 1, r.x() + r.width() - 2, r.y() + r.height() - 1 );
+ p->drawLine( r.x(), r.y() + 1, r.x(), r.y() + r.height() - 2 );
+ p->drawLine( r.x() + r.width() - 1, r.y() + 1, r.x() + r.width() - 1, r.y() + r.height() - 2 );
+
+ p->setPen( midColor( use[ 3 ], cg.background() ) );
+ p->drawPoint( r.x(), r.y() );
+ p->drawPoint( r.x(), r.y() + r.width() - 1 );
+ p->drawPoint( r.x() + r.height() - 1, r.y() );
+ p->drawPoint( r.x() + r.height() - 1, r.y() + r.width() - 1 );
+ } else if ( APPEARANCE_FLAT == appearance || borderButton )
+ p->drawRect( r.x(), r.y(), QTC_CHECK_SIZE, QTC_CHECK_SIZE );
+
+ if ( on )
+ drawPrimitive( PE_CheckMark, p, r, cg, flags );
+ break;
+ }
+ case PE_CheckListExclusiveIndicator: {
+ QCheckListItem *item = data.checkListItem();
+
+ if ( item ) {
+ const QColor & bgnd = cg.background(),
+ &on = flags & Style_Enabled
+ ? cg.text()
+ : cg.mid();
+ bool set
+ = flags & QStyle::Style_On;
+ QPointArray outer,
+ inner,
+ aa;
+ int x = r.x(), y = r.y() + 2;
+
+ outer.setPoints( 24, x, y + 8, x, y + 4, x + 1, y + 3, x + 1, y + 2,
+ x + 2, y + 1, x + 3, y + 1, x + 4, y, x + 8, y,
+ x + 9, y + 1, x + 10, y + 1, x + 11, y + 2, x + 11, y + 3,
+ x + 12, y + 4, x + 12, y + 8, x + 11, y + 9, x + 11, y + 10,
+ x + 10, y + 11, x + 9, y + 11, x + 8, y + 12, x + 4, y + 12,
+ x + 3, y + 11, x + 2, y + 11, x + 1, y + 10, x + 1, y + 9 );
+ inner.setPoints( 20, x + 1, y + 8, x + 1, y + 4, x + 2, y + 3, x + 2, y + 2,
+ x + 3, y + 2, x + 4, y + 1, x + 8, y + 1, x + 9, y + 2,
+ x + 10, y + 2, x + 10, y + 3, x + 11, y + 4, x + 11, y + 8,
+ x + 10, y + 9, x + 10, y + 10, x + 9, y + 10, x + 8, y + 11,
+ x + 4, y + 11, x + 3, y + 10, x + 2, y + 10, x + 2, y + 9 );
+ aa.setPoints( 16, x + 2, y + 4, x + 4, y + 2, x + 8, y + 2, x + 10, y + 4,
+ x + 10, y + 8, x + 8, y + 10, x + 4, y + 10, x + 2, y + 8,
+ x, y + 3, x + 3, y, x + 9, y, x + 12, y + 3,
+ x + 12, y + 9, x + 9, y + 12, x + 3, y + 12, x, y + 9 );
+ p->setBrush( on );
+ p->drawPolyline( outer );
+ p->drawPolyline( inner );
+ p->setPen( midColor( on, bgnd, 1.5 ) );
+ p->drawPoints( aa );
+
+ if ( set
+ ) {
+ p->setPen( midColor( on, bgnd ) );
+ p->drawLine( x + 5, y + 4, x + 7, y + 4 );
+ p->drawLine( x + 5, y + 8, x + 7, y + 8 );
+ p->drawLine( x + 4, y + 5, x + 4, y + 7 );
+ p->drawLine( x + 8, y + 5, x + 8, y + 7 );
+ p->setBrush( on );
+ p->setPen( NoPen );
+ p->drawRect( x + 5, y + 5, 3, 3 );
+ }
+ }
+ break;
+ }
+ case PE_ExclusiveIndicator:
+ case PE_ExclusiveIndicatorMask: {
+ int x = r.x(), y = r.y();
+ QPointArray outer;
+ outer.setPoints( 24, x, y + 8, x, y + 4, x + 1, y + 3, x + 1, y + 2,
+ x + 2, y + 1, x + 3, y + 1, x + 4, y, x + 8, y,
+ x + 9, y + 1, x + 10, y + 1, x + 11, y + 2, x + 11, y + 3,
+ x + 12, y + 4, x + 12, y + 8, x + 11, y + 9, x + 11, y + 10,
+ x + 10, y + 11, x + 9, y + 11, x + 8, y + 12, x + 4, y + 12,
+ x + 3, y + 11, x + 2, y + 11, x + 1, y + 10, x + 1, y + 9 );
+
+ if ( PE_ExclusiveIndicatorMask == pe ) {
+ p->fillRect( r, color0 );
+ p->setPen( Qt::color1 );
+ p->setBrush( Qt::color1 );
+ p->drawPolygon( outer );
+ } else {
+ QPointArray shadow;
+ const QColor &bgnd = flags & Style_Enabled ? cg.base() : cg.background(),
+ &on = flags & Style_Enabled
+ ? flags & Style_Selected
+ ? cg.highlightedText()
+ : cg.text()
+ : cg.mid();
+ QColor indBgnd = bgnd;
+ const QColor *use = buttonColors( cg );
+ QColor leftShadowColor,
+ rightShadowColor,
+ outerLeftColor,
+ outerRightColor;
+ bool set
+ = flags & QStyle::Style_On;
+
+ if ( APPEARANCE_FLAT != appearance && !borderButton )
+ shadow.setPoints( 14, x + 1, y + 10, x + 1, y + 9, x, y + 8, x, y + 4,
+ x + 1, y + 3, x + 1, y + 2, x + 2, y + 1, x + 3, y + 1,
+ x + 4, y, x + 8, y, x + 9, y + 1, x + 10, y + 1,
+ x + 11, y + 2, x + 11, y + 3 );
+ else
+ shadow.setPoints( 9, x + 2, y + 11, x + 2, y + 9, x + 1, y + 8, x + 1, y + 4,
+ x + 2, y + 3, x + 2, y + 2, x + 3, y + 2, x + 4, y + 1,
+ x + 8, y + 1 );
+
+ p->fillRect( r, crLabelHighlight && flags & Style_MouseOver
+ ? cg.background().light( QTC_HIGHLIGHT_FACTOR ) : cg.background() );
+
+ if ( APPEARANCE_FLAT != appearance ) {
+ indBgnd = getFill( flags, use );
+ p->setClipRegion( QRegion( outer ) );
+ drawBevelGradient( indBgnd, !set, 0, p,
+ QRect( x + 1, y + 1, r.width() - 2, r.height() - 2 ), true,
+ set ? SHADE_BEVEL_GRAD_SEL_LIGHT( appearance ) : SHADE_BEVEL_GRAD_LIGHT( appearance ),
+ set ? SHADE_BEVEL_GRAD_SEL_DARK( appearance ) : SHADE_BEVEL_GRAD_DARK( appearance ) );
+
+ p->setClipping( false );
+
+ if ( ( !set
+ && !( flags & Style_Down ) ) || !borderButton ) {
+ leftShadowColor = set
+ ? !borderButton ? use[ 5 ] : use[ 4 ] : use[ 0 ];
+ p->setPen( leftShadowColor );
+ p->drawPolyline( shadow );
+
+ if ( APPEARANCE_LIGHT_GRADIENT == appearance )
+ rightShadowColor = indBgnd;
+ else {
+ if ( !borderButton )
+ shadow.setPoints( 10, x + 12, y + 4, x + 12, y + 8, x + 11, y + 9,
+ x + 11, y + 10, x + 10, y + 11, x + 9, y + 11,
+ x + 8, y + 12, x + 4, y + 12, x + 3, y + 11,
+ x + 2, y + 11 );
+ else
+ shadow.setPoints( 9, x + 10, y + 2, x + 10, y + 3, x + 11, y + 4,
+ x + 11, y + 8, x + 10, y + 9, x + 10, y + 10,
+ x + 9, y + 10, x + 8, y + 11, x + 4, y + 11 );
+ rightShadowColor = set
+ ? use[ 0 ] : !borderButton ? use[ 5 ] : use[ 4 ];
+ p->setPen( rightShadowColor );
+ p->drawPolyline( shadow );
+ }
+ }
+ else
+ leftShadowColor = rightShadowColor = indBgnd;
+ } else {
+ rightShadowColor = bgnd;
+ p->setBrush( bgnd );
+ p->setPen( bgnd );
+ p->drawEllipse( x, y, QTC_RADIO_SIZE, QTC_RADIO_SIZE );
+ p->setPen( use[ 4 ] );
+ leftShadowColor = use[ 4 ];
+ p->drawPolyline( shadow );
+ }
+
+ if ( APPEARANCE_FLAT == appearance || borderButton ) {
+ p->setPen( use[ 5 ] );
+ p->drawPolyline( outer );
+ shade( use[ 5 ], &outerRightColor, 1.1 );
+ } else {
+ shade( leftShadowColor, &outerLeftColor, 1.1 );
+ shade( rightShadowColor, &outerRightColor, 1.1 );
+ }
+ if ( set
+ ) {
+ p->setPen( midColor( on, indBgnd ) );
+ p->drawLine( x + 5, y + 4, x + 7, y + 4 );
+ p->drawLine( x + 5, y + 8, x + 7, y + 8 );
+ p->drawLine( x + 4, y + 5, x + 4, y + 7 );
+ p->drawLine( x + 8, y + 5, x + 8, y + 7 );
+ p->setBrush( on );
+ p->setPen( NoPen );
+ p->drawRect( x + 5, y + 5, 3, 3 );
+ }
+
+ if ( !formMode ) {
+ QPointArray outerAaLeft,
+ outerAaRight;
+
+ outerAaLeft.setPoints( 8, x, y + 3, x + 1, y + 1, x + 3, y,
+ x + 9, y, x + 11, y + 1, x + 12, y + 3,
+ x + 1, y + 11, x, y + 9 );
+ outerAaRight.setPoints( 4, x + 12, y + 9, x + 11, y + 11, x + 9, y + 12,
+ x + 3, y + 12 );
+
+ p->setPen( midColor( outerRightColor, cg.background() ) );
+ p->drawPoints( outerAaRight );
+ if ( APPEARANCE_FLAT != appearance && !borderButton )
+ p->setPen( midColor( outerLeftColor, cg.background() ) );
+ p->drawPoints( outerAaLeft );
+ if ( APPEARANCE_LIGHT_GRADIENT == appearance )
+ p->setPen( midColor( indBgnd, use[ 5 ], 1.75 ) );
+ else
+ p->setPen( midColor( use[ 5 ], indBgnd, 1.5 ) );
+
+ if ( APPEARANCE_FLAT != appearance ) {
+ QPointArray innerAa;
+
+ if ( !set
+ && !( flags & Style_Down ) ) {
+ if ( borderButton ) {
+ innerAa.setPoints( 3, x + 1, y + 4, x + 2, y + 2, x + 4, y + 1 );
+ p->drawPoints( innerAa );
+ p->setPen( midColor( outerRightColor, cg.background() ) );
+ p->drawPoint( x + 2, y + 10 );
+ } else {
+ innerAa.setPoints( 4, x + 4, y + 11, x + 8, y + 11, x + 10, y + 10,
+ x + 11, y + 8 );
+ p->drawPoints( innerAa );
+ }
+ }
+ } else {
+ QPointArray innerAa;
+
+ innerAa.setPoints( 6, x + 4, y + 11, x + 8, y + 11, x + 10, y + 10,
+ x + 11, y + 8, x + 11, y + 4, x + 10, y + 2 );
+ p->drawPoints( innerAa );
+ }
+ }
+ }
+ break;
+ }
+ case PE_DockWindowSeparator: {
+ QPoint p1,
+ p2;
+ //const QColor *use=backgroundColors(cg);
+
+ if ( flags & Style_Horizontal ) {
+ int offset = r.height() > 18 ? 6 : r.height() > 12 ? 4 : r.height() > 6 ? 2 : 0;
+
+ p1 = QPoint( r.width() >> 1, 0 + offset );
+ p2 = QPoint( p1.x(), r.height() - offset );
+ } else {
+ int offset = r.width() > 18 ? 6 : r.width() > 12 ? 4 : r.width() > 6 ? 2 : 0;
+
+ p1 = QPoint( 0 + offset, r.height() >> 1 );
+ p2 = QPoint( r.width() - offset, p1.y() );
+ }
+ p->fillRect( r, cg.background() );
+ p->setPen( cg.background().dark( 111 ) );
+ p->drawLine( p1, p2 );
+
+ break;
+ }
+ case PE_Splitter: {
+ const QColor *use = buttonColors( cg );
+
+ if ( hoverWidget && hoverWidget == p->device() )
+ flags |= Style_MouseOver;
+
+ if ( borderSplitter )
+ drawLightBevelButton( p, r, cg, QStyle::Style_Raised, false,
+ ROUNDED_NONE, getFill( flags, use ), use );
+ else {
+ p->fillRect( r,
+ QColor( flags & Style_MouseOver ?
+ cg.background().light( QTC_HIGHLIGHT_FACTOR ) :
+ cg.background() ) );
+ drawLines( p, r, flags & Style_Horizontal, 70, 1, use, 0, TRUE,
+ APPEARANCE_LIGHT_GRADIENT == appearance );
+ }
+ break;
+ }
+ case PE_DockWindowResizeHandle:
+ p->fillRect( r, cg.background() );
+ if ( flags & Style_Horizontal ) {
+ p->setPen( cg.highlight().light() );
+ p->drawLine( r.left() + 1, r.top() + 1, r.right() - 1, r.top() + 1 );
+ p->setPen( cg.highlight() );
+ p->drawLine( r.left() + 1, r.top() + 2, r.right() - 1, r.top() + 2 );
+ p->setPen( cg.highlight().dark() );
+ p->drawLine( r.left() + 1, r.top() + 3, r.right() - 1, r.top() + 3 );
+ } else {
+ p->setPen( cg.highlight().light() );
+ p->drawLine( r.left() + 1, r.top() + 1, r.left() + 1, r.bottom() - 1 );
+ p->setPen( cg.highlight() );
+ p->drawLine( r.left() + 2, r.top() + 1, r.left() + 2, r.bottom() - 1 );
+ p->setPen( cg.highlight().dark() );
+ p->drawLine( r.left() + 3, r.top() + 1, r.left() + 3, r.bottom() - 1 );
+ }
+ break;
+
+ case PE_StatusBarSection: {
+ p->setPen( cg.background().dark(120) );
+ p->drawRect(x-2, y-2, r.width()+3, r.height()+3);
+ break;
+ }
+
+ case PE_PanelLineEdit: {
+ const QColor *use = backgroundColors( cg );
+ p->setPen( use[ 4 ].light(80) );
+ p->drawRect( r );
+ break;
+ }
+
+ case PE_PanelPopup: {
+ const QColor *use = backgroundColors( cg );
+
+ if ( borderFrame && ( data.isDefault() || data.lineWidth() > 1 ) ) {
+ p->setPen( use[ 4 ].light(70) );
+ p->setBrush( NoBrush );
+ drawPopupRect (p, r, cg);
+ //p->drawRect( r );
+#ifdef MENU_POPUP_SHADOW
+
+ qDrawShadePanel( p, r.x() + 1, r.y() + 1, r.width() - 2, r.height() - 2,
+ QColorGroup( use[ 4 ], use[ NUM_SHADES ], use[ 0 ], use[ 4 ], use[ 2 ],
+ cg.text(), use[ NUM_SHADES ] ),
+ flags & Style_Sunken,
+ data.isDefault() ? QTC_BORDERED_FRAME_WIDTH - 1 : data.lineWidth() - 1 );
+#endif
+
+ } else
+ qDrawShadePanel( p, r,
+ QColorGroup(
+ use[ 5 ], use[ NUM_SHADES ], use[ 0 ], use[ 5 ], use[ 2 ],
+ cg.text(), use[ NUM_SHADES ]
+ ),
+ flags & Style_Sunken, data.isDefault() ? QTC_DEF_FRAME_WIDTH : data.lineWidth() );
+ break;
+ }
+ case PE_PanelTabWidget: {
+ const QColor *use = backgroundColors( cg );
+
+ if ( borderFrame && ( data.isDefault() || data.lineWidth() > 1 ) ) {
+ p->setPen( use[ 4 ] );
+ p->setBrush( NoBrush );
+ p->drawRect( r );
+#ifdef MENU_POPUP_SHADOW
+
+ qDrawShadePanel( p, r.x() + 1, r.y() + 1, r.width() - 2, r.height() - 2,
+ QColorGroup( use[ 4 ], use[ NUM_SHADES ], use[ 0 ], use[ 4 ], use[ 2 ],
+ cg.text(), use[ NUM_SHADES ] ),
+ flags & Style_Sunken,
+ data.isDefault() ? QTC_BORDERED_FRAME_WIDTH - 1 : data.lineWidth() - 1 );
+#endif
+
+ } else
+ qDrawShadePanel( p, r,
+ QColorGroup(
+ use[ 5 ], use[ NUM_SHADES ], use[ 0 ], use[ 5 ], use[ 2 ],
+ cg.text(), use[ NUM_SHADES ]
+ ),
+ flags & Style_Sunken, data.isDefault() ? QTC_DEF_FRAME_WIDTH : data.lineWidth() );
+ break;
+ }
+ case PE_PanelDockWindow:
+ case PE_PanelMenuBar: {
+ const QColor *use = backgroundColors( cg );
+ switch ( toolbarBorders ) {
+ case TB_DARK:
+ qDrawShadePanel( p,
+ r.x(), r.y(), r.width(), r.height(),
+ QColorGroup(
+ use[ 5 ].dark( 120 ), use[ NUM_SHADES ], use[ 0 ],
+ use[ 5 ].dark( 120 ), use[ 2 ],
+ cg.text(), use[ NUM_SHADES ] ),
+ flags & Style_Sunken, 1
+ );
+
+#ifdef QTC_GRADIENT_TOOLBARS_AND_MENUBARS
+
+ if ( APPEARANCE_FLAT != appearance )
+ drawBevelGradient( use[ NUM_SHADES ],
+ true, 1, p, r, true,
+ SHADE_BAR_LIGHT, SHADE_BAR_DARK
+ );
+#endif
+
+ break;
+ case TB_LIGHT:
+ qDrawShadePanel( p,
+ r.x(), r.y(), r.width(), r.height(),
+ QColorGroup(
+ use[ 3 ], use[ NUM_SHADES ], use[ 0 ],
+ use[ 3 ], use[ 2 ],
+ cg.text(), use[ NUM_SHADES ]
+ ),
+ flags & Style_Sunken, 1
+ );
+
+#ifdef QTC_GRADIENT_TOOLBARS_AND_MENUBARS
+
+ if ( APPEARANCE_FLAT != appearance )
+ drawBevelGradient( use[ NUM_SHADES ],
+ true, 1, p, r, true,
+ SHADE_BAR_LIGHT, SHADE_BAR_DARK );
+#endif
+
+ break;
+ case TB_NONE:
+ break;
+
+ } /* switch */
+
+ break;
+ }
+ case PE_ScrollBarAddLine:
+ case PE_ScrollBarSubLine: {
+ bool down = ( flags & ( QStyle::Style_Down | QStyle::Style_On | QStyle::Style_Sunken ) );
+ const QColor *use = buttonColors( cg );
+
+ pe = flags & Style_Horizontal
+ ? PE_ScrollBarAddLine == pe
+ ? PE_ArrowRight
+ : PE_ArrowLeft
+ : PE_ScrollBarAddLine == pe
+ ? PE_ArrowDown
+ : PE_ArrowUp;
+
+ drawLightBevelButton( p, r, cg,
+ down ? flags : flags | ( ( ( flags & Style_Enabled ) ? Style_Raised : Style_Default ) ),
+ true,
+ PE_ArrowRight == pe ? ROUNDED_RIGHT :
+ PE_ArrowLeft == pe ? ROUNDED_LEFT :
+ PE_ArrowDown == pe ? ROUNDED_BOTTOM :
+ PE_ArrowUp == pe ? ROUNDED_TOP : ROUNDED_NONE,
+ getFill( flags, use ), use );
+ drawPrimitive( pe, p, r, cg, flags );
+ break;
+ }
+ case PE_ScrollBarSubPage:
+ case PE_ScrollBarAddPage: {
+ const QColor *use = backgroundColors( cg );
+
+ if ( borderButton ) {
+ if ( flags & Style_Horizontal ) {
+ p->fillRect( r.x(), r.y() + 1, r.width(), r.height() - 2, use[ 2 ] );
+ p->setPen( use[ 5 ] );
+ p->drawLine( r.left(), r.top(), r.right(), r.top() );
+ p->drawLine( r.left(), r.bottom(), r.right(), r.bottom() );
+ } else {
+ p->fillRect( r.x() + 1, r.y(), r.width() - 2, r.height(), use[ 2 ] );
+ p->setPen( use[ 5 ] );
+ p->drawLine( r.left(), r.top(), r.left(), r.bottom() );
+ p->drawLine( r.right(), r.top(), r.right(), r.bottom() );
+ }
+ } else
+ p->fillRect( r.x(), r.y(), r.width(), r.height(), use[ 2 ] );
+ break;
+ }
+ case PE_ScrollBarSlider: {
+ const QColor *use = buttonColors( cg );
+
+
+
+ if ( flags & Style_Down )
+ flags -= Style_Down;
+ flags |= flags & Style_Enabled ? Style_Raised : Style_Default;
+
+ drawLightBevelButton( p, r, cg, flags, true, ROUNDED_NONE, getFill( flags, use ), use );
+
+ if ( GROOVE_NONE != sliderThumbs &&
+ ( ( flags & Style_Horizontal && r.width() >= 20 ) || r.height() >= 20 ) )
+ drawLines( p, r, !( flags & Style_Horizontal ), 3, 4, use, 0, GROOVE_SUNKEN == sliderThumbs,
+ APPEARANCE_LIGHT_GRADIENT == appearance );
+ break;
+ }
+ case PE_FocusRect: {
+ p->drawWinFocusRect( r, cg.background() );
+ break;
+ }
+ case PE_ArrowUp:
+ case PE_ArrowDown:
+ case PE_ArrowRight:
+ case PE_ArrowLeft:
+ drawArrow( p, r, cg, flags, pe );
+ break;
+ case PE_SpinWidgetUp:
+ case PE_SpinWidgetDown: {
+ QRect sr( r );
+ const QColor *use = buttonColors( cg );
+
+ drawLightBevelButton( p, sr, cg,
+ flags | Style_Horizontal, true, PE_SpinWidgetDown == pe ? ROUNDED_BOTTOM : ROUNDED_TOP,
+ getFill( flags, use ), use );
+
+ if ( vArrow ) {
+ if ( PE_SpinWidgetDown == pe )
+ sr.setY( sr.y() - 1 );
+ } else
+ sr.setY( sr.y() + ( PE_SpinWidgetDown == pe ? -2 : 1 ) );
+
+ drawArrow( p, sr, cg, flags, pe == PE_SpinWidgetUp ? PE_ArrowUp : PE_ArrowDown, true );
+ break;
+ }
+ default:
+ KStyle::drawPrimitive( pe, p, r, cg, flags, data );
+ }
+}
+
+void KlearlookStyle::drawKStylePrimitive( KStylePrimitive kpe, QPainter *p, const QWidget *widget, const QRect &r,
+ const QColorGroup &cg, SFlags flags, const QStyleOption &opt ) const {
+ switch ( kpe ) {
+ case KPE_ToolBarHandle:
+ case KPE_GeneralHandle:
+ drawLines( p, r, !( flags & Style_Horizontal ), 2,
+ APP_KICKER == themedApp ? 1 : KPE_ToolBarHandle == kpe ? 4 : 2, gray,
+ APP_KICKER == themedApp ? 1 : KPE_ToolBarHandle == kpe ? -2 : 0, GROOVE_SUNKEN == handles,
+ APPEARANCE_LIGHT_GRADIENT == appearance );
+ break;
+ case KPE_SliderGroove:
+ drawSliderGroove( p, r, flags, widget );
+ break;
+ case KPE_SliderHandle:
+ drawSliderHandle( p, r, cg, flags );
+ break;
+ case KPE_ListViewExpander: {
+ int lvSize = QTC_LV_SIZE( lvExpander );
+ QRect ar( r.x() + ( ( r.width() - ( lvSize + 4 ) ) >> 1 ),
+ r.y() + ( ( r.height() - ( lvSize + 4 ) ) >> 1 ), lvSize + 4, lvSize + 4 );
+
+ p->setPen( /*lvDark ? cg.text() : */cg.mid() );
+
+ if ( LV_LINES_NONE != lvLines ) {
+ int lo = rounded ? 2 : 0;
+
+ p->drawLine( ar.x() + lo, ar.y(), ( ar.x() + ar.width() - 1 ) - lo, ar.y() );
+ p->drawLine( ar.x() + lo, ar.y() + ar.height() - 1,
+ ( ar.x() + ar.width() - 1 ) - lo, ar.y() + ar.height() - 1 );
+ p->drawLine( ar.x(), ar.y() + lo, ar.x(), ( ar.y() + ar.height() - 1 ) - lo );
+ p->drawLine( ar.x() + ar.width() - 1,
+ ar.y() + lo, ar.x() + ar.width() - 1, ( ar.y() + ar.height() - 1 ) - lo );
+
+ if ( rounded ) {
+ p->drawPoint( ar.x() + 1, ar.y() + 1 );
+ p->drawPoint( ar.x() + 1, ar.y() + ar.height() - 2 );
+ p->drawPoint( ar.x() + ar.width() - 2, ar.y() + 1 );
+ p->drawPoint( ar.x() + ar.width() - 2, ar.y() + ar.height() - 2 );
+ p->setPen( midColor( /*lvDark ? cg.text() : */cg.mid(), cg.background() ) );
+ p->drawLine( ar.x(), ar.y() + 1, ar.x() + 1, ar.y() );
+ p->drawLine( ar.x() + ar.width() - 2, ar.y(), ar.x() + ar.width() - 1, ar.y() + 1 );
+ p->drawLine( ar.x(), ar.y() + ar.height() - 2, ar.x() + 1, ar.y() + ar.height() - 1 );
+ p->drawLine( ar.x() + ar.width() - 2, ar.y() + ar.height() - 1,
+ ar.x() + ar.width() - 1, ar.y() + ar.height() - 2 );
+ }
+ }
+
+ if ( LV_EXP_ARR == lvExpander )
+ drawArrow( p, ar, cg, flags | Style_Enabled, flags & Style_On // Collapsed = On
+ ? QApplication::reverseLayout()
+ ? PE_ArrowLeft
+ : PE_ArrowRight
+ : PE_ArrowDown );
+ else {
+ int xo = ( ar.width() - lvSize ) >> 1,
+ yo = ( ar.height() - lvSize ) >> 1;
+ int mid = lvSize >> 1;
+
+ p->setPen( cg.text() );
+ p->drawLine( ar.x() + xo + ( mid - 2 ), ar.y() + yo + mid,
+ ar.x() + xo + lvSize - ( mid - 1 ), ar.y() + yo + mid );
+ if ( flags & Style_On ) // Collapsed = On
+ p->drawLine( ar.x() + xo + mid, ar.y() + yo + ( mid - 2 ),
+ ar.x() + xo + mid, ar.y() + yo + lvSize - ( mid - 1 ) );
+ }
+ break;
+ }
+ case KPE_ListViewBranch:
+ switch ( lvLines ) {
+ case LV_LINES_NONE:
+ break;
+ case LV_LINES_DOTTED:
+ // Taken and modified (colour wise) from kstyle.cpp - which in turn comes from
+ // qwindowsstyl.cpp
+ {
+ static QBitmap *verticalLine = 0,
+ *horizontalLine = 0;
+ static QCleanupHandler<QBitmap> lvCleanupBitmap;
+
+ // Create the dotline pixmaps if not already created
+ if ( !verticalLine ) {
+ // make 128*1 and 1*128 bitmaps that can be used for drawing the right sort of lines.
+ verticalLine = new QBitmap( 1, 129, true );
+ horizontalLine = new QBitmap( 128, 1, true );
+ QPointArray a( 64 );
+ QPainter p2;
+
+ p2.begin( verticalLine );
+
+ int i;
+ for ( i = 0; i < 64; i++ )
+ a.setPoint( i, 0, i * 2 + 1 );
+
+ p2.setPen( color1 );
+ p2.drawPoints( a );
+ p2.end();
+ QApplication::flushX();
+ verticalLine->setMask( *verticalLine );
+
+ p2.begin( horizontalLine );
+
+ for ( i = 0; i < 64; i++ )
+ a.setPoint( i, i * 2 + 1, 0 );
+
+ p2.setPen( color1 );
+ p2.drawPoints( a );
+ p2.end();
+ QApplication::flushX();
+ horizontalLine->setMask( *horizontalLine );
+
+ lvCleanupBitmap.add( &verticalLine );
+ lvCleanupBitmap.add( &horizontalLine );
+ }
+
+ p->setPen( lvDark ? cg.text() : cg.mid() ); // Wow, my big modification...
+
+ if ( flags & Style_Horizontal ) {
+ int point = r.x(),
+ other = r.y(),
+ end = r.x() + r.width(),
+ thickness = r.height();
+
+ while ( point < end ) {
+ int i = 128;
+
+ if ( i + point > end )
+ i = end - point;
+ p->drawPixmap( point, other, *horizontalLine, 0, 0, i, thickness );
+ point += i;
+ }
+ } else {
+ int point = r.y(),
+ other = r.x(),
+ end = r.y() + r.height(),
+ thickness = r.width(),
+ pixmapoffset = ( flags & Style_NoChange ) ? 0 : 1; // ### Hackish
+
+ while ( point < end ) {
+ int i = 128;
+
+ if ( i + point > end )
+ i = end - point;
+ p->drawPixmap( other, point, *verticalLine, 0, pixmapoffset, thickness, i );
+ point += i;
+ }
+ }
+ break;
+ }
+ case LV_LINES_SOLID:
+ p->setPen( cg.mid() );
+ p->drawLine( r.x(), r.y(), r.x() + r.width() - 1, r.y() + r.height() - 1 );
+ break;
+ }
+ break;
+ default:
+ KStyle::drawKStylePrimitive( kpe, p, widget, r, cg, flags, opt );
+ }
+}
+
+void KlearlookStyle::drawControl(
+ ControlElement control,
+ QPainter *p,
+ const QWidget *widget,
+ const QRect &r,
+ const QColorGroup &cg, SFlags flags, const QStyleOption &data ) const
+{
+ if ( widget == hoverWidget )
+ flags |= Style_MouseOver;
+
+ switch ( control ) {
+ case CE_TabBarTab: {
+ const QTabBar * tb = ( const QTabBar * ) widget;
+ bool cornerWidget = false,
+ firstTab = 0 == tb->indexOf( data.tab() ->identifier() );
+
+ if ( ::qt_cast<const QTabWidget *>( tb->parent() ) ) {
+ const QTabWidget * tw = ( const QTabWidget* ) tb->parent();
+
+ // is there a corner widget in the (top) left edge?
+ if ( tw->cornerWidget( Qt::TopLeft ) )
+ cornerWidget = true;
+ }
+
+ if ( borderFrame ) {
+ QRect tr( r ),
+ fr( r );
+ int offset = rounded ? 2 : 0;
+
+ switch ( tb->shape() ) {
+ case QTabBar::TriangularAbove:
+ case QTabBar::RoundedAbove:
+ if ( flags & Style_Selected ) {
+ tr.addCoords( 0, 0, 0, -1 );
+ fr.addCoords( 2, 2, -2, -2 );
+ p->setPen( gray[ 5 ] );
+ p->drawLine( tr.left(),
+ firstTab && !cornerWidget ?
+ tr.bottom() + 1 : tr.bottom(), tr.left(),
+ tr.top() + offset );
+ p->drawLine( tr.left() + offset, tr.top(), tr.right() - offset, tr.top() );
+ p->drawLine( tr.right(), tr.top() + 1, tr.right(), tr.bottom() );
+
+ p->setPen( gray[ 0 ] );
+ p->drawLine( tr.left() + 1, tr.bottom() + 1, tr.left() + 1, tr.top() + 2 );
+ p->drawLine( tr.left() + 1, tr.top() + 1, tr.right() - 1, tr.top() + 1 );
+ p->drawLine( tr.right() - 1, tr.bottom() + 1, tr.right(), tr.bottom() + 1 );
+
+ if ( cornerWidget )
+ p->drawPoint( tr.left(), tr.bottom() + 1 );
+
+ p->setPen( gray[ 4 ] );
+ p->drawLine( tr.right() - 1, tr.top() + 1, tr.right() - 1, tr.bottom() );
+
+ if ( rounded ) {
+ p->setPen( gray[ 5 ] );
+ p->drawPoint( tr.x() + 1, tr.y() + 1 );
+ p->drawPoint( tr.x() + tr.width() - 2, tr.y() + 1 );
+ p->setPen( gray[ 4 ] );
+ p->drawLine( tr.x(), tr.y() + 1, tr.x() + 1, tr.y() );
+ p->drawLine( tr.x() + tr.width() - 2,
+ tr.y(), tr.x() + tr.width() - 1, tr.y() + 1 );
+ }
+ } else {
+ tr.addCoords( 0, 2, 0, -1 );
+ fr.addCoords( 2, 4, -2, -2 );
+
+ p->setPen( gray[ 5 ] );
+ p->drawLine( tr.left(),
+ firstTab && !cornerWidget ? tr.bottom() + 1 : tr.bottom(), tr.left(),
+ tr.top() + 1 );
+ p->drawLine( rounded ? tr.left() + 1 : tr.left(),
+ tr.top(), rounded ? tr.right() - 1 : tr.right(), tr.top() );
+ p->drawLine( tr.right(), tr.top() + 1, tr.right(), tr.bottom() );
+ p->drawLine( tr.left(), tr.bottom(), tr.right(), tr.bottom() );
+ p->setPen( gray[ 0 ] );
+ p->drawLine( tr.left() + 1, tr.top() + 1, tr.right() - 1, tr.top() + 1 );
+
+ if ( cornerWidget )
+ p->drawPoint( tr.left(), tr.bottom() + 1 );
+
+ if ( !firstTab )
+ p->setPen( gray[ 2 ] );
+
+ p->drawLine( tr.left() + 1, tr.bottom() - 1, tr.left() + 1, tr.top() + 2 );
+ p->setPen( gray[ 0 ] );
+ p->drawLine( tr.left() + 1, tr.bottom() + 1, tr.right(), tr.bottom() + 1 );
+ p->setPen( gray[ 4 ] );
+ p->drawLine( tr.right() - 1, tr.top() + 1, tr.right() - 1, tr.bottom() - 1 );
+ }
+
+ if ( APPEARANCE_FLAT != appearance ) {
+ if ( flags & Style_Selected ) {
+ drawBevelGradient( cg.background(), true, 0, p, fr, true,
+ SHADE_TAB_SEL_LIGHT( appearance ),
+ SHADE_TAB_SEL_DARK( appearance ) );
+
+ p->setPen(menuPbar[ GRADIENT_BASE ]);
+ p->drawLine( fr.left()-1, fr.top()-1, fr.right()+1, fr.top()-1);
+ p->drawLine( fr.left()-1, fr.top(), fr.right()+1, fr.top());
+
+ p->setPen(menuPbar[ GRADIENT_DARK ].dark(118));
+ p->drawLine( fr.left(), fr.top()-2, fr.right(), fr.top()-2);
+ p->drawPoint( tr.x() + 1, tr.y() + 1 );
+ p->drawPoint( tr.x(), tr.y() + 2 );
+ p->drawPoint( tr.x() + tr.width() - 2, tr.y() + 1 );
+ p->drawPoint( tr.x() + tr.width() - 1, tr.y() + 2 );
+
+ } else
+ drawBevelGradient( gray[ 2 ], true, 0, p, fr, true,
+ SHADE_TAB_LIGHT( appearance ), SHADE_TAB_DARK( appearance ) );
+
+
+ } else
+ p->fillRect( fr, flags & Style_Selected ? cg.background() : gray[ 2 ] );
+
+ break;
+ case QTabBar::TriangularBelow:
+ case QTabBar::RoundedBelow:
+ if ( flags & Style_Selected ) {
+ fr.addCoords( 3, 2, -3, -3 );
+ p->setPen( gray[ 5 ] );
+
+ p->drawLine( tr.left(), tr.bottom() - offset, tr.left(),
+ firstTab && !cornerWidget ? tr.top() : tr.top() + 1 );
+ p->drawLine( rounded ? tr.left() + 1 : tr.left(),
+ tr.bottom(), tr.right() - offset, tr.bottom() );
+ p->drawLine( tr.right(), tr.top() + 1, tr.right(), tr.bottom() - 1 );
+ p->setPen( gray[ 0 ] );
+ p->drawLine( tr.left() + 1, tr.bottom() - 2, tr.left() + 1,
+ firstTab && !cornerWidget ? tr.top() : tr.top() - 1 );
+ p->setPen( gray[ 4 ] );
+ p->drawLine( tr.left() + 1, tr.bottom() - 1, tr.right() - 1, tr.bottom() - 1 );
+ p->drawLine( tr.right() - 1, tr.bottom() - 2, tr.right() - 1, tr.top() - 1 );
+ p->drawPoint( tr.right(), tr.top() );
+ if ( cornerWidget )
+ p->drawPoint( tr.left(), tr.top() );
+
+
+ if ( rounded ) {
+ p->setPen( gray[ 5 ] );
+ p->drawPoint( tr.x() + 1, tr.y() + tr.height() - 2 );
+ p->drawPoint( tr.x() + tr.width() - 2, tr.y() + tr.height() - 2 );
+ p->setPen( gray[ 4 ] );
+ p->drawLine( tr.x(), tr.y() + tr.height() - 2,
+ tr.x() + 1, tr.y() + tr.height() - 1 );
+ p->drawLine( tr.x() + tr.width() - 2,
+ tr.y() + tr.height() - 1, tr.x() + tr.width() - 1,
+ tr.y() + tr.height() - 2 );
+ }
+ } else {
+ tr.addCoords( 0, 1, 0, -2 );
+ fr.addCoords( 1, 2, -2, -4 );
+
+ p->setPen( gray[ 5 ] );
+ p->drawLine( tr.left(), tr.bottom() - 1, tr.left(),
+ firstTab && !cornerWidget ? tr.top() : tr.top() + 1 );
+ p->drawLine( rounded ? tr.left() + 1 : tr.left(), tr.bottom(),
+ rounded ? tr.right() - 1 : tr.right(), tr.bottom() );
+ p->drawLine( tr.right(), tr.top() + 1, tr.right(), tr.bottom() - 1 );
+ p->drawLine( tr.right(), tr.top(), tr.left(), tr.top() );
+ p->setPen( gray[ 4 ] );
+ p->drawLine( tr.left(), tr.top() - 1, tr.right(), tr.top() - 1 );
+ p->drawLine( tr.left() + 1, tr.bottom() - 1, tr.right() - 1, tr.bottom() - 1 );
+ p->drawLine( tr.right() - 1, tr.bottom() - 2, tr.right() - 1, tr.top() + 1 );
+ }
+ if ( APPEARANCE_FLAT != appearance )
+ if ( flags & Style_Selected )
+ drawBevelGradient( cg.background(), false, -1, p, fr, true,
+ SHADE_BOTTOM_TAB_SEL_DARK( appearance ),
+ SHADE_BOTTOM_TAB_SEL_LIGHT( appearance ) );
+ else
+ drawBevelGradient( gray[ 2 ], false, 0, p, fr, true,
+ SHADE_BOTTOM_TAB_DARK( appearance ),
+ SHADE_BOTTOM_TAB_LIGHT( appearance ) );
+ else
+ p->fillRect( fr, flags & Style_Selected ? cg.background() : gray[ 2 ] );
+ break;
+ default:
+ KStyle::drawControl( control, p, widget, r, cg, flags, data );
+ }
+ } else {
+ QRect br( r );
+
+ switch ( tb->shape() ) {
+ case QTabBar::TriangularAbove:
+ case QTabBar::RoundedAbove:
+ if ( flags & Style_Selected ) {
+ p->setPen( cg.background() );
+ p->drawLine( br.bottomLeft(), br.bottomRight() );
+ p->setPen( gray[ 0 ] );
+ p->drawPoint( br.bottomLeft() );
+ p->setPen( gray[ 5 ] );
+ p->drawPoint( br.bottomRight() );
+ br.addCoords( 0, 0, 0, -1 );
+ } else {
+ p->setPen( gray[ 0 ] );
+ p->drawLine( br.left(), br.bottom(), br.right(), br.bottom() );
+ br.addCoords( 0, 1, -1, -1 );
+ }
+
+ p->setPen( gray[ 0 == r.left() || flags & Style_Selected ? 0 : 5 ] );
+ p->drawLine( br.bottomLeft(), br.topLeft() );
+ p->setPen( gray[ 0 ] );
+ p->drawLine( br.left() + 1, br.top(), br.right() - 1, br.top() );
+ p->setPen( gray[ 5 ] );
+ p->drawLine( br.right(), br.top(), br.right(), br.bottom() );
+ br.addCoords( 1, 1, -1, 0 );
+ if ( APPEARANCE_FLAT != appearance )
+ if ( flags & Style_Selected )
+ drawBevelGradient( cg.background(), true, 0, p, br,
+ true, SHADE_TAB_SEL_LIGHT( appearance ),
+ SHADE_TAB_SEL_DARK( appearance ) );
+ else
+ drawBevelGradient( gray[ 2 ], true, 0, p, br, true,
+ SHADE_TAB_LIGHT( appearance ), SHADE_TAB_DARK( appearance ) );
+ else
+ p->fillRect( br, flags & Style_Selected ? cg.background() : gray[ 2 ] );
+ break;
+ case QTabBar::TriangularBelow:
+ case QTabBar::RoundedBelow:
+ if ( flags & Style_Selected ) {
+ p->setPen( cg.background() );
+ p->drawLine( br.topLeft(), br.topRight() );
+ p->setPen( gray[ 0 ] );
+ p->drawPoint( br.topLeft() );
+ p->setPen( gray[ 5 ] );
+ p->drawPoint( br.topRight() );
+ br.addCoords( 0, 1, 0, 0 );
+ } else {
+ p->setPen( gray[ 5 ] );
+ p->drawLine( br.left(), br.top(),
+ br.right(), br.top() );
+ br.addCoords( 0, 1, -1, -1 );
+ }
+
+ if ( 0 == r.left() || flags & Style_Selected ) {
+ p->setPen( gray[ 0 ] );
+ p->drawLine( br.bottomLeft(), br.topLeft() );
+ }
+ p->setPen( gray[ 5 ] );
+ p->drawLine( br.bottomLeft(), br.bottomRight() );
+ p->drawLine( br.right(), br.top(), br.right(), br.bottom() );
+ br.addCoords( 1, 0, -1, -1 );
+ if ( APPEARANCE_FLAT != appearance )
+ if ( flags & Style_Selected )
+ drawBevelGradient(
+ cg.background(), false, 0, p, br, true,
+ SHADE_BOTTOM_TAB_SEL_DARK( appearance ),
+ SHADE_BOTTOM_TAB_SEL_LIGHT( appearance ) );
+ else
+ drawBevelGradient( gray[ 2 ], false, 0, p, br,
+ true, SHADE_BOTTOM_TAB_DARK( appearance ),
+ SHADE_BOTTOM_TAB_LIGHT( appearance ) );
+ else
+ p->fillRect( br, flags & Style_Selected ? cg.background() : gray[ 2 ] );
+ }
+ }
+ break;
+ }
+#if QT_VERSION >= 0x030200
+ case CE_TabBarLabel: {
+ if ( data.isDefault() )
+ break;
+
+ const QTabBar *tb = ( const QTabBar * ) widget;
+ QTab *t = data.tab();
+ QRect tr = r;
+
+ if ( t->identifier() == tb->currentTab() ) {
+ if ( QTabBar::RoundedAbove == tb->shape() || QTabBar::TriangularAbove == tb->shape() )
+ tr.setBottom( tr.bottom() - pixelMetric( QStyle::PM_TabBarTabShiftVertical, tb ) );
+ } else
+ if ( QTabBar::RoundedBelow == tb->shape() || QTabBar::TriangularBelow == tb->shape() )
+ tr.setTop( tr.top() + pixelMetric( QStyle::PM_TabBarTabShiftVertical, tb ) );
+
+ drawItem( p, tr, AlignCenter | ShowPrefix, cg, flags & Style_Enabled, 0, t->text() );
+
+ if ( ( flags & Style_HasFocus ) && !t->text().isEmpty() )
+ drawPrimitive( PE_FocusRect, p, r, cg );
+ break;
+ }
+#endif
+ case CE_PushButtonLabel: // Taken from Highcolour and Plastik...
+ {
+ int x, y, w, h;
+
+ r.rect( &x, &y, &w, &h );
+
+ const QPushButton *button = static_cast<const QPushButton *>( widget );
+ bool active = button->isOn() || button->isDown(),
+ cornArrow = false;
+
+ // Shift button contents if pushed.
+ if ( active ) {
+ x += pixelMetric( PM_ButtonShiftHorizontal, widget );
+ y += pixelMetric( PM_ButtonShiftVertical, widget );
+ flags |= Style_Sunken;
+ }
+
+ // Does the button have a popup menu?
+ if ( button->isMenuButton() ) {
+ int dx = pixelMetric( PM_MenuButtonIndicator, widget ),
+ margin = pixelMetric( PM_ButtonMargin, widget );
+
+ if ( button->iconSet() && !button->iconSet() ->isNull() &&
+ ( dx + button->iconSet() ->pixmap ( QIconSet::Small, QIconSet::Normal,
+ QIconSet::Off ).width() ) >= w )
+ cornArrow = true; //To little room. Draw the arrow in the corner, don't adjust the widget
+ else {
+ drawPrimitive( PE_ArrowDown,
+ p, visualRect( QRect(
+ ( x + w ) - ( dx + margin ), y, dx,
+ h ), r ), cg, flags, data );
+
+ w -= dx;
+ }
+ }
+
+ // Draw the icon if there is one
+ if ( button->iconSet() && !button->iconSet() ->isNull() ) {
+ QIconSet::Mode mode = QIconSet::Disabled;
+ QIconSet::State state = QIconSet::Off;
+
+ if ( button->isEnabled() )
+ mode = button->hasFocus() ? QIconSet::Active : QIconSet::Normal;
+ if ( button->isToggleButton() && button->isOn() )
+ state = QIconSet::On;
+
+ QPixmap pixmap = button->iconSet() ->pixmap( QIconSet::Small, mode, state );
+
+ static const int constSpace = 2;
+
+ int xo = 0,
+ pw = pixmap.width(),
+ iw = 0;
+
+ if ( button->text().isEmpty() && !button->pixmap() )
+ p->drawPixmap( x + ( w >> 1 ) - ( pixmap.width() >> 1 ),
+ y + ( h >> 1 ) - ( pixmap.height() >> 1 ),
+ pixmap );
+ else {
+ iw = button->pixmap() ? button->pixmap() ->width()
+ : widget->fontMetrics().size( Qt::ShowPrefix, button->text() ).width();
+
+ int cw = iw + pw + constSpace;
+
+ xo = cw < w ? ( w - cw ) >> 1 : constSpace;
+ p->drawPixmap( x + xo, y + ( h >> 1 ) - ( pixmap.height() >> 1 ), pixmap );
+ xo += pw;
+ }
+
+ if ( cornArrow ) //Draw over the icon
+ drawPrimitive( PE_ArrowDown, p, visualRect( QRect( x + w - 6, x + h - 6, 7, 7 ), r ),
+ cg, flags, data );
+
+ if ( xo && iw ) {
+ x += xo + constSpace;
+ w = iw;
+ } else {
+ x += pw + constSpace;
+ w -= pw + constSpace;
+ }
+ }
+
+ // Make the label indicate if the button is a default button or not
+ int i,
+ j = boldDefText && button->isDefault() ? 2 : 1;
+
+ for ( i = 0; i < j; i++ )
+ drawItem( p, QRect( x + i, y, w, h ),
+ AlignCenter | ShowPrefix,
+ button->colorGroup(),
+ button->isEnabled(),
+ button->pixmap(),
+ button->text(), -1,
+ &button->colorGroup().buttonText() );
+
+ //Draw a focus rect if the button has focus
+ if ( flags & Style_HasFocus )
+ drawPrimitive( PE_FocusRect, p,
+ QStyle::visualRect( subRect( SR_PushButtonFocusRect, widget ), widget ), cg, flags );
+
+ break;
+ }
+ case CE_PopupMenuItem: {
+ if ( !widget || data.isDefault() )
+ break;
+
+ const QPopupMenu *popupmenu = ( const QPopupMenu * ) widget;
+ QMenuItem *mi = data.menuItem();
+ int tab = data.tabWidth(),
+ maxpmw = data.maxIconWidth(),
+ x, y, w, h;
+
+ r.rect( &x, &y, &w, &h );
+
+ if ( ( flags & Style_Active ) && ( flags & Style_Enabled ) ) {
+ drawPBarOrMenu( p, r, true, cg, true );
+ } else if ( widget->erasePixmap() && !widget->erasePixmap() ->isNull() )
+ p->drawPixmap( x, y, *widget->erasePixmap(), x, y, w, h );
+ else {
+ // lighter background in popup menu
+ p->fillRect( r, cg.background().light( 100 + popupmenuHighlightLevel ) );
+ }
+
+ if ( !mi )
+ break;
+
+ if ( mi->isSeparator() ) {
+ const QColor * use = backgroundColors( cg );
+ p->setPen( cg.background().dark(105) );
+ p->drawLine( r.left() + 5, r.top() + 3, r.right() - 5, r.top() + 3 );
+ break;
+ }
+
+ maxpmw = QMAX( maxpmw, 16 );
+
+ QRect cr, ir, tr, sr;
+ if (menuIcons) {
+ // check column
+ cr.setRect( r.left(), r.top(), maxpmw, r.height() );
+ // submenu indicator column
+ sr.setCoords( r.right() - maxpmw, r.top(), r.right(), r.bottom() );
+ // tab/accelerator column
+ tr.setCoords( sr.left() - tab - 4, r.top(), sr.left(), r.bottom() );
+ // item column
+ ir.setCoords( cr.right() + 2, r.top(), tr.right() - 4, r.bottom() );
+ } else {
+ // item column
+ ir.setCoords( r.left() + 4, r.top(), r.width() , r.bottom() );
+ // check column
+ cr.setCoords( r.right() - maxpmw, r.top(), r.right(), r.bottom() );
+ // submenu indicator column
+ sr.setCoords( r.right() - maxpmw, r.top(), r.right(), r.bottom() );
+ // tab/accelerator column
+ tr.setCoords( sr.left() - tab - 4, r.top(), sr.left(), r.bottom() );
+ }
+
+ bool reverse = QApplication::reverseLayout();
+
+ if ( reverse ) {
+ cr = visualRect( cr, r );
+ sr = visualRect( sr, r );
+ tr = visualRect( tr, r );
+ ir = visualRect( ir, r );
+ }
+
+ if ( mi->iconSet() && menuIcons ) {
+ // Select the correct icon from the iconset
+ QIconSet::Mode mode = flags & Style_Active
+ ? ( mi->isEnabled() ? QIconSet::Active : QIconSet::Disabled )
+ : ( mi->isEnabled() ? QIconSet::Normal : QIconSet::Disabled );
+ cr = visualRect( QRect( x, y, maxpmw, h ), r );
+
+ // Do we have an icon and are checked at the same time?
+ // Then draw a "pressed" background behind the icon
+ if ( popupmenu->isCheckable() && mi->isChecked() ) {
+
+ QBrush brush( gray[ 3 ] );
+
+ qDrawShadePanel( p,
+ cr.x() + 1, cr.y() + 2, cr.width() - 2, cr.height() - 4,
+ QColorGroup( gray[ 5 ], gray[ NUM_SHADES ], gray[ 0 ], gray[ 5 ],
+ gray[ 2 ], cg.text(), gray[ NUM_SHADES ] ),
+ true, 1, &brush );
+ }
+ // Draw the icon
+ QPixmap pixmap = mi->iconSet() ->pixmap( QIconSet::Small, mode );
+ QRect pmr( 0, 0, pixmap.width(), pixmap.height() );
+
+ pmr.moveCenter( cr.center() );
+ p->drawPixmap( pmr.topLeft(), pixmap );
+
+ } else if ( popupmenu->isCheckable() && mi->isChecked() ) {
+
+ // check column
+ cr.setRect( r.left(), r.top(), maxpmw, r.height() );
+ // submenu indicator column
+ sr.setCoords( r.right() - maxpmw, r.top(), r.right(), r.bottom() );
+ // tab/accelerator column
+ tr.setCoords( sr.left() - tab - 4, r.top(), sr.left(), r.bottom() );
+ // item column
+ ir.setCoords( cr.right() + 2, r.top(), tr.right() - 4, r.bottom() );
+
+ QBrush brush( mi->isEnabled() ? cg.highlightedText() : cg.background() );
+ drawPrimitiveMenu( PE_CheckMark, p, cr, cg,
+ ( flags & ( Style_Enabled | Style_Active ) ) | Style_On );
+ }
+
+ QColor textcolor, embosscolor;
+
+ if ( flags & Style_Active ) {
+ if ( !( flags & Style_Enabled ) ) {
+ textcolor = cg.text();
+ embosscolor = cg.light();
+ } else {
+ textcolor = cg.highlightedText();
+ embosscolor = cg.midlight().light();
+ }
+ } else if ( !( flags & Style_Enabled ) ) {
+ textcolor = cg.text();
+ embosscolor = cg.light();
+ } else
+ textcolor = embosscolor = cg.buttonText();
+ p->setPen( textcolor );
+
+ if ( mi->custom() ) {
+ p->save();
+ if ( !( flags & Style_Enabled ) ) {
+ p->setPen( cg.light() );
+ mi->custom() ->paint( p, cg, ( flags & Style_Enabled ) ? ( flags & Style_Active ) : 0,
+ flags & Style_Enabled, ir.x() + 1, ir.y() + 1, ir.width() - 1,
+ ir.height() - 1 );
+ p->setPen( textcolor );
+ }
+ mi->custom() ->paint( p, cg, ( flags & Style_Enabled ) ? ( flags & Style_Active ) : 0,
+ flags & Style_Enabled, ir.x(), ir.y(), ir.width(), ir.height() );
+ p->restore();
+ }
+
+ QString text = mi->text();
+
+ if ( !text.isNull() ) {
+ int t = text.find( '\t' );
+
+ // draw accelerator/tab-text
+ if ( t >= 0 ) {
+ int alignFlag = AlignVCenter | ShowPrefix | DontClip | SingleLine;
+
+ alignFlag |= ( reverse ? AlignLeft : AlignRight );
+
+ if ( !( flags & Style_Enabled ) ) {
+ p->setPen( embosscolor );
+ tr.moveBy( 1, 1 );
+ p->drawText( tr, alignFlag, text.mid( t + 1 ) );
+ tr.moveBy( -1, -1 );
+ p->setPen( textcolor );
+ }
+
+ p->drawText( tr, alignFlag, text.mid( t + 1 ) );
+ }
+
+ int alignFlag = AlignVCenter | ShowPrefix | DontClip | SingleLine;
+
+ alignFlag |= ( reverse ? AlignRight : AlignLeft );
+
+ if ( !( flags & Style_Enabled ) ) {
+ p->setPen( embosscolor );
+ ir.moveBy( 1, 1 );
+ p->drawText( ir, alignFlag, text, t );
+ ir.moveBy( -1, -1 );
+ p->setPen( textcolor );
+ }
+
+ p->drawText( ir, alignFlag, text, t );
+ } else if ( mi->pixmap() ) {
+ QPixmap pixmap = *mi->pixmap();
+
+ if ( 1 == pixmap.depth() )
+ p->setBackgroundMode( OpaqueMode );
+ p->drawPixmap( ir.x(), ( ir.height() - pixmap.height() ) >> 1, pixmap );
+ if ( pixmap.depth() == 1 )
+ p->setBackgroundMode( TransparentMode );
+ }
+
+ if ( mi->popup() )
+ drawArrow( p, sr, cg, flags, reverse ? PE_ArrowLeft : PE_ArrowRight, false, true );
+ break;
+ }
+ case CE_MenuBarItem: {
+ if ( ( flags & Style_Enabled ) &&
+ ( flags & Style_Active ) &&
+ ( flags & Style_Down ) ) {
+ drawPBarOrMenu2( p, QRect(r.x(), r.y(), r.width(), r.height()),
+ true, cg, true );
+ } else
+#ifdef QTC_GRADIENT_TOOLBARS_AND_MENUBARS
+ if ( APPEARANCE_FLAT != appearance )
+ drawBevelGradient( cg.background(), true, 0, p,
+ r,
+ true, SHADE_BAR_LIGHT, SHADE_BAR_DARK );
+ else
+#endif
+ if (darkMenubar) {
+ drawBevelGradient( cg.background(), true, 1, p,
+ QRect(r.x()-1, r.y()-1, r.width()+2, r.height()+2),
+ true,
+ SHADE_BEVEL_MENU_GRAD_LIGHT( appearance ), SHADE_BEVEL_MENU_GRAD_DARK( appearance ));
+ } else
+ p->fillRect( r, cg.background() );
+
+ if ( data.isDefault() )
+ break;
+
+ QMenuItem *mi = data.menuItem();
+
+ if ( flags & Style_Active && ( flags & Style_Down ) )
+ drawItem( p, r, AlignCenter | ShowPrefix | DontClip | SingleLine,
+ cg, flags & Style_Enabled, mi->pixmap(), mi->text(), -1, &cg.highlightedText() );
+ else
+ drawItem( p, r, AlignCenter | ShowPrefix | DontClip | SingleLine, cg,
+ flags & Style_Enabled, mi->pixmap(), mi->text(), -1, &cg.buttonText() );
+ break;
+ }
+ case CE_MenuBarEmptyArea:
+ if (darkMenubar) {
+ //p->fillRect( r, cg.background().dark( 106 ) );
+ QColor b;
+ b.setRgb(cg.background().red(), cg.background().green(), cg.background().blue());
+ drawBevelGradient( b, true, 1, p,
+ QRect(r.x()-1, r.y()-1, r.width()+2, r.height()+2 ),
+ true,
+ SHADE_BEVEL_MENU_GRAD_LIGHT( appearance ), SHADE_BEVEL_MENU_GRAD_DARK( appearance ));
+ } else
+ p->fillRect( r, cg.background() );
+ break;
+
+ case CE_DockWindowEmptyArea:
+#ifdef QTC_GRADIENT_TOOLBARS_AND_MENUBARS
+
+ if ( APPEARANCE_FLAT != appearance )
+ drawBevelGradient( cg.background(), true, 1, p, r, true, SHADE_BAR_LIGHT, SHADE_BAR_DARK );
+ else
+#endif
+
+ p->fillRect( r, cg.background() );
+ break;
+ case CE_ProgressBarGroove:
+ p->setBrush( gray[ NUM_SHADES ] );
+ p->drawRect( r );
+ qDrawShadePanel( p, r,
+ QColorGroup( gray[ 5 ], gray[ NUM_SHADES ], gray[ 0 ], gray[ 5 ], gray[ 2 ],
+ cg.text(), gray[ NUM_SHADES ] ), true, 1 );
+
+ break;
+
+ case CE_ProgressBarContents: {
+ // ### Take into account totalSteps()for busy indicator
+ const QProgressBar *pb = ( const QProgressBar* ) widget;
+ QRect cr = subRect( SR_ProgressBarContents, widget );
+ double progress = pb->progress();
+ bool reverse = QApplication::reverseLayout();
+ int steps = pb->totalSteps();
+
+ if ( cr.isValid() && ( progress > 0 || steps == 0 ) ) {
+ double pg = ( steps == 0 ) ? 0.1 : progress / steps;
+ int width = QMIN( cr.width(), ( int ) ( pg * cr.width() ) );
+
+ if ( 0 == steps ) //Busy indicator
+ {
+ if ( width < 1 )
+ width = 1; //A busy indicator with width 0 is kind of useless
+
+ int remWidth = cr.width() - width; //Never disappear completely
+
+ if ( remWidth <= 0 )
+ remWidth = 1; //Do something non-crashy when too small...
+
+ int pstep = int( progress ) % ( 2 * remWidth );
+
+ if ( pstep > remWidth ) {
+ //Bounce about.. We're remWidth +some delta, we want to be remWidth-delta...
+ //-((remWidth +some delta)-2* remWidth)=-(some deleta-remWidth)=remWidth-some delta..
+ pstep = -( pstep - 2 * remWidth );
+ }
+
+ if ( reverse )
+ drawPBarOrMenu( p, QRect( cr.x() + cr.width() - width - pstep,
+ cr.y(), width, cr.height() ), true, cg );
+ else
+ drawPBarOrMenu( p, QRect( cr.x() + pstep, cr.y(), width,
+ cr.height() ), true, cg );
+ } else
+ if ( reverse )
+ drawPBarOrMenu( p, QRect( cr.x() + ( cr.width() - width ),
+ cr.y(), width, cr.height() ), true, cg );
+ else
+ drawPBarOrMenu( p, QRect( cr.x(), cr.y(), width, cr.height() ), true, cg );
+ }
+ break;
+ }
+ case CE_PushButton: {
+ const QPushButton *button = static_cast<const QPushButton *>( widget );
+ QRect br( r );
+ int dbi = pixelMetric( PM_ButtonDefaultIndicator, widget );
+
+ if ( rounded && isFormWidget( widget ) )
+ formMode = true;
+
+ if ( widget == hoverWidget )
+ flags |= Style_MouseOver;
+
+ if ( IND_BORDER == defBtnIndicator )
+ br.setCoords( br.left() + dbi, br.top() + dbi, br.right() - dbi, br.bottom() - dbi );
+ else if ( IND_FONT_COLOUR == defBtnIndicator && button->isDefault() )
+ flags |= Style_ButtonDefault;
+
+ p->save();
+ p->setBrushOrigin( -widget->backgroundOffset().x(), -widget->backgroundOffset().y() );
+ // draw button
+ drawPrimitive( PE_ButtonCommand, p, br, cg, flags );
+ if ( button->isDefault() && IND_FONT_COLOUR != defBtnIndicator )
+ drawPrimitive( PE_ButtonDefault, p, r, cg, flags );
+ p->restore();
+ formMode = false;
+ break;
+ }
+ case CE_CheckBox:
+ drawPrimitive( PE_Indicator, p, r, cg, flags, data );
+ break;
+ case CE_CheckBoxLabel:
+ if ( crLabelHighlight ) {
+ const QCheckBox * checkbox = ( const QCheckBox * ) widget;
+
+ if ( flags & Style_MouseOver &&
+#if QT_VERSION >= 0x030200
+ HOVER_CHECK == hover && hoverWidget == widget &&
+#endif
+ !isFormWidget( widget ) ) {
+#if QT_VERSION >= 0x030200
+ QRect cr( checkbox->rect() );
+ QRegion r( QRect( cr.x(), cr.y(),
+ visualRect( subRect( SR_CheckBoxFocusRect, widget ), widget ).width() +
+ pixelMetric( PM_IndicatorWidth ) + 4, cr.height() ) );
+
+#else
+
+ QRegion r( checkbox->rect() );
+#endif
+
+ r -= visualRect( subRect( SR_CheckBoxIndicator, widget ), widget );
+ p->setClipRegion( r );
+ p->fillRect( checkbox->rect(), cg.background().light( QTC_HIGHLIGHT_FACTOR ) );
+ p->setClipping( false );
+ }
+ int alignment = QApplication::reverseLayout() ? AlignRight : AlignLeft;
+
+ drawItem( p, r, alignment | AlignVCenter | ShowPrefix, cg,
+ flags & Style_Enabled, checkbox->pixmap(), checkbox->text() );
+
+ if ( checkbox->hasFocus() )
+ drawPrimitive( PE_FocusRect, p,
+ visualRect( subRect( SR_CheckBoxFocusRect, widget ), widget ), cg, flags );
+ } else
+ KStyle::drawControl( control, p, widget, r, cg, flags, data );
+ break;
+ case CE_RadioButton:
+ formMode = isFormWidget( widget );
+ drawPrimitive( PE_ExclusiveIndicator, p, r, cg, flags, data );
+ formMode = false;
+ break;
+ case CE_RadioButtonLabel:
+ if ( crLabelHighlight ) {
+ const QRadioButton * radiobutton = ( const QRadioButton * ) widget;
+
+ if ( flags & Style_MouseOver &&
+#if QT_VERSION >= 0x030200
+ HOVER_RADIO == hover && hoverWidget == widget &&
+#endif
+ !isFormWidget( widget ) ) {
+#if QT_VERSION >= 0x030200
+ QRect rb( radiobutton->rect() );
+ QRegion r( QRect( rb.x(), rb.y(),
+ visualRect( subRect( SR_RadioButtonFocusRect, widget ), widget ).width() +
+ pixelMetric( PM_ExclusiveIndicatorWidth ) + 4, rb.height() ) );
+#else
+
+ QRegion r( radiobutton->rect() );
+#endif
+
+ r -= visualRect( subRect( SR_RadioButtonIndicator, widget ), widget );
+ p->setClipRegion( r );
+ p->fillRect( radiobutton->rect(), cg.background().light( QTC_HIGHLIGHT_FACTOR ) );
+ p->setClipping( false );
+ }
+
+ int alignment = QApplication::reverseLayout() ? AlignRight : AlignLeft;
+
+ drawItem( p, r, alignment | AlignVCenter | ShowPrefix, cg,
+ flags & Style_Enabled, radiobutton->pixmap(), radiobutton->text() );
+
+ if ( radiobutton->hasFocus() )
+ drawPrimitive( PE_FocusRect, p,
+ visualRect( subRect( SR_RadioButtonFocusRect, widget ), widget ), cg, flags );
+ break;
+ }
+ default:
+ KStyle::drawControl( control, p, widget, r, cg, flags, data );
+ }
+}
+
+void KlearlookStyle::drawControlMask( ControlElement control, QPainter *p, const QWidget *widget, const QRect &r,
+ const QStyleOption &data ) const {
+ switch ( control ) {
+ case CE_PushButton:
+ if ( rounded ) {
+ int offset = r.width() < QTC_MIN_BTN_SIZE || r.height() < QTC_MIN_BTN_SIZE ? 1 : 2;
+
+ p->fillRect( r, color0 );
+ p->fillRect( r.x() + 1, r.y() + 1, r.width() - 2, r.height() - 2, color1 );
+ p->setPen( color1 );
+ p->drawLine( r.x() + offset, r.y(), r.x() + r.width() - ( offset + 1 ), r.y() );
+ p->drawLine( r.x() + offset, r.y() + r.height() - 1,
+ r.x() + r.width() - ( offset + 1 ), r.y() + r.height() - 1 );
+ p->drawLine( r.x(), r.y() + offset, r.x(), r.y() + r.height() - ( offset + 1 ) );
+ p->drawLine( r.x() + r.width() - 1, r.y() + offset,
+ r.x() + r.width() - 1, r.y() + r.height() - ( offset + 1 ) );
+ } else
+ p->fillRect( r, color1 );
+ break;
+ default:
+ KStyle::drawControlMask( control, p, widget, r, data );
+ }
+}
+
+void KlearlookStyle::drawComplexControlMask( ComplexControl control, QPainter *p, const QWidget *widget, const QRect &r,
+ const QStyleOption &data ) const {
+ switch ( control ) {
+ case CC_ToolButton:
+ case CC_ComboBox:
+ drawControlMask( CE_PushButton, p, widget, r, data );
+ break;
+ default:
+ KStyle::drawComplexControlMask( control, p, widget, r, data );
+ }
+}
+
+QRect KlearlookStyle::subRect( SubRect subrect, const QWidget *widget ) const {
+ QRect rect,
+ wrect( widget->rect() );
+
+ switch ( subrect ) {
+ case SR_PushButtonFocusRect: {
+ // const QPushButton *button=(const QPushButton *)widget;
+ int dbw1 = 0,
+ dbw2 = 0;
+
+ // if(button->isDefault() || button->autoDefault())
+ // {
+ dbw1 = pixelMetric( PM_ButtonDefaultIndicator, widget );
+ dbw2 = dbw1 * 2;
+ // }
+
+ rect.setRect( wrect.x() + 3 + dbw1,
+ wrect.y() +3 + dbw1,
+ wrect.width() - 6 - dbw2,
+ wrect.height() - 6 - dbw2 );
+ break;
+ }
+ case SR_CheckBoxIndicator: {
+ int h = pixelMetric( PM_IndicatorHeight );
+
+ rect.setRect( ( widget->rect().height() - h ) >> 1,
+ ( widget->rect().height() - h ) >> 1,
+ pixelMetric( PM_IndicatorWidth ),
+ h );
+ break;
+ }
+ case SR_RadioButtonIndicator: {
+ int h = pixelMetric( PM_ExclusiveIndicatorHeight );
+
+ rect.setRect( ( widget->rect().height() - h ) >> 1,
+ ( widget->rect().height() - h ) >> 1,
+ pixelMetric( PM_ExclusiveIndicatorWidth ), h );
+ break;
+ }
+ case SR_ProgressBarContents:
+ rect = QRect( wrect.x() + 1,
+ wrect.y() + 1,
+ wrect.width() - 2,
+ wrect.height() - 2 );
+ break;
+ default:
+ rect = KStyle::subRect( subrect, widget );
+ }
+
+ return rect;
+}
+
+void KlearlookStyle::drawComplexControl(
+ ComplexControl control,
+ QPainter *p,
+ const QWidget *widget,
+ const QRect &r,
+ const QColorGroup &cg,
+ SFlags flags,
+ SCFlags controls,
+ SCFlags active,
+ const QStyleOption &data ) const
+{
+ if ( widget == hoverWidget )
+ flags |= Style_MouseOver;
+
+ switch ( control ) {
+ case CC_ToolButton: {
+
+ const QToolButton * toolbutton = ( const QToolButton * ) widget;
+ QRect button ( querySubControlMetrics( control, widget, SC_ToolButton, data ) ),
+ menuarea( querySubControlMetrics( control, widget, SC_ToolButtonMenu, data ) );
+
+ SFlags bflags = flags, mflags = flags;
+
+ if ( APP_KORN == themedApp ) {
+ drawPrimitive( PE_ButtonTool, p, button, cg, bflags, data );
+ break;
+ }
+
+ bool onControlButtons = false,
+ onToolbar = widget->parentWidget() && ::qt_cast<QToolBar *>( widget->parentWidget() ),
+ onExtender = !onToolbar &&
+ widget->parentWidget() &&
+ widget->parentWidget() ->inherits( "QToolBarExtensionWidget" ) &&
+ ::qt_cast<QToolBar *>( widget->parentWidget() ->parentWidget() );
+
+ if ( !onToolbar && !onExtender && widget->parentWidget() &&
+ !qstrcmp( widget->parentWidget() ->name(), "qt_maxcontrols" ) )
+ onControlButtons = true;
+
+ if ( active & SC_ToolButton )
+ bflags |= Style_Down;
+
+ if ( active & SC_ToolButtonMenu )
+ mflags |= Style_Down;
+
+ if ( controls & SC_ToolButton ) {
+ // If we're pressed, on, or raised...
+#if KDE_VERSION >= 0x30200
+ if ( bflags & ( Style_Down | Style_On | Style_Raised ) || onControlButtons )
+#else
+ // CPD: Style_MouseOver obove is *needed* for KDE's KToggleActions...
+ if ( bflags & ( Style_Down | Style_On | Style_Raised | Style_MouseOver ) || onControlButtons )
+#endif
+
+ {
+ //Make sure the standalone toolbuttons have a gradient in the right direction
+ if ( !onToolbar && !onControlButtons )
+ bflags |= Style_Horizontal;
+
+ drawPrimitive( PE_ButtonTool, p, button, cg, bflags, data );
+ }
+
+ // Check whether to draw a background pixmap
+ else if ( toolbutton->parentWidget() &&
+ toolbutton->parentWidget() ->backgroundPixmap() &&
+ !toolbutton->parentWidget() ->backgroundPixmap() ->isNull() ) {
+ p->drawTiledPixmap( r,
+ *( toolbutton->parentWidget() ->backgroundPixmap() ), toolbutton->pos() );
+ } else if ( widget->parent() ) {
+ if ( ::qt_cast<const QToolBar *>( widget->parent() ) ) {
+ QToolBar * parent = ( QToolBar* ) widget->parent();
+
+#ifdef QTC_GRADIENT_TOOLBARS_AND_MENUBARS
+
+ if ( APPEARANCE_FLAT != appearance )
+ drawBevelGradient( cg.background(), true, 0,
+ p, parent->rect(), true, SHADE_BAR_LIGHT, SHADE_BAR_DARK );
+ else
+#endif
+
+ p->fillRect( parent->rect(), cg.background() );
+ } else if ( widget->parent() ->inherits( "QToolBarExtensionWidget" ) ) {
+ QWidget * parent = ( QWidget* ) widget->parent();
+ QToolBar *toolbar = ( QToolBar* ) parent->parent();
+
+#ifdef QTC_GRADIENT_TOOLBARS_AND_MENUBARS
+
+ if ( APPEARANCE_FLAT != appearance )
+ drawBevelGradient( cg.background(), true, 0, p, toolbar->rect(),
+ true, SHADE_BAR_LIGHT, SHADE_BAR_DARK );
+ else
+#endif
+
+ p->fillRect( toolbar->rect(), cg.background() );
+ }
+ }
+ }
+
+ if ( controls & SC_ToolButtonMenu ) {
+ if ( mflags & ( Style_Down | Style_On | Style_Raised ) )
+ drawPrimitive( PE_ButtonDropDown, p, menuarea, cg, mflags, data );
+ drawPrimitive( PE_ArrowDown, p, menuarea, cg, mflags, data );
+ }
+
+ if ( toolbutton->hasFocus() && !toolbutton->focusProxy() ) {
+ QRect fr = toolbutton->rect();
+ fr.addCoords( 3, 3, -3, -3 );
+ drawPrimitive( PE_FocusRect, p, fr, cg );
+ }
+ break;
+ }
+ case CC_ComboBox: {
+ const QComboBox *combobox = ( const QComboBox * ) widget;
+
+ QRect frame( QStyle::visualRect( querySubControlMetrics( CC_ComboBox,
+ widget,SC_ComboBoxFrame,data ),widget ) ),
+
+ arrow( QStyle::visualRect( querySubControlMetrics( CC_ComboBox,
+ widget,SC_ComboBoxArrow,data),widget)),
+
+ field( QStyle::visualRect( querySubControlMetrics(CC_ComboBox,
+ widget,SC_ComboBoxEditField,data),widget));
+
+ const QColor *use = buttonColors( cg );
+
+ if ( rounded && isFormWidget( widget ) )
+ formMode = true;
+
+ if ( controls & SC_ComboBoxFrame && frame.isValid() ) {
+ if ( controls & SC_ComboBoxEditField && field.isValid() && combobox->editable() ) {
+ QRect f2( field );
+ QRegion reg( r );
+
+ f2.addCoords( -1, -1, 1, 1 );
+ reg -= f2;
+ p->setClipRegion( reg );
+ }
+ drawLightBevelButton( p, r, cg, flags | Style_Raised | Style_Horizontal,
+ true, ROUNDED_ALL, getFill( flags, use ),
+ use );
+ p->setClipping( false );
+ }
+
+ if ( controls & SC_ComboBoxArrow && arrow.isValid() ) {
+ drawPrimitive( PE_ArrowDown, p, arrow, cg, flags & ~Style_MouseOver );
+ p->setPen( use[ 4 ].light(70) );
+ arrow.addCoords( -1, -1, -1, 1 );
+ p->drawLine( arrow.left(), arrow.top(), arrow.left(), arrow.bottom() );
+ }
+
+ if ( controls & SC_ComboBoxEditField && field.isValid() ) {
+ if ( ( flags & Style_HasFocus ) && ( ! combobox->editable() ) ) {
+ QRect fr = QStyle::visualRect( subRect( SR_ComboBoxFocusRect, widget ), widget );
+
+ fr.addCoords( 0, 0, -2, 0 );
+ drawPrimitive( PE_FocusRect,
+ p, fr, cg, flags | Style_FocusAtBorder, QStyleOption( cg.highlight() ) );
+ }
+ }
+
+ p->setPen( flags & Style_Enabled ? cg.buttonText() : cg.mid() );
+ formMode = false;
+ break;
+ }
+ case CC_SpinWidget: {
+ const QSpinWidget *spinwidget = ( const QSpinWidget * ) widget;
+ QRect frame( querySubControlMetrics( CC_SpinWidget, widget, SC_SpinWidgetFrame, data ) ),
+ up( spinwidget->upRect() ),
+ down( spinwidget->downRect() );
+
+ if ( hoverWidget && spinwidget == hoverWidget )
+ flags |= Style_MouseOver;
+
+ if ( ( controls & SC_SpinWidgetFrame ) && frame.isValid() )
+ qDrawShadePanel(
+ p, r, QColorGroup( gray[ 5 ], gray[ NUM_SHADES ], gray[ 0 ],
+ gray[ 5 ], gray[ 2 ], cg.text(), gray[ NUM_SHADES ] ),
+ true, pixelMetric( PM_SpinBoxFrameWidth )
+ );
+
+ if ( ( controls & SC_SpinWidgetUp ) && up.isValid() ) {
+ PrimitiveElement pe = PE_SpinWidgetUp;
+ SFlags upflags = flags;
+
+ if ( spinwidget->buttonSymbols() == QSpinWidget::PlusMinus )
+ pe = PE_SpinWidgetPlus;
+ if ( !spinwidget->isUpEnabled() )
+ upflags ^= Style_Enabled;
+ drawPrimitive(
+ pe, p, up, cg,
+ upflags | ( ( active == SC_SpinWidgetUp ) ? Style_On | Style_Sunken : Style_Raised )
+ );
+ }
+
+ if ( ( controls & SC_SpinWidgetDown ) && down.isValid() ) {
+ PrimitiveElement pe = PE_SpinWidgetDown;
+ SFlags downflags = flags;
+
+ if ( spinwidget->buttonSymbols() == QSpinWidget::PlusMinus )
+ pe = PE_SpinWidgetMinus;
+ if ( !spinwidget->isDownEnabled() )
+ downflags ^= Style_Enabled;
+ drawPrimitive(
+ pe, p, down, cg,
+ downflags | ( ( active == SC_SpinWidgetDown ) ? Style_On | Style_Sunken : Style_Raised )
+ );
+ }
+ const QColor *use = backgroundColors( cg );
+ p->setPen( use[ 4 ].light(80) );
+ p->drawRect( r );
+ break;
+ }
+ case CC_ScrollBar: {
+ const QScrollBar *scrollbar = ( const QScrollBar * ) widget;
+ bool hw = hoverWidget == scrollbar;
+ QRect subline( querySubControlMetrics( control, widget, SC_ScrollBarSubLine, data ) ),
+ addline( querySubControlMetrics( control, widget, SC_ScrollBarAddLine, data ) ),
+ subpage( querySubControlMetrics( control, widget, SC_ScrollBarSubPage, data ) ),
+ addpage( querySubControlMetrics( control, widget, SC_ScrollBarAddPage, data ) ),
+ slider( querySubControlMetrics( control, widget, SC_ScrollBarSlider, data ) ),
+ first( querySubControlMetrics( control, widget, SC_ScrollBarFirst, data ) ),
+ last( querySubControlMetrics( control, widget, SC_ScrollBarLast, data ) );
+
+ if ( ( controls & SC_ScrollBarSubLine ) && subline.isValid() )
+ drawPrimitive(
+ PE_ScrollBarSubLine, p, subline, cg,
+ ( hw && HOVER_SB_SUB == hover ? Style_MouseOver : Style_Default ) |
+ Style_Enabled |
+ ( ( active == SC_ScrollBarSubLine ) ? Style_Down : Style_Default ) |
+ ( ( scrollbar->orientation() == Qt::Horizontal ) ? Style_Horizontal : Style_Default )
+ );
+ if ( ( controls & SC_ScrollBarAddLine ) && addline.isValid() )
+ drawPrimitive(
+ PE_ScrollBarAddLine, p, addline, cg,
+ ( hw && HOVER_SB_ADD == hover ? Style_MouseOver : Style_Default ) |
+ Style_Enabled |
+ ( ( active == SC_ScrollBarAddLine ) ? Style_Down : Style_Default ) |
+ ( ( scrollbar->orientation() == Qt::Horizontal ) ? Style_Horizontal : Style_Default )
+ );
+ if ( ( controls & SC_ScrollBarSubPage ) && subpage.isValid() )
+ drawPrimitive( PE_ScrollBarSubPage, p, subpage, cg,
+ Style_Enabled |
+ ( ( active == SC_ScrollBarSubPage ) ? Style_Down : Style_Default ) |
+ ( ( scrollbar->orientation() == Qt::Horizontal ) ? Style_Horizontal : Style_Default ) );
+ if ( ( controls & SC_ScrollBarAddPage ) && addpage.isValid() )
+ drawPrimitive( PE_ScrollBarAddPage, p, addpage, cg,
+ ( ( scrollbar->minValue() == scrollbar->maxValue() ) ? Style_Default : Style_Enabled ) |
+ ( ( active == SC_ScrollBarAddPage ) ? Style_Down : Style_Default ) |
+ ( ( scrollbar->orientation() == Qt::Horizontal ) ? Style_Horizontal : Style_Default ) );
+ if ( ( controls & SC_ScrollBarFirst ) && first.isValid() )
+ drawPrimitive( PE_ScrollBarFirst, p, first, cg,
+ Style_Enabled |
+ ( ( active == SC_ScrollBarFirst ) ? Style_Down : Style_Default ) |
+ ( ( scrollbar->orientation() == Qt::Horizontal ) ? Style_Horizontal : Style_Default ) );
+ if ( ( controls & SC_ScrollBarLast ) && last.isValid() )
+ drawPrimitive( PE_ScrollBarLast, p, last, cg,
+ Style_Enabled |
+ ( ( active == SC_ScrollBarLast ) ? Style_Down : Style_Default ) |
+ ( ( scrollbar->orientation() == Qt::Horizontal ) ? Style_Horizontal : Style_Default ) );
+ if ( ( controls & SC_ScrollBarSlider ) && slider.isValid() ) {
+ drawPrimitive( PE_ScrollBarSlider, p, slider, cg,
+ ( hw && HOVER_SB_SLIDER == hover ? Style_MouseOver : Style_Default ) |
+ Style_Enabled |
+ ( ( active == SC_ScrollBarSlider ) ? Style_Down : Style_Default ) |
+ ( ( scrollbar->orientation() == Qt::Horizontal ) ? Style_Horizontal : Style_Default ) );
+
+ // ### perhaps this should not be able to accept focus if maxedOut?
+ if ( scrollbar->hasFocus() )
+ drawPrimitive( PE_FocusRect, p,
+ QRect( slider.x() + 2, slider.y() + 2, slider.width() - 5, slider.height() - 5 ),
+ cg, Style_Default );
+ }
+ break;
+ }
+ case CC_Slider: {
+ QRect groove = querySubControlMetrics( CC_Slider, widget, SC_SliderGroove, data ),
+ handle = querySubControlMetrics( CC_Slider, widget, SC_SliderHandle, data );
+
+ if ( ( controls & SC_SliderGroove ) && groove.isValid() )
+ drawSliderGroove( p, groove, flags, widget );
+ if ( ( controls & SC_SliderHandle ) && handle.isValid() )
+ drawSliderHandle( p, handle, cg, flags );
+ if ( controls & SC_SliderTickmarks )
+ QCommonStyle::drawComplexControl(
+ control, p, widget, r, cg, flags, SC_SliderTickmarks, active, data
+ );
+ break;
+ }
+ default:
+ KStyle::drawComplexControl( control, p, widget, r, cg, flags, controls, active, data );
+ }
+}
+
+QRect KlearlookStyle::querySubControlMetrics( ComplexControl control, const QWidget *widget, SubControl sc,
+ const QStyleOption &data ) const {
+ switch ( control ) {
+ case CC_SpinWidget: {
+ if ( !widget )
+ return QRect();
+
+ int fw = pixelMetric( PM_SpinBoxFrameWidth, 0 );
+ QSize bs;
+
+ bs.setHeight( widget->height() >> 1 );
+ if ( bs.height() < 8 )
+ bs.setHeight( 8 );
+ bs.setWidth( QMIN( bs.height() * 8 / 6, widget->width() / 4 ) );
+ bs = bs.expandedTo( QApplication::globalStrut() );
+
+ if ( !( bs.width() % 2 ) )
+ bs.setWidth( bs.width() + 1 );
+
+ int extra = bs.height() * 2 == widget->height() ? 0 : 1;
+ int y = 0,
+ x = widget->width() - y - bs.width(),
+ lx = fw,
+ rx = x - fw * 2;
+
+ switch ( sc ) {
+ case SC_SpinWidgetUp:
+ return QRect( x, y, bs.width(), bs.height() );
+ case SC_SpinWidgetDown:
+ return QRect( x, y + bs.height(), bs.width(), bs.height() + extra );
+ case SC_SpinWidgetButtonField:
+ return QRect( x, y, bs.width(), widget->height() - 2 * fw );
+ case SC_SpinWidgetEditField:
+ return QRect( lx, fw, rx, widget->height() - 2 * fw );
+ case SC_SpinWidgetFrame:
+ return QRect( widget->x(), widget->y(), widget->width() - bs.width(), widget->height() );
+ }
+ }
+ default:
+ return KStyle::querySubControlMetrics( control, widget, sc, data );
+ }
+}
+
+int KlearlookStyle::pixelMetric( PixelMetric metric, const QWidget *widget ) const {
+ switch ( metric ) {
+ case PM_MenuButtonIndicator:
+ return 7;
+ case PM_MenuBarItemSpacing: {
+ return 5;
+ }
+ case PM_ButtonMargin:
+ return 5;
+#if QT_VERSION >= 0x030200
+
+ case PM_TabBarTabShiftVertical: {
+ const QTabBar *tb = ::qt_cast<const QTabBar *>( widget );
+
+ return QTabBar::RoundedAbove == tb->shape() || QTabBar::TriangularAbove == tb->shape()
+ ? 1
+ : -1;
+ }
+ case PM_TabBarTabShiftHorizontal:
+ return 0;
+#endif
+
+ case PM_TabBarTabVSpace: {
+ const QTabBar * tb = ( const QTabBar * ) widget;
+ if ( tb->shape() == QTabBar::RoundedAbove ||
+ tb->shape() == QTabBar::RoundedBelow )
+ return 12;
+ else
+ return 4;
+ }
+
+
+ case PM_ButtonShiftHorizontal:
+ case PM_ButtonShiftVertical:
+ return 1;
+ case PM_ButtonDefaultIndicator:
+ return IND_BORDER == defBtnIndicator ? 1 : 0;
+ case PM_DefaultFrameWidth:
+ return borderFrame && widget && ( ::qt_cast<const QTabBar *>( widget ) ||
+ ::qt_cast<const QWidgetStack *>( widget ) ||
+ ::qt_cast<const QPopupMenu *>( widget ) )
+ ? 2
+ : QTC_DEF_FRAME_WIDTH;
+ case PM_SpinBoxFrameWidth:
+ return 1;
+ case PM_MenuBarFrameWidth:
+ return 1;
+ case PM_IndicatorWidth:
+ case PM_IndicatorHeight:
+ return QTC_CHECK_SIZE;
+ case PM_ExclusiveIndicatorWidth:
+ case PM_ExclusiveIndicatorHeight:
+ return QTC_RADIO_SIZE;
+ case PM_TabBarTabOverlap:
+ return 1;
+ case PM_ProgressBarChunkWidth:
+ return 2;
+ case PM_DockWindowSeparatorExtent:
+ return 4;
+ case PM_DockWindowHandleExtent:
+ return 10;
+ case PM_SplitterWidth:
+ return 4;
+ case PM_ScrollBarSliderMin:
+ return 16;
+ case PM_ScrollBarExtent:
+ case PM_SliderControlThickness:
+ case PM_SliderThickness:
+ return 15;
+ case PM_SliderLength:
+ return 24;
+ case PM_MaximumDragDistance:
+ return -1;
+ default:
+ return KStyle::pixelMetric( metric, widget );
+ }
+}
+
+int KlearlookStyle::kPixelMetric( KStylePixelMetric kpm, const QWidget *widget ) const {
+ switch ( kpm ) {
+ case KPM_MenuItemSeparatorHeight:
+ return 4;
+ default:
+ return KStyle::kPixelMetric( kpm, widget );
+ }
+}
+
+QSize KlearlookStyle::sizeFromContents( ContentsType t,
+ const QWidget *widget,
+ const QSize &s,
+ const QStyleOption &opt ) const {
+ switch ( t ) {
+ case CT_PopupMenuItem: {
+ if ( !widget || opt.isDefault() )
+ return s;
+
+ const QPopupMenu *popup = dynamic_cast<const QPopupMenu *>( widget );
+ QMenuItem *mi = opt.menuItem();
+ int maxpmw = opt.maxIconWidth();
+ int w = s.width(), h = s.height();
+ bool checkable = popup->isCheckable();
+
+ if ( mi->custom() ) {
+ w = mi->custom() ->sizeHint().width();
+ h = mi->custom() ->sizeHint().height();
+ if ( !mi->custom() ->fullSpan() )
+ h += 4;
+ } else if ( mi->widget() ) {
+ // don't change the size in this case.
+ } else if ( mi->isSeparator() ) {
+ w = 20;
+ h = 8;
+ } else {
+ if ( mi->pixmap() ) {
+ h = QMAX( h, mi->pixmap() ->height() + 2 );
+ } else {
+ h = QMAX( h, 21 );
+ QSettings s;
+ if ( menuIcons )
+ h = QMAX( h, popup->fontMetrics().height() + MENU_POPUP_ITEM_HIGH_HI );
+ else
+ h = QMAX( h, popup->fontMetrics().height() + MENU_POPUP_ITEM_HIGH_LO );
+ }
+
+ if ( mi->iconSet() ) {
+ h = QMAX( h, mi->iconSet() ->pixmap( QIconSet::Small, QIconSet::Normal ).height() + 2 );
+ }
+ }
+
+ if ( !mi->text().isNull() && ( mi->text().find( '\t' ) >= 0 ) ) {
+ w += itemHMargin + itemFrame * 2 + 7;
+ } else if ( mi->popup() ) {
+ w += 2 * arrowHMargin;
+ }
+
+ if ( maxpmw ) {
+ w += maxpmw + 6;
+ }
+ if ( checkable && maxpmw < 20 ) {
+ w += 20 - maxpmw;
+ }
+ if ( checkable || maxpmw > 0 ) {
+ w += 12;
+ }
+
+ w += rightBorder;
+
+ return QSize( w-25, h );
+ }
+
+ case CT_PushButton: {
+ const QPushButton* btn = static_cast<const QPushButton*>( widget );
+
+ int w = s.width() + 2 * pixelMetric( PM_ButtonMargin, widget );
+ int h = s.height() + 2 * pixelMetric( PM_ButtonMargin, widget );
+ if ( btn->text().isEmpty() && s.width() < 32 )
+ return QSize( w, h );
+ // return button size
+ return QSize( w + 25, h + 3 );
+ }
+
+ case CT_ToolButton: {
+ if ( widget->parent() && ::qt_cast<QToolBar*>( widget->parent() ) )
+ return QSize( s.width() + 2 * 4, s.height() + 2 * 4 );
+ else {
+ return KStyle::sizeFromContents ( t, widget, s, opt );
+ }
+ }
+
+ default:
+ return KStyle::sizeFromContents ( t, widget, s, opt );
+ }
+
+ return KStyle::sizeFromContents ( t, widget, s, opt );
+}
+
+
+
+int KlearlookStyle::styleHint( StyleHint stylehint, const QWidget *widget, const QStyleOption &option, QStyleHintReturn *returnData ) const {
+ switch ( stylehint ) {
+ case SH_EtchDisabledText:
+ case SH_Slider_SnapToValue:
+ case SH_PrintDialog_RightAlignButtons:
+ case SH_FontDialog_SelectAssociatedText:
+ case SH_MenuBar_AltKeyNavigation:
+ case SH_MenuBar_MouseTracking:
+ case SH_PopupMenu_MouseTracking:
+ case SH_PopupMenu_SpaceActivatesItem:
+ case SH_ComboBox_ListMouseTracking:
+ case SH_ScrollBar_MiddleClickAbsolutePosition:
+ return 1;
+ case SH_MainWindow_SpaceBelowMenuBar:
+ return 0;
+ case SH_ComboBox_Popup:
+ return 0;
+ case SH_PopupMenu_SubMenuPopupDelay:
+ return 300;
+ case SH_PopupMenu_AllowActiveAndDisabled:
+ return 0;
+ default:
+ return KStyle::styleHint( stylehint, widget, option, returnData );
+ }
+}
+
+void KlearlookStyle::drawPBarOrMenu(
+ QPainter *p,
+ QRect const &r,
+ bool horiz,
+ const QColorGroup &cg,
+ bool menu ) const
+{
+ switch ( pmProfile ) {
+ case PROFILE_SUNKEN:
+ drawGradientWithBorder( p, r, horiz );
+ break;
+ case PROFILE_RAISED: {
+ int flags = QStyle::Style_Raised;
+
+ if ( horiz )
+ flags |= Style_Horizontal;
+
+ drawLightBevel( p, r,
+ cg, flags, true,
+ menu ? ROUNDED_ALL : ROUNDED_NONE,
+ getFill( flags, menuPbar ),
+ menuPbar, true
+ );
+
+ break;
+ }
+ default:
+ p->fillRect( r, menuPbar[ GRADIENT_BASE ] );
+ break;
+ }
+}
+void KlearlookStyle::drawPBarOrMenu2(
+ QPainter *p,
+ QRect const &r,
+ bool horiz,
+ const QColorGroup &cg,
+ bool menu ) const
+{
+ switch ( pmProfile ) {
+ case PROFILE_SUNKEN:
+ drawGradientWithBorder( p, r, horiz );
+ break;
+ case PROFILE_RAISED: {
+ int flags = QStyle::Style_Raised;
+
+ if ( horiz )
+ flags |= Style_Horizontal;
+
+ drawLightBevel( p, r,
+ cg, flags, true,
+ menu ? ROUNDED_TOP : ROUNDED_NONE,
+ getFill( flags, menuPbar ),
+ menuPbar, true
+ );
+
+ break;
+ }
+ default:
+ p->fillRect( r, menuPbar[ GRADIENT_BASE ] );
+ break;
+ }
+}
+
+void KlearlookStyle::drawGradientWithBorder(
+ QPainter *p,
+ QRect const &r,
+ bool horiz ) const
+{
+ QRect r2( r );
+
+ drawGradient( menuPbar[ GRADIENT_TOP ],
+ menuPbar[ GRADIENT_BOTTOM ], true, borderFrame ? 2 : 1, p, r, horiz );
+ // 3d border effect...
+ if ( borderFrame ) {
+ p->setPen( menuPbar[ GRADIENT_BASE ] );
+ p->setBrush( NoBrush );
+ p->drawRect( r );
+ } else
+ r2.addCoords( -1, -1, 1, 1 );
+
+ p->setPen( menuPbar[ GRADIENT_LIGHT ] );
+ p->drawLine( r2.left() + 1, r2.top() + 1, r2.right() - 1, r2.top() + 1 );
+ p->drawLine( r2.left() + 1, r2.top() + 1, r2.left() + 1, r2.bottom() - 1 );
+ p->setPen( menuPbar[ GRADIENT_DARK ] );
+ p->drawLine( r2.left() + 1, r2.bottom() - 1, r2.right() - 1, r2.bottom() - 1 );
+ p->drawLine( r2.right() - 1, r2.bottom() - 1, r2.right() - 1, r2.top() + 1 );
+}
+
+void KlearlookStyle::drawBevelGradient(
+ const QColor &base,
+ bool increase,
+ int border,
+ QPainter *p,
+ QRect const &r,
+ bool horiz, double shadeTop, double shadeBot )
+const {
+ //CPD TODO: Store last settings to make faster!
+ QColor top, bot;
+
+ if ( equal( 1.0, shadeTop ) )
+ top = base;
+ else
+ shade( base, &top, shadeTop );
+ if ( equal( 1.0, shadeBot ) )
+ bot = base;
+ else
+ shade( base, &bot, shadeBot );
+
+ drawGradient( top, bot, increase, border, p, r, horiz );
+}
+
+void KlearlookStyle::drawGradient(
+ const QColor &top,
+ const QColor &bot,
+ bool increase,
+ int border,
+ QPainter *p,
+ QRect const &r,
+ bool horiz ) const
+{
+ if ( r.width() > 0 && r.height() > 0 ) {
+ QRect grad(
+ r.left() + border,
+ r.top() + border,
+ r.width() - ( border * 2 ),
+ r.height() - ( border * 2 )
+ );
+
+ if ( top == bot )
+ p->fillRect( grad, top );
+ else {
+ QRect grad(
+ r.left() + border,
+ r.top() + border,
+ r.width() - ( border * 2 ),
+ r.height() - ( border * 2 )
+ );
+
+ int i,
+ s = horiz ? grad.top() : grad.left(),
+ e = horiz ? grad.bottom() : grad.right();
+
+ double amt = ( horiz ? grad.height() : grad.width() ) ,
+ dr = ( ( double ) ( bot.red() - top.red() ) ) / amt ,
+ dg = ( ( double ) ( bot.green() - top.green() ) ) / amt ,
+ db = ( ( double ) ( bot.blue() - top.blue() ) ) / amt,
+ rc = 0, gc = 0, bc = 0;
+
+ if ( increase )
+ for ( i = s; i <= e ; i++ ) {
+ p->setPen( QColor(
+ limit( top.red() + rc ),
+ limit( top.green() + gc ),
+ limit( top.blue() + bc )
+ ));
+
+ if ( horiz )
+ p->drawLine( grad.left(), i, grad.right(), i );
+ else
+ p->drawLine( i, grad.top(), i, grad.bottom() );
+ rc += dr;
+ gc += dg;
+ bc += db;
+ }
+ else
+ for ( i = e; i >= s; i-- ) {
+ p->setPen(
+ QColor(
+ limit( top.red() + rc ),
+ limit( top.green() + gc ),
+ limit( top.blue() + bc )
+ ) );
+
+ if ( horiz )
+ p->drawLine( grad.left(), i, grad.right(), i );
+
+ else
+ p->drawLine( i, grad.top(), i, grad.bottom() );
+
+ rc += dr;
+ gc += dg;
+ bc += db;
+ }
+ }
+ }
+}
+
+
+void KlearlookStyle::drawPopupRect( QPainter *p, const QRect &r, const QColorGroup &cg ) const
+{
+ const QColor *use = backgroundColors( cg );
+ p->setPen( use[ 4 ].light(70) );
+ p->setBrush( NoBrush );
+ p->drawRect( r );
+}
+
+void KlearlookStyle::drawSliderHandle(
+ QPainter *p,
+ const QRect &r,
+ const QColorGroup &cg, QStyle::SFlags flags
+) const
+{
+ const QColor * use = buttonColors( cg );
+
+ if ( r.width() > r.height() )
+ flags |= Style_Horizontal;
+ flags |= Style_Raised;
+
+ drawLightBevelButton( p, r, cg, flags, true, ROUNDED_ALL, getFill( flags, use ), use );
+
+ if ( GROOVE_NONE != sliderThumbs &&
+ ( ( flags & Style_Horizontal && r.width() >= 14 ) || r.height() >= 14 ) )
+ drawLines( p, r,
+ r.width() < r.height(),
+ 4, 3, use, 0,
+ GROOVE_SUNKEN == sliderThumbs,
+ APPEARANCE_LIGHT_GRADIENT == appearance );
+}
+
+void KlearlookStyle::drawSliderGroove
+ ( QPainter *p,
+ const QRect &r,
+ QStyle::SFlags flags,
+ const QWidget *widget ) const
+{
+ const QSlider * slider = ( const QSlider * ) widget;
+ QRect groove( r );
+
+ if ( flags & Style_HasFocus ) {
+ QRect fr( groove );
+
+ fr.addCoords( -1, -1, 1, 1 );
+ drawPrimitive( PE_FocusRect, p, fr, QColorGroup() );
+ }
+
+ if ( Qt::Horizontal == slider->orientation() ) {
+ int dh = ( groove.height() - 5 ) >> 1;
+
+ groove.addCoords( 0, dh, 0, -dh );
+ } else {
+ int dw = ( groove.width() - 5 ) >> 1;
+
+ groove.addCoords( dw, 0, -dw, 0 );
+ }
+ p->setBrush( gray[ 2 ] );
+ p->setPen( gray[ 5 ] );
+ p->drawRect( groove );
+ p->setPen( gray[ 4 ] );
+ p->drawLine( groove.x() + 1, groove.y() + 1, groove.x() + groove.width() - 2, groove.y() + 1 );
+ p->drawLine( groove.x() + 1, groove.y() + 1, groove.x() + 1, groove.y() + groove.height() - 2 );
+}
+
+void KlearlookStyle::shadeColors( const QColor &base, QColor *vals ) const {
+ QTC_SHADES
+
+ int i;
+
+ for ( i = 0; i < NUM_SHADES; ++i )
+ shade( base, &vals[ i ], QTC_SHADE( appearance, contrast, i ) );
+
+ vals[ NUM_SHADES ] = base;
+}
+
+const QColor * KlearlookStyle::buttonColors( const QColorGroup &cg ) const {
+ if ( cg.button() != button[ NUM_SHADES ] ) {
+ shadeColors( cg.button(), buttonColoured );
+ return buttonColoured;
+ }
+
+ return button;
+}
+
+const QColor * KlearlookStyle::backgroundColors( const QColorGroup &cg ) const {
+ if ( cg.background() != gray[ NUM_SHADES ] ) {
+ shadeColors( cg.background(), backgroundColoured );
+ return backgroundColoured;
+ }
+
+ return gray;
+}
+
+bool KlearlookStyle::redrawHoverWidget() {
+ if ( !hoverWidget )
+ return false;
+
+ QPoint cursor( QCursor::pos() ),
+ widgetZero( hoverWidget->mapToGlobal( QPoint( 0, 0 ) ) );
+
+#if QT_VERSION >= 0x030200
+
+ //
+ // Qt>=3.2 sets the sensitive part of a check/radio to the image + label -> anything else
+ // is not sensitive. But,
+ // the widget can ocupy a larger area - and this whole are will react to mouse over.
+ // This needs to be coounteracted
+ // so that it looks as if only the sensitive area mouse-overs...
+ QRadioButton *rb = dynamic_cast<QRadioButton *>( hoverWidget );
+
+ if ( rb ) {
+ QRect rect( widgetZero.x(), widgetZero.y(),
+ visualRect( subRect( SR_RadioButtonFocusRect, rb ), rb ).width() +
+ pixelMetric( PM_ExclusiveIndicatorWidth ) + 4, hoverWidget->height() );
+
+ hover = rect.contains( cursor ) ? HOVER_RADIO : HOVER_NONE;
+ return ( HOVER_NONE != hover && !rect.contains( oldCursor ) ) ||
+ ( HOVER_NONE == hover && rect.contains( oldCursor ) );
+ } else {
+ QCheckBox *cb = dynamic_cast<QCheckBox *>( hoverWidget );
+
+ if ( cb ) {
+ QRect rect( widgetZero.x(), widgetZero.y(),
+ visualRect( subRect( SR_CheckBoxFocusRect, cb ), cb ).width() +
+ pixelMetric( PM_IndicatorWidth ) + 4, hoverWidget->height() );
+
+ hover = rect.contains( cursor ) ? HOVER_CHECK : HOVER_NONE;
+ return ( HOVER_NONE != hover && !rect.contains( oldCursor ) ) ||
+ ( HOVER_NONE == hover && rect.contains( oldCursor ) );
+ } else {
+#endif
+ QScrollBar *sb = dynamic_cast<QScrollBar *>( hoverWidget );
+
+ if ( sb ) // So, are we over add button, sub button, slider, or none?
+ {
+ QRect subline( querySubControlMetrics( CC_ScrollBar, hoverWidget, SC_ScrollBarSubLine ) ),
+ addline( querySubControlMetrics( CC_ScrollBar, hoverWidget, SC_ScrollBarAddLine ) ),
+ slider( querySubControlMetrics( CC_ScrollBar, hoverWidget, SC_ScrollBarSlider ) );
+
+ subline.moveLeft( subline.x() + widgetZero.x() );
+ subline.moveTop( subline.y() + widgetZero.y() );
+ addline.moveLeft( addline.x() + widgetZero.x() );
+ addline.moveTop( addline.y() + widgetZero.y() );
+ slider.moveLeft( slider.x() + widgetZero.x() );
+ slider.moveTop( slider.y() + widgetZero.y() );
+
+ if ( slider.contains( cursor ) )
+ hover = HOVER_SB_SLIDER;
+ else if ( subline.contains( cursor ) )
+ hover = HOVER_SB_SUB;
+ else if ( addline.contains( cursor ) )
+ hover = HOVER_SB_ADD;
+ else
+ hover = HOVER_NONE;
+
+ return ( HOVER_SB_SLIDER == hover && !slider.contains( oldCursor ) ) ||
+ ( HOVER_SB_SLIDER != hover && slider.contains( oldCursor ) ) ||
+ ( HOVER_SB_SUB == hover && !subline.contains( oldCursor ) ) ||
+ ( HOVER_SB_SUB != hover && subline.contains( oldCursor ) ) ||
+ ( HOVER_SB_ADD == hover && !addline.contains( oldCursor ) ) ||
+ ( HOVER_SB_ADD != hover && addline.contains( oldCursor ) );
+ } else {
+#if KDE_VERSION >= 0x30400
+ QToolButton *tb = dynamic_cast<QToolButton *>( hoverWidget );
+
+ if ( tb ) {
+ hover = APP_KICKER == themedApp ? HOVER_KICKER : HOVER_NONE;
+ return HOVER_KICKER == hover;
+ } else {
+#endif
+ QHeader *hd = dynamic_cast<QHeader *>( hoverWidget );
+
+ if ( hd ) {
+ // Hmm... this ones tricky, as there's only 1 widget - but it has different sections...
+ // and the ones that aren't clickable should not highlight on mouse over!
+
+ QRect rect(
+ widgetZero.x(),
+ widgetZero.y(),
+ hoverWidget->width(),
+ hoverWidget->height()
+ );
+
+ int s = 0;
+ bool redraw = false;
+
+ hover = rect.contains( cursor ) ? HOVER_HEADER : HOVER_NONE;
+ hoverSect = QTC_NO_SECT;
+
+ for ( s = 0; s < hd->count() && ( QTC_NO_SECT == hoverSect || !redraw ); ++s ) {
+ QRect r( hd->sectionRect( s ) );
+
+ r.moveLeft( r.x() + widgetZero.x() );
+ r.moveTop( r.y() + widgetZero.y() );
+
+ bool hasNew = r.contains( cursor );
+
+ if ( hasNew )
+ hoverSect = s;
+
+ if ( !redraw ) {
+ bool hasOld = r.contains( oldCursor );
+
+ if ( ( hasNew && !hasOld ) || ( !hasNew && hasOld ) )
+ redraw = true;
+ }
+ }
+ return redraw;
+ } else
+ return oldCursor == QPoint( -1, -1 );
+#if KDE_VERSION >= 0x30400
+
+ }
+#endif
+
+ }
+#if QT_VERSION >= 0x030200
+
+ }
+ }
+#endif
+
+ return false;
+}
+
+#define gdouble double
+
+EDefBtnIndicator qtc_to_ind( const char *str ) {
+ if ( 0 == memcmp( str, "fontcolour", 10 ) )
+ return IND_FONT_COLOUR;
+ if ( 0 == memcmp( str, "border", 6 ) )
+ return IND_BORDER;
+ if ( 0 == memcmp( str, "none", 4 ) )
+ return IND_NONE;
+ return IND_CORNER;
+}
+
+EGroove qtc_to_groove( const char *str ) {
+ if ( 0 == memcmp( str, "raised", 6 ) )
+ return GROOVE_RAISED;
+ if ( 0 == memcmp( str, "none", 4 ) )
+ return GROOVE_NONE;
+ return GROOVE_SUNKEN;
+}
+
+ETBarBorder qtc_to_tbar_border( const char *str ) {
+ if ( 0 == memcmp( str, "dark", 4 ) )
+ return TB_DARK;
+ if ( 0 == memcmp( str, "none", 4 ) )
+ return TB_NONE;
+ if ( 0 == memcmp( str, "light", 5 ) )
+ return TB_LIGHT;
+ return TB_LIGHT;
+}
+
+ELvExpander qtc_to_lv_expander( const char *str ) {
+ return 0 == memcmp( str, "arrow", 5 ) ? LV_EXP_ARR : LV_EXP_PM;
+}
+
+ELvLines qtc_to_lv_lines( const char *str ) {
+ if ( 0 == memcmp( str, "none", 4 ) )
+ return LV_LINES_NONE;
+ if ( 0 == memcmp( str, "dotted", 6 ) )
+ return LV_LINES_DOTTED;
+ return LV_LINES_SOLID;
+}
+
+EProfile qtc_to_profile( const char *str ) {
+ if ( 0 == memcmp( str, "flat", 4 ) )
+ return PROFILE_FLAT;
+ if ( 0 == memcmp( str, "raised", 6 ) )
+ return PROFILE_RAISED;
+ return PROFILE_SUNKEN;
+}
+
+EAppearance qtc_to_appearance( const char *str ) {
+ if ( 0 == memcmp( str, "flat", 4 ) )
+ return APPEARANCE_FLAT;
+ if ( 0 == memcmp( str, "gradient", 8 ) )
+ return APPEARANCE_GRADIENT;
+ return APPEARANCE_LIGHT_GRADIENT;
+}
+
+
+#include "klearlook.moc"
diff --git a/src/gui/kdeext/klearlook.h b/src/gui/kdeext/klearlook.h
new file mode 100644
index 0000000..dd3ab74
--- /dev/null
+++ b/src/gui/kdeext/klearlook.h
@@ -0,0 +1,344 @@
+/* $Id: klearlook.h,v 1.8 2006/04/26 18:55:41 jck Exp $
+*/
+
+/*
+
+ Klearlook (C) Joerg C. Koenig, 2005 jck@gmx.org
+
+----
+
+ Based upon QtCurve (C) Craig Drummond, 2003 Craig.Drummond@lycos.co.uk
+ Bernhard Rosenkränzer <bero@r?dh?t.com>
+ Preston Brown <pbrown@r?dh?t.com>
+ Than Ngo <than@r?dh?t.com>
+
+ Released under the GNU General Public License (GPL) v2.
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files(the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef __KLEARLOOK_H__
+#define __KLEARLOOK_H__
+
+#define USE_SINGLE_STYLE 1
+
+#include <kdeversion.h>
+#include <kstyle.h>
+#include <qcolor.h>
+#include <qpoint.h>
+
+#define QTC_CHECK_SIZE 13
+#define QTC_RADIO_SIZE 13
+#define QTC_MIN_BTN_SIZE 10
+#define QTC_LV_SIZE(X) LV_EXP_ARR==(X) ? 7 : 5
+
+#define NUM_SHADES 7
+
+/* Progressbar and selected menu items... */
+#define SHADE_GRADIENT_TOP 0.92
+#define SHADE_GRADIENT_BOTTOM 1.66
+#define SHADE_GRADIENT_LIGHT 1.62
+#define SHADE_GRADIENT_DARK 1.05
+
+/* 3d effect - i.e. buttons, etc */
+#define QTC_SHADES \
+ double shades[2][11][NUM_SHADES]=\
+ { \
+ { \
+ { 1.01, 1.03, 0.868, 0.780, 0.752, 0.74, 1.8 }, \
+ { 1.02, 1.03, 0.870, 0.781, 0.753, 0.70, 1.8 }, \
+ { 1.03, 1.03, 0.872, 0.782, 0.754, 0.68, 1.8 }, \
+ { 1.04, 1.04, 0.875, 0.783, 0.755, 0.64, 1.8 }, \
+ { 1.05, 1.04, 0.878, 0.784, 0.756, 0.62, 1.8 }, \
+ { 1.06, 1.05, 0.881, 0.785, 0.758, 0.58, 1.8 }, \
+ { 1.07, 1.05, 0.884, 0.786, 0.760, 0.54, 1.8 }, \
+ { 1.09, 1.06, 0.887, 0.787, 0.762, 0.50, 1.8 }, /* default */ \
+ { 1.11, 1.06, 0.890, 0.788, 0.764, 0.45, 1.8 }, \
+ { 1.13, 1.07, 0.893, 0.789, 0.766, 0.40, 1.8 }, \
+ { 1.15, 1.07, 0.896, 0.790, 0.768, 0.35, 1.8 } \
+ }, \
+ { \
+ { 1.08, 1.03, 0.868, 0.780, 0.800, 0.74, 1.8 }, \
+ { 1.09, 1.03, 0.870, 0.781, 0.810, 0.72, 1.8 }, \
+ { 1.10, 1.03, 0.872, 0.782, 0.820, 0.70, 1.8 }, \
+ { 1.11, 1.04, 0.875, 0.783, 0.840, 0.68, 1.8 }, \
+ { 1.12, 1.04, 0.878, 0.784, 0.860, 0.66, 1.8 }, \
+ { 1.13, 1.05, 0.881, 0.785, 0.880, 0.64, 1.8 }, \
+ { 1.14, 1.05, 0.884, 0.786, 0.900, 0.62, 1.8 }, \
+ { 1.15, 1.06, 0.887, 0.787, 0.920, 0.60, 1.8 }, /* default */ \
+ { 1.17, 1.06, 0.890, 0.788, 0.764, 0.45, 1.8 }, \
+ { 1.19, 1.07, 0.893, 0.789, 0.766, 0.40, 1.8 }, \
+ { 1.21, 1.07, 0.896, 0.790, 0.768, 0.35, 1.8 } \
+ } \
+ };
+
+#define QTC_SHADE(a, c, s) \
+ (c>10 || c<0 || s>=NUM_SHADES || s<0 ? 1.0 : shades[APPEARANCE_LIGHT_GRADIENT!=(a) ? 0 : 1][c][s])
+
+/* Shades used when gradient effect is selected */
+#define SHADE_BEVEL_GRAD_LIGHT(A) (APPEARANCE_LIGHT_GRADIENT!=(A) ? 1.0 : 1.0 )
+#define SHADE_BEVEL_GRAD_DARK(A) (APPEARANCE_LIGHT_GRADIENT!=(A) ? 0.75 : 0.75 )
+#define SHADE_BEVEL_GRAD_SEL_LIGHT(A) (APPEARANCE_LIGHT_GRADIENT!=(A) ? 1.1 : 1.05 )
+#define SHADE_BEVEL_GRAD_SEL_DARK(A) (APPEARANCE_LIGHT_GRADIENT!=(A) ? 0.95 : 0.95 )
+
+#define SHADE_BEVEL_BUTTON_GRAD_LIGHT(A) 1.00
+#define SHADE_BEVEL_BUTTON_GRAD_DARK(A) 0.89
+
+#define SHADE_BEVEL_MENU_GRAD_LIGHT(A) (APPEARANCE_LIGHT_GRADIENT!=(A) ? 0.97 : 0.97 )
+#define SHADE_BEVEL_MENU_GRAD_DARK(A) (APPEARANCE_LIGHT_GRADIENT!=(A) ? 0.93 : 0.93 )
+
+#define SHADE_TAB_LIGHT(A) (APPEARANCE_LIGHT_GRADIENT!=(A) ? 1.1 : 1.0 )
+#define SHADE_TAB_DARK(A) (APPEARANCE_LIGHT_GRADIENT!=(A) ? 1.0 : 1.0 )
+#define SHADE_TAB_SEL_LIGHT(A) (APPEARANCE_LIGHT_GRADIENT!=(A) ? 1.0 : 1.0 )
+#define SHADE_TAB_SEL_DARK(A) (APPEARANCE_LIGHT_GRADIENT!=(A) ? 1.0 : 1.0 )
+
+#define SHADE_BOTTOM_TAB_LIGHT(A) (APPEARANCE_LIGHT_GRADIENT!=(A) ? 1.0 : 1.0 )
+#define SHADE_BOTTOM_TAB_DARK(A) (APPEARANCE_LIGHT_GRADIENT!=(A) ? 0.95 : 1.0 )
+#define SHADE_BOTTOM_TAB_SEL_LIGHT(A) (APPEARANCE_LIGHT_GRADIENT!=(A) ? 1.0 : 1.0 )
+#define SHADE_BOTTOM_TAB_SEL_DARK(A) (APPEARANCE_LIGHT_GRADIENT!=(A) ? 1.0 : 1.0 )
+
+typedef enum
+{
+ IND_BORDER,
+ IND_CORNER,
+ IND_FONT_COLOUR,
+ IND_NONE
+} EDefBtnIndicator;
+
+typedef enum
+{
+ GROOVE_RAISED,
+ GROOVE_SUNKEN,
+ GROOVE_NONE
+} EGroove;
+
+typedef enum
+{
+ TB_NONE,
+ TB_LIGHT,
+ TB_DARK
+} ETBarBorder;
+
+typedef enum
+{
+ LV_EXP_PM,
+ LV_EXP_ARR
+} ELvExpander;
+
+typedef enum
+{
+ LV_LINES_NONE,
+ LV_LINES_DOTTED,
+ LV_LINES_SOLID
+} ELvLines;
+
+typedef enum
+{
+ PROFILE_FLAT,
+ PROFILE_RAISED,
+ PROFILE_SUNKEN
+} EProfile;
+
+typedef enum
+{
+ APPEARANCE_FLAT,
+ APPEARANCE_GRADIENT,
+ APPEARANCE_LIGHT_GRADIENT
+} EAppearance;
+
+#define DEF_IND_STR "corner"
+#define DEF_HANDLE_STR "sunken"
+#define DEF_TB_BORDER "light"
+#define DEF_APPEARANCE_STR "lightgradient"
+#define DEF_PROFILE_STR "raised"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ extern EDefBtnIndicator qtc_to_ind( const char * str );
+ extern EGroove qtc_to_groove( const char * str );
+ extern ETBarBorder qtc_to_tbar_border( const char * str );
+ extern ELvExpander qtc_to_lv_expander( const char * str );
+ extern ELvLines qtc_to_lv_lines( const char * str );
+ extern EProfile qtc_to_profile( const char * str );
+ extern EAppearance qtc_to_appearance( const char * str );
+#ifdef __cplusplus
+
+}
+#endif
+
+class KlearlookStyle : public KStyle {
+ Q_OBJECT
+
+ public:
+
+ enum EApp
+ {
+ APP_KICKER,
+ APP_KORN,
+ APP_OPENOFFICE,
+ APP_OTHER
+ };
+
+ enum
+ {
+ GRADIENT_BASE,
+ GRADIENT_LIGHT,
+ GRADIENT_DARK,
+ GRADIENT_TOP,
+ GRADIENT_BOTTOM,
+ GRADIENT_NUM_COLS
+ };
+
+ enum ERound
+ {
+ ROUNDED_NONE,
+ ROUNDED_TOP,
+ ROUNDED_BOTTOM,
+ ROUNDED_LEFT,
+ ROUNDED_RIGHT,
+ ROUNDED_ALL
+ };
+
+ enum EHover
+ {
+ HOVER_NONE,
+ HOVER_CHECK,
+ HOVER_RADIO,
+ HOVER_SB_ADD,
+ HOVER_SB_SUB,
+ HOVER_SB_SLIDER,
+ HOVER_HEADER,
+ HOVER_KICKER
+ };
+
+#ifdef USE_SINGLE_STYLE
+
+ KlearlookStyle();
+#else
+
+ KlearlookStyle( bool gpm, bool bb = false, bool bf = false, bool round = false,
+ EGroove st = GROOVE_RAISED, h = GROOVE_RAISED,
+ bool ge = false, bool va = true, bool bdt = false, bool crlh = true, EDefBtnIndicator dbi = IND_BORDER,
+ int tbb = 5, ELvExpander lve = LV_EXP_PM, lvl = LV_LINES_DOTTED, bool lvd = true, bool icon = true,
+ int popupmenuHighlightLevel = 130 );
+#endif
+
+ virtual ~KlearlookStyle() {}
+
+ void polish( QApplication *app );
+ void polish( QPalette &pal );
+ void polish( QWidget *widget );
+ void unPolish( QWidget *widget );
+ void drawLightBevel( QPainter *p, const QRect &r, const QColorGroup &cg, QStyle::SFlags flags, bool useGrad, ERound round,
+ const QColor &fill, const QColor *custom = NULL, bool light = false ) const;
+ void drawLightBevelButton( QPainter *p, const QRect &r, const QColorGroup &cg, QStyle::SFlags flags, bool useGrad, ERound round,
+ const QColor &fill, const QColor *custom = NULL, bool light = false ) const;
+ void drawArrow( QPainter *p, const QRect &r, const QColorGroup &cg, QStyle::SFlags flags, QStyle::PrimitiveElement pe,
+ bool small = false, bool checkActive = false ) const;
+ void drawPrimitive( PrimitiveElement, QPainter *, const QRect &, const QColorGroup &, SFlags = Style_Default,
+ const QStyleOption & = QStyleOption::Default ) const;
+ void drawPrimitiveMenu( PrimitiveElement, QPainter *, const QRect &, const QColorGroup &, SFlags = Style_Default,
+ const QStyleOption & = QStyleOption::Default ) const;
+ void drawKStylePrimitive( KStylePrimitive kpe, QPainter* p, const QWidget* widget, const QRect &r,
+ const QColorGroup &cg, SFlags flags, const QStyleOption &opt ) const;
+ void drawControl( ControlElement, QPainter *, const QWidget *, const QRect &, const QColorGroup &,
+ SFlags = Style_Default, const QStyleOption & = QStyleOption::Default ) const;
+ void drawControlMask( ControlElement, QPainter *, const QWidget *, const QRect &,
+ const QStyleOption & = QStyleOption::Default ) const;
+ void drawComplexControlMask( ComplexControl control, QPainter *p, const QWidget *widget, const QRect &r,
+ const QStyleOption &data ) const;
+ QRect subRect( SubRect, const QWidget * ) const;
+ void drawComplexControl( ComplexControl, QPainter *, const QWidget *, const QRect &, const QColorGroup &,
+ SFlags = Style_Default, SCFlags = SC_All, SCFlags = SC_None,
+ const QStyleOption & = QStyleOption::Default ) const;
+ QRect querySubControlMetrics( ComplexControl, const QWidget *, SubControl,
+ const QStyleOption & = QStyleOption::Default ) const;
+ int pixelMetric( PixelMetric, const QWidget *widget = 0 ) const;
+ int kPixelMetric( KStylePixelMetric kpm, const QWidget *widget ) const;
+ QSize sizeFromContents( ContentsType, const QWidget *, const QSize &,
+ const QStyleOption & = QStyleOption::Default ) const;
+ int styleHint( StyleHint, const QWidget *widget = 0, const QStyleOption & = QStyleOption::Default,
+ QStyleHintReturn *returnData = 0 ) const;
+
+ protected:
+
+ bool eventFilter( QObject *object, QEvent *event );
+ void drawPBarOrMenu( QPainter *p, QRect const &r, bool horiz, const QColorGroup &cg, bool menu = false ) const;
+ void drawPBarOrMenu2( QPainter *p, QRect const &r, bool horiz, const QColorGroup &cg, bool menu = false ) const;
+ void drawGradientWithBorder( QPainter *p, QRect const &r, bool horiz = true ) const;
+ void drawBevelGradient( const QColor &base, bool increase, int border, QPainter *p, QRect const &r, bool horiz,
+ double shadeTop, double shadeBot ) const;
+ void drawGradient( const QColor &top, const QColor &bot, bool increase, int border, QPainter *p, QRect const &r,
+ bool horiz = true ) const;
+ void drawSliderHandle( QPainter *p, const QRect &r, const QColorGroup &cg, QStyle::SFlags flags ) const;
+ void drawPopupRect( QPainter *p, const QRect &r, const QColorGroup &cg) const ;
+
+ void drawSliderGroove( QPainter *p, const QRect &r, QStyle::SFlags flags, const QWidget *widget ) const;
+
+
+ private:
+
+ void shadeColors( const QColor &base, QColor *vals ) const;
+ const QColor * buttonColors( const QColorGroup &cg ) const;
+ const QColor * backgroundColors( const QColorGroup &cg ) const;
+ bool redrawHoverWidget();
+
+ private:
+
+ QColor menuPbar[ GRADIENT_NUM_COLS < NUM_SHADES + 1 ? NUM_SHADES + 1 : GRADIENT_NUM_COLS ],
+ gray[ NUM_SHADES + 1 ],
+ button[ NUM_SHADES + 1 ]; // Last color = base color, for comparisons!
+ mutable QColor buttonColoured[ NUM_SHADES + 1 ];
+ mutable QColor backgroundColoured[ NUM_SHADES + 1 ];
+ EApp themedApp;
+ int popupmenuHighlightLevel;
+ bool borderButton,
+ menuIcons,
+ darkMenubar,
+ borderFrame,
+ rounded,
+ vArrow,
+ boldDefText,
+ crLabelHighlight,
+ lvDark,
+ borderSplitter;
+ EDefBtnIndicator defBtnIndicator;
+ EGroove sliderThumbs,
+ handles;
+ ETBarBorder toolbarBorders;
+ ELvExpander lvExpander;
+ ELvLines lvLines;
+ EProfile pmProfile;
+ EAppearance appearance;
+#if KDE_VERSION >= 0x30200
+
+ bool isTransKicker;
+#endif
+
+ EHover hover;
+ int contrast;
+ QPoint oldCursor;
+ mutable bool formMode;
+ QWidget *hoverWidget;
+ int hoverSect;
+};
+
+#endif
diff --git a/src/gui/rulers/ChordNameRuler.cpp b/src/gui/rulers/ChordNameRuler.cpp
new file mode 100644
index 0000000..2fc98f2
--- /dev/null
+++ b/src/gui/rulers/ChordNameRuler.cpp
@@ -0,0 +1,523 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ChordNameRuler.h"
+#include "misc/Debug.h"
+
+#include <klocale.h>
+#include "misc/Strings.h"
+#include "base/AnalysisTypes.h"
+#include "base/Composition.h"
+#include "base/CompositionTimeSliceAdapter.h"
+#include "base/Instrument.h"
+#include "base/NotationTypes.h"
+#include "base/Profiler.h"
+#include "base/PropertyName.h"
+#include "base/NotationQuantizer.h"
+#include "base/RefreshStatus.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "base/Studio.h"
+#include "base/Track.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/MultiViewCommandHistory.h"
+#include "gui/general/GUIPalette.h"
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qobject.h>
+#include <qpainter.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qtooltip.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+ChordNameRuler::ChordNameRuler(RulerScale *rulerScale,
+ RosegardenGUIDoc *doc,
+ double xorigin,
+ int height,
+ QWidget *parent,
+ const char *name) :
+ QWidget(parent, name),
+ m_xorigin(xorigin),
+ m_height(height),
+ m_currentXOffset(0),
+ m_width( -1),
+ m_ready(false),
+ m_rulerScale(rulerScale),
+ m_composition(&doc->getComposition()),
+ m_regetSegmentsOnChange(true),
+ m_currentSegment(0),
+ m_studio(0),
+ m_chordSegment(0),
+ m_fontMetrics(m_boldFont),
+ TEXT_FORMAL_X("TextFormalX"),
+ TEXT_ACTUAL_X("TextActualX")
+{
+ m_font.setPointSize(11);
+ m_font.setPixelSize(12);
+ m_boldFont.setPointSize(11);
+ m_boldFont.setPixelSize(12);
+ m_boldFont.setBold(true);
+ m_fontMetrics = QFontMetrics(m_boldFont);
+ setBackgroundColor(GUIPalette::getColour(GUIPalette::ChordNameRulerBackground));
+
+ m_compositionRefreshStatusId = m_composition->getNewRefreshStatusId();
+
+ QObject::connect(doc->getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(update()));
+
+ QToolTip::add
+ (this, i18n("Chord name ruler.\nTurn it on and off from the Settings->Rulers menu."));
+
+}
+
+ChordNameRuler::ChordNameRuler(RulerScale *rulerScale,
+ RosegardenGUIDoc *doc,
+ std::vector<Segment *> &segments,
+ double xorigin,
+ int height,
+ QWidget *parent,
+ const char *name) :
+ QWidget(parent, name),
+ m_xorigin(xorigin),
+ m_height(height),
+ m_currentXOffset(0),
+ m_width( -1),
+ m_ready(false),
+ m_rulerScale(rulerScale),
+ m_composition(&doc->getComposition()),
+ m_regetSegmentsOnChange(false),
+ m_currentSegment(0),
+ m_studio(0),
+ m_chordSegment(0),
+ m_fontMetrics(m_boldFont),
+ TEXT_FORMAL_X("TextFormalX"),
+ TEXT_ACTUAL_X("TextActualX")
+{
+ m_font.setPointSize(11);
+ m_font.setPixelSize(12);
+ m_boldFont.setPointSize(11);
+ m_boldFont.setPixelSize(12);
+ m_boldFont.setBold(true);
+ m_fontMetrics = QFontMetrics(m_boldFont);
+ setBackgroundColor(GUIPalette::getColour(GUIPalette::ChordNameRulerBackground));
+
+ m_compositionRefreshStatusId = m_composition->getNewRefreshStatusId();
+
+ QObject::connect(doc->getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(update()));
+
+ for (std::vector<Segment *>::iterator i = segments.begin();
+ i != segments.end(); ++i) {
+ m_segments.insert(SegmentRefreshMap::value_type
+ (*i, (*i)->getNewRefreshStatusId()));
+ }
+}
+
+ChordNameRuler::~ChordNameRuler()
+{
+ delete m_chordSegment;
+}
+
+void
+ChordNameRuler::setReady()
+{
+ m_ready = true;
+ update();
+}
+
+void
+ChordNameRuler::setCurrentSegment(Segment *segment)
+{
+ m_currentSegment = segment;
+}
+
+void
+ChordNameRuler::setStudio(Studio *studio)
+{
+ m_studio = studio;
+}
+
+void
+ChordNameRuler::slotScrollHoriz(int x)
+{
+ int w = width(), h = height();
+ int dx = x - ( -m_currentXOffset);
+ m_currentXOffset = -x;
+
+ if (dx == 0)
+ return ;
+
+ if (dx > w*7 / 8 || dx < -w*7 / 8) {
+ update();
+ return ;
+ }
+
+ if (dx > 0) { // moving right, so the existing stuff moves left
+ bitBlt(this, 0, 0, this, dx, 0, w - dx, h);
+ repaint(w - dx, 0, dx, h);
+ } else { // moving left, so the existing stuff moves right
+ bitBlt(this, -dx, 0, this, 0, 0, w + dx, h);
+ repaint(0, 0, -dx, h);
+ }
+}
+
+QSize
+ChordNameRuler::sizeHint() const
+{
+ double width =
+ m_rulerScale->getBarPosition(m_rulerScale->getLastVisibleBar()) +
+ m_rulerScale->getBarWidth(m_rulerScale->getLastVisibleBar());
+
+ NOTATION_DEBUG << "Returning chord-label ruler width as " << width << endl;
+
+ QSize res(std::max(int(width), m_width), m_height);
+
+ return res;
+}
+
+QSize
+ChordNameRuler::minimumSizeHint() const
+{
+ double firstBarWidth = m_rulerScale->getBarWidth(0);
+ QSize res = QSize(int(firstBarWidth), m_height);
+ return res;
+}
+
+void
+ChordNameRuler::recalculate(timeT from, timeT to)
+{
+ if (!m_ready)
+ return ;
+
+ Profiler profiler("ChordNameRuler::recalculate");
+ NOTATION_DEBUG << "ChordNameRuler[" << this << "]::recalculate" << endl;
+
+ bool regetSegments = false;
+
+ enum RecalcLevel { RecalcNone, RecalcVisible, RecalcWhole };
+ RecalcLevel level = RecalcNone;
+
+ if (m_segments.empty()) {
+
+ regetSegments = true;
+
+ } else if (m_regetSegmentsOnChange) {
+
+ RefreshStatus &rs =
+ m_composition->getRefreshStatus(m_compositionRefreshStatusId);
+
+ if (rs.needsRefresh()) {
+ rs.setNeedsRefresh(false);
+ regetSegments = true;
+ }
+ }
+
+ if (regetSegments) {
+
+ SegmentSelection ss;
+
+ for (Composition::iterator ci = m_composition->begin();
+ ci != m_composition->end(); ++ci) {
+
+ if (m_studio) {
+
+ TrackId ti = (*ci)->getTrack();
+
+ Instrument *instr = m_studio->getInstrumentById
+ (m_composition->getTrackById(ti)->getInstrument());
+
+ if (instr &&
+ instr->getInstrumentType() == Instrument::Midi &&
+ instr->isPercussion()) {
+ continue;
+ }
+ }
+
+ ss.insert(*ci);
+ }
+
+ std::vector<SegmentRefreshMap::iterator> eraseThese;
+
+ for (SegmentRefreshMap::iterator si = m_segments.begin();
+ si != m_segments.end(); ++si) {
+ if (ss.find(si->first) == ss.end()) {
+ eraseThese.push_back(si);
+ level = RecalcWhole;
+ NOTATION_DEBUG << "Segment deleted, updating (now have " << m_segments.size() << " segments)" << endl;
+ }
+ }
+
+ for (std::vector<SegmentRefreshMap::iterator>::iterator ei = eraseThese.begin();
+ ei != eraseThese.end(); ++ei) {
+ m_segments.erase(*ei);
+ }
+
+
+ for (SegmentSelection::iterator si = ss.begin();
+ si != ss.end(); ++si) {
+
+ if (m_segments.find(*si) == m_segments.end()) {
+ m_segments.insert(SegmentRefreshMap::value_type
+ (*si, (*si)->getNewRefreshStatusId()));
+ level = RecalcWhole;
+ NOTATION_DEBUG << "Segment created, adding (now have " << m_segments.size() << " segments)" << endl;
+ }
+ }
+
+ if (m_currentSegment &&
+ ss.find(m_currentSegment) == ss.end()) {
+ m_currentSegment = 0;
+ level = RecalcWhole;
+ }
+ }
+
+ if (!m_chordSegment)
+ m_chordSegment = new Segment();
+ if (m_segments.empty())
+ return ;
+
+ SegmentRefreshStatus overallStatus;
+ overallStatus.setNeedsRefresh(false);
+
+ for (SegmentRefreshMap::iterator i = m_segments.begin();
+ i != m_segments.end(); ++i) {
+ SegmentRefreshStatus &status =
+ i->first->getRefreshStatus(i->second);
+ if (status.needsRefresh()) {
+ overallStatus.push(status.from(), status.to());
+ }
+ }
+
+ // We now have the overall area affected by these changes, across
+ // all segments. If it's entirely within our displayed area, just
+ // recalculate the displayed area; if it overlaps, calculate the
+ // union of the two areas; if it's entirely without, calculate
+ // nothing.
+
+ if (level == RecalcNone) {
+ if (from == to) {
+ NOTATION_DEBUG << "ChordNameRuler::recalculate: from==to, recalculating all" << endl;
+ level = RecalcWhole;
+ } else if (overallStatus.from() == overallStatus.to()) {
+ NOTATION_DEBUG << "ChordNameRuler::recalculate: overallStatus.from==overallStatus.to, ignoring" << endl;
+ level = RecalcNone;
+ } else if (overallStatus.from() >= from && overallStatus.to() <= to) {
+ NOTATION_DEBUG << "ChordNameRuler::recalculate: change is " << overallStatus.from() << "->" << overallStatus.to() << ", I show " << from << "->" << to << ", recalculating visible area" << endl;
+ level = RecalcVisible;
+ } else if (overallStatus.from() >= to || overallStatus.to() <= from) {
+ NOTATION_DEBUG << "ChordNameRuler::recalculate: change is " << overallStatus.from() << "->" << overallStatus.to() << ", I show " << from << "->" << to << ", ignoring" << endl;
+ level = RecalcNone;
+ } else {
+ NOTATION_DEBUG << "ChordNameRuler::recalculate: change is " << overallStatus.from() << "->" << overallStatus.to() << ", I show " << from << "->" << to << ", recalculating whole" << endl;
+ level = RecalcWhole;
+ }
+ }
+
+ if (level == RecalcNone)
+ return ;
+
+ for (SegmentRefreshMap::iterator i = m_segments.begin();
+ i != m_segments.end(); ++i) {
+ i->first->getRefreshStatus(i->second).setNeedsRefresh(false);
+ }
+
+ if (!m_currentSegment) { //!!! arbitrary, must do better
+ //!!! need a segment starting at zero or so with a clef and key in it!
+ m_currentSegment = m_segments.begin()->first;
+ }
+
+ /*!!!
+
+ for (Composition::iterator ci = m_composition->begin();
+ ci != m_composition->end(); ++ci) {
+
+ if ((*ci)->getEndMarkerTime() >= from &&
+ ((*ci)->getStartTime() <= from ||
+ (clefKeySegment &&
+ (*ci)->getStartTime() < clefKeySegment->getStartTime()))) {
+
+ clefKeySegment = *ci;
+ }
+ }
+
+ if (!clefKeySegment) return;
+ }
+ */
+
+ if (level == RecalcWhole) {
+
+ m_chordSegment->clear();
+
+ timeT clefKeyTime = m_currentSegment->getStartTime();
+ //(from < m_currentSegment->getStartTime() ?
+ // m_currentSegment->getStartTime() : from);
+
+ Clef clef = m_currentSegment->getClefAtTime(clefKeyTime);
+ m_chordSegment->insert(clef.getAsEvent( -1));
+
+ ::Rosegarden::Key key = m_currentSegment->getKeyAtTime(clefKeyTime);
+ m_chordSegment->insert(key.getAsEvent( -1));
+
+ from = 0;
+ to = 0;
+
+ } else {
+ Segment::iterator i = m_chordSegment->findTime(from);
+ Segment::iterator j = m_chordSegment->findTime(to);
+ m_chordSegment->erase(i, j);
+ }
+
+ SegmentSelection selection;
+ for (SegmentRefreshMap::iterator si = m_segments.begin(); si != m_segments.end();
+ ++si) {
+ selection.insert(si->first);
+ }
+
+ CompositionTimeSliceAdapter adapter(m_composition, &selection, from, to);
+ AnalysisHelper helper;
+ helper.labelChords(adapter, *m_chordSegment, m_composition->getNotationQuantizer());
+}
+
+void
+ChordNameRuler::paintEvent(QPaintEvent* e)
+{
+ if (!m_composition || !m_ready)
+ return ;
+
+ NOTATION_DEBUG << "*** Chord Name Ruler: paintEvent" << endl;
+
+ Profiler profiler1("ChordNameRuler::paintEvent (whole)");
+
+ QPainter paint(this);
+ paint.setPen(GUIPalette::getColour(GUIPalette::ChordNameRulerForeground));
+
+ paint.setClipRegion(e->region());
+ paint.setClipRect(e->rect().normalize());
+
+ QRect clipRect = paint.clipRegion().boundingRect();
+
+ timeT from = m_rulerScale->getTimeForX
+ (clipRect.x() - m_currentXOffset - m_xorigin - 50);
+ timeT to = m_rulerScale->getTimeForX
+ (clipRect.x() + clipRect.width() - m_currentXOffset - m_xorigin + 50);
+
+ recalculate(from, to);
+
+ if (!m_chordSegment)
+ return ;
+
+ Profiler profiler2("ChordNameRuler::paintEvent (paint)");
+
+ QRect boundsForHeight = m_fontMetrics.boundingRect("^j|lM");
+ int fontHeight = boundsForHeight.height();
+ int textY = (height() - 6) / 2 + fontHeight / 2;
+
+ double prevX = 0;
+ timeT keyAt = from - 1;
+ std::string keyText;
+
+ NOTATION_DEBUG << "*** Chord Name Ruler: paint " << from << " -> " << to << endl;
+
+ for (Segment::iterator i = m_chordSegment->findTime(from);
+ i != m_chordSegment->findTime(to); ++i) {
+
+ NOTATION_DEBUG << "type " << (*i)->getType() << " at " << (*i)->getAbsoluteTime()
+ << endl;
+
+ if (!(*i)->isa(Text::EventType) ||
+ !(*i)->has(Text::TextPropertyName) ||
+ !(*i)->has(Text::TextTypePropertyName))
+ continue;
+
+ std::string text((*i)->get
+ <String>(Text::TextPropertyName));
+
+ if ((*i)->get
+ <String>(Text::TextTypePropertyName) == Text::KeyName) {
+ timeT myTime = (*i)->getAbsoluteTime();
+ if (myTime == keyAt && text == keyText)
+ continue;
+ else {
+ keyAt = myTime;
+ keyText = text;
+ }
+ }
+
+ double x = m_rulerScale->getXForTime((*i)->getAbsoluteTime());
+ (*i)->set
+ <Int>(TEXT_FORMAL_X, (long)x);
+
+ QRect textBounds = m_fontMetrics.boundingRect(strtoqstr(text));
+ int width = textBounds.width();
+
+ x -= width / 2;
+ if (prevX >= x - 3)
+ x = prevX + 3;
+ (*i)->set
+ <Int>(TEXT_ACTUAL_X, long(x));
+ prevX = x + width;
+ }
+
+ for (Segment::iterator i = m_chordSegment->findTime(from);
+ i != m_chordSegment->findTime(to); ++i) {
+
+ if (!(*i)->isa(Text::EventType))
+ continue;
+ std::string text((*i)->get
+ <String>(Text::TextPropertyName));
+ std::string type((*i)->get
+ <String>(Text::TextTypePropertyName));
+
+ if (!(*i)->has(TEXT_FORMAL_X))
+ continue;
+
+ long formalX = (*i)->get
+ <Int>(TEXT_FORMAL_X);
+ long actualX = (*i)->get
+ <Int>(TEXT_ACTUAL_X);
+
+ formalX += m_currentXOffset + long(m_xorigin);
+ actualX += m_currentXOffset + long(m_xorigin);
+
+ paint.drawLine(formalX, height() - 4, formalX, height());
+
+ if (type == Text::KeyName) {
+ paint.setFont(m_boldFont);
+ } else {
+ paint.setFont(m_font);
+ }
+
+ paint.drawText(actualX, textY, strtoqstr(text));
+ }
+}
+
+}
+#include "ChordNameRuler.moc"
diff --git a/src/gui/rulers/ChordNameRuler.h b/src/gui/rulers/ChordNameRuler.h
new file mode 100644
index 0000000..70cdc12
--- /dev/null
+++ b/src/gui/rulers/ChordNameRuler.h
@@ -0,0 +1,146 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_CHORDNAMERULER_H_
+#define _RG_CHORDNAMERULER_H_
+
+#include "base/PropertyName.h"
+#include <map>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qsize.h>
+#include <qwidget.h>
+#include <vector>
+#include "base/Event.h"
+
+
+class QPaintEvent;
+
+
+namespace Rosegarden
+{
+
+class Studio;
+class Segment;
+class RulerScale;
+class RosegardenGUIDoc;
+class Composition;
+
+
+/**
+ * ChordNameRuler is a widget that shows a strip of text strings
+ * describing the chords in a composition.
+ */
+
+class ChordNameRuler : public QWidget
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Construct a ChordNameRuler that displays the chords in the
+ * given Composition at positions calculated by the given
+ * RulerScale. Be aware that it will not be refreshed until
+ * setReady is called (because the first refresh is expensive).
+ */
+ ChordNameRuler(RulerScale *rulerScale,
+ RosegardenGUIDoc *doc,
+ double xorigin = 0.0,
+ int height = 0,
+ QWidget* parent = 0,
+ const char *name = 0);
+
+ /**
+ * Construct a ChordNameRuler that displays the chords in the
+ * given Segments at positions calculated by the given
+ * RulerScale. Be aware that it will not be refreshed until
+ * setReady is called (because the first refresh is expensive).
+ */
+ ChordNameRuler(RulerScale *rulerScale,
+ RosegardenGUIDoc *doc,
+ std::vector<Segment *> &segments,
+ double xorigin = 0.0,
+ int height = 0,
+ QWidget* parent = 0,
+ const char *name = 0);
+
+ ~ChordNameRuler();
+
+ /// Indicate that the chord-name ruler should make itself ready and refresh
+ void setReady();
+
+ // may have one of these; can be changed at any time (to any in given composition):
+ void setCurrentSegment(Segment *segment);
+
+ // may have one of these (to avoid using percussion tracks in chords):
+ void setStudio(Studio *studio);
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ void setMinimumWidth(int width) { m_width = width; }
+
+public slots:
+ void slotScrollHoriz(int x);
+
+protected:
+ virtual void paintEvent(QPaintEvent *);
+
+private:
+ void recalculate(timeT from = 0,
+ timeT to = 0);
+
+ double m_xorigin;
+ int m_height;
+ int m_currentXOffset;
+ int m_width;
+ bool m_ready;
+
+ RulerScale *m_rulerScale;
+
+ Composition *m_composition;
+ unsigned int m_compositionRefreshStatusId;
+
+ typedef std::map<Segment *, int> SegmentRefreshMap;
+ SegmentRefreshMap m_segments; // map to refresh status id
+ bool m_regetSegmentsOnChange;
+
+ Segment *m_currentSegment;
+ Studio *m_studio;
+
+ Segment *m_chordSegment;
+
+ QFont m_font;
+ QFont m_boldFont;
+ QFontMetrics m_fontMetrics;
+
+ const PropertyName TEXT_FORMAL_X;
+ const PropertyName TEXT_ACTUAL_X;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/ControlChangeCommand.cpp b/src/gui/rulers/ControlChangeCommand.cpp
new file mode 100644
index 0000000..f6f5d0e
--- /dev/null
+++ b/src/gui/rulers/ControlChangeCommand.cpp
@@ -0,0 +1,50 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ControlChangeCommand.h"
+#include "ControlItem.h"
+#include "misc/Debug.h"
+#include <klocale.h>
+
+namespace Rosegarden {
+
+ControlChangeCommand::ControlChangeCommand(QCanvasItemList selectedItems,
+ Segment &segment,
+ Rosegarden::timeT start, Rosegarden::timeT end)
+ : BasicCommand(i18n("Control Change"), segment, start, end, true),
+ m_selectedItems(selectedItems)
+{
+ RG_DEBUG << "ControlChangeCommand : from " << start << " to " << end << endl;
+}
+
+
+void ControlChangeCommand::modifySegment()
+{
+ for (QCanvasItemList::Iterator it=m_selectedItems.begin(); it!=m_selectedItems.end(); ++it) {
+ if (ControlItem *item = dynamic_cast<ControlItem*>(*it))
+ item->updateValue();
+ }
+}
+
+}
diff --git a/src/gui/rulers/ControlChangeCommand.h b/src/gui/rulers/ControlChangeCommand.h
new file mode 100644
index 0000000..cc334a4
--- /dev/null
+++ b/src/gui/rulers/ControlChangeCommand.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_CONTROLCHANGECOMMAND_H_
+#define _RG_CONTROLCHANGECOMMAND_H_
+
+#include "document/BasicCommand.h"
+#include <qcanvas.h>
+
+namespace Rosegarden {
+
+/**
+ * Command defining a change (property change or similar) from the control ruler
+ */
+class ControlChangeCommand : public BasicCommand
+{
+public:
+
+ ControlChangeCommand(QCanvasItemList selectedItems,
+ Segment &segment,
+ Rosegarden::timeT start, Rosegarden::timeT end);
+ virtual ~ControlChangeCommand() {;}
+
+
+protected:
+
+ virtual void modifySegment();
+
+ QCanvasItemList m_selectedItems;
+};
+
+}
+
+#endif /*CONTROLCHANGECOMMAND_H_*/
diff --git a/src/gui/rulers/ControlItem.cpp b/src/gui/rulers/ControlItem.cpp
new file mode 100644
index 0000000..623fbf3
--- /dev/null
+++ b/src/gui/rulers/ControlItem.cpp
@@ -0,0 +1,195 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ControlItem.h"
+#include "ControlRuler.h"
+#include "ElementAdapter.h"
+#include "misc/Debug.h"
+
+namespace Rosegarden {
+
+const unsigned int ControlItem::BorderThickness = 1;
+const unsigned int ControlItem::DefaultWidth = 20;
+static int _canvasItemZ = 30;
+
+ControlItem::ControlItem(ControlRuler* ruler, ElementAdapter* elementAdapter,
+ int xx, int width)
+ : QCanvasRectangle(ruler->canvas()),
+ m_value(0),
+ m_controlRuler(ruler),
+ m_elementAdapter(elementAdapter)
+{
+ if (width < DefaultWidth/4) {
+ width = DefaultWidth/4; // avoid invisible zero-duration items
+ }
+ setWidth(width);
+ setPen(QPen(Qt::black, BorderThickness));
+ setBrush(Qt::blue);
+
+ setX(xx);
+ setY(canvas()->height());
+ setZ(_canvasItemZ++); // we should make this work against controlruler
+
+ updateFromValue();
+ setEnabled(false);
+ //RG_DEBUG << "ControlItem x = " << x() << " - y = " << y() << " - width = " << width << endl;
+ show();
+}
+
+ControlItem::~ControlItem()
+{
+ delete m_elementAdapter;
+}
+
+
+void ControlItem::setValue(long v)
+{
+// RG_DEBUG << "ControlItem::setValue(" << v << ") x = " << x() << endl;
+
+ m_value = v;
+}
+
+void ControlItem::updateValue()
+{
+ m_elementAdapter->setValue(m_value);
+}
+
+void ControlItem::updateFromValue()
+{
+// RG_DEBUG << "ControlItem::updateFromValue() : " << this << endl;
+
+ if (m_elementAdapter->getValue(m_value)) {
+// RG_DEBUG << "ControlItem::updateFromValue() : value = " << m_value << endl;
+ setHeight(m_controlRuler->valueToHeight(m_value));
+ }
+}
+
+typedef std::pair<int, QCanvasItem*> ItemPair;
+struct ItemCmp
+{
+ bool operator()(const ItemPair &i1, const ItemPair &i2)
+ {
+ return i1.first > i2.first;
+ }
+};
+
+void ControlItem::draw(QPainter &painter)
+{
+ if (!isEnabled())
+ updateFromValue();
+
+ setBrush(m_controlRuler->valueToColour(m_controlRuler->getMaxItemValue(), m_value));
+
+ QCanvasRectangle::draw(painter);
+
+
+ /*
+
+ // Attempt to get overlapping rectangles ordered automatically -
+ // probably best not to do this here - rwb
+
+ // calculate collisions and assign Z values accordingly
+ //
+ QCanvasItemList l = collisions(false);
+
+ std::vector<ItemPair> sortList;
+
+ for (QCanvasItemList::Iterator it=l.begin(); it!=l.end(); ++it) {
+
+ // skip all but rectangles
+ if ((*it)->rtti() != QCanvasItem::Rtti_Rectangle) continue;
+
+ if (QCanvasRectangle *rect = dynamic_cast<QCanvasRectangle*>(*it))
+ sortList.push_back(ItemPair(rect->height(), *it));
+ }
+
+ // sort the list by height
+ std::sort(sortList.begin(), sortList.end(), ItemCmp());
+
+ int z = 20;
+
+ for (std::vector<ItemPair>::iterator it = sortList.begin();
+ it != sortList.end(); ++it)
+ {
+ RG_DEBUG << "HEIGHT = " << (*it).first << endl;
+ (*it).second->setZ(z++);
+ }
+
+ RG_DEBUG << endl << endl;
+
+ canvas()->update();
+
+ */
+
+}
+
+void ControlItem::handleMouseButtonPress(QMouseEvent*)
+{
+// RG_DEBUG << "ControlItem::handleMouseButtonPress()" << this << endl;
+ setEnabled(true);
+}
+
+void ControlItem::handleMouseButtonRelease(QMouseEvent*)
+{
+// RG_DEBUG << "ControlItem::handleMouseButtonRelease()" << this << endl;
+ setEnabled(false);
+}
+
+void ControlItem::handleMouseMove(QMouseEvent*, int /*deltaX*/, int deltaY)
+{
+// RG_DEBUG << "ControlItem::handleMouseMove()" << this << endl;
+
+ // height is always negative
+ //
+
+ m_controlRuler->applyTool(x(), deltaY);
+
+ int absNewHeight = -(getHeight() + deltaY);
+
+ // Make sure height is within bounds
+ if (absNewHeight > ControlRuler::MaxItemHeight)
+ absNewHeight = ControlRuler::MaxItemHeight;
+ else if (absNewHeight < ControlRuler::MinItemHeight)
+ absNewHeight = ControlRuler::MinItemHeight;
+
+ setHeight(-absNewHeight);
+ setValue(m_controlRuler->heightToValue(getHeight()));
+}
+
+void ControlItem::handleMouseWheel(QWheelEvent *)
+{
+// RG_DEBUG << "ControlItem::handleMouseWheel - got wheel event" << endl;
+}
+
+void ControlItem::setSelected(bool s)
+{
+ QCanvasItem::setSelected(s);
+
+ if (s) setPen(QPen(Qt::red, BorderThickness));
+ else setPen(QPen(Qt::black, BorderThickness));
+
+ canvas()->update();
+}
+
+}
diff --git a/src/gui/rulers/ControlItem.h b/src/gui/rulers/ControlItem.h
new file mode 100644
index 0000000..44f9e22
--- /dev/null
+++ b/src/gui/rulers/ControlItem.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 <qcanvas.h>
+
+namespace Rosegarden {
+
+class ControlRuler;
+class ElementAdapter;
+
+class ControlItem : public QCanvasRectangle
+{
+public:
+ ControlItem(ControlRuler* controlRuler,
+ ElementAdapter* adapter,
+ int x, int width = DefaultWidth);
+
+ ~ControlItem();
+
+ virtual void setValue(long);
+ int getValue() const { return m_value; }
+
+ void setWidth(int w) { setSize(w, height()); }
+ void setHeight(int h) { setSize(width(), h); }
+ int getHeight() { return size().height(); }
+
+ virtual void draw(QPainter &painter);
+
+ virtual void handleMouseButtonPress(QMouseEvent *e);
+ virtual void handleMouseButtonRelease(QMouseEvent *e);
+ virtual void handleMouseMove(QMouseEvent *e, int deltaX, int deltaY);
+ virtual void handleMouseWheel(QWheelEvent *e);
+
+ virtual void setSelected(bool yes);
+
+ /// recompute height according to represented value prior to a canvas repaint
+ virtual void updateFromValue();
+
+ /// update value according to height after a user edit
+ virtual void updateValue();
+
+ ElementAdapter* getElementAdapter() { return m_elementAdapter; }
+
+protected:
+
+ //--------------- Data members ---------------------------------
+
+ long m_value;
+ bool m_handlingMouseMove;
+
+ ControlRuler* m_controlRuler;
+ ElementAdapter* m_elementAdapter;
+
+ static const unsigned int BorderThickness;
+ static const unsigned int DefaultWidth;
+};
+
+}
diff --git a/src/gui/rulers/ControlRuler.cpp b/src/gui/rulers/ControlRuler.cpp
new file mode 100644
index 0000000..12064f5
--- /dev/null
+++ b/src/gui/rulers/ControlRuler.cpp
@@ -0,0 +1,539 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ControlRuler.h"
+
+#include "base/Event.h"
+#include "misc/Debug.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "ControlChangeCommand.h"
+#include "ControlItem.h"
+#include "ControlSelector.h"
+#include "ControlTool.h"
+#include "DefaultVelocityColour.h"
+#include "ElementAdapter.h"
+#include "gui/general/EditView.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "gui/widgets/TextFloat.h"
+#include <kmainwindow.h>
+#include <qcanvas.h>
+#include <qcolor.h>
+#include <qcursor.h>
+#include <qpoint.h>
+#include <qpopupmenu.h>
+#include <qscrollbar.h>
+#include <qscrollview.h>
+#include <qstring.h>
+#include <qwidget.h>
+#include <algorithm>
+
+
+namespace Rosegarden
+{
+
+const int ControlRuler::DefaultRulerHeight = 75;
+const int ControlRuler::MinItemHeight = 5;
+const int ControlRuler::MaxItemHeight = 64 + 5;
+const int ControlRuler::ItemHeightRange = 64;
+
+ControlRuler::ControlRuler(Segment *segment,
+ RulerScale* rulerScale,
+ EditViewBase* parentView,
+ QCanvas* c, QWidget* parent,
+ const char* name, WFlags f) :
+ RosegardenCanvasView(c, parent, name, f),
+ m_parentEditView(parentView),
+ m_mainHorizontalScrollBar(0),
+ m_rulerScale(rulerScale),
+ m_eventSelection(new EventSelection(*segment)),
+ m_segment(segment),
+ m_currentItem(0),
+ m_tool(0),
+ m_maxItemValue(127),
+ m_staffOffset(0),
+ m_currentX(0.0),
+ m_itemMoved(false),
+ m_selecting(false),
+ m_selector(new ControlSelector(this)),
+ m_selectionRect(new QCanvasRectangle(canvas())),
+ m_menu(0)
+{
+ setHScrollBarMode(QScrollView::AlwaysOff);
+
+ m_selectionRect->setPen(Qt::red);
+
+ setFixedHeight(sizeHint().height());
+
+ connect(this, SIGNAL(stateChange(const QString&, bool)),
+ m_parentEditView, SLOT(slotStateChanged(const QString&, bool)));
+
+ m_numberFloat = new TextFloat(this);
+ m_numberFloat->hide();
+
+ m_segment->addObserver(this);
+
+ emit stateChange("have_controller_item_selected", false);
+}
+
+ControlRuler::~ControlRuler()
+{
+ if (m_segment) {
+ m_segment->removeObserver(this);
+ }
+}
+
+void ControlRuler::slotUpdate()
+{
+ RG_DEBUG << "ControlRuler::slotUpdate()\n";
+
+ canvas()->setAllChanged(); // TODO: be a bit more subtle, call setChanged(<time area>)
+
+ canvas()->update();
+ repaint();
+}
+
+void ControlRuler::slotUpdateElementsHPos()
+{
+ computeStaffOffset();
+
+ QCanvasItemList list = canvas()->allItems();
+
+ QCanvasItemList::Iterator it = list.begin();
+ for (; it != list.end(); ++it) {
+ ControlItem* item = dynamic_cast<ControlItem*>(*it);
+ if (!item)
+ continue;
+ layoutItem(item);
+ }
+
+ canvas()->update();
+}
+
+void ControlRuler::layoutItem(ControlItem* item)
+{
+ timeT itemTime = item->getElementAdapter()->getTime();
+
+ double x = m_rulerScale->getXForTime(itemTime);
+
+ item->setX(x + m_staffOffset);
+ int itemElementDuration = item->getElementAdapter()->getDuration();
+
+ int width = int(m_rulerScale->getXForTime(itemTime + itemElementDuration) - x);
+
+ item->setWidth(width);
+
+ // RG_DEBUG << "ControlRuler::layoutItem ControlItem x = " << x << " - width = " << width << endl;
+}
+
+void ControlRuler::setControlTool(ControlTool* tool)
+{
+ if (m_tool)
+ delete m_tool;
+ m_tool = tool;
+}
+
+void
+ControlRuler::segmentDeleted(const Segment *)
+{
+ m_segment = 0;
+}
+
+void ControlRuler::contentsMousePressEvent(QMouseEvent* e)
+{
+ if (e->button() != Qt::LeftButton) {
+ m_numberFloat->hide();
+ m_selecting = false;
+ return ;
+ }
+
+ RG_DEBUG << "ControlRuler::contentsMousePressEvent()\n";
+
+ QPoint p = inverseMapPoint(e->pos());
+
+ QCanvasItemList l = canvas()->collisions(p);
+
+ if (l.count() == 0) { // de-select current item
+ clearSelectedItems();
+ m_selecting = true;
+ m_selector->handleMouseButtonPress(e);
+ RG_DEBUG << "ControlRuler::contentsMousePressEvent : entering selection mode\n";
+ return ;
+ }
+
+ // clear selection unless control was pressed, in which case
+ // add the event to the current selection
+ if (!(e->state() && QMouseEvent::ControlButton)) {
+ clearSelectedItems();
+ }
+
+ ControlItem *topItem = 0;
+ for (QCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it) {
+
+ if (ControlItem *item = dynamic_cast<ControlItem*>(*it)) {
+
+ if (topItem == 0)
+ topItem = item;
+
+ if (item->isSelected()) { // if the item which was clicked
+ // on is part of a selection,
+ // propagate mousepress on all
+ // selected items
+
+ item->handleMouseButtonPress(e);
+
+ for (QCanvasItemList::Iterator it = m_selectedItems.begin();
+ it != m_selectedItems.end(); ++it) {
+ if (ControlItem *selectedItem =
+ dynamic_cast<ControlItem*>(*it)) {
+ selectedItem->handleMouseButtonPress(e);
+ }
+ }
+
+
+ } else { // select it
+
+ if (!(e->state() && QMouseEvent::ControlButton)) {
+ if (item->z() > topItem->z())
+ topItem = item;
+
+ } else {
+ m_selectedItems << item;
+ item->setSelected(true);
+ item->handleMouseButtonPress(e);
+ ElementAdapter* adapter = item->getElementAdapter();
+ m_eventSelection->addEvent(adapter->getEvent());
+ }
+ }
+ }
+ }
+
+ if (topItem && !m_selectedItems.contains(topItem)) { // select the top item
+ m_selectedItems << topItem;
+ topItem->setSelected(true);
+ topItem->handleMouseButtonPress(e);
+ ElementAdapter* adapter = topItem->getElementAdapter();
+ m_eventSelection->addEvent(adapter->getEvent());
+ }
+
+ m_itemMoved = false;
+ m_lastEventPos = p;
+}
+
+void ControlRuler::contentsMouseReleaseEvent(QMouseEvent* e)
+{
+ if (e->button() != Qt::LeftButton) {
+ m_numberFloat->hide();
+ m_selecting = false;
+ return ;
+ }
+
+ if (m_selecting) {
+ updateSelection();
+ m_selector->handleMouseButtonRelease(e);
+ RG_DEBUG << "ControlRuler::contentsMouseReleaseEvent : leaving selection mode\n";
+ m_selecting = false;
+ return ;
+ }
+
+ for (QCanvasItemList::Iterator it = m_selectedItems.begin(); it != m_selectedItems.end(); ++it) {
+ if (ControlItem *item = dynamic_cast<ControlItem*>(*it)) {
+
+ ElementAdapter * adapter = item->getElementAdapter();
+ m_eventSelection->addEvent(adapter->getEvent());
+ item->handleMouseButtonRelease(e);
+ }
+ }
+
+ emit stateChange("have_controller_item_selected", true);
+
+ if (m_itemMoved) {
+
+ m_lastEventPos = inverseMapPoint(e->pos());
+
+ // Add command to history
+ ControlChangeCommand* command = new ControlChangeCommand(m_selectedItems,
+ *m_segment,
+ m_eventSelection->getStartTime(),
+ m_eventSelection->getEndTime());
+
+ RG_DEBUG << "ControlRuler::contentsMouseReleaseEvent : adding command\n";
+ m_parentEditView->addCommandToHistory(command);
+
+ m_itemMoved = false;
+ }
+
+ m_numberFloat->hide();
+}
+
+void ControlRuler::contentsMouseMoveEvent(QMouseEvent* e)
+{
+ QPoint p = inverseMapPoint(e->pos());
+
+ int deltaX = p.x() - m_lastEventPos.x(),
+ deltaY = p.y() - m_lastEventPos.y();
+ m_lastEventPos = p;
+
+ if (m_selecting) {
+ updateSelection();
+ m_selector->handleMouseMove(e, deltaX, deltaY);
+ slotScrollHorizSmallSteps(p.x());
+ return ;
+ }
+
+ m_itemMoved = true;
+
+ // Borrowed from Rotary - compute total position within window
+ //
+ QPoint totalPos = mapTo(topLevelWidget(), QPoint(0, 0));
+
+ int scrollX = dynamic_cast<EditView*>(m_parentEditView)->getRawCanvasView()->
+ horizontalScrollBar()->value();
+
+ /*
+ RG_DEBUG << "ControlRuler::contentsMouseMoveEvent - total pos = " << totalPos.x()
+ << ",e pos = " << e->pos().x()
+ << ", scroll bar = " << scrollX
+ << endl;
+ */
+
+ // Allow for scrollbar
+ //
+ m_numberFloat->move(totalPos.x() + e->pos().x() - scrollX + 20,
+ totalPos.y() + e->pos().y() - 10);
+
+ int value = 0;
+
+ for (QCanvasItemList::Iterator it = m_selectedItems.begin(); it != m_selectedItems.end(); ++it) {
+ if (ControlItem *item = dynamic_cast<ControlItem*>(*it)) {
+ item->handleMouseMove(e, deltaX, deltaY);
+ // ElementAdapter* adapter = item->getElementAdapter();
+
+ // set value to highest in selection
+ if (item->getValue() >= value) {
+ value = item->getValue();
+ m_numberFloat->setText(QString("%1").arg(value));
+ }
+ }
+ }
+ canvas()->update();
+
+ m_numberFloat->show();
+
+}
+
+void
+ControlRuler::contentsWheelEvent(QWheelEvent *e)
+{
+ // not sure what to do yet
+ QCanvasView::contentsWheelEvent(e);
+}
+
+void ControlRuler::updateSelection()
+{
+ clearSelectedItems();
+
+ bool haveSelectedItems = false;
+
+ QCanvasItemList l = getSelectionRectangle()->collisions(true);
+
+ for (QCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it) {
+
+ if (ControlItem *item = dynamic_cast<ControlItem*>(*it)) {
+ item->setSelected(true);
+ m_selectedItems << item;
+ haveSelectedItems = true;
+
+ ElementAdapter* adapter = item->getElementAdapter();
+ m_eventSelection->addEvent(adapter->getEvent());
+ }
+ }
+
+ emit stateChange("have_controller_item_selected", haveSelectedItems);
+}
+
+void ControlRuler::contentsContextMenuEvent(QContextMenuEvent* e)
+{
+ if (!m_menu && !m_menuName.isEmpty())
+ createMenu();
+
+ if (m_menu) {
+ RG_DEBUG << "ControlRuler::showMenu() - show menu with" << m_menu->count() << " items\n";
+ m_lastEventPos = inverseMapPoint(e->pos());
+ m_menu->exec(QCursor::pos());
+ } else
+ RG_DEBUG << "ControlRuler::showMenu() : no menu to show\n";
+
+}
+
+void ControlRuler::createMenu()
+{
+ RG_DEBUG << "ControlRuler::createMenu()\n";
+
+ KMainWindow* parentMainWindow = dynamic_cast<KMainWindow*>(topLevelWidget());
+
+ if (parentMainWindow && parentMainWindow->factory()) {
+ m_menu = static_cast<QPopupMenu*>(parentMainWindow->factory()->container(m_menuName, parentMainWindow));
+
+ if (!m_menu) {
+ RG_DEBUG << "ControlRuler::createMenu() failed\n";
+ }
+ } else {
+ RG_DEBUG << "ControlRuler::createMenu() failed: no parent factory\n";
+ }
+}
+
+void
+ControlRuler::clearSelectedItems()
+{
+ for (QCanvasItemList::Iterator it = m_selectedItems.begin(); it != m_selectedItems.end(); ++it) {
+ (*it)->setSelected(false);
+ }
+ m_selectedItems.clear();
+
+ delete m_eventSelection;
+ m_eventSelection = new EventSelection(*m_segment);
+}
+
+void ControlRuler::clear()
+{
+ QCanvasItemList allItems = canvas()->allItems();
+
+ for (QCanvasItemList::Iterator it = allItems.begin(); it != allItems.end(); ++it) {
+ if (ControlItem *item = dynamic_cast<ControlItem*>(*it)) {
+ delete item;
+ }
+ }
+}
+
+int ControlRuler::valueToHeight(long val)
+{
+ long scaleVal = val * (ItemHeightRange);
+
+ int res = -(int(scaleVal / getMaxItemValue()) + MinItemHeight);
+
+ //RG_DEBUG << "ControlRuler::valueToHeight : val = " << val << " - height = " << res
+ //<< " - scaleVal = " << scaleVal << endl;
+
+ return res;
+}
+
+long ControlRuler::heightToValue(int h)
+{
+ long val = -h;
+ val -= MinItemHeight;
+ val *= getMaxItemValue();
+ val /= (ItemHeightRange);
+ val = std::min(val, long(getMaxItemValue()));
+ return val;
+}
+
+QColor ControlRuler::valueToColour(int max, int val)
+{
+ int maxDefault = DefaultVelocityColour::getInstance()->getMaxValue();
+
+ int value = val;
+
+ // Scale value accordingly
+ //
+ if (maxDefault != max)
+ value = int(double(maxDefault) * double(val) / double(max));
+
+ return DefaultVelocityColour::getInstance()->getColour(value);
+}
+
+int ControlRuler::applyTool(double x, int val)
+{
+ if (m_tool)
+ return (*m_tool)(x, val);
+ return val;
+}
+
+void ControlRuler::flipForwards()
+{
+ std::pair<int, int> minMax = getZMinMax();
+
+ QCanvasItemList l = canvas()->allItems();
+ for (QCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it) {
+
+ // skip all but rectangles
+ if ((*it)->rtti() != QCanvasItem::Rtti_Rectangle)
+ continue;
+
+ // match min
+ if ((*it)->z() == minMax.second)
+ (*it)->setZ(minMax.first);
+ else
+ (*it)->setZ((*it)->z() + 1);
+ }
+
+ canvas()->update();
+}
+
+void ControlRuler::flipBackwards()
+{
+ std::pair<int, int> minMax = getZMinMax();
+
+ QCanvasItemList l = canvas()->allItems();
+ for (QCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it) {
+
+ // skip all but rectangles
+ if ((*it)->rtti() != QCanvasItem::Rtti_Rectangle)
+ continue;
+
+ // match min
+ if ((*it)->z() == minMax.first)
+ (*it)->setZ(minMax.second);
+ else
+ (*it)->setZ((*it)->z() - 1);
+ }
+
+ canvas()->update();
+}
+
+std::pair<int, int> ControlRuler::getZMinMax()
+{
+ QCanvasItemList l = canvas()->allItems();
+ std::vector<int> zList;
+ for (QCanvasItemList::Iterator it=l.begin(); it!=l.end(); ++it) {
+
+ // skip all but rectangles
+ if ((*it)->rtti() != QCanvasItem::Rtti_Rectangle) continue;
+ zList.push_back(int((*it)->z()));
+ }
+
+ std::sort(zList.begin(), zList.end());
+
+ return std::pair<int, int>(zList[0], zList[zList.size() - 1]);
+}
+
+QScrollBar* ControlRuler::getMainHorizontalScrollBar()
+{
+ return m_mainHorizontalScrollBar ? m_mainHorizontalScrollBar : horizontalScrollBar();
+}
+
+}
+#include "ControlRuler.moc"
diff --git a/src/gui/rulers/ControlRuler.h b/src/gui/rulers/ControlRuler.h
new file mode 100644
index 0000000..ac6eba4
--- /dev/null
+++ b/src/gui/rulers/ControlRuler.h
@@ -0,0 +1,182 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_CONTROLRULER_H_
+#define _RG_CONTROLRULER_H_
+
+#include "base/Segment.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include <qcolor.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include <utility>
+
+
+class QWidget;
+class QWheelEvent;
+class QScrollBar;
+class QPopupMenu;
+class QMouseEvent;
+class QContextMenuEvent;
+class QCanvasRectangle;
+class QCanvas;
+
+
+namespace Rosegarden
+{
+
+class ControlTool;
+class ControlSelector;
+class ControlItem;
+class TextFloat;
+class Segment;
+class RulerScale;
+class EventSelection;
+class EditViewBase;
+
+
+/**
+ * ControlRuler : base class for Control Rulers
+ */
+class ControlRuler : public RosegardenCanvasView, public SegmentObserver
+{
+ Q_OBJECT
+
+ friend class ControlItem;
+
+public:
+ ControlRuler(Segment*,
+ RulerScale*,
+ EditViewBase* parentView,
+ QCanvas*,
+ QWidget* parent=0, const char* name=0, WFlags f=0);
+ virtual ~ControlRuler();
+
+ virtual QString getName() = 0;
+
+ int getMaxItemValue() { return m_maxItemValue; }
+ void setMaxItemValue(int val) { m_maxItemValue = val; }
+
+ void clear();
+
+ void setControlTool(ControlTool*);
+
+ int applyTool(double x, int val);
+
+ QCanvasRectangle* getSelectionRectangle() { return m_selectionRect; }
+
+ RulerScale* getRulerScale() { return m_rulerScale; }
+
+ // SegmentObserver interface
+ virtual void segmentDeleted(const Segment *);
+
+ static const int DefaultRulerHeight;
+ static const int MinItemHeight;
+ static const int MaxItemHeight;
+ static const int ItemHeightRange;
+
+ void flipForwards();
+ void flipBackwards();
+
+ void setMainHorizontalScrollBar(QScrollBar* s ) { m_mainHorizontalScrollBar = s; }
+
+signals:
+ void stateChange(const QString&, bool);
+
+public slots:
+ /// override RosegardenCanvasView - we don't want to change the main hscrollbar
+ virtual void slotUpdate();
+ virtual void slotUpdateElementsHPos();
+
+protected:
+ virtual void contentsMousePressEvent(QMouseEvent*);
+ virtual void contentsMouseReleaseEvent(QMouseEvent*);
+ virtual void contentsMouseMoveEvent(QMouseEvent*);
+ virtual void contentsWheelEvent(QWheelEvent*);
+ virtual void contentsContextMenuEvent(QContextMenuEvent*);
+
+ virtual QScrollBar* getMainHorizontalScrollBar();
+
+ virtual void computeStaffOffset() {};
+
+ virtual void layoutItem(ControlItem*);
+
+
+
+ // Stacking of the SegmentItems on the canvas
+ //
+ std::pair<int, int> getZMinMax();
+
+ virtual void init() = 0;
+ virtual void drawBackground() = 0;
+
+ int valueToHeight(long val);
+ long heightToValue(int height);
+ QColor valueToColour(int max, int val);
+
+ void clearSelectedItems();
+ void updateSelection();
+
+ void setMenuName(QString menuName) { m_menuName = menuName; }
+ void createMenu();
+
+ //--------------- Data members ---------------------------------
+
+ EditViewBase* m_parentEditView;
+ QScrollBar* m_mainHorizontalScrollBar;
+ RulerScale* m_rulerScale;
+ EventSelection* m_eventSelection;
+ Segment* m_segment;
+
+ ControlItem* m_currentItem;
+ QCanvasItemList m_selectedItems;
+
+ ControlTool *m_tool;
+
+ int m_maxItemValue;
+
+ double m_staffOffset;
+
+ double m_currentX;
+
+ QPoint m_lastEventPos;
+ bool m_itemMoved;
+
+ bool m_selecting;
+ ControlSelector* m_selector;
+ QCanvasRectangle* m_selectionRect;
+
+ QString m_menuName;
+ QPopupMenu* m_menu;
+
+ TextFloat *m_numberFloat;
+
+ bool m_hposUpdatePending;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/ControlRulerEventEraseCommand.cpp b/src/gui/rulers/ControlRulerEventEraseCommand.cpp
new file mode 100644
index 0000000..7a1e493
--- /dev/null
+++ b/src/gui/rulers/ControlRulerEventEraseCommand.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ControlRulerEventEraseCommand.h"
+#include "ControlItem.h"
+#include "ElementAdapter.h"
+#include "misc/Debug.h"
+#include <klocale.h>
+
+namespace Rosegarden
+{
+
+ControlRulerEventEraseCommand::ControlRulerEventEraseCommand(QCanvasItemList selectedItems,
+ Segment &segment,
+ Rosegarden::timeT start, Rosegarden::timeT end)
+ : BasicCommand(i18n("Erase Controller Event(s)"),
+ segment,
+ start,
+ (start == end) ? start + 10 : end,
+ true),
+ m_selectedItems(selectedItems)
+{
+ RG_DEBUG << "ControllerEventEraseCommand : from " << start << " to " << end << endl;
+}
+
+
+void ControlRulerEventEraseCommand::modifySegment()
+{
+ Segment &segment(getSegment());
+
+ for (QCanvasItemList::Iterator it=m_selectedItems.begin(); it!=m_selectedItems.end(); ++it) {
+ if (ControlItem *item = dynamic_cast<ControlItem*>(*it))
+ segment.eraseSingle(item->getElementAdapter()->getEvent());
+ }
+}
+
+}
diff --git a/src/gui/rulers/ControlRulerEventEraseCommand.h b/src/gui/rulers/ControlRulerEventEraseCommand.h
new file mode 100644
index 0000000..15e213b
--- /dev/null
+++ b/src/gui/rulers/ControlRulerEventEraseCommand.h
@@ -0,0 +1,54 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_CONTROLLEREVENTERASECOMMAND_H_
+#define _RG_CONTROLLEREVENTERASECOMMAND_H_
+
+#include "base/Event.h"
+#include "document/BasicCommand.h"
+#include <qcanvas.h>
+
+namespace Rosegarden
+{
+
+class ControlRulerEventEraseCommand : public BasicCommand
+{
+public:
+
+ ControlRulerEventEraseCommand(QCanvasItemList selectedItems,
+ Segment &segment,
+ Rosegarden::timeT start, Rosegarden::timeT end);
+ virtual ~ControlRulerEventEraseCommand() {;}
+
+
+protected:
+
+ virtual void modifySegment();
+
+ QCanvasItemList m_selectedItems;
+};
+
+}
+
+#endif /*CONTROLLEREVENTERASECOMMAND_H_*/
diff --git a/src/gui/rulers/ControlRulerEventInsertCommand.cpp b/src/gui/rulers/ControlRulerEventInsertCommand.cpp
new file mode 100644
index 0000000..ab6a73c
--- /dev/null
+++ b/src/gui/rulers/ControlRulerEventInsertCommand.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ControlRulerEventInsertCommand.h"
+#include "base/MidiTypes.h"
+#include <klocale.h>
+
+namespace Rosegarden
+{
+
+ControlRulerEventInsertCommand::ControlRulerEventInsertCommand(const std::string &type,
+ timeT insertTime,
+ long number, long initialValue,
+ Segment &segment)
+ : BasicCommand(i18n("Insert Controller Event"),
+ segment,
+ insertTime,
+ (insertTime + Rosegarden::Note(Rosegarden::Note::Quaver).getDuration())), // must have a duration other undo doesn't work
+ m_type(type),
+ m_number(number),
+ m_initialValue(initialValue)
+{
+}
+
+void ControlRulerEventInsertCommand::modifySegment()
+{
+ Event* controllerEvent = new Event(m_type, getStartTime());
+
+ if (m_type == Rosegarden::Controller::EventType)
+ {
+ controllerEvent->set<Rosegarden::Int>(Rosegarden::Controller::VALUE, m_initialValue);
+ controllerEvent->set<Rosegarden::Int>(Rosegarden::Controller::NUMBER, m_number);
+ }
+ else if (m_type == Rosegarden::PitchBend::EventType)
+ {
+ // Convert to PitchBend MSB/LSB
+ int lsb = m_initialValue & 0x7f;
+ int msb = (m_initialValue >> 7) & 0x7f;
+ controllerEvent->set<Rosegarden::Int>(Rosegarden::PitchBend::MSB, msb);
+ controllerEvent->set<Rosegarden::Int>(Rosegarden::PitchBend::LSB, lsb);
+ }
+
+ getSegment().insert(controllerEvent);
+}
+
+}
diff --git a/src/gui/rulers/ControlRulerEventInsertCommand.h b/src/gui/rulers/ControlRulerEventInsertCommand.h
new file mode 100644
index 0000000..a180006
--- /dev/null
+++ b/src/gui/rulers/ControlRulerEventInsertCommand.h
@@ -0,0 +1,56 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_CONTROLRULEREVENTINSERTCOMMAND_H_
+#define _RG_CONTROLRULEREVENTINSERTCOMMAND_H_
+
+#include "document/BasicCommand.h"
+#include <string>
+
+namespace Rosegarden
+{
+
+class ControlRulerEventInsertCommand : public BasicCommand
+{
+public:
+ ControlRulerEventInsertCommand(const std::string &type,
+ timeT insertTime,
+ long number,
+ long initialValue,
+ Segment &segment);
+
+ virtual ~ControlRulerEventInsertCommand() {;}
+
+protected:
+
+ virtual void modifySegment();
+
+ std::string m_type;
+ long m_number;
+ long m_initialValue;
+};
+
+}
+
+#endif /*CONTROLRULEREVENTINSERTCOMMAND_H_*/
diff --git a/src/gui/rulers/ControlSelector.cpp b/src/gui/rulers/ControlSelector.cpp
new file mode 100644
index 0000000..fb4abbb
--- /dev/null
+++ b/src/gui/rulers/ControlSelector.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_CONTROLSELECTOR_CPP_
+#define _RG_CONTROLSELECTOR_CPP_
+
+#include "ControlSelector.h"
+
+namespace Rosegarden {
+
+ControlSelector::ControlSelector(ControlRuler* parent)
+ : QObject(parent),
+ m_ruler(parent)
+{
+}
+
+void ControlSelector::handleMouseButtonPress(QMouseEvent *e)
+{
+ QPoint p = m_ruler->inverseMapPoint(e->pos());
+
+ getSelectionRectangle()->setX(p.x());
+ getSelectionRectangle()->setY(p.y());
+ getSelectionRectangle()->setSize(0,0);
+
+ getSelectionRectangle()->show();
+ m_ruler->canvas()->update();
+}
+
+void ControlSelector::handleMouseButtonRelease(QMouseEvent*)
+{
+ getSelectionRectangle()->hide();
+ m_ruler->canvas()->update();
+}
+
+void ControlSelector::handleMouseMove(QMouseEvent *e, int, int)
+{
+ QPoint p = m_ruler->inverseMapPoint(e->pos());
+
+ int w = int(p.x() - getSelectionRectangle()->x());
+ int h = int(p.y() - getSelectionRectangle()->y());
+ if (w > 0) ++w; else --w;
+ if (h > 0) ++h; else --h;
+
+ getSelectionRectangle()->setSize(w, h);
+
+ m_ruler->canvas()->update();
+}
+
+}
+
+#endif /*CONTROLSELECTOR_CPP_*/
diff --git a/src/gui/rulers/ControlSelector.h b/src/gui/rulers/ControlSelector.h
new file mode 100644
index 0000000..99ec773
--- /dev/null
+++ b/src/gui/rulers/ControlSelector.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_CONTROLSELECTOR_H_
+#define _RG_CONTROLSELECTOR_H_
+
+#include "ControlRuler.h"
+
+class QCanvasRectangle;
+
+namespace Rosegarden {
+
+
+/**
+ * Selector tool for the ControlRuler
+ *
+ * Allow the user to select several ControlItems so he can change them
+ * all at the same time
+ */
+class ControlSelector : public QObject
+{
+public:
+ ControlSelector(ControlRuler* parent);
+ virtual ~ControlSelector() {};
+
+ virtual void handleMouseButtonPress(QMouseEvent *e);
+ virtual void handleMouseButtonRelease(QMouseEvent *e);
+ virtual void handleMouseMove(QMouseEvent *e, int deltaX, int deltaY);
+
+ QCanvasRectangle* getSelectionRectangle() { return m_ruler->getSelectionRectangle(); }
+protected:
+ //--------------- Data members ---------------------------------
+
+ ControlRuler* m_ruler;
+};
+
+}
+
+#endif /*CONTROLSELECTOR_H_*/
diff --git a/src/gui/rulers/ControlTool.h b/src/gui/rulers/ControlTool.h
new file mode 100644
index 0000000..fcc528a
--- /dev/null
+++ b/src/gui/rulers/ControlTool.h
@@ -0,0 +1,39 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 CONTROLTOOL_H_
+#define CONTROLTOOL_H_
+
+namespace Rosegarden {
+
+class ControlTool
+{
+public:
+ virtual ~ControlTool() {};
+ virtual int operator()(double x, int val) = 0;
+};
+
+}
+
+#endif /*CONTROLTOOL_H_*/
diff --git a/src/gui/rulers/ControllerEventAdapter.cpp b/src/gui/rulers/ControllerEventAdapter.cpp
new file mode 100644
index 0000000..55abd50
--- /dev/null
+++ b/src/gui/rulers/ControllerEventAdapter.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ControllerEventAdapter.h"
+#include "base/MidiTypes.h"
+#include "misc/Debug.h"
+
+namespace Rosegarden {
+
+bool ControllerEventAdapter::getValue(long& val)
+{
+ if (m_event->getType() == Rosegarden::Controller::EventType)
+ {
+ return m_event->get<Rosegarden::Int>(Rosegarden::Controller::VALUE, val);
+ }
+ else if (m_event->getType() == Rosegarden::PitchBend::EventType)
+ {
+ long msb = 0, lsb = 0;
+ m_event->get<Rosegarden::Int>(Rosegarden::PitchBend::MSB, msb);
+ m_event->get<Rosegarden::Int>(Rosegarden::PitchBend::LSB, lsb);
+
+ long value = msb;
+ value <<= 7;
+ value |= lsb;
+
+ //RG_DEBUG << "PitchBend Get Value = " << value << endl;
+
+ val = value;
+ return true;
+ }
+
+ return false;
+}
+
+void ControllerEventAdapter::setValue(long val)
+{
+ if (m_event->getType() == Rosegarden::Controller::EventType)
+ {
+ m_event->set<Rosegarden::Int>(Rosegarden::Controller::VALUE, val);
+ }
+ else if (m_event->getType() == Rosegarden::PitchBend::EventType)
+ {
+ RG_DEBUG << "PitchBend Set Value = " << val << endl;
+
+ int lsb = val & 0x7f;
+ int msb = (val >> 7) & 0x7f;
+ m_event->set<Rosegarden::Int>(Rosegarden::PitchBend::MSB, msb);
+ m_event->set<Rosegarden::Int>(Rosegarden::PitchBend::LSB, lsb);
+ }
+}
+
+timeT ControllerEventAdapter::getTime()
+{
+ return m_event->getAbsoluteTime();
+}
+
+timeT ControllerEventAdapter::getDuration()
+{
+ return m_event->getDuration();
+}
+
+}
diff --git a/src/gui/rulers/ControllerEventAdapter.h b/src/gui/rulers/ControllerEventAdapter.h
new file mode 100644
index 0000000..4f71884
--- /dev/null
+++ b/src/gui/rulers/ControllerEventAdapter.h
@@ -0,0 +1,53 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_CONTROLLEREVENTADAPTER_H_
+#define _RG_CONTROLLEREVENTADAPTER_H_
+
+#include "ElementAdapter.h"
+
+namespace Rosegarden {
+
+class ControllerEventAdapter : public Rosegarden::ElementAdapter
+{
+public:
+ ControllerEventAdapter(Event* e) : m_event(e) {}
+
+ virtual bool getValue(long&);
+ virtual void setValue(long);
+ virtual timeT getTime();
+ virtual timeT getDuration();
+
+ virtual Event* getEvent() { return m_event; }
+
+protected:
+
+ //--------------- Data members ---------------------------------
+
+ Event* m_event;
+};
+
+}
+
+#endif /*CONTROLLEREVENTADAPTER_H_*/
diff --git a/src/gui/rulers/ControllerEventsRuler.cpp b/src/gui/rulers/ControllerEventsRuler.cpp
new file mode 100644
index 0000000..265a701
--- /dev/null
+++ b/src/gui/rulers/ControllerEventsRuler.cpp
@@ -0,0 +1,499 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ControllerEventsRuler.h"
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/ControlParameter.h"
+#include "base/Event.h"
+#include "base/MidiTypes.h"
+#include "base/NotationTypes.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "commands/edit/EraseCommand.h"
+#include "ControlRuler.h"
+#include "ControlItem.h"
+#include "ControllerEventAdapter.h"
+#include "ControlRulerEventInsertCommand.h"
+#include "ControlRulerEventEraseCommand.h"
+#include "gui/general/EditViewBase.h"
+#include "gui/widgets/TextFloat.h"
+#include <klineeditdlg.h>
+#include <qcanvas.h>
+#include <qcolor.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include <qvalidator.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+ControllerEventsRuler::ControllerEventsRuler(Segment *segment,
+ RulerScale* rulerScale,
+ EditViewBase* parentView,
+ QCanvas* c,
+ QWidget* parent,
+ const ControlParameter *controller,
+ const char* name, WFlags f)
+ : ControlRuler(segment, rulerScale, parentView, c, parent, name, f),
+ m_defaultItemWidth(20),
+ m_controlLine(new QCanvasLine(canvas())),
+ m_controlLineShowing(false),
+ m_controlLineX(0),
+ m_controlLineY(0)
+{
+ // Make a copy of the ControlParameter if we have one
+ //
+ if (controller)
+ m_controller = new ControlParameter(*controller);
+ else
+ m_controller = 0;
+
+ setMenuName("controller_events_ruler_menu");
+ drawBackground();
+ init();
+}
+
+void
+ControllerEventsRuler::setSegment(Segment *segment)
+{
+ RG_DEBUG << "ControllerEventsRuler::setSegment(" << segment << ")" << endl;
+
+ m_segment->removeObserver(this);
+ m_segment = segment;
+ m_segment->addObserver(this);
+
+ while (child(NULL))
+ delete (child(NULL));
+
+ drawBackground();
+ init();
+}
+
+void
+ControllerEventsRuler::init()
+{
+ // Reset range information for this controller type (for the moment
+ // this assumes min is always 0.
+ //
+ setMaxItemValue(m_controller->getMax());
+
+ for (Segment::iterator i = m_segment->begin();
+ i != m_segment->end(); ++i) {
+
+ // skip if not the same type of event that we're expecting
+ //
+ if (m_controller->getType() != (*i)->getType())
+ continue;
+
+ int width = getDefaultItemWidth();
+
+ // Check for specific controller value if we need to
+ //
+ if (m_controller->getType() == Controller::EventType) {
+ try {
+ if ((*i)->get
+ <Int>(Controller::NUMBER)
+ != m_controller->getControllerValue())
+ continue;
+ } catch (...) {
+ continue;
+ }
+ } else if (m_controller->getType() == PitchBend::EventType)
+ width /= 4;
+
+ //RG_DEBUG << "ControllerEventsRuler: adding element\n";
+
+ double x = m_rulerScale->getXForTime((*i)->getAbsoluteTime());
+ new ControlItem(this, new ControllerEventAdapter(*i),
+ int(x + m_staffOffset), width);
+ }
+}
+
+void
+ControllerEventsRuler::drawBackground()
+{
+ // Draw some minimum and maximum controller value guide lines
+ //
+ QCanvasLine *topLine = new QCanvasLine(canvas());
+ QCanvasLine *topQLine = new QCanvasLine(canvas());
+ QCanvasLine *midLine = new QCanvasLine(canvas());
+ QCanvasLine *botQLine = new QCanvasLine(canvas());
+ QCanvasLine *bottomLine = new QCanvasLine(canvas());
+ //m_controlLine->setPoints(m_controlLineX, m_controlLineY, m_controlLineX, m_controlLineY);
+ int cHeight = canvas()->height();
+ int cWidth = canvas()->width();
+
+ topLine->setPen(QColor(127, 127, 127));
+ topLine->setPoints(0, 0, cWidth, 0);
+ topLine->setZ( -10);
+ topLine->show();
+
+ topQLine->setPen(QColor(192, 192, 192));
+ topQLine->setPoints(0, cHeight / 4, cWidth, cHeight / 4);
+ topQLine->setZ( -10);
+ topQLine->show();
+
+ midLine->setPen(QColor(127, 127, 127));
+ midLine->setPoints(0, cHeight / 2, cWidth, cHeight / 2);
+ midLine->setZ( -10);
+ midLine->show();
+
+ botQLine->setPen(QColor(192, 192, 192));
+ botQLine->setPoints(0, 3*cHeight / 4, cWidth, 3*cHeight / 4);
+ botQLine->setZ( -10);
+ botQLine->show();
+
+ bottomLine->setPen(QColor(127, 127, 127));
+ bottomLine->setPoints(0, cHeight - 1, cWidth, cHeight - 1);
+ bottomLine->setZ( -10);
+ bottomLine->show();
+
+ canvas()->update();
+}
+
+ControllerEventsRuler::~ControllerEventsRuler()
+{}
+
+QString ControllerEventsRuler::getName()
+{
+ if (m_controller) {
+ QString name = i18n("Unsupported Event Type");
+
+ if (m_controller->getType() == Controller::EventType) {
+ QString hexValue;
+ hexValue.sprintf("0x%x", m_controller->getControllerValue());
+
+ name = QString("%1 (%2 / %3)").arg(strtoqstr(m_controller->getName()))
+ .arg(int(m_controller->getControllerValue()))
+ .arg(hexValue);
+ } else if (m_controller->getType() == PitchBend::EventType) {
+ name = i18n("Pitch Bend");
+ }
+
+ return name;
+ } else
+ return i18n("Controller Events");
+}
+
+void ControllerEventsRuler::eventAdded(const Segment*, Event *e)
+{
+ if (e->getType() != m_controller->getType())
+ return ;
+
+ // Check for specific controller value if we need to
+ //
+ if (e->getType() == Controller::EventType) {
+ try {
+ if (e->get
+ <Int>(Controller::NUMBER) !=
+ m_controller->getControllerValue())
+ return ;
+ } catch (...) {
+ return ;
+ }
+ }
+
+ RG_DEBUG << "ControllerEventsRuler::elementAdded()\n";
+
+ double x = m_rulerScale->getXForTime(e->getAbsoluteTime());
+
+ int width = getDefaultItemWidth();
+
+ if (m_controller->getType() == PitchBend::EventType)
+ width /= 4;
+
+ new ControlItem(this, new ControllerEventAdapter(e), int(x + m_staffOffset), width);
+}
+
+void ControllerEventsRuler::eventRemoved(const Segment*, Event *e)
+{
+ if (e->getType() != m_controller->getType())
+ return ;
+
+ clearSelectedItems();
+
+ QCanvasItemList allItems = canvas()->allItems();
+
+ for (QCanvasItemList::Iterator it = allItems.begin(); it != allItems.end(); ++it) {
+ if (ControlItem *item = dynamic_cast<ControlItem*>(*it)) {
+ ControllerEventAdapter * adapter = dynamic_cast<ControllerEventAdapter*>(item->getElementAdapter());
+ if (adapter->getEvent() == e) {
+ delete item;
+ break;
+ }
+ }
+ }
+}
+
+void ControllerEventsRuler::insertControllerEvent()
+{
+ timeT insertTime = m_rulerScale->getTimeForX(m_lastEventPos.x());
+
+
+ // compute initial value from cursor height
+ //
+ long initialValue = heightToValue(m_lastEventPos.y() - canvas()->height());
+
+ RG_DEBUG << "ControllerEventsRuler::insertControllerEvent() : inserting event at "
+ << insertTime
+ << " - initial value = " << initialValue
+ << endl;
+
+ // ask controller number to user
+ long number = 0;
+
+ if (m_controller) {
+ number = m_controller->getControllerValue();
+ } else {
+ bool ok = false;
+ QIntValidator intValidator(0, 128, this);
+ QString res = KLineEditDlg::getText(i18n("Controller Event Number"), "0",
+ &ok, this, &intValidator);
+ if (ok)
+ number = res.toULong();
+ }
+
+ ControlRulerEventInsertCommand* command =
+ new ControlRulerEventInsertCommand(m_controller->getType(),
+ insertTime, number,
+ initialValue, *m_segment);
+
+ m_parentEditView->addCommandToHistory(command);
+}
+
+void ControllerEventsRuler::eraseControllerEvent()
+{
+ RG_DEBUG << "ControllerEventsRuler::eraseControllerEvent() : deleting selected events\n";
+
+ ControlRulerEventEraseCommand* command =
+ new ControlRulerEventEraseCommand(m_selectedItems,
+ *m_segment,
+ m_eventSelection->getStartTime(),
+ m_eventSelection->getEndTime());
+ m_parentEditView->addCommandToHistory(command);
+ updateSelection();
+}
+
+void ControllerEventsRuler::clearControllerEvents()
+{
+ EventSelection *es = new EventSelection(*m_segment);
+
+ for (Segment::iterator it = m_segment->begin(); it != m_segment->end(); ++it) {
+ if (!(*it)->isa(Controller::EventType))
+ continue;
+ {
+ if (m_controller) // ensure we have only the controller events we want for this ruler
+ {
+ try
+ {
+ if ((*it)->get
+ <Int>(Controller::NUMBER)
+ != m_controller->getControllerValue())
+ continue;
+ } catch (...)
+ {
+ continue;
+ }
+
+ es->addEvent(*it);
+ }
+ }
+ }
+
+ EraseCommand *command = new EraseCommand(*es);
+ m_parentEditView->addCommandToHistory(command);
+
+}
+
+void ControllerEventsRuler::startControlLine()
+{
+ m_controlLineShowing = true;
+ this->setCursor(Qt::pointingHandCursor);
+}
+
+void ControllerEventsRuler::contentsMousePressEvent(QMouseEvent *e)
+{
+ if (!m_controlLineShowing) {
+ if (e->button() == MidButton)
+ m_lastEventPos = inverseMapPoint(e->pos());
+
+ ControlRuler::contentsMousePressEvent(e); // send super
+
+ return ;
+ }
+
+ // cancel control line mode
+ if (e->button() == RightButton) {
+ m_controlLineShowing = false;
+ m_controlLine->hide();
+
+ this->setCursor(Qt::arrowCursor);
+ return ;
+ }
+
+ if (e->button() == LeftButton) {
+ QPoint p = inverseMapPoint(e->pos());
+
+ m_controlLine->show();
+ m_controlLineX = p.x();
+ m_controlLineY = p.y();
+ m_controlLine->setPoints(m_controlLineX, m_controlLineY, m_controlLineX, m_controlLineY);
+ canvas()->update();
+ }
+}
+
+void ControllerEventsRuler::contentsMouseReleaseEvent(QMouseEvent *e)
+{
+ if (!m_controlLineShowing) {
+ if (e->button() == MidButton)
+ insertControllerEvent();
+
+ ControlRuler::contentsMouseReleaseEvent(e); // send super
+
+ return ;
+ } else {
+ QPoint p = inverseMapPoint(e->pos());
+
+ timeT startTime = m_rulerScale->getTimeForX(m_controlLineX);
+ timeT endTime = m_rulerScale->getTimeForX(p.x());
+
+ long startValue = heightToValue(m_controlLineY - canvas()->height());
+ long endValue = heightToValue(p.y() - canvas()->height());
+
+ RG_DEBUG << "ControllerEventsRuler::contentsMouseReleaseEvent - "
+ << "starttime = " << startTime
+ << ", endtime = " << endTime
+ << ", startValue = " << startValue
+ << ", endValue = " << endValue
+ << endl;
+
+ drawControlLine(startTime, endTime, startValue, endValue);
+
+ m_controlLineShowing = false;
+ m_controlLine->hide();
+ this->setCursor(Qt::arrowCursor);
+ canvas()->update();
+ }
+}
+
+void ControllerEventsRuler::contentsMouseMoveEvent(QMouseEvent *e)
+{
+ if (!m_controlLineShowing) {
+ // Don't send super if we're using the middle button
+ //
+ if (e->button() == MidButton) {
+ m_lastEventPos = inverseMapPoint(e->pos());
+ return ;
+ }
+
+ ControlRuler::contentsMouseMoveEvent(e); // send super
+ return ;
+ }
+
+ QPoint p = inverseMapPoint(e->pos());
+
+ m_controlLine->setPoints(m_controlLineX, m_controlLineY, p.x(), p.y());
+ canvas()->update();
+
+}
+
+void ControllerEventsRuler::layoutItem(ControlItem* item)
+{
+ timeT itemTime = item->getElementAdapter()->getTime();
+
+ double x = m_rulerScale->getXForTime(itemTime) + m_staffOffset;
+
+ item->setX(x);
+
+ int width = getDefaultItemWidth(); // TODO: how to scale that ??
+
+ if (m_controller->getType() == PitchBend::EventType)
+ width /= 4;
+
+ item->setWidth(width);
+
+ //RG_DEBUG << "ControllerEventsRuler::layoutItem ControlItem x = " << x
+ //<< " - width = " << width << endl;
+}
+
+void
+ControllerEventsRuler::drawControlLine(timeT startTime,
+ timeT endTime,
+ int startValue,
+ int endValue)
+{
+ if (m_controller == 0)
+ return ;
+ if (startTime > endTime) {
+ std::swap(startTime, endTime);
+ std::swap(startValue, endValue);
+ }
+
+ timeT quantDur = Note(Note::Quaver).getDuration();
+
+ // If inserting a line of PitchBends then we want a smoother curve
+ //
+ if (m_controller->getType() == PitchBend::EventType)
+ quantDur = Note(Note::Demisemiquaver).getDuration();
+
+ // for the moment enter a quantized set of events
+ timeT time = startTime, newTime = 0;
+ double step = double(endValue - startValue) / double(endTime - startTime);
+
+ KMacroCommand *macro = new KMacroCommand(i18n("Add line of controllers"));
+
+ while (time < endTime) {
+ int value = startValue + int(step * double(time - startTime));
+
+ // hit the buffers
+ if (value < m_controller->getMin())
+ value = m_controller->getMin();
+ else if (value > m_controller->getMax())
+ value = m_controller->getMax();
+
+ ControlRulerEventInsertCommand* command =
+ new ControlRulerEventInsertCommand(m_controller->getType(),
+ time, m_controller->getControllerValue(), value, *m_segment);
+
+ macro->addCommand(command);
+
+ // get new time - do it by quantized distances
+ newTime = (time / quantDur) * quantDur;
+ if (newTime > time)
+ time = newTime;
+ else
+ time += quantDur;
+ }
+
+ m_parentEditView->addCommandToHistory(macro);
+}
+
+}
diff --git a/src/gui/rulers/ControllerEventsRuler.h b/src/gui/rulers/ControllerEventsRuler.h
new file mode 100644
index 0000000..2b42274
--- /dev/null
+++ b/src/gui/rulers/ControllerEventsRuler.h
@@ -0,0 +1,118 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_CONTROLLEREVENTSRULER_H_
+#define _RG_CONTROLLEREVENTSRULER_H_
+
+#include "ControlRuler.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QWidget;
+class QMouseEvent;
+class QCanvasLine;
+class QCanvas;
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class RulerScale;
+class Event;
+class EditViewBase;
+class ControlParameter;
+class ControlItem;
+
+
+/**
+ * ControllerEventsRuler : edit Controller events
+ */
+class ControllerEventsRuler : public ControlRuler
+{
+public:
+ ControllerEventsRuler(Segment*,
+ RulerScale*,
+ EditViewBase* parentView,
+ QCanvas*,
+ QWidget* parent=0,
+ const ControlParameter *controller = 0,
+ const char* name=0, WFlags f=0);
+
+ virtual ~ControllerEventsRuler();
+
+ virtual QString getName();
+ int getDefaultItemWidth() { return m_defaultItemWidth; }
+
+ // Allow something external to reset the selection of Events
+ // that this ruler is displaying
+ //
+ void setSegment(Segment *);
+
+ /// SegmentObserver interface
+ virtual void eventAdded(const Segment *, Event *);
+ virtual void eventRemoved(const Segment *, Event *);
+
+ virtual void insertControllerEvent();
+ virtual void eraseControllerEvent();
+ virtual void clearControllerEvents();
+ virtual void startControlLine();
+
+ ControlParameter* getControlParameter() { return m_controller; }
+
+protected:
+
+ virtual void init();
+ virtual void drawBackground();
+
+ // Let's override these again here
+ //
+ virtual void contentsMousePressEvent(QMouseEvent*);
+ virtual void contentsMouseReleaseEvent(QMouseEvent*);
+ virtual void contentsMouseMoveEvent(QMouseEvent*);
+
+ virtual void layoutItem(ControlItem*);
+
+ void drawControlLine(timeT startTime,
+ timeT endTime,
+ int startValue,
+ int endValue);
+
+ //--------------- Data members ---------------------------------
+ int m_defaultItemWidth;
+
+ ControlParameter *m_controller;
+ QCanvasLine *m_controlLine;
+
+ bool m_controlLineShowing;
+ int m_controlLineX;
+ int m_controlLineY;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/DefaultVelocityColour.cpp b/src/gui/rulers/DefaultVelocityColour.cpp
new file mode 100644
index 0000000..21cf75e
--- /dev/null
+++ b/src/gui/rulers/DefaultVelocityColour.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "DefaultVelocityColour.h"
+
+#include "gui/general/GUIPalette.h"
+#include <qcolor.h>
+#include "VelocityColour.h"
+
+
+namespace Rosegarden
+{
+
+DefaultVelocityColour::DefaultVelocityColour()
+ : VelocityColour(GUIPalette::getColour(GUIPalette::LevelMeterRed),
+ GUIPalette::getColour(GUIPalette::LevelMeterOrange),
+ GUIPalette::getColour(GUIPalette::LevelMeterGreen),
+ 127, // max knee
+ 115, // red knee
+ 75, // orange knee
+ 25) // green knee
+{}
+
+DefaultVelocityColour* DefaultVelocityColour::getInstance()
+{
+ if (!m_instance) m_instance = new DefaultVelocityColour;
+
+ return m_instance;
+}
+
+DefaultVelocityColour* DefaultVelocityColour::m_instance = 0;
+
+}
diff --git a/src/gui/rulers/DefaultVelocityColour.h b/src/gui/rulers/DefaultVelocityColour.h
new file mode 100644
index 0000000..09430f3
--- /dev/null
+++ b/src/gui/rulers/DefaultVelocityColour.h
@@ -0,0 +1,54 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_DEFAULTVELOCITYCOLOUR_H_
+#define _RG_DEFAULTVELOCITYCOLOUR_H_
+
+#include "VelocityColour.h"
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+class DefaultVelocityColour : public VelocityColour
+{
+public:
+ static DefaultVelocityColour* getInstance();
+
+protected:
+ DefaultVelocityColour();
+
+ static DefaultVelocityColour* m_instance;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/ElementAdapter.h b/src/gui/rulers/ElementAdapter.h
new file mode 100644
index 0000000..e14ee63
--- /dev/null
+++ b/src/gui/rulers/ElementAdapter.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ELEMENTADAPTER_H_
+#define _RG_ELEMENTADAPTER_H_
+
+#include "base/Event.h"
+
+namespace Rosegarden {
+
+class ElementAdapter
+{
+public:
+ virtual ~ElementAdapter() {};
+
+ virtual bool getValue(long&) = 0;
+ virtual void setValue(long) = 0;
+ virtual timeT getTime() = 0;
+ virtual timeT getDuration() = 0;
+ virtual Event* getEvent() = 0;
+};
+
+}
+
+#endif /*ELEMENTADAPTER_H_*/
diff --git a/src/gui/rulers/LoopRuler.cpp b/src/gui/rulers/LoopRuler.cpp
new file mode 100644
index 0000000..bdf6c5e
--- /dev/null
+++ b/src/gui/rulers/LoopRuler.cpp
@@ -0,0 +1,363 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "LoopRuler.h"
+
+#include "misc/Debug.h"
+#include "base/RulerScale.h"
+#include "base/SnapGrid.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/HZoomable.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include <qpainter.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qwidget.h>
+#include <qtooltip.h>
+#include <klocale.h>
+#include <kaction.h>
+#include <qpainter.h>
+#include <qpointarray.h>
+#include "document/RosegardenGUIDoc.h"
+
+
+namespace Rosegarden
+{
+
+LoopRuler::LoopRuler(RosegardenGUIDoc *doc,
+ RulerScale *rulerScale,
+ int height,
+ double xorigin,
+ bool invert,
+ QWidget *parent,
+ const char *name) :
+ QWidget(parent, name),
+ m_doc(doc),
+ m_height(height),
+ m_xorigin(xorigin),
+ m_invert(invert),
+ m_currentXOffset(0),
+ m_width( -1),
+ m_activeMousePress(false),
+ m_rulerScale(rulerScale),
+ m_defaultGrid(rulerScale),
+ m_loopGrid(rulerScale),
+ m_grid(&m_defaultGrid),
+ m_loopingMode(false),
+ m_startLoop(0), m_endLoop(0),
+ m_quickMarkerPen(QPen(GUIPalette::getColour(GUIPalette::QuickMarker), 4))
+{
+ /*
+ * I need to understand if this ruler is being built for the main
+ * window (Track Editor) or for any of the segment editors. Apparently
+ * the name parameter is supplied (non-NULL) only for the main window.
+ * I can't find of any other (more decent) way to do this. Sorry.
+ */
+ m_mainWindow = (name != 0 && std::string(name).length() != 0);
+
+ setBackgroundColor(GUIPalette::getColour(GUIPalette::LoopRulerBackground));
+
+ // Always snap loop extents to beats; by default apply no snap to
+ // pointer position
+ //
+ m_defaultGrid.setSnapTime(SnapGrid::NoSnap);
+ m_loopGrid.setSnapTime(SnapGrid::SnapToBeat);
+
+ QToolTip::add
+ (this, i18n("Click and drag to move the playback pointer.\nShift-click and drag to set a range for looping or editing.\nShift-click to clear the loop or range.\nDouble-click to start playback."));
+}
+
+LoopRuler::~LoopRuler()
+{}
+
+void
+LoopRuler::setSnapGrid(SnapGrid *grid)
+{
+ if (grid == 0) m_grid = &m_defaultGrid;
+ else m_grid = grid;
+}
+
+void LoopRuler::scrollHoriz(int x)
+{
+ if (getHScaleFactor() != 1.0) {
+ m_currentXOffset = static_cast<int>( -x / getHScaleFactor());
+ repaint();
+ return;
+ }
+
+ int w = width(), h = height();
+ int dx = x - ( -m_currentXOffset);
+ m_currentXOffset = -x;
+
+ if (dx > w*3 / 4 || dx < -w*3 / 4) {
+ update();
+ return ;
+ }
+
+ if (dx > 0) { // moving right, so the existing stuff moves left
+ bitBlt(this, 0, 0, this, dx, 0, w - dx, h);
+ repaint(w - dx, 0, dx, h);
+ } else { // moving left, so the existing stuff moves right
+ bitBlt(this, -dx, 0, this, 0, 0, w + dx, h);
+ repaint(0, 0, -dx, h);
+ }
+}
+
+QSize LoopRuler::sizeHint() const
+{
+ double width =
+ m_rulerScale->getBarPosition(m_rulerScale->getLastVisibleBar()) +
+ m_rulerScale->getBarWidth(m_rulerScale->getLastVisibleBar()) +
+ m_xorigin;
+
+ QSize res(std::max(int(width), m_width), m_height);
+
+ return res;
+}
+
+QSize LoopRuler::minimumSizeHint() const
+{
+ double firstBarWidth = m_rulerScale->getBarWidth(0) + m_xorigin;
+
+ QSize res = QSize(int(firstBarWidth), m_height);
+
+ return res;
+}
+
+void LoopRuler::paintEvent(QPaintEvent* e)
+{
+ QPainter paint(this);
+
+ if (getHScaleFactor() != 1.0)
+ paint.scale(getHScaleFactor(), 1.0);
+
+ paint.setClipRegion(e->region());
+ paint.setClipRect(e->rect().normalize());
+
+ paint.setBrush(colorGroup().foreground());
+ drawBarSections(&paint);
+ drawLoopMarker(&paint);
+
+ if (m_mainWindow) {
+ timeT tQM = m_doc->getQuickMarkerTime();
+ if (tQM >= 0) {
+ // draw quick marker
+ double xQM = m_rulerScale->getXForTime(tQM)
+ + m_xorigin + m_currentXOffset;
+
+ paint.setPen(m_quickMarkerPen);
+
+ // looks necessary to compensate for shift in the CompositionView (cursor)
+ paint.translate(1, 0);
+
+ // draw red segment
+ paint.drawLine(int(xQM), 1, int(xQM), m_height-1);
+ }
+ }
+}
+
+void LoopRuler::drawBarSections(QPainter* paint)
+{
+ QRect clipRect = paint->clipRegion().boundingRect();
+
+ int firstBar = m_rulerScale->getBarForX(clipRect.x() -
+ m_currentXOffset -
+ m_xorigin);
+ int lastBar = m_rulerScale->getLastVisibleBar();
+ if (firstBar < m_rulerScale->getFirstVisibleBar()) {
+ firstBar = m_rulerScale->getFirstVisibleBar();
+ }
+
+ paint->setPen(GUIPalette::getColour(GUIPalette::LoopRulerForeground));
+
+ for (int i = firstBar; i <= lastBar; ++i) {
+
+ double x = m_rulerScale->getBarPosition(i) + m_currentXOffset + m_xorigin;
+ if ((x * getHScaleFactor()) > clipRect.x() + clipRect.width())
+ break;
+
+ double width = m_rulerScale->getBarWidth(i);
+ if (width == 0)
+ continue;
+
+ if (x + width < clipRect.x())
+ continue;
+
+ if (m_invert) {
+ paint->drawLine(int(x), 0, int(x), 5*m_height / 7);
+ } else {
+ paint->drawLine(int(x), 2*m_height / 7, int(x), m_height);
+ }
+
+ double beatAccumulator = m_rulerScale->getBeatWidth(i);
+ double inc = beatAccumulator;
+ if (inc == 0)
+ continue;
+
+ for (; beatAccumulator < width; beatAccumulator += inc) {
+ if (m_invert) {
+ paint->drawLine(int(x + beatAccumulator), 0,
+ int(x + beatAccumulator), 2 * m_height / 7);
+ } else {
+ paint->drawLine(int(x + beatAccumulator), 5*m_height / 7,
+ int(x + beatAccumulator), m_height);
+ }
+ }
+ }
+}
+
+void
+LoopRuler::drawLoopMarker(QPainter* paint)
+{
+ double x1 = (int)m_rulerScale->getXForTime(m_startLoop);
+ double x2 = (int)m_rulerScale->getXForTime(m_endLoop);
+
+ if (x1 > x2) {
+ x2 = x1;
+ x1 = (int)m_rulerScale->getXForTime(m_endLoop);
+ }
+
+ x1 += m_currentXOffset + m_xorigin;
+ x2 += m_currentXOffset + m_xorigin;
+
+ paint->save();
+ paint->setBrush(GUIPalette::getColour(GUIPalette::LoopHighlight));
+ paint->setPen(GUIPalette::getColour(GUIPalette::LoopHighlight));
+ paint->drawRect(static_cast<int>(x1), 0, static_cast<int>(x2 - x1), m_height);
+ paint->restore();
+
+}
+
+void
+LoopRuler::mousePressEvent(QMouseEvent *mE)
+{
+ RG_DEBUG << "LoopRuler::mousePressEvent: x = " << mE->x() << endl;
+
+ Qt::ButtonState bs = mE->state();
+ setLoopingMode((bs & Qt::ShiftButton) != 0);
+
+ if (mE->button() == LeftButton) {
+ double x = mE->pos().x() / getHScaleFactor() - m_currentXOffset - m_xorigin;
+
+ if (m_loopingMode) {
+ m_endLoop = m_startLoop = m_loopGrid.snapX(x);
+ } else {
+ // No -- now that we're emitting when the button is
+ // released, we _don't_ want to emit here as well --
+ // otherwise we get an irritating stutter when simply
+ // clicking on the ruler during playback
+// RG_DEBUG << "emitting setPointerPosition(" << m_rulerScale->getTimeForX(x) << ")" << endl;
+// emit setPointerPosition(m_rulerScale->getTimeForX(x));
+ }
+
+ m_activeMousePress = true;
+ emit startMouseMove(RosegardenCanvasView::FollowHorizontal);
+ }
+}
+
+void
+LoopRuler::mouseReleaseEvent(QMouseEvent *mE)
+{
+ if (mE->button() == LeftButton) {
+ if (m_loopingMode) {
+ // Cancel the loop if there was no drag
+ //
+ if (m_endLoop == m_startLoop) {
+ m_endLoop = m_startLoop = 0;
+
+ // to clear any other loop rulers
+ emit setLoop(m_startLoop, m_endLoop);
+ update();
+ }
+
+ // emit with the args around the right way
+ //
+ if (m_endLoop < m_startLoop)
+ emit setLoop(m_endLoop, m_startLoop);
+ else
+ emit setLoop(m_startLoop, m_endLoop);
+ } else {
+ // we need to re-emit this signal so that when the user releases the button
+ // after dragging the pointer, the pointer's position is updated again in the
+ // other views (typically, in the seg. canvas while the user has dragged the pointer
+ // in an edit view)
+ //
+ double x = mE->pos().x() / getHScaleFactor() - m_currentXOffset - m_xorigin;
+ emit setPointerPosition(m_grid->snapX(x));
+ }
+ emit stopMouseMove();
+ m_activeMousePress = false;
+ }
+}
+
+void
+LoopRuler::mouseDoubleClickEvent(QMouseEvent *mE)
+{
+ double x = mE->pos().x() / getHScaleFactor() - m_currentXOffset - m_xorigin;
+ if (x < 0)
+ x = 0;
+
+ RG_DEBUG << "LoopRuler::mouseDoubleClickEvent: x = " << x << ", looping = " << m_loopingMode << endl;
+
+ if (mE->button() == LeftButton && !m_loopingMode)
+ emit setPlayPosition(m_grid->snapX(x));
+}
+
+void
+LoopRuler::mouseMoveEvent(QMouseEvent *mE)
+{
+ double x = mE->pos().x() / getHScaleFactor() - m_currentXOffset - m_xorigin;
+ if (x < 0)
+ x = 0;
+
+ if (m_loopingMode) {
+ if (m_grid->snapX(x) != m_endLoop) {
+ m_endLoop = m_loopGrid.snapX(x);
+ emit dragLoopToPosition(m_endLoop);
+ update();
+ }
+ } else {
+ emit dragPointerToPosition(m_grid->snapX(x));
+ }
+
+ emit mouseMove();
+}
+
+void LoopRuler::slotSetLoopMarker(timeT startLoop,
+ timeT endLoop)
+{
+ m_startLoop = startLoop;
+ m_endLoop = endLoop;
+
+ QPainter paint(this);
+ paint.setBrush(colorGroup().foreground());
+ drawBarSections(&paint);
+ drawLoopMarker(&paint);
+
+ update();
+}
+
+}
+#include "LoopRuler.moc"
diff --git a/src/gui/rulers/LoopRuler.h b/src/gui/rulers/LoopRuler.h
new file mode 100644
index 0000000..7c30cc8
--- /dev/null
+++ b/src/gui/rulers/LoopRuler.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_LOOPRULER_H_
+#define _RG_LOOPRULER_H_
+
+#include "base/SnapGrid.h"
+#include "gui/general/HZoomable.h"
+#include <qsize.h>
+#include <qwidget.h>
+#include <qpen.h>
+#include "base/Event.h"
+
+
+class QPaintEvent;
+class QPainter;
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class RulerScale;
+class RosegardenGUIDoc;
+
+
+/**
+ * LoopRuler is a widget that shows bar and beat durations on a
+ * ruler-like scale, and reacts to mouse clicks by sending relevant
+ * signals to modify position pointer and playback/looping states.
+*/
+class LoopRuler : public QWidget, public HZoomable
+{
+ Q_OBJECT
+
+public:
+ LoopRuler(RosegardenGUIDoc *doc,
+ RulerScale *rulerScale,
+ int height = 0,
+ double xorigin = 0.0,
+ bool invert = false,
+ QWidget* parent = 0,
+ const char *name = 0);
+
+ ~LoopRuler();
+
+ void setSnapGrid(SnapGrid *grid);
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ void scrollHoriz(int x);
+
+ void setMinimumWidth(int width) { m_width = width; }
+
+ void setHorizScaleFactor(double dy) { m_hScaleFactor = dy; }
+
+ bool hasActiveMousePress() { return m_activeMousePress; }
+
+ bool getLoopingMode() { return m_loopingMode; }
+
+public slots:
+ void slotSetLoopMarker(timeT startLoop,
+ timeT endLoop);
+
+protected:
+ // ActiveItem interface
+ virtual void mousePressEvent (QMouseEvent*);
+ virtual void mouseReleaseEvent (QMouseEvent*);
+ virtual void mouseDoubleClickEvent (QMouseEvent*);
+ virtual void mouseMoveEvent (QMouseEvent*);
+
+ virtual void paintEvent(QPaintEvent*);
+
+ void setLoopingMode(bool value) { m_loopingMode = value; }
+ void drawBarSections(QPainter*);
+ void drawLoopMarker(QPainter*); // between loop positions
+
+signals:
+ // The three main functions that this class performs
+ //
+ /// Set the pointer position on mouse single click
+ void setPointerPosition(timeT);
+
+ /// Set the pointer position on mouse drag
+ void dragPointerToPosition(timeT);
+
+ /// Set pointer position and start playing on double click
+ void setPlayPosition(timeT);
+
+ /// Set a playing loop
+ void setLoop(timeT, timeT);
+
+ /// Set the loop end position on mouse drag
+ void dragLoopToPosition(timeT);
+
+ void startMouseMove(int directionConstraint);
+ void stopMouseMove();
+ void mouseMove();
+
+protected:
+
+ //--------------- Data members ---------------------------------
+ int m_height;
+ double m_xorigin;
+ bool m_invert;
+ int m_currentXOffset;
+ int m_width;
+ bool m_activeMousePress;
+
+ RosegardenGUIDoc *m_doc;
+ bool m_mainWindow;
+ RulerScale *m_rulerScale;
+ SnapGrid m_defaultGrid;
+ SnapGrid m_loopGrid;
+ SnapGrid *m_grid;
+ QPen m_quickMarkerPen;
+ bool m_loopingMode;
+ timeT m_startLoop;
+ timeT m_endLoop;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/MarkerRuler.cpp b/src/gui/rulers/MarkerRuler.cpp
new file mode 100644
index 0000000..7dcb67a
--- /dev/null
+++ b/src/gui/rulers/MarkerRuler.cpp
@@ -0,0 +1,490 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "MarkerRuler.h"
+
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/Composition.h"
+#include "base/RulerScale.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/HZoomable.h"
+#include "gui/dialogs/MarkerModifyDialog.h"
+#include "commands/edit/ModifyMarkerCommand.h"
+#include "document/MultiViewCommandHistory.h"
+#include <kxmlguifactory.h>
+#include <qbrush.h>
+#include <qcursor.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qpainter.h>
+#include <qpen.h>
+#include <qpoint.h>
+#include <qpopupmenu.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qstring.h>
+#include <qwidget.h>
+#include <klocale.h>
+#include <kaction.h>
+#include <kstddirs.h>
+#include <qtooltip.h>
+
+
+namespace Rosegarden
+{
+
+MarkerRuler::MarkerRuler(RosegardenGUIDoc *doc,
+ RulerScale *rulerScale,
+ int barHeight,
+ double xorigin,
+ QWidget* parent,
+ const char* name,
+ WFlags f)
+ : QWidget(parent, name, f),
+ m_barHeight(barHeight),
+ m_xorigin(xorigin),
+ m_currentXOffset(0),
+ m_width(-1),
+ m_clickX(0),
+ m_menu(0),
+ m_doc(doc),
+ m_rulerScale(rulerScale),
+ m_parentMainWindow(dynamic_cast<KMainWindow*>(doc->parent()))
+{
+ // If the parent window has a main window above it, we need to use
+ // that as the parent main window, not the document's parent.
+ // Otherwise we'll end up adding all actions to the same
+ // (document-level) action collection regardless of which window
+ // we're in.
+ QObject *probe = parent;
+ while (probe && !dynamic_cast<KMainWindow *>(probe)) probe = probe->parent();
+ if (probe) m_parentMainWindow = dynamic_cast<KMainWindow *>(probe);
+
+ // m_barFont = new QFont("helvetica", 12);
+ // m_barFont->setPixelSize(12);
+ m_barFont = new QFont();
+ m_barFont->setPointSize(10);
+
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QIconSet icon;
+
+ // Use the event insert, delete, edit icons because they are
+ // actually generic enough to serve for anything. Let's hope they
+ // don't become more event-specific in future...
+
+ icon = QIconSet(QPixmap(pixmapDir + "/toolbar/event-insert.png"));
+ new KAction(i18n("Insert Marker"), icon, 0, this,
+ SLOT(slotInsertMarkerHere()), actionCollection(),
+ "insert_marker_here");
+
+ new KAction(i18n("Insert Marker at Playback Position"), 0, this,
+ SLOT(slotInsertMarkerAtPointer()), actionCollection(),
+ "insert_marker_at_pointer");
+
+ icon = QIconSet(QPixmap(pixmapDir + "/toolbar/event-delete.png"));
+ new KAction(i18n("Delete Marker"), icon, 0, this,
+ SLOT(slotDeleteMarker()), actionCollection(),
+ "delete_marker");
+
+ icon = QIconSet(QPixmap(pixmapDir + "/toolbar/event-edit.png"));
+ new KAction(i18n("Edit Marker..."), icon, 0, this,
+ SLOT(slotEditMarker()), actionCollection(),
+ "edit_marker");
+
+ QToolTip::add
+ (this, i18n("Click on a marker to move the playback pointer.\nShift-click to set a range between markers.\nDouble-click to open the marker editor."));
+}
+
+MarkerRuler::~MarkerRuler()
+{
+ delete m_barFont;
+ // we have to do this so that the menu is re-created properly
+ // when the main window is itself recreated (on a File->New for instance)
+ KXMLGUIFactory* factory = m_parentMainWindow->factory();
+ if (factory)
+ factory->removeClient(this);
+}
+
+void
+MarkerRuler::createMenu()
+{
+ setXMLFile("markerruler.rc");
+
+ KXMLGUIFactory* factory = m_parentMainWindow->factory();
+ factory->addClient(this);
+
+ QWidget* tmp = factory->container("marker_ruler_menu", this);
+
+// if (!tmp) {
+// RG_DEBUG << "MarkerRuler::createMenu() menu not found\n"
+// << domDocument().toString(4) << endl;
+// }
+
+ m_menu = dynamic_cast<QPopupMenu*>(tmp);
+
+ if (!m_menu) {
+ RG_DEBUG << "MarkerRuler::createMenu() failed\n";
+ }
+}
+
+
+void
+MarkerRuler::scrollHoriz(int x)
+{
+ m_currentXOffset = static_cast<int>( -x / getHScaleFactor());
+ repaint();
+}
+
+QSize
+MarkerRuler::sizeHint() const
+{
+ int lastBar =
+ m_rulerScale->getLastVisibleBar();
+ double width =
+ m_rulerScale->getBarPosition(lastBar) +
+ m_rulerScale->getBarWidth(lastBar) + m_xorigin;
+
+ return QSize(std::max(int(width), m_width), m_barHeight);
+}
+
+QSize
+MarkerRuler::minimumSizeHint() const
+{
+ double firstBarWidth = m_rulerScale->getBarWidth(0) + m_xorigin;
+
+ return QSize(static_cast<int>(firstBarWidth), m_barHeight);
+}
+
+void
+MarkerRuler::slotInsertMarkerHere()
+{
+ emit addMarker(getClickPosition());
+}
+
+void
+MarkerRuler::slotInsertMarkerAtPointer()
+{
+ emit addMarker(m_doc->getComposition().getPosition());
+}
+
+void
+MarkerRuler::slotDeleteMarker()
+{
+ RG_DEBUG << "MarkerRuler::slotDeleteMarker()\n";
+
+ Rosegarden::Marker* marker = getMarkerAtClickPosition();
+
+ if (marker)
+ emit deleteMarker(marker->getID(),
+ marker->getTime(),
+ marker->getName(),
+ marker->getDescription());
+}
+
+void
+MarkerRuler::slotEditMarker()
+{
+ Rosegarden::Marker* marker = getMarkerAtClickPosition();
+
+ if (!marker) return;
+
+ // I think the ruler should be doing all this stuff itself, or
+ // emitting signals connected to a dedicated marker model object,
+ // not just relying on the app object. Same goes for practically
+ // everything else we do. Hey ho. Having this here is
+ // inconsistent with the other methods, so if anyone wants to move
+ // it, be my guest.
+
+ MarkerModifyDialog dialog(this, &m_doc->getComposition(), marker);
+ if (dialog.exec() == QDialog::Accepted) {
+ ModifyMarkerCommand *command =
+ new ModifyMarkerCommand(&m_doc->getComposition(),
+ marker->getID(),
+ dialog.getOriginalTime(),
+ dialog.getTime(),
+ qstrtostr(dialog.getName()),
+ qstrtostr(dialog.getDescription()));
+ m_doc->getCommandHistory()->addCommand(command);
+ }
+}
+
+timeT
+MarkerRuler::getClickPosition()
+{
+ timeT t = m_rulerScale->getTimeForX
+ (m_clickX - m_xorigin - m_currentXOffset);
+
+ return t;
+}
+
+Rosegarden::Marker*
+MarkerRuler::getMarkerAtClickPosition()
+{
+ QRect clipRect = visibleRect();
+
+ int firstBar = m_rulerScale->getBarForX(clipRect.x() -
+ m_currentXOffset -
+ m_xorigin);
+ int lastBar = m_rulerScale->getLastVisibleBar();
+ if (firstBar < m_rulerScale->getFirstVisibleBar()) {
+ firstBar = m_rulerScale->getFirstVisibleBar();
+ }
+
+ Composition &comp = m_doc->getComposition();
+ Composition::markercontainer markers = comp.getMarkers();
+
+ timeT start = comp.getBarStart(firstBar);
+ timeT end = comp.getBarEnd(lastBar);
+
+ // need these to calculate the visible extents of a marker tag
+ QPainter painter(this);
+ painter.setFont(*m_barFont);
+ QFontMetrics metrics = painter.fontMetrics();
+
+ for (Composition::markerconstiterator i = markers.begin();
+ i != markers.end(); ++i) {
+
+ if ((*i)->getTime() >= start && (*i)->getTime() < end) {
+
+ QString name(strtoqstr((*i)->getName()));
+
+ int x = m_rulerScale->getXForTime((*i)->getTime())
+ + m_xorigin + m_currentXOffset;
+
+ int width = metrics.width(name) + 5;
+
+ int nextX = -1;
+ Composition::markerconstiterator j = i;
+ ++j;
+ if (j != markers.end()) {
+ nextX = m_rulerScale->getXForTime((*j)->getTime())
+ + m_xorigin + m_currentXOffset;
+ }
+
+ if (m_clickX >= x && m_clickX <= x + width) {
+
+ if (nextX < x || m_clickX <= nextX) {
+
+ return *i;
+ }
+ }
+ }
+ }
+
+ return 0L;
+}
+
+void
+MarkerRuler::paintEvent(QPaintEvent*)
+{
+ QPainter painter(this);
+ painter.setFont(*m_barFont);
+
+ if (getHScaleFactor() != 1.0)
+ painter.scale(getHScaleFactor(), 1.0);
+
+ QRect clipRect = visibleRect();
+
+ int firstBar = m_rulerScale->getBarForX(clipRect.x() -
+ m_currentXOffset -
+ m_xorigin);
+ int lastBar = m_rulerScale->getLastVisibleBar();
+ if (firstBar < m_rulerScale->getFirstVisibleBar()) {
+ firstBar = m_rulerScale->getFirstVisibleBar();
+ }
+
+ painter.drawLine(m_currentXOffset, 0, static_cast<int>(visibleRect().width() / getHScaleFactor()), 0);
+
+ float minimumWidth = 25.0;
+ float testSize = ((float)(m_rulerScale->getBarPosition(firstBar + 1) -
+ m_rulerScale->getBarPosition(firstBar)))
+ / minimumWidth;
+
+ int every = 0;
+ int count = 0;
+
+ if (testSize < 1.0) {
+ every = (int(1.0 / testSize));
+
+ if (every % 2 == 0)
+ every++;
+ }
+
+ for (int i = firstBar; i <= lastBar; ++i) {
+
+ double x = m_rulerScale->getBarPosition(i) + m_xorigin + m_currentXOffset;
+
+ if ((x * getHScaleFactor()) > clipRect.x() + clipRect.width())
+ break;
+
+ // always the first bar number
+ if (every && i != firstBar) {
+ if (count < every) {
+ count++;
+ continue;
+ }
+
+ // reset count if we passed
+ count = 0;
+ }
+
+ // adjust count for first bar line
+ if (every == firstBar)
+ count++;
+
+ if (i != lastBar) {
+ painter.drawLine(static_cast<int>(x), 0, static_cast<int>(x), m_barHeight);
+
+ // disable worldXForm for text
+ QPoint textDrawPoint = painter.xForm(QPoint(static_cast<int>(x + 4), 12));
+
+ bool enableXForm = painter.hasWorldXForm();
+ painter.setWorldXForm(false);
+
+ if (i >= 0)
+ painter.drawText(textDrawPoint, QString("%1").arg(i + 1));
+
+ painter.setWorldXForm(enableXForm);
+ } else {
+ const QPen normalPen = painter.pen();
+ ;
+ QPen endPen(black, 2);
+ painter.setPen(endPen);
+ painter.drawLine(static_cast<int>(x), 0, static_cast<int>(x), m_barHeight);
+ painter.setPen(normalPen);
+ }
+ }
+
+ if (m_doc) {
+ Composition &comp = m_doc->getComposition();
+ Composition::markercontainer markers = comp.getMarkers();
+ Composition::markerconstiterator it;
+
+ timeT start = comp.getBarStart(firstBar);
+ timeT end = comp.getBarEnd(lastBar);
+
+ QFontMetrics metrics = painter.fontMetrics();
+
+ for (it = markers.begin(); it != markers.end(); ++it) {
+ if ((*it)->getTime() >= start && (*it)->getTime() < end) {
+ QString name(strtoqstr((*it)->getName()));
+
+ double x = m_rulerScale->getXForTime((*it)->getTime())
+ + m_xorigin + m_currentXOffset;
+
+ painter.fillRect(static_cast<int>(x), 1,
+ static_cast<int>(metrics.width(name) + 5),
+ m_barHeight - 2,
+ QBrush(GUIPalette::getColour(GUIPalette::MarkerBackground)));
+
+ painter.drawLine(int(x), 1, int(x), m_barHeight - 2);
+ painter.drawLine(int(x) + 1, 1, int(x) + 1, m_barHeight - 2);
+
+ QPoint textDrawPoint = painter.xForm
+ (QPoint(static_cast<int>(x + 3), m_barHeight - 4));
+
+ // disable worldXForm for text
+ bool enableXForm = painter.hasWorldXForm();
+ painter.setWorldXForm(false);
+
+ painter.drawText(textDrawPoint, name);
+
+ painter.setWorldXForm(enableXForm);
+ }
+ }
+ }
+}
+
+void
+MarkerRuler::mousePressEvent(QMouseEvent *e)
+{
+ RG_DEBUG << "MarkerRuler::mousePressEvent: x = " << e->x() << endl;
+
+ if (!m_doc || !e)
+ return;
+
+ m_clickX = e->x();
+ Rosegarden::Marker* clickedMarker = getMarkerAtClickPosition();
+
+ // if right-click, show popup menu
+ //
+ if (e->button() == RightButton) {
+ if (!m_menu)
+ createMenu();
+ if (m_menu) {
+ actionCollection()->action("delete_marker")->setEnabled(clickedMarker != 0);
+ actionCollection()->action("edit_marker")->setEnabled(clickedMarker != 0);
+ m_menu->exec(QCursor::pos());
+ }
+ return;
+ }
+
+ bool shiftPressed = ((e->state() & Qt::ShiftButton) != 0);
+
+ Composition &comp = m_doc->getComposition();
+ Composition::markercontainer markers = comp.getMarkers();
+
+ if (shiftPressed) { // set loop
+
+ timeT t = m_rulerScale->getTimeForX
+ (e->x() - m_xorigin - m_currentXOffset);
+
+ timeT prev = 0;
+
+ for (Composition::markerconstiterator i = markers.begin();
+ i != markers.end(); ++i) {
+
+ timeT cur = (*i)->getTime();
+
+ if (cur >= t) {
+ emit setLoop(prev, cur);
+ return ;
+ }
+
+ prev = cur;
+ }
+
+ if (prev > 0)
+ emit setLoop(prev, comp.getEndMarker());
+
+ } else { // set pointer to clicked marker
+
+ if (clickedMarker)
+ emit setPointerPosition(clickedMarker->getTime());
+ }
+}
+
+void
+MarkerRuler::mouseDoubleClickEvent(QMouseEvent *)
+{
+ RG_DEBUG << "MarkerRuler::mouseDoubleClickEvent" << endl;
+
+ emit editMarkers();
+}
+
+}
+#include "MarkerRuler.moc"
diff --git a/src/gui/rulers/MarkerRuler.h b/src/gui/rulers/MarkerRuler.h
new file mode 100644
index 0000000..c77e6a9
--- /dev/null
+++ b/src/gui/rulers/MarkerRuler.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_MARKERRULER_H_
+#define _RG_MARKERRULER_H_
+
+#include "gui/general/HZoomable.h"
+#include <qsize.h>
+#include <qwidget.h>
+#include <kxmlguiclient.h>
+#include "base/Event.h"
+
+
+class QPaintEvent;
+class QMouseEvent;
+class QFont;
+class QPopupMenu;
+class KMainWindow;
+
+namespace Rosegarden
+{
+
+class Marker;
+class RulerScale;
+class RosegardenGUIDoc;
+
+
+class MarkerRuler : public QWidget, public HZoomable, public KXMLGUIClient
+{
+ Q_OBJECT
+
+public:
+ MarkerRuler(RosegardenGUIDoc *doc,
+ RulerScale *rulerScale,
+ int buttonHeight,
+ double xorigin = 0.0,
+ QWidget* parent = 0,
+ const char* name = 0,
+ WFlags f=0);
+
+ virtual ~MarkerRuler();
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ void scrollHoriz(int x);
+
+ void setWidth(int width) { m_width = width; }
+
+signals:
+ /// Set the pointer position on mouse single click
+ void setPointerPosition(timeT);
+
+ /// Open the marker editor window on double click
+ void editMarkers();
+
+ /// add a marker
+ void addMarker(timeT);
+
+ void deleteMarker(int, timeT, QString name, QString description);
+
+ /// Set a loop range
+ void setLoop(timeT, timeT);
+
+protected slots:
+ void slotInsertMarkerHere();
+ void slotInsertMarkerAtPointer();
+ void slotDeleteMarker();
+ void slotEditMarker();
+
+protected:
+ virtual void paintEvent(QPaintEvent*);
+ virtual void mousePressEvent(QMouseEvent *e);
+ virtual void mouseDoubleClickEvent(QMouseEvent *e);
+
+ void createMenu();
+ timeT getClickPosition();
+ Rosegarden::Marker* getMarkerAtClickPosition();
+
+ //--------------- Data members ---------------------------------
+ int m_barHeight;
+ double m_xorigin;
+ int m_currentXOffset;
+ int m_width;
+ int m_clickX;
+
+ QFont *m_barFont;
+ QPopupMenu *m_menu;
+
+ RosegardenGUIDoc *m_doc;
+ RulerScale *m_rulerScale;
+ KMainWindow* m_parentMainWindow;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/PercussionPitchRuler.cpp b/src/gui/rulers/PercussionPitchRuler.cpp
new file mode 100644
index 0000000..a89ae89
--- /dev/null
+++ b/src/gui/rulers/PercussionPitchRuler.cpp
@@ -0,0 +1,204 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "PercussionPitchRuler.h"
+
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/MidiProgram.h"
+#include "gui/editors/matrix/MatrixStaff.h"
+#include "gui/editors/matrix/MatrixView.h"
+#include "gui/general/MidiPitchLabel.h"
+#include "PitchRuler.h"
+#include <qcolor.h>
+#include <qevent.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qpainter.h>
+#include <qsize.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+PercussionPitchRuler::PercussionPitchRuler(QWidget *parent,
+ const MidiKeyMapping *mapping,
+ int lineSpacing) :
+ PitchRuler(parent),
+ m_mapping(mapping),
+ m_lineSpacing(lineSpacing),
+ m_mouseDown(false),
+ m_lastHoverHighlight( -1)
+{
+ m_font = new QFont();
+ m_font->setPixelSize(9);
+ m_fontMetrics = new QFontMetrics(*m_font);
+ m_width = m_fontMetrics->width(" A#2 Acoustic Bass Drum ");
+
+ setPaletteBackgroundColor(QColor(238, 238, 224));
+
+ setMouseTracking(true);
+}
+
+QSize PercussionPitchRuler::sizeHint() const
+{
+ return QSize(m_width,
+ (m_lineSpacing + 1) * m_mapping->getPitchExtent());
+}
+
+QSize PercussionPitchRuler::minimumSizeHint() const
+{
+ return QSize(m_width, m_lineSpacing + 1);
+}
+
+void PercussionPitchRuler::paintEvent(QPaintEvent*)
+{
+ QPainter paint(this);
+
+ paint.setFont(*m_font);
+
+ int minPitch = m_mapping->getPitchForOffset(0);
+ int extent = m_mapping->getPitchExtent();
+
+ for (int i = 0; i < extent; ++i) {
+ paint.drawLine(0, i * (m_lineSpacing + 1),
+ m_width, i * (m_lineSpacing + 1));
+ }
+
+ int lw = m_fontMetrics->width("A#2");
+
+ for (int i = 0; i < extent; ++i) {
+
+ MidiPitchLabel label(minPitch + i);
+ std::string key = m_mapping->getMapForKeyName(minPitch + i);
+
+ RG_DEBUG << i << ": " << label.getQString() << ": " << key << endl;
+
+ paint.drawText
+ (2, (extent - i - 1) * (m_lineSpacing + 1) +
+ m_fontMetrics->ascent() + 1,
+ label.getQString());
+
+ paint.drawText
+ (9 + lw, (extent - i - 1) * (m_lineSpacing + 1) +
+ m_fontMetrics->ascent() + 1,
+ strtoqstr(key));
+ }
+}
+
+void PercussionPitchRuler::enterEvent(QEvent *)
+{}
+
+void PercussionPitchRuler::leaveEvent(QEvent*)
+{
+ // m_hoverHighlight->hide();
+}
+
+void PercussionPitchRuler::drawHoverNote(int evPitch)
+{
+ QPainter paint(this);
+ paint.setFont(*m_font);
+
+ if (m_lastHoverHighlight != evPitch) {
+
+ int minPitch = m_mapping->getPitchForOffset(0);
+ int extent = m_mapping->getPitchExtent();
+
+ int lw = m_fontMetrics->width("A#2");
+
+ if (m_lastHoverHighlight >= 0) {
+
+ int y = (extent - (m_lastHoverHighlight - minPitch) - 1) * (m_lineSpacing + 1);
+ paint.setBrush(QColor(238, 238, 224));
+ paint.setPen(QColor(238, 238, 224));
+ paint.drawRect(lw + 7, y + 1, m_width - lw, m_lineSpacing);
+ std::string key = m_mapping->getMapForKeyName(m_lastHoverHighlight);
+ paint.setPen(Qt::black);
+ paint.drawText
+ (9 + lw, y + m_fontMetrics->ascent() + 1,
+ strtoqstr(key));
+ }
+
+ int y = (extent - (evPitch - minPitch) - 1) * (m_lineSpacing + 1);
+ m_lastHoverHighlight = evPitch;
+ paint.setBrush(paint.pen().color());
+ paint.drawRect(lw + 7, y, m_width - lw, m_lineSpacing + 1);
+ paint.setPen(QColor(238, 238, 224));
+
+ std::string key = m_mapping->getMapForKeyName(evPitch);
+ paint.drawText
+ (9 + lw, y + m_fontMetrics->ascent() + 1,
+ strtoqstr(key));
+ }
+}
+
+void PercussionPitchRuler::mouseMoveEvent(QMouseEvent* e)
+{
+ // ugh
+
+ MatrixView *matrixView = dynamic_cast<MatrixView*>(topLevelWidget());
+ if (matrixView) {
+ MatrixStaff *staff = matrixView->getStaff(0);
+ if (staff) {
+ drawHoverNote(staff->getHeightAtCanvasCoords(e->x(), e->y()));
+ }
+ }
+
+ if (m_mouseDown)
+ if (m_selecting)
+ emit keySelected(e->y(), true);
+ else
+ emit keyPressed(e->y(), true); // we're swooshing
+ else
+ emit hoveredOverKeyChanged(e->y());
+}
+
+void PercussionPitchRuler::mousePressEvent(QMouseEvent *e)
+{
+ Qt::ButtonState bs = e->state();
+
+ if (e->button() == LeftButton) {
+
+ m_mouseDown = true;
+ m_selecting = (bs & Qt::ShiftButton);
+
+ if (m_selecting)
+ emit keySelected(e->y(), false);
+ else
+ emit keyPressed(e->y(), false);
+ }
+}
+
+void PercussionPitchRuler::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (e->button() == LeftButton) {
+ m_mouseDown = false;
+ m_selecting = false;
+ }
+}
+
+}
+#include "PercussionPitchRuler.moc"
diff --git a/src/gui/rulers/PercussionPitchRuler.h b/src/gui/rulers/PercussionPitchRuler.h
new file mode 100644
index 0000000..cae61ec
--- /dev/null
+++ b/src/gui/rulers/PercussionPitchRuler.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_PERCUSSIONPITCHRULER_H_
+#define _RG_PERCUSSIONPITCHRULER_H_
+
+#include "PitchRuler.h"
+#include <qsize.h>
+
+
+class QWidget;
+class QPaintEvent;
+class QMouseEvent;
+class QFontMetrics;
+class QFont;
+class QEvent;
+
+
+namespace Rosegarden
+{
+
+class MidiKeyMapping;
+
+
+class PercussionPitchRuler : public PitchRuler
+{
+ Q_OBJECT
+public:
+ PercussionPitchRuler(QWidget *parent,
+ const MidiKeyMapping *mapping,
+ int lineSpacing);
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ void drawHoverNote(int evPitch);
+
+signals:
+ void keyPressed(unsigned int y, bool repeating);
+ void keySelected(unsigned int y, bool repeating);
+ 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 *);
+
+ const MidiKeyMapping *m_mapping;
+
+ int m_width;
+ int m_lineSpacing;
+
+ bool m_mouseDown;
+ bool m_selecting;
+
+ int m_lastHoverHighlight;
+
+ QFont *m_font;
+ QFontMetrics *m_fontMetrics;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/PitchRuler.cpp b/src/gui/rulers/PitchRuler.cpp
new file mode 100644
index 0000000..55f4b00
--- /dev/null
+++ b/src/gui/rulers/PitchRuler.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "PitchRuler.h"
+
+#include <qsize.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+PitchRuler::
+PitchRuler(QWidget *parent) :
+ QWidget(parent)
+{
+ // nothing else
+}
+
+QSize
+PitchRuler::sizeHint() const
+{
+ return QWidget::sizeHint();
+}
+
+QSize
+PitchRuler::minimumSizeHint() const
+{
+ return QWidget::minimumSizeHint();
+}
+
+}
+#include "PitchRuler.moc"
diff --git a/src/gui/rulers/PitchRuler.h b/src/gui/rulers/PitchRuler.h
new file mode 100644
index 0000000..3c47709
--- /dev/null
+++ b/src/gui/rulers/PitchRuler.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_PITCHRULER_H_
+#define _RG_PITCHRULER_H_
+
+#include <qsize.h>
+#include <qwidget.h>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+class PitchRuler : public QWidget
+{
+ Q_OBJECT
+public:
+ PitchRuler(QWidget *parent);
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ virtual void drawHoverNote(int evPitch) = 0;
+
+signals:
+
+ /**
+ * A pitch has been clicked.
+ * y is the simple event y-coordinate.
+ * If the user is in the middle of dragging, repeating will be set.
+ */
+ void keyPressed(unsigned int y, bool repeating);
+
+ /**
+ * A pitch has been clicked with the selection modifier pressed.
+ * y is the simple event y-coordinate.
+ * If the user is in the middle of dragging, repeating will be set.
+ */
+ void keySelected(unsigned int y, bool repeating);
+
+ /**
+ * Emitted when the mouse cursor moves to a different pitch when
+ * not clicking or selecting.
+ * y is the simple event y-coordinate.
+ */
+ void hoveredOverKeyChanged(unsigned int y);
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/PropertyBox.cpp b/src/gui/rulers/PropertyBox.cpp
new file mode 100644
index 0000000..38d67ef
--- /dev/null
+++ b/src/gui/rulers/PropertyBox.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "PropertyBox.h"
+
+#include "gui/general/GUIPalette.h"
+#include <qpainter.h>
+#include <qsize.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+PropertyBox::PropertyBox(QString label,
+ int width,
+ int height,
+ QWidget *parent,
+ const char *name):
+ QWidget(parent, name),
+ m_label(label),
+ m_width(width),
+ m_height(height)
+{}
+
+QSize
+PropertyBox::sizeHint() const
+{
+ return QSize(m_width, m_height);
+}
+
+QSize
+PropertyBox::minimumSizeHint() const
+{
+ return QSize(m_width, m_height);
+}
+
+void
+PropertyBox::paintEvent(QPaintEvent *e)
+{
+ QPainter paint(this);
+
+ paint.setPen(GUIPalette::getColour(GUIPalette::MatrixElementBorder));
+ //paint.setBrush(GUIPalette::getColour(GUIPalette::MatrixElementBlock));
+
+ paint.setClipRegion(e->region());
+ paint.setClipRect(e->rect().normalize());
+
+ paint.drawRect(2, 2, m_width - 3, m_height - 3);
+ paint.drawText(10, 2 * m_height / 3, m_label);
+}
+
+}
+#include "PropertyBox.moc"
diff --git a/src/gui/rulers/PropertyBox.h b/src/gui/rulers/PropertyBox.h
new file mode 100644
index 0000000..1b36f0b
--- /dev/null
+++ b/src/gui/rulers/PropertyBox.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_PROPERTYBOX_H_
+#define _RG_PROPERTYBOX_H_
+
+#include <qsize.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+class QPaintEvent;
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * We use a ControlBox to help modify events on the ruler - set tools etc.
+ * and provide extra information or options.
+ *
+ */
+class PropertyBox : public QWidget
+{
+ Q_OBJECT
+
+public:
+ PropertyBox(QString label,
+ int width,
+ int height,
+ QWidget *parent=0,
+ const char *name = 0);
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+protected:
+ virtual void paintEvent(QPaintEvent *);
+
+ //--------------- Data members ---------------------------------
+
+ QString m_label;
+ int m_width;
+ int m_height;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/PropertyControlRuler.cpp b/src/gui/rulers/PropertyControlRuler.cpp
new file mode 100644
index 0000000..7dc1258
--- /dev/null
+++ b/src/gui/rulers/PropertyControlRuler.cpp
@@ -0,0 +1,441 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "PropertyControlRuler.h"
+
+#include "ControlRuler.h"
+#include "ControlItem.h"
+#include "ViewElementAdapter.h"
+#include "misc/Debug.h"
+#include "base/BaseProperties.h"
+#include "base/NotationTypes.h"
+#include "base/PropertyName.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "base/Staff.h"
+#include "base/ViewElement.h"
+#include "commands/edit/SelectionPropertyCommand.h"
+#include "gui/general/EditViewBase.h"
+#include "gui/widgets/TextFloat.h"
+#include "gui/general/LinedStaff.h"
+#include <qcanvas.h>
+#include <qcolor.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+PropertyControlRuler::PropertyControlRuler(PropertyName propertyName,
+ Staff* staff,
+ RulerScale* rulerScale,
+ EditViewBase* parentView,
+ QCanvas* c, QWidget* parent,
+ const char* name, WFlags f) :
+ ControlRuler(&(staff->getSegment()), rulerScale,
+ parentView, c, parent, name, f),
+ m_propertyName(propertyName),
+ m_staff(staff),
+ m_propertyLine(new QCanvasLine(canvas())),
+ m_propertyLineShowing(false),
+ m_propertyLineX(0),
+ m_propertyLineY(0)
+{
+ m_staff->addObserver(this);
+ m_propertyLine->setZ(1000); // bring to front
+
+ setMenuName("property_ruler_menu");
+ drawBackground();
+ init();
+}
+
+void
+PropertyControlRuler::setStaff(Staff *staff)
+{
+ RG_DEBUG << "PropertyControlRuler::setStaff(" << staff << ")" << endl;
+
+ m_staff->removeObserver(this);
+ m_segment->removeObserver(this);
+ m_staff = staff;
+ m_segment = &m_staff->getSegment();
+ m_staff->addObserver(this);
+ m_segment->addObserver(this);
+
+ //!!! need to delete the control items here
+
+ drawBackground();
+ init();
+}
+
+void
+PropertyControlRuler::drawBackground()
+{
+ // Draw some minimum and maximum controller value guide lines
+ //
+ QCanvasLine *topLine = new QCanvasLine(canvas());
+ QCanvasLine *topQLine = new QCanvasLine(canvas());
+ QCanvasLine *midLine = new QCanvasLine(canvas());
+ QCanvasLine *botQLine = new QCanvasLine(canvas());
+ QCanvasLine *bottomLine = new QCanvasLine(canvas());
+ //m_controlLine->setPoints(m_controlLineX, m_controlLineY, m_controlLineX, m_controlLineY);
+ int cHeight = canvas()->height();
+ int cWidth = canvas()->width();
+
+ topLine->setPen(QColor(127, 127, 127));
+ topLine->setPoints(0, 0, cWidth, 0);
+ topLine->setZ( -10);
+ topLine->show();
+
+ topQLine->setPen(QColor(192, 192, 192));
+ topQLine->setPoints(0, cHeight / 4, cWidth, cHeight / 4);
+ topQLine->setZ( -10);
+ topQLine->show();
+
+ midLine->setPen(QColor(127, 127, 127));
+ midLine->setPoints(0, cHeight / 2, cWidth, cHeight / 2);
+ midLine->setZ( -10);
+ midLine->show();
+
+ botQLine->setPen(QColor(192, 192, 192));
+ botQLine->setPoints(0, 3*cHeight / 4, cWidth, 3*cHeight / 4);
+ botQLine->setZ( -10);
+ botQLine->show();
+
+ bottomLine->setPen(QColor(127, 127, 127));
+ bottomLine->setPoints(0, cHeight - 1, cWidth, cHeight - 1);
+ bottomLine->setZ( -10);
+ bottomLine->show();
+}
+
+PropertyControlRuler::~PropertyControlRuler()
+{
+ if (m_staff) {
+ m_staff->removeObserver(this);
+ }
+}
+
+QString PropertyControlRuler::getName()
+{
+ return getPropertyName().c_str();
+}
+
+void PropertyControlRuler::init()
+{
+ ViewElementList* viewElementList = m_staff->getViewElementList();
+
+ LinedStaff* lStaff = dynamic_cast<LinedStaff*>(m_staff);
+
+ if (lStaff)
+ m_staffOffset = lStaff->getX();
+
+ for (ViewElementList::iterator i = viewElementList->begin();
+ i != viewElementList->end(); ++i) {
+
+ if ((*i)->event()->isa(Note::EventRestType))
+ continue;
+
+ double x = m_rulerScale->getXForTime((*i)->getViewAbsoluteTime());
+ new ControlItem(this, new ViewElementAdapter(*i, getPropertyName()), int(x + m_staffOffset),
+ int(m_rulerScale->getXForTime((*i)->getViewAbsoluteTime() +
+ (*i)->getViewDuration()) - x));
+
+ }
+}
+
+void PropertyControlRuler::elementAdded(const Staff *, ViewElement *el)
+{
+ RG_DEBUG << "PropertyControlRuler::elementAdded()\n";
+
+ if (el->event()->isa(Note::EventRestType))
+ return ;
+
+ double x = m_rulerScale->getXForTime(el->getViewAbsoluteTime());
+
+ new ControlItem(this, new ViewElementAdapter(el, getPropertyName()), int(x + m_staffOffset),
+ int(m_rulerScale->getXForTime(el->getViewAbsoluteTime() +
+ el->getViewDuration()) - x));
+}
+
+void PropertyControlRuler::elementRemoved(const Staff *, ViewElement *el)
+{
+ RG_DEBUG << "PropertyControlRuler::elementRemoved(\n";
+
+ clearSelectedItems();
+
+ QCanvasItemList allItems = canvas()->allItems();
+
+ for (QCanvasItemList::Iterator it = allItems.begin(); it != allItems.end(); ++it) {
+ if (ControlItem *item = dynamic_cast<ControlItem*>(*it)) {
+ ViewElementAdapter * adapter = dynamic_cast<ViewElementAdapter*>(item->getElementAdapter());
+ if (adapter->getViewElement() == el) {
+ delete item;
+ break;
+ }
+ }
+ }
+}
+
+void PropertyControlRuler::staffDeleted(const Staff *)
+{
+ m_staff = 0;
+}
+
+void
+PropertyControlRuler::endMarkerTimeChanged(const Segment *s, bool)
+{
+ timeT endMarkerTime = s->getEndMarkerTime();
+
+ RG_DEBUG << "PropertyControlRuler::endMarkerTimeChanged() " << endMarkerTime << endl;
+
+ clearSelectedItems();
+
+ clear();
+ init();
+}
+
+void PropertyControlRuler::computeStaffOffset()
+{
+ LinedStaff* lStaff = dynamic_cast<LinedStaff*>(m_staff);
+ if (lStaff)
+ m_staffOffset = lStaff->getX();
+}
+
+void PropertyControlRuler::startPropertyLine()
+{
+ RG_DEBUG << "PropertyControlRuler::startPropertyLine\n";
+ m_propertyLineShowing = true;
+ this->setCursor(Qt::pointingHandCursor);
+}
+
+void
+PropertyControlRuler::contentsMousePressEvent(QMouseEvent *e)
+{
+ RG_DEBUG << "PropertyControlRuler::contentsMousePressEvent\n";
+
+ if (!m_propertyLineShowing) {
+ if (e->button() == MidButton)
+ m_lastEventPos = inverseMapPoint(e->pos());
+
+ ControlRuler::contentsMousePressEvent(e); // send super
+
+ return ;
+ }
+
+ // cancel control line mode
+ if (e->button() == RightButton) {
+ m_propertyLineShowing = false;
+ m_propertyLine->hide();
+
+ this->setCursor(Qt::arrowCursor);
+ return ;
+ }
+
+ if (e->button() == LeftButton) {
+ QPoint p = inverseMapPoint(e->pos());
+
+ m_propertyLine->show();
+ m_propertyLineX = p.x();
+ m_propertyLineY = p.y();
+ m_propertyLine->setPoints(m_propertyLineX, m_propertyLineY, m_propertyLineX, m_propertyLineY);
+ canvas()->update();
+ }
+}
+
+void
+PropertyControlRuler::contentsMouseReleaseEvent(QMouseEvent *e)
+{
+ RG_DEBUG << "PropertyControlRuler::contentsMouseReleaseEvent\n";
+
+ /*
+ if (m_propertyLineShowing)
+ {
+ this->setCursor(Qt::arrowCursor);
+ m_propertyLineShowing = false;
+ canvas()->update();
+ }
+ */
+
+ if (!m_propertyLineShowing) {
+ /*
+ if (e->button() == MidButton)
+ insertControllerEvent();
+ */
+
+ ControlRuler::contentsMouseReleaseEvent(e); // send super
+ return ;
+ } else {
+ QPoint p = inverseMapPoint(e->pos());
+
+ timeT startTime = m_rulerScale->getTimeForX(m_propertyLineX);
+ timeT endTime = m_rulerScale->getTimeForX(p.x());
+
+ long startValue = heightToValue(m_propertyLineY - canvas()->height());
+ long endValue = heightToValue(p.y() - canvas()->height());
+
+ RG_DEBUG << "PropertyControlRuler::contentsMouseReleaseEvent - "
+ << "starttime = " << startTime
+ << ", endtime = " << endTime
+ << ", startValue = " << startValue
+ << ", endValue = " << endValue
+ << endl;
+
+ drawPropertyLine(startTime, endTime, startValue, endValue);
+
+ m_propertyLineShowing = false;
+ m_propertyLine->hide();
+ this->setCursor(Qt::arrowCursor);
+ canvas()->update();
+ }
+}
+
+void
+PropertyControlRuler::contentsMouseMoveEvent(QMouseEvent *e)
+{
+ RG_DEBUG << "PropertyControlRuler::contentsMouseMoveEvent\n";
+
+ if (!m_propertyLineShowing) {
+ // Don't send super if we're using the middle button
+ //
+ if (e->button() == MidButton) {
+ m_lastEventPos = inverseMapPoint(e->pos());
+ return ;
+ }
+
+ ControlRuler::contentsMouseMoveEvent(e); // send super
+ return ;
+ }
+
+ QPoint p = inverseMapPoint(e->pos());
+
+ m_propertyLine->setPoints(m_propertyLineX, m_propertyLineY, p.x(), p.y());
+ canvas()->update();
+}
+
+void PropertyControlRuler::contentsContextMenuEvent(QContextMenuEvent* e)
+{
+ RG_DEBUG << "PropertyControlRuler::contentsContextMenuEvent\n";
+
+ // check if we actually have some control items
+ QCanvasItemList list = canvas()->allItems();
+ bool haveItems = false;
+
+ QCanvasItemList::Iterator it = list.begin();
+ for (; it != list.end(); ++it) {
+ if (dynamic_cast<ControlItem*>(*it)) {
+ haveItems = true;
+ break;
+ }
+ }
+
+ RG_DEBUG << "PropertyControlRuler::contentsContextMenuEvent : haveItems = "
+ << haveItems << endl;
+
+ emit stateChange("have_note_events_in_segment", haveItems);
+
+ ControlRuler::contentsContextMenuEvent(e);
+}
+
+void
+PropertyControlRuler::drawPropertyLine(timeT startTime,
+ timeT endTime,
+ int startValue,
+ int endValue)
+{
+ if (startTime > endTime) {
+ std::swap(startTime, endTime);
+ std::swap(startValue, endValue);
+ }
+
+ RG_DEBUG << "PropertyControlRuler::drawPropertyLine - set velocity from "
+ << startTime
+ << " to " << endTime << endl;
+
+ // Add the "true" to catch Events overlapping this line
+ //
+ EventSelection selection(*m_segment, startTime, endTime, true);
+ PropertyPattern pattern = DecrescendoPattern;
+
+ bool haveNotes = selection.contains(Note::EventType);
+
+ if (haveNotes) {
+
+ SelectionPropertyCommand *command =
+ new SelectionPropertyCommand(&selection,
+ BaseProperties::VELOCITY,
+ pattern,
+ startValue,
+ endValue);
+
+ m_parentEditView->addCommandToHistory(command);
+
+ } else {
+
+ RG_DEBUG << "PropertyControlRuler::drawPropertyLine - no notes in selection\n";
+
+ }
+}
+
+void
+PropertyControlRuler::selectAllProperties()
+{
+ RG_DEBUG << "PropertyControlRuler::selectAllProperties" << endl;
+
+ /*
+ for(Segment::iterator i = m_segment.begin();
+ i != m_segment.end(); ++i)
+ if (!m_eventSelection->contains(*i)) m_eventSelection->addEvent(*i);
+ */
+
+ clearSelectedItems();
+
+ QCanvasItemList l = canvas()->allItems();
+ for (QCanvasItemList::Iterator it = l.begin(); it != l.end(); ++it) {
+ if (ControlItem *item = dynamic_cast<ControlItem*>(*it)) {
+ m_selectedItems << item;
+ (*it)->setSelected(true);
+ ElementAdapter* adapter = item->getElementAdapter();
+ m_eventSelection->addEvent(adapter->getEvent());
+ }
+ }
+
+ /*
+ m_eventSelection->addFromSelection(&selection);
+ for (QCanvasItemList::Iterator it=m_selectedItems.begin(); it!=m_selectedItems.end(); ++it) {
+ if (ControlItem *item = dynamic_cast<ControlItem*>(*it)) {
+
+ ElementAdapter* adapter = item->getElementAdapter();
+ m_eventSelection->addEvent(adapter->getEvent());
+ item->handleMouseButtonRelease(e);
+ }
+ }
+ */
+
+ emit stateChange("have_controller_item_selected", true);
+}
+
+}
diff --git a/src/gui/rulers/PropertyControlRuler.h b/src/gui/rulers/PropertyControlRuler.h
new file mode 100644
index 0000000..f94f3e1
--- /dev/null
+++ b/src/gui/rulers/PropertyControlRuler.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_PROPERTYCONTROLRULER_H_
+#define _RG_PROPERTYCONTROLRULER_H_
+
+#include "base/PropertyName.h"
+#include "base/Staff.h"
+#include "ControlRuler.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QWidget;
+class QMouseEvent;
+class QContextMenuEvent;
+class QCanvasLine;
+class QCanvas;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class Staff;
+class Segment;
+class RulerScale;
+class EditViewBase;
+
+
+/**
+ * PropertyControlRuler : edit a property on events on a staff (only
+ * events with a ViewElement attached, mostly notes)
+ */
+class PropertyControlRuler : public ControlRuler, public StaffObserver
+{
+public:
+ PropertyControlRuler(PropertyName propertyName,
+ Staff*,
+ RulerScale*,
+ EditViewBase* parentView,
+ QCanvas*,
+ QWidget* parent=0, const char* name=0, WFlags f=0);
+
+ virtual ~PropertyControlRuler();
+
+ virtual QString getName();
+
+ const PropertyName& getPropertyName() { return m_propertyName; }
+
+ // Allow something external to reset the selection of Events
+ // that this ruler is displaying
+ //
+ void setStaff(Staff *);
+
+ // StaffObserver interface
+ virtual void elementAdded(const Staff *, ViewElement*);
+ virtual void elementRemoved(const Staff *, ViewElement*);
+ virtual void staffDeleted(const Staff *);
+ virtual void startPropertyLine();
+ virtual void selectAllProperties();
+
+ /// SegmentObserver interface
+ virtual void endMarkerTimeChanged(const Segment *, bool shorten);
+
+protected:
+
+ virtual void contentsMousePressEvent(QMouseEvent*);
+ virtual void contentsMouseReleaseEvent(QMouseEvent*);
+ virtual void contentsMouseMoveEvent(QMouseEvent*);
+ virtual void contentsContextMenuEvent(QContextMenuEvent*);
+
+ void drawPropertyLine(timeT startTime,
+ timeT endTime,
+ int startValue,
+ int endValue);
+
+ virtual void init();
+ virtual void drawBackground();
+ virtual void computeStaffOffset();
+
+ //--------------- Data members ---------------------------------
+
+ PropertyName m_propertyName;
+ Staff* m_staff;
+
+ QCanvasLine *m_propertyLine;
+
+ bool m_propertyLineShowing;
+ int m_propertyLineX;
+ int m_propertyLineY;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/PropertyViewRuler.cpp b/src/gui/rulers/PropertyViewRuler.cpp
new file mode 100644
index 0000000..cf5d89d
--- /dev/null
+++ b/src/gui/rulers/PropertyViewRuler.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "PropertyViewRuler.h"
+
+#include "base/Event.h"
+#include <klocale.h>
+#include "misc/Strings.h"
+#include "base/PropertyName.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "DefaultVelocityColour.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/HZoomable.h"
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qpainter.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+PropertyViewRuler::PropertyViewRuler(RulerScale *rulerScale,
+ Segment *segment,
+ const PropertyName &property,
+ double xorigin,
+ int height,
+ QWidget *parent,
+ const char *name) :
+ QWidget(parent, name),
+ m_propertyName(property),
+ m_xorigin(xorigin),
+ m_height(height),
+ m_currentXOffset(0),
+ m_width( -1),
+ m_segment(segment),
+ m_rulerScale(rulerScale),
+ m_fontMetrics(m_boldFont)
+{
+ m_boldFont.setBold(true);
+ m_fontMetrics = QFontMetrics(m_boldFont);
+
+ setBackgroundColor(GUIPalette::getColour(GUIPalette::SegmentCanvas));
+
+ QString tip = i18n("%1 controller").arg(strtoqstr(property));
+ QToolTip::add
+ (this, tip);
+}
+
+PropertyViewRuler::~PropertyViewRuler()
+{
+ // nothing
+}
+
+void
+PropertyViewRuler::slotScrollHoriz(int x)
+{
+ int w = width(), h = height();
+ x = int(double(x) / getHScaleFactor());
+ int dx = x - ( -m_currentXOffset);
+ m_currentXOffset = -x;
+
+ if (dx > w*3 / 4 || dx < -w*3 / 4) {
+ update();
+ return ;
+ }
+
+ if (dx > 0) { // moving right, so the existing stuff moves left
+ bitBlt(this, 0, 0, this, dx, 0, w - dx, h);
+ repaint(w - dx, 0, dx, h);
+ } else { // moving left, so the existing stuff moves right
+ bitBlt(this, -dx, 0, this, 0, 0, w + dx, h);
+ repaint(0, 0, -dx, h);
+ }
+}
+
+QSize
+PropertyViewRuler::sizeHint() const
+{
+ double width =
+ m_rulerScale->getBarPosition(m_rulerScale->getLastVisibleBar()) +
+ m_rulerScale->getBarWidth(m_rulerScale->getLastVisibleBar()) +
+ m_xorigin;
+
+ QSize res(std::max(int(width), m_width), m_height);
+
+ return res;
+}
+
+QSize
+PropertyViewRuler::minimumSizeHint() const
+{
+ double firstBarWidth = m_rulerScale->getBarWidth(0) + m_xorigin;
+ QSize res = QSize(int(firstBarWidth), m_height);
+ return res;
+}
+
+void
+PropertyViewRuler::paintEvent(QPaintEvent* e)
+{
+ QPainter paint(this);
+
+ if (getHScaleFactor() != 1.0)
+ paint.scale(getHScaleFactor(), 1.0);
+
+ paint.setPen(GUIPalette::getColour(GUIPalette::MatrixElementBorder));
+
+ QRect clipRect = e->rect().normalize();
+
+ timeT from = m_rulerScale->getTimeForX
+ (clipRect.x() - m_currentXOffset - m_xorigin);
+
+ Segment::iterator it = m_segment->findNearestTime(from);
+
+ for (; m_segment->isBeforeEndMarker(it); it++) {
+ long value = 0;
+
+ if (!(*it)->get
+ <Int>(m_propertyName, value))
+ continue;
+
+ int x = int(m_rulerScale->getXForTime((*it)->getAbsoluteTime()))
+ + m_currentXOffset + int(m_xorigin);
+
+ int xPos = x * int(getHScaleFactor());
+
+ if (xPos < clipRect.x())
+ continue;
+
+ if (xPos > (clipRect.x() + clipRect.width()))
+ break;
+
+ // include fiddle factor (+2)
+ int width =
+ int(m_rulerScale->getXForTime((*it)->getAbsoluteTime() +
+ (*it)->getDuration()) + 2)
+ + m_currentXOffset + int(m_xorigin) - x;
+
+ int blockHeight = int(double(height()) * (value / 127.0));
+
+ paint.setBrush(DefaultVelocityColour::getInstance()->getColour(value));
+
+ paint.drawRect(x, height() - blockHeight, width, blockHeight);
+ }
+}
+
+}
+#include "PropertyViewRuler.moc"
diff --git a/src/gui/rulers/PropertyViewRuler.h b/src/gui/rulers/PropertyViewRuler.h
new file mode 100644
index 0000000..b7d479c
--- /dev/null
+++ b/src/gui/rulers/PropertyViewRuler.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_PROPERTYVIEWRULER_H_
+#define _RG_PROPERTYVIEWRULER_H_
+
+#include "base/PropertyName.h"
+#include "gui/general/HZoomable.h"
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qsize.h>
+#include <qwidget.h>
+
+
+class QPaintEvent;
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class RulerScale;
+
+
+/**
+ * PropertyViewRuler is a widget that shows a range of Property
+ * (velocity, typically) values for a set of Rosegarden Events.
+ */
+class PropertyViewRuler : public QWidget, public HZoomable
+{
+ Q_OBJECT
+
+public:
+ PropertyViewRuler(RulerScale *rulerScale,
+ Segment *segment,
+ const PropertyName &property,
+ double xorigin = 0.0,
+ int height = 0,
+ QWidget* parent = 0,
+ const char *name = 0);
+
+ ~PropertyViewRuler();
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ void setMinimumWidth(int width) { m_width = width; }
+
+ /**
+ * Get the property name
+ */
+ PropertyName getPropertyName() const { return m_propertyName; }
+
+public slots:
+ void slotScrollHoriz(int x);
+
+protected:
+ virtual void paintEvent(QPaintEvent *);
+
+ //--------------- Data members ---------------------------------
+
+ PropertyName m_propertyName;
+
+ double m_xorigin;
+ int m_height;
+ int m_currentXOffset;
+ int m_width;
+
+ Segment *m_segment;
+ RulerScale *m_rulerScale;
+
+ QFont m_font;
+ QFont m_boldFont;
+ QFontMetrics m_fontMetrics;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/RawNoteRuler.cpp b/src/gui/rulers/RawNoteRuler.cpp
new file mode 100644
index 0000000..cc7d6e4
--- /dev/null
+++ b/src/gui/rulers/RawNoteRuler.cpp
@@ -0,0 +1,573 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "RawNoteRuler.h"
+
+#include "misc/Debug.h"
+#include "base/BaseProperties.h"
+#include "base/Composition.h"
+#include "base/NotationTypes.h"
+#include "base/NotationQuantizer.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "DefaultVelocityColour.h"
+#include "gui/general/GUIPalette.h"
+#include <klocale.h>
+#include <qcolor.h>
+#include <qpainter.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qtooltip.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+RawNoteRuler::RawNoteRuler(RulerScale *rulerScale,
+ Segment *segment,
+ double xorigin,
+ int height,
+ QWidget *parent,
+ const char *name) :
+ QWidget(parent, name),
+ m_xorigin(xorigin),
+ m_height(height),
+ m_currentXOffset(0),
+ m_width( -1),
+ m_segment(segment),
+ m_rulerScale(rulerScale)
+{
+ setBackgroundColor(GUIPalette::getColour(GUIPalette::RawNoteRulerBackground));
+ QToolTip::add(this,"");
+}
+
+RawNoteRuler::~RawNoteRuler()
+{
+ QToolTip::remove(this);
+ // nothing else
+}
+
+void
+RawNoteRuler::slotScrollHoriz(int x)
+{
+ int w = width(), h = height();
+ int dx = x - ( -m_currentXOffset);
+ if (dx == 0)
+ return ;
+ m_currentXOffset = -x;
+
+ if (dx > w*3 / 4 || dx < -w*3 / 4) {
+ update();
+ return ;
+ }
+
+ if (dx > 0) { // moving right, so the existing stuff moves left
+ bitBlt(this, 0, 0, this, dx, 0, w - dx, h);
+ repaint(w - dx, 0, dx, h);
+ } else { // moving left, so the existing stuff moves right
+ bitBlt(this, -dx, 0, this, 0, 0, w + dx, h);
+ repaint(0, 0, -dx, h);
+ }
+}
+
+QSize
+RawNoteRuler::sizeHint() const
+{
+ double width =
+ m_rulerScale->getBarPosition(m_rulerScale->getLastVisibleBar()) +
+ m_rulerScale->getBarWidth(m_rulerScale->getLastVisibleBar()) +
+ m_xorigin;
+
+ QSize res(std::max(int(width), m_width), m_height);
+
+ return res;
+}
+
+QSize
+RawNoteRuler::minimumSizeHint() const
+{
+ double firstBarWidth = m_rulerScale->getBarWidth(0) + m_xorigin;
+ QSize res = QSize(int(firstBarWidth), m_height);
+ return res;
+}
+
+std::pair<timeT, timeT>
+RawNoteRuler::getExtents(Segment::iterator i)
+{
+ const Quantizer *q =
+ m_segment->getComposition()->getNotationQuantizer();
+
+ timeT u0 = (*i)->getAbsoluteTime();
+ timeT u1 = u0 + (*i)->getDuration();
+
+ timeT q0 = q->getQuantizedAbsoluteTime(*i);
+ timeT q1 = q0 + q->getQuantizedDuration(*i);
+
+ timeT t0 = std::min(u0, q0);
+ timeT t1 = std::max(u1, q1);
+
+ return std::pair<timeT, timeT>(t0, t1);
+}
+
+Segment::iterator
+RawNoteRuler::addChildren(Segment *s,
+ Segment::iterator to,
+ timeT rightBound,
+ EventTreeNode *node)
+{
+ Segment::iterator i = node->node;
+
+ std::pair<timeT, timeT> iex = getExtents(i);
+ Segment::iterator j = i;
+ Segment::iterator rightmost = to;
+
+#ifdef DEBUG_RAW_NOTE_RULER
+
+ RG_DEBUG << "addChildren called for extents " << iex.first << "->" << iex.second << ", rightBound " << rightBound << endl;
+#endif
+
+ for (++j; j != to && s->isBeforeEndMarker(j); ) {
+
+ if (!(*j)->isa(Note::EventType)) {
+ ++j;
+ continue;
+ }
+ std::pair<timeT, timeT> jex = getExtents(j);
+
+#ifdef DEBUG_RAW_NOTE_RULER
+
+ RG_DEBUG << "addChildren: event at " << (*j)->getAbsoluteTime() << ", extents " << jex.first << "->" << jex.second << endl;
+#endif
+
+ if (jex.first == jex.second) {
+ ++j;
+ continue;
+ }
+ if (jex.first >= iex.second || jex.first >= rightBound)
+ break;
+
+#ifdef DEBUG_RAW_NOTE_RULER
+
+ RG_DEBUG << "addChildren: adding" << endl;
+#endif
+
+ EventTreeNode *subnode = new EventTreeNode(j);
+
+ Segment::iterator subRightmost = addChildren(s, to, rightBound, subnode);
+ if (subRightmost != to)
+ rightmost = subRightmost;
+ else
+ rightmost = j;
+
+ node->children.push_back(subnode);
+ j = s->findTime(jex.second);
+ }
+
+ return rightmost;
+}
+
+void
+RawNoteRuler::buildForest(Segment *s,
+ Segment::iterator from,
+ Segment::iterator to)
+{
+ for (EventTreeNode::NodeList::iterator i = m_forest.begin();
+ i != m_forest.end(); ++i) {
+ delete *i;
+ }
+ m_forest.clear();
+
+ timeT endTime = (s->isBeforeEndMarker(to) ? (*to)->getAbsoluteTime() :
+ s->getEndMarkerTime());
+
+ for (Segment::iterator i = from; i != to && s->isBeforeEndMarker(i); ) {
+
+ if (!(*i)->isa(Note::EventType)) {
+ ++i;
+ continue;
+ }
+
+ std::pair<timeT, timeT> iex = getExtents(i);
+
+#ifdef DEBUG_RAW_NOTE_RULER
+
+ RG_DEBUG << "buildForest: event at " << (*i)->getAbsoluteTime() << ", extents " << iex.first << "->" << iex.second << endl;
+#endif
+
+ if (iex.first == iex.second) {
+ ++i;
+ continue;
+ }
+ if (iex.first >= endTime)
+ break;
+
+ EventTreeNode *node = new EventTreeNode(i);
+ Segment::iterator rightmost = addChildren(s, to, iex.second, node);
+ m_forest.push_back(node);
+
+ if (rightmost != to) {
+ i = rightmost;
+ ++i;
+ } else {
+ i = s->findTime(iex.second);
+ }
+
+#ifdef DEBUG_RAW_NOTE_RULER
+ RG_DEBUG << "findTime " << iex.second << " returned iterator at " << (i == s->end() ? -1 : (*i)->getAbsoluteTime()) << endl;
+#endif
+
+ }
+}
+
+void
+RawNoteRuler::dumpSubtree(EventTreeNode *node, int depth)
+{
+ if (!node)
+ return ;
+#ifdef DEBUG_RAW_NOTE_RULER
+
+ for (int i = 0; i < depth; ++i)
+ std::cerr << " ";
+ if (depth > 0)
+ std::cerr << "->";
+ std::cerr << (*node->node)->getAbsoluteTime() << ","
+ << (*node->node)->getDuration() << " [";
+ long pitch = 0;
+ if ((*node->node)->get
+ <Int>(PITCH, pitch)) {
+ std::cerr << pitch << "]" << std::endl;
+ }
+ else {
+ std::cerr << "no-pitch]" << std::endl;
+ }
+ for (EventTreeNode::NodeList::iterator i = node->children.begin();
+ i != node->children.end(); ++i) {
+ dumpSubtree(*i, depth + 1);
+ }
+#endif
+ (void)depth; // avoid warnings
+}
+
+void
+RawNoteRuler::dumpForest(EventTreeNode::NodeList *forest)
+{
+#ifdef DEBUG_RAW_NOTE_RULER
+ std::cerr << "\nFOREST:\n" << std::endl;
+
+ for (unsigned int i = 0; i < forest->size(); ++i) {
+
+ std::cerr << "\nTREE " << i << ":\n" << std::endl;
+ dumpSubtree((*forest)[i], 0);
+ }
+
+ std::cerr << std::endl;
+#endif
+
+ (void)forest; // avoid warnings
+}
+
+int
+RawNoteRuler::EventTreeNode::getDepth()
+{
+ int subchildrenDepth = 0;
+ for (NodeList::iterator i = children.begin();
+ i != children.end(); ++i) {
+ int subchildDepth = (*i)->getDepth();
+ if (subchildDepth > subchildrenDepth)
+ subchildrenDepth = subchildDepth;
+ }
+ return subchildrenDepth + 1;
+}
+
+int
+RawNoteRuler::EventTreeNode::getChildrenAboveOrBelow(bool below, int p)
+{
+ long pitch(p);
+ if (pitch < 0)
+ (*node)->get
+ <Int>(BaseProperties::PITCH, pitch);
+
+ int max = 0;
+
+ for (NodeList::iterator i = children.begin();
+ i != children.end(); ++i) {
+ int forThisChild = (*i)->getChildrenAboveOrBelow(below, pitch);
+ long thisChildPitch = pitch;
+ (*(*i)->node)->get
+ <Int>(BaseProperties::PITCH, thisChildPitch);
+ if (below ? (thisChildPitch < pitch) : (thisChildPitch > pitch)) {
+ ++forThisChild;
+ }
+ if (forThisChild > max)
+ max = forThisChild;
+ }
+
+ return max;
+}
+
+void
+RawNoteRuler::drawNode(QPainter &paint, DefaultVelocityColour &vc,
+ EventTreeNode *node, double height, double yorigin)
+{
+ int depth = node->getDepth();
+ int above = node->getChildrenAboveOrBelow(false);
+
+#ifdef DEBUG_RAW_NOTE_RULER
+
+ int below = node->getChildrenAboveOrBelow(true);
+
+ NOTATION_DEBUG << "RawNoteRuler::drawNode: children above: "
+ << above << ", below: " << below << endl;
+#endif
+
+ int toFit = depth;
+
+ double heightPer = double(height) / toFit;
+ if (heightPer > m_height / 4)
+ heightPer = m_height / 4;
+ if (heightPer < 2)
+ heightPer = 2;
+
+ double myOrigin = yorigin + (heightPer * above);
+ long myPitch = 60;
+ (*node->node)->get
+ <Int>(BaseProperties::PITCH, myPitch);
+
+ long velocity = 100;
+ (*node->node)->get
+ <Int>(BaseProperties::VELOCITY, velocity);
+ QColor colour = vc.getColour(velocity);
+
+ timeT start = (*node->node)->getAbsoluteTime();
+ timeT end = (*node->node)->getDuration() + start;
+
+ double u0 = m_rulerScale->getXForTime(start);
+ double u1 = m_rulerScale->getXForTime(end);
+
+ u0 += m_currentXOffset + m_xorigin;
+ u1 += m_currentXOffset + m_xorigin;
+
+ start = m_segment->getComposition()->getNotationQuantizer()->
+ getQuantizedAbsoluteTime(*node->node);
+ end = start + m_segment->getComposition()->getNotationQuantizer()->
+ getQuantizedDuration(*node->node);
+
+ double q0 = m_rulerScale->getXForTime(start);
+ double q1 = m_rulerScale->getXForTime(end);
+
+ q0 += m_currentXOffset + m_xorigin;
+ q1 += m_currentXOffset + m_xorigin;
+
+#ifdef DEBUG_RAW_NOTE_RULER
+
+ NOTATION_DEBUG << "RawNoteRuler: (" << int(start) << "," << myOrigin
+ << ") -> (" << int(end) << "," << myOrigin << ")" << endl;
+#endif
+
+ int qi0 = int(q0);
+ int ui0 = int(u0);
+ int qi1 = int(q1);
+ int ui1 = int(u1);
+ // int qiw = int(q1-q0) - 1;
+ int uiw = int(u1 - u0) - 1;
+ // int iy = int(myOrigin + (height - heightPer) / 2);
+ int iy = int(myOrigin);
+ int ih = int(heightPer);
+
+#ifdef DEBUG_RAW_NOTE_RULER
+
+ NOTATION_DEBUG << "RawNoteRuler: height " << height << ", heightPer "
+ << heightPer << ", iy " << iy << endl;
+#endif
+
+ paint.setPen(colour);
+ paint.setBrush(colour);
+ paint.drawRect(ui0 + 1, iy + 1, uiw, ih - 1);
+
+ paint.setPen(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground));
+ paint.setBrush(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground));
+ paint.drawLine(qi0, iy, qi1 - 1, iy);
+ paint.drawLine(qi0, iy + ih, qi1 - 1, iy + ih);
+ paint.drawLine(ui0, iy + 1, ui0, iy + ih - 1);
+ paint.drawLine(ui1 - 1, iy + 1, ui1 - 1, iy + ih - 1);
+
+ for (EventTreeNode::NodeList::iterator i = node->children.begin();
+ i != node->children.end(); ++i) {
+
+ long nodePitch = myPitch;
+ (*(*i)->node)->get
+ <Int>(BaseProperties::PITCH, nodePitch);
+
+ if (nodePitch < myPitch) {
+
+ drawNode(paint, vc, *i,
+ height - heightPer - myOrigin, myOrigin + heightPer);
+
+ } else {
+
+ drawNode(paint, vc, *i,
+ myOrigin - yorigin, yorigin);
+ }
+ }
+}
+
+void
+RawNoteRuler::paintEvent(QPaintEvent* e)
+{
+ if (!m_segment || !m_segment->getComposition())
+ return ;
+
+ // Tooltips
+ {
+ QToolTip::remove(this);
+ TrackId trackId = m_segment->getTrack();
+ Track *track =
+ m_segment->getComposition()->getTrackById(trackId);
+ int trackPosition = -1;
+ if (track)
+ trackPosition = track->getPosition();
+
+ QToolTip::add(this,i18n("Track #%1, Segment \"%2\" (runtime id %3)")
+ .arg(trackPosition + 1)
+ .arg(m_segment->getLabel())
+ .arg(m_segment->getRuntimeId()));
+ }
+
+ // START_TIMING;
+
+ QPainter paint(this);
+ paint.setClipRegion(e->region());
+ paint.setClipRect(e->rect().normalize());
+
+ QRect clipRect = paint.clipRegion().boundingRect();
+
+ timeT from = m_rulerScale->getTimeForX
+ (clipRect.x() - m_currentXOffset - 100 - m_xorigin);
+ timeT to = m_rulerScale->getTimeForX
+ (clipRect.x() + clipRect.width() - m_currentXOffset + 100 - m_xorigin);
+
+ paint.setPen(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground));
+ paint.setBrush(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground));
+ paint.drawLine(0, 0, width(), 0);
+
+ // draw the extent of the segment using its color
+
+ QColor brushColor = GUIPalette::convertColour(m_segment->getComposition()->
+ getSegmentColourMap().getColourByIndex(m_segment->getColourIndex()));
+ paint.setPen(brushColor);
+ paint.setBrush(brushColor);
+ int x0 = int(m_rulerScale->getXForTime(m_segment->getStartTime()) +
+ m_currentXOffset + m_xorigin);
+ int x1 = int(m_rulerScale->getXForTime(m_segment->getEndTime()) +
+ m_currentXOffset + m_xorigin);
+ paint.drawRect(x0, 1, x1-x0+1, height()-1);
+
+ // draw the bar divisions
+
+ int firstBar = m_segment->getComposition()->getBarNumber(from);
+ int lastBar = m_segment->getComposition()->getBarNumber(to);
+ std::vector<int> divisions;
+
+ for (int barNo = firstBar; barNo <= lastBar; ++barNo) {
+
+ bool isNew = false;
+ TimeSignature timeSig =
+ m_segment->getComposition()->getTimeSignatureInBar(barNo, isNew);
+ if (isNew || barNo == firstBar) {
+ timeSig.getDivisions(3, divisions);
+ if (timeSig == TimeSignature()) // special case for 4/4
+ divisions[0] = 2;
+ }
+
+ timeT barStart = m_segment->getComposition()->getBarStart(barNo);
+ timeT base = timeSig.getBarDuration();
+ timeT barEnd = barStart + base;
+
+ paint.setPen(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground));
+ paint.setBrush(GUIPalette::getColour(GUIPalette::RawNoteRulerForeground));
+
+ int x = int(m_rulerScale->getXForTime(barStart) +
+ m_currentXOffset + m_xorigin);
+ paint.drawLine(x, 1, x, m_height);
+
+ for (int depth = 0; depth < 3; ++depth) {
+
+ int grey = depth * 60 + 60;
+ paint.setPen(QColor(grey, grey, grey));
+ paint.setBrush(QColor(grey, grey, grey));
+
+ base /= divisions[depth];
+ timeT t(barStart + base);
+ while (t < barEnd) {
+ if ((t - barStart) % (base * divisions[depth]) != 0) {
+ int x = int(m_rulerScale->getXForTime(t) +
+ m_currentXOffset + m_xorigin);
+ paint.drawLine(x, 1, x, m_height);
+ }
+ t += base;
+ }
+ }
+ }
+
+ // PRINT_ELAPSED("RawNoteRuler::paintEvent: drawing bar lines and divisions");
+
+#ifdef DEBUG_RAW_NOTE_RULER
+ NOTATION_DEBUG << "RawNoteRuler: from is " << from << ", to is " << to << endl;
+#endif
+
+ Segment::iterator i = m_segment->findNearestTime(from);
+ if (i == m_segment->end())
+ i = m_segment->begin();
+
+ // somewhat experimental, as is this whole class
+ Segment::iterator j = m_segment->findTime(to);
+ buildForest(m_segment, i, j);
+
+ // PRINT_ELAPSED("RawNoteRuler::paintEvent: buildForest");
+
+ dumpForest(&m_forest);
+
+ // PRINT_ELAPSED("RawNoteRuler::paintEvent: dumpForest");
+
+ for (EventTreeNode::NodeList::iterator fi = m_forest.begin();
+ fi != m_forest.end(); ++fi) {
+
+ // Each tree in the forest should represent a note that starts
+ // at a time when no other notes are playing (at least of
+ // those that started no earlier than the paint start time).
+ // Each node in that tree represents a note that starts
+ // playing during its parent node's note, or at the same time
+ // as it.
+
+ drawNode(paint, *DefaultVelocityColour::getInstance(), *fi, m_height - 3, 2);
+
+ }
+
+ // PRINT_ELAPSED("RawNoteRuler::paintEvent: complete");
+}
+
+}
+#include "RawNoteRuler.moc"
diff --git a/src/gui/rulers/RawNoteRuler.h b/src/gui/rulers/RawNoteRuler.h
new file mode 100644
index 0000000..f194062
--- /dev/null
+++ b/src/gui/rulers/RawNoteRuler.h
@@ -0,0 +1,128 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_RAWNOTERULER_H_
+#define _RG_RAWNOTERULER_H_
+
+#include "base/Segment.h"
+#include <qsize.h>
+#include <qwidget.h>
+#include <utility>
+#include <vector>
+#include "base/Event.h"
+
+
+class QPaintEvent;
+class QPainter;
+
+
+namespace Rosegarden
+{
+
+class RulerScale;
+class DefaultVelocityColour;
+
+
+/**
+ * RawNoteRuler is a ruler that shows in a vaguely matrix-like fashion
+ * when notes start and end, for use with a notation view that can't
+ * otherwise show this relatively precise unquantized information.
+ * It has no editing function (yet?)
+ */
+
+class RawNoteRuler : public QWidget
+{
+ Q_OBJECT
+
+public:
+ RawNoteRuler(RulerScale *rulerScale,
+ Segment *segment,
+ double xorigin = 0.0,
+ int height = 0,
+ QWidget* parent = 0,
+ const char *name = 0);
+
+ ~RawNoteRuler();
+
+ void setCurrentSegment(Segment *segment) {
+ m_segment = segment;
+ }
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ void setMinimumWidth(int width) { m_width = width; }
+
+public slots:
+ void slotScrollHoriz(int x);
+
+protected:
+ virtual void paintEvent(QPaintEvent*);
+
+private:
+ double m_xorigin;
+ int m_height;
+ int m_currentXOffset;
+ int m_width;
+
+ Segment *m_segment;
+ RulerScale *m_rulerScale;
+
+ struct EventTreeNode
+ {
+ typedef std::vector<EventTreeNode *> NodeList;
+
+ EventTreeNode(Segment::iterator n) : node(n) { }
+ ~EventTreeNode() {
+ for (NodeList::iterator i = children.begin();
+ i != children.end(); ++i) {
+ delete *i;
+ }
+ }
+
+ int getDepth();
+ int getChildrenAboveOrBelow(bool below = false, int pitch = -1);
+
+ Segment::iterator node;
+ NodeList children;
+ };
+
+ std::pair<timeT, timeT> getExtents(Segment::iterator);
+ Segment::iterator addChildren(Segment *, Segment::iterator, timeT, EventTreeNode *);
+ void dumpSubtree(EventTreeNode *, int);
+ void dumpForest(std::vector<EventTreeNode *> *);
+ void buildForest(Segment *, Segment::iterator, Segment::iterator);
+
+ void drawNode(QPainter &, DefaultVelocityColour &, EventTreeNode *,
+ double height, double yorigin);
+
+ // needs to be class with dtor &c and containing above methods
+ EventTreeNode::NodeList m_forest;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/StandardRuler.cpp b/src/gui/rulers/StandardRuler.cpp
new file mode 100644
index 0000000..611c991
--- /dev/null
+++ b/src/gui/rulers/StandardRuler.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "StandardRuler.h"
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "MarkerRuler.h"
+#include "base/RulerScale.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/MultiViewCommandHistory.h"
+#include "gui/application/RosegardenGUIApp.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/rulers/LoopRuler.h"
+#include "document/RosegardenGUIDoc.h"
+#include <qobject.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+StandardRuler::StandardRuler(RosegardenGUIDoc *doc,
+ RulerScale *rulerScale,
+ double xorigin,
+ int barHeight,
+ bool invert,
+ QWidget* parent,
+ const char* name,
+ WFlags f):
+ QVBox(parent, name, f),
+ m_invert(invert),
+ m_loopRulerHeight(10),
+ m_currentXOffset(0),
+ m_doc(doc),
+ m_rulerScale(rulerScale),
+ m_hButtonBar(0)
+{
+ setSpacing(0);
+
+ if (!m_invert) {
+ m_hButtonBar = new MarkerRuler
+ (m_doc, m_rulerScale, barHeight - m_loopRulerHeight, xorigin, this);
+ }
+
+ m_loopRuler = new LoopRuler
+ (m_doc, m_rulerScale, m_loopRulerHeight, xorigin, m_invert, this, name);
+
+ if (m_invert) {
+ m_hButtonBar = new MarkerRuler
+ (m_doc, m_rulerScale, barHeight - m_loopRulerHeight, xorigin, this);
+ }
+
+ QObject::connect
+ (doc->getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(update()));
+
+}
+
+void StandardRuler::setSnapGrid(SnapGrid *grid)
+{
+ m_loopRuler->setSnapGrid(grid);
+}
+
+void StandardRuler::connectRulerToDocPointer(RosegardenGUIDoc *doc)
+{
+
+ RG_DEBUG << "StandardRuler::connectRulerToDocPointer" << endl;
+
+ // use the document as a hub for pointer and loop set related signals
+ // pointer and loop drag signals are specific to the current view,
+ // so they are re-emitted from the loop ruler by this widget
+ //
+ QObject::connect
+ (m_loopRuler, SIGNAL(setPointerPosition(timeT)),
+ doc, SLOT(slotSetPointerPosition(timeT)));
+
+ QObject::connect
+ (m_hButtonBar, SIGNAL(setPointerPosition(timeT)),
+ doc, SLOT(slotSetPointerPosition(timeT)));
+
+ QObject::connect
+ (m_hButtonBar, SIGNAL(editMarkers()),
+ RosegardenGUIApp::self(), SLOT(slotEditMarkers()));
+
+ QObject::connect
+ (m_hButtonBar, SIGNAL(addMarker(timeT)),
+ RosegardenGUIApp::self(), SLOT(slotAddMarker(timeT)));
+
+ QObject::connect
+ (m_hButtonBar, SIGNAL(deleteMarker(int, timeT, QString, QString)),
+ RosegardenGUIApp::self(), SLOT(slotDeleteMarker(int, timeT, QString, QString)));
+
+ QObject::connect
+ (m_loopRuler, SIGNAL(dragPointerToPosition(timeT)),
+ this, SIGNAL(dragPointerToPosition(timeT)));
+
+ QObject::connect
+ (m_loopRuler, SIGNAL(dragLoopToPosition(timeT)),
+ this, SIGNAL(dragLoopToPosition(timeT)));
+
+ QObject::connect
+ (m_loopRuler, SIGNAL(setPlayPosition(timeT)),
+ RosegardenGUIApp::self(), SLOT(slotSetPlayPosition(timeT)));
+
+ QObject::connect
+ (m_hButtonBar, SIGNAL(setLoop(timeT, timeT)),
+ doc, SLOT(slotSetLoop(timeT, timeT)));
+
+ QObject::connect
+ (m_loopRuler, SIGNAL(setLoop(timeT, timeT)),
+ doc, SLOT(slotSetLoop(timeT, timeT)));
+
+ QObject::connect
+ (doc, SIGNAL(loopChanged(timeT, timeT)),
+ m_loopRuler,
+ SLOT(slotSetLoopMarker(timeT, timeT)));
+
+ m_loopRuler->setBackgroundColor(GUIPalette::getColour(GUIPalette::PointerRuler));
+}
+
+void StandardRuler::slotScrollHoriz(int x)
+{
+ m_loopRuler->scrollHoriz(x);
+ m_hButtonBar->scrollHoriz(x);
+}
+
+void StandardRuler::setMinimumWidth(int width)
+{
+ m_hButtonBar->setMinimumWidth(width);
+ m_loopRuler->setMinimumWidth(width);
+}
+
+void StandardRuler::setHScaleFactor(double dy)
+{
+ m_hButtonBar->setHScaleFactor(dy);
+ m_loopRuler->setHScaleFactor(dy);
+}
+
+void StandardRuler::paintEvent(QPaintEvent *e)
+{
+ m_hButtonBar->update();
+ m_loopRuler->update();
+ QWidget::paintEvent(e);
+}
+
+}
+#include "StandardRuler.moc"
diff --git a/src/gui/rulers/StandardRuler.h b/src/gui/rulers/StandardRuler.h
new file mode 100644
index 0000000..de9804d
--- /dev/null
+++ b/src/gui/rulers/StandardRuler.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_STANDARDRULER_H_
+#define _RG_STANDARDRULER_H_
+
+#include <qvbox.h>
+#include "base/Event.h"
+
+
+class QWidget;
+class QPaintEvent;
+
+
+namespace Rosegarden
+{
+
+class RulerScale;
+class RosegardenGUIDoc;
+class LoopRuler;
+class MarkerRuler;
+class SnapGrid;
+
+
+class StandardRuler : public QVBox
+{
+ Q_OBJECT
+
+public:
+ StandardRuler(RosegardenGUIDoc *doc,
+ RulerScale *rulerScale,
+ double xorigin,
+ int buttonHeight,
+ bool invert = false, // draw upside-down
+ QWidget* parent = 0,
+ const char* name = 0,
+ WFlags f=0);
+
+ void setSnapGrid(SnapGrid *grid);
+
+ LoopRuler* getLoopRuler() { return m_loopRuler; }
+
+ /**
+ * Make connections from the LoopRuler to the document's
+ * position pointer -- the standard use for a LoopRuler.
+ * If you don't call this, you'll have to connect the
+ * LoopRuler's signals up to something yourself.
+ */
+ void connectRulerToDocPointer(RosegardenGUIDoc *doc);
+
+ void setMinimumWidth(int width);
+
+ void setHScaleFactor(double dy);
+
+public slots:
+ void slotScrollHoriz(int x);
+
+signals:
+ /// reflected from the loop ruler
+ void dragPointerToPosition(timeT);
+
+ /// reflected from the loop ruler
+ void dragLoopToPosition(timeT);
+
+
+protected:
+ virtual void paintEvent(QPaintEvent *);
+
+private:
+ //--------------- Data members ---------------------------------
+ bool m_invert;
+ int m_loopRulerHeight;
+ int m_currentXOffset;
+
+ RosegardenGUIDoc *m_doc;
+ RulerScale *m_rulerScale;
+
+ MarkerRuler *m_hButtonBar;
+ LoopRuler *m_loopRuler;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/TempoColour.cpp b/src/gui/rulers/TempoColour.cpp
new file mode 100644
index 0000000..5ab396a
--- /dev/null
+++ b/src/gui/rulers/TempoColour.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "TempoColour.h"
+
+#include "gui/general/GUIPalette.h"
+#include <qcolor.h>
+
+
+namespace Rosegarden
+{
+
+QColor
+TempoColour::getColour(double tempo)
+{
+ int h, s, v;
+ QColor c = GUIPalette::getColour(GUIPalette::TempoBase);
+ c.hsv(&h, &s, &v);
+ v += 20;
+ if (v > 255)
+ v = 255;
+
+ h = (90 + int(tempo));
+
+ while (h < 0)
+ h += 360;
+ while (h >= 360)
+ h -= 360;
+
+ return QColor(h, s, v, QColor::Hsv);
+}
+
+}
diff --git a/src/gui/rulers/TempoColour.h b/src/gui/rulers/TempoColour.h
new file mode 100644
index 0000000..be5e3fa
--- /dev/null
+++ b/src/gui/rulers/TempoColour.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_TEMPOCOLOUR_H_
+#define _RG_TEMPOCOLOUR_H_
+
+#include <qcolor.h>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+class TempoColour
+{
+
+public:
+ TempoColour():m_tempo(0) {;}
+ TempoColour(double tempo):m_tempo(tempo) {;}
+
+ // Get the colour for a tempo
+ //
+ QColor getColour() { return getColour(m_tempo); }
+ static QColor getColour(double tempo);
+
+private:
+
+ double m_tempo;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/TempoRuler.cpp b/src/gui/rulers/TempoRuler.cpp
new file mode 100644
index 0000000..270b224
--- /dev/null
+++ b/src/gui/rulers/TempoRuler.cpp
@@ -0,0 +1,1091 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "TempoRuler.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/RulerScale.h"
+#include "base/SnapGrid.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/MultiViewCommandHistory.h"
+#include "gui/application/RosegardenGUIApp.h"
+#include "gui/dialogs/TempoDialog.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/widgets/TextFloat.h"
+#include "TempoColour.h"
+#include <kaction.h>
+#include <kglobal.h>
+#include <kxmlguiclient.h>
+#include <kxmlguifactory.h>
+#include <qcolor.h>
+#include <qcursor.h>
+#include <qevent.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qiconset.h>
+#include <qobject.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qpopupmenu.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+TempoRuler::TempoRuler(RulerScale *rulerScale,
+ RosegardenGUIDoc *doc,
+ KMainWindow *parentMainWindow,
+ double xorigin,
+ int height,
+ bool small,
+ QWidget *parent,
+ const char *name) :
+ QWidget(parent, name),
+ m_xorigin(xorigin),
+ m_height(height),
+ m_currentXOffset(0),
+ m_width( -1),
+ m_small(small),
+ m_illuminate( -1),
+ m_illuminatePoint(false),
+ m_illuminateTarget(false),
+ m_refreshLinesOnly(false),
+ m_dragVert(false),
+ m_dragTarget(false),
+ m_dragHoriz(false),
+ m_dragStartY(0),
+ m_dragStartX(0),
+ m_dragFine(false),
+ m_clickX(0),
+ m_dragStartTempo( -1),
+ m_dragStartTarget( -1),
+ m_dragOriginalTempo( -1),
+ m_dragOriginalTarget( -1),
+ m_composition(&doc->getComposition()),
+ m_rulerScale(rulerScale),
+ m_menu(0),
+ m_parentMainWindow(parentMainWindow),
+ m_fontMetrics(m_boldFont)
+{
+ // m_font.setPointSize(m_small ? 9 : 11);
+ // m_boldFont.setPointSize(m_small ? 9 : 11);
+
+ // m_font.setPixelSize(m_height * 2 / 3);
+ // m_boldFont.setPixelSize(m_height * 2 / 3);
+
+ m_font.setPixelSize(m_height / 3);
+ m_boldFont.setPixelSize(m_height * 2 / 5);
+ m_boldFont.setBold(true);
+ m_fontMetrics = QFontMetrics(m_boldFont);
+
+ m_textFloat = new TextFloat(this);
+ m_textFloat->hide();
+
+ // setBackgroundColor(GUIPalette::getColour(GUIPalette::TextRulerBackground));
+ setBackgroundMode(Qt::NoBackground);
+
+ QObject::connect
+ (doc->getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(update()));
+
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QIconSet icon;
+
+ icon = QIconSet(QPixmap(pixmapDir + "/toolbar/event-insert-tempo.png"));
+ new KAction(i18n("Insert Tempo Change"), icon, 0, this,
+ SLOT(slotInsertTempoHere()), actionCollection(),
+ "insert_tempo_here");
+
+ new KAction(i18n("Insert Tempo Change at Playback Position"), 0, 0, this,
+ SLOT(slotInsertTempoAtPointer()), actionCollection(),
+ "insert_tempo_at_pointer");
+
+ icon = QIconSet(QPixmap(pixmapDir + "/toolbar/event-delete.png"));
+ new KAction(i18n("Delete Tempo Change"), icon, 0, this,
+ SLOT(slotDeleteTempoChange()), actionCollection(),
+ "delete_tempo");
+
+ new KAction(i18n("Ramp Tempo to Next Tempo"), 0, 0, this,
+ SLOT(slotRampToNext()), actionCollection(),
+ "ramp_to_next");
+
+ new KAction(i18n("Un-Ramp Tempo"), 0, 0, this,
+ SLOT(slotUnramp()), actionCollection(),
+ "unramp");
+
+ icon = QIconSet(QPixmap(pixmapDir + "/toolbar/event-edit.png"));
+ new KAction(i18n("Edit Tempo..."), icon, 0, this,
+ SLOT(slotEditTempo()), actionCollection(),
+ "edit_tempo");
+
+ new KAction(i18n("Edit Time Signature..."), 0, 0, this,
+ SLOT(slotEditTimeSignature()), actionCollection(),
+ "edit_time_signature");
+
+ new KAction(i18n("Open Tempo and Time Signature Editor"), 0, 0, this,
+ SLOT(slotEditTempos()), actionCollection(),
+ "edit_tempos");
+
+ setMouseTracking(false);
+}
+
+TempoRuler::~TempoRuler()
+{
+ // we have to do this so that the menu is re-created properly
+ // when the main window is itself recreated (on a File->New for instance)
+ KXMLGUIFactory* factory = m_parentMainWindow->factory();
+ if (factory)
+ factory->removeClient(this);
+}
+
+void
+TempoRuler::connectSignals()
+{
+ connect(this,
+ SIGNAL(doubleClicked(timeT)),
+ RosegardenGUIApp::self(),
+ SLOT(slotEditTempos(timeT)));
+
+ connect(this,
+ SIGNAL(changeTempo(timeT,
+ tempoT,
+ tempoT,
+ TempoDialog::TempoDialogAction)),
+ RosegardenGUIApp::self(),
+ SLOT(slotChangeTempo(timeT,
+ tempoT,
+ tempoT,
+ TempoDialog::TempoDialogAction)));
+
+ connect(this,
+ SIGNAL(moveTempo(timeT,
+ timeT)),
+ RosegardenGUIApp::self(),
+ SLOT(slotMoveTempo(timeT,
+ timeT)));
+
+ connect(this,
+ SIGNAL(deleteTempo(timeT)),
+ RosegardenGUIApp::self(),
+ SLOT(slotDeleteTempo(timeT)));
+
+ connect(this,
+ SIGNAL(editTempo(timeT)),
+ RosegardenGUIApp::self(),
+ SLOT(slotEditTempo(timeT)));
+
+ connect(this,
+ SIGNAL(editTimeSignature(timeT)),
+ RosegardenGUIApp::self(),
+ SLOT(slotEditTimeSignature(timeT)));
+
+ connect(this,
+ SIGNAL(editTempos(timeT)),
+ RosegardenGUIApp::self(),
+ SLOT(slotEditTempos(timeT)));
+}
+
+void
+TempoRuler::slotScrollHoriz(int x)
+{
+ int w = width(), h = height();
+ int dx = x - ( -m_currentXOffset);
+ m_currentXOffset = -x;
+
+ if (dx > w*3 / 4 || dx < -w*3 / 4) {
+ update();
+ return ;
+ }
+
+ if (dx > 0) { // moving right, so the existing stuff moves left
+ bitBlt(this, 0, 0, this, dx, 0, w - dx, h);
+ repaint(w - dx, 0, dx, h);
+ } else { // moving left, so the existing stuff moves right
+ bitBlt(this, -dx, 0, this, 0, 0, w + dx, h);
+ repaint(0, 0, -dx, h);
+ }
+}
+
+void
+TempoRuler::mousePressEvent(QMouseEvent *e)
+{
+ if (e->button() == LeftButton) {
+
+ if (e->type() == QEvent::MouseButtonDblClick) {
+ timeT t = m_rulerScale->getTimeForX
+ (e->x() - m_currentXOffset - m_xorigin);
+ emit doubleClicked(t);
+ return ;
+ }
+
+ int x = e->x() + 1;
+ int y = e->y();
+ timeT t = m_rulerScale->getTimeForX(x - m_currentXOffset - m_xorigin);
+ int tcn = m_composition->getTempoChangeNumberAt(t);
+
+ if (tcn < 0 || tcn >= m_composition->getTempoChangeCount())
+ return ;
+
+ std::pair<timeT, tempoT> tc = m_composition->getTempoChange(tcn);
+ std::pair<bool, tempoT> tr = m_composition->getTempoRamping(tcn, true);
+
+ m_dragStartY = y;
+ m_dragStartX = x;
+ m_dragStartTime = tc.first;
+ m_dragPreviousTime = m_dragStartTime;
+ m_dragStartTempo = tc.second;
+ m_dragStartTarget = tr.first ? tr.second : -1;
+ m_dragOriginalTempo = m_dragStartTempo;
+ m_dragOriginalTarget = m_dragStartTarget;
+ m_dragFine = ((e->state() & Qt::ShiftButton) != 0);
+
+ int px = m_rulerScale->getXForTime(tc.first) + m_currentXOffset + m_xorigin;
+ if (x >= px && x < px + 5) {
+ m_dragHoriz = true;
+ m_dragVert = false;
+ setCursor(Qt::SplitHCursor);
+ } else {
+ timeT nt = m_composition->getEndMarker();
+ if (tcn < m_composition->getTempoChangeCount() - 1) {
+ nt = m_composition->getTempoChange(tcn + 1).first;
+ }
+ int nx = m_rulerScale->getXForTime(nt) + m_currentXOffset + m_xorigin;
+ if (x > px + 5 && x > nx - 5) {
+ m_dragTarget = true;
+ setCursor(Qt::SizeVerCursor);
+ } else {
+ m_dragTarget = false;
+ setCursor(Qt::SplitVCursor);
+ }
+ m_dragVert = true;
+ m_dragHoriz = false;
+ }
+
+ } else if (e->button() == RightButton) {
+
+ m_clickX = e->x();
+ if (!m_menu)
+ createMenu();
+ if (m_menu) {
+ // enable 'delete' action only if cursor is actually over a tempo change
+ actionCollection()->action("delete_tempo")->setEnabled(m_illuminatePoint);
+ m_menu->exec(QCursor::pos());
+ }
+
+ }
+}
+
+void
+TempoRuler::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (m_dragVert) {
+
+ m_dragVert = false;
+ unsetCursor();
+
+ if (e->x() < 0 || e->x() >= width() ||
+ e->y() < 0 || e->y() >= height()) {
+ leaveEvent(0);
+ }
+
+ // First we make a note of the values that we just set and
+ // restore the tempo to whatever it was previously, so that
+ // the undo for any following command will work correctly.
+ // Then we emit so that our user can issue the right command.
+
+ int tcn = m_composition->getTempoChangeNumberAt(m_dragStartTime);
+ std::pair<timeT, tempoT> tc = m_composition->getTempoChange(tcn);
+ std::pair<bool, tempoT> tr = m_composition->getTempoRamping(tcn, true);
+
+ if (tc.second != m_dragOriginalTempo) {
+ m_composition->addTempoAtTime(m_dragStartTime,
+ m_dragOriginalTempo,
+ m_dragOriginalTarget);
+ emit changeTempo(m_dragStartTime, tc.second,
+ tr.first ? tr.second : -1,
+ TempoDialog::AddTempo);
+ }
+
+ return ;
+
+ } else if (m_dragHoriz) {
+
+ m_dragHoriz = false;
+ unsetCursor();
+
+ if (e->x() < 0 || e->x() >= width() ||
+ e->y() < 0 || e->y() >= height()) {
+ leaveEvent(0);
+ }
+
+ if (m_dragPreviousTime != m_dragStartTime) {
+
+ // As above, restore the original tempo and then emit a
+ // signal to ensure a proper command happens.
+
+ int tcn = m_composition->getTempoChangeNumberAt(m_dragPreviousTime);
+ m_composition->removeTempoChange(tcn);
+ m_composition->addTempoAtTime(m_dragStartTime,
+ m_dragStartTempo,
+ m_dragStartTarget);
+
+ emit moveTempo(m_dragStartTime, m_dragPreviousTime);
+ }
+
+ return ;
+ }
+}
+
+void
+TempoRuler::mouseMoveEvent(QMouseEvent *e)
+{
+ bool shiftPressed = ((e->state() & Qt::ShiftButton) != 0);
+
+ if (m_dragVert) {
+
+ if (shiftPressed != m_dragFine) {
+
+ m_dragFine = shiftPressed;
+ m_dragStartY = e->y();
+
+ // reset the start tempi to whatever we last updated them
+ // to as we switch into or out of fine mode
+ int tcn = m_composition->getTempoChangeNumberAt(m_dragStartTime);
+ std::pair<timeT, tempoT> tc = m_composition->getTempoChange(tcn);
+ std::pair<bool, tempoT> tr = m_composition->getTempoRamping(tcn, true);
+ m_dragStartTempo = tc.second;
+ m_dragStartTarget = tr.first ? tr.second : -1;
+ }
+
+ int diff = m_dragStartY - e->y(); // +ve for upwards drag
+ tempoT newTempo = m_dragStartTempo;
+ tempoT newTarget = m_dragStartTarget;
+
+ if (diff != 0) {
+
+ float qpm = m_composition->getTempoQpm(newTempo);
+
+ if (m_dragTarget && newTarget > 0) {
+ qpm = m_composition->getTempoQpm(newTarget);
+ }
+
+ float qdiff = (m_dragFine ? diff * 0.05 : diff * 0.5);
+ qpm += qdiff;
+ if (qpm < 1)
+ qpm = 1;
+
+ if (m_dragTarget) {
+
+ newTarget = m_composition->getTempoForQpm(qpm);
+
+ } else {
+
+ newTempo = m_composition->getTempoForQpm(qpm);
+
+ if (newTarget >= 0) {
+ qpm = m_composition->getTempoQpm(newTarget);
+ qpm += qdiff;
+ if (qpm < 1)
+ qpm = 1;
+ newTarget = m_composition->getTempoForQpm(qpm);
+ }
+ }
+ }
+
+ showTextFloat(newTempo, newTarget, m_dragStartTime);
+ m_composition->addTempoAtTime(m_dragStartTime, newTempo, newTarget);
+ update();
+
+ } else if (m_dragHoriz) {
+
+ int x = e->x();
+
+ SnapGrid grid(m_rulerScale);
+ if (shiftPressed) {
+ grid.setSnapTime(SnapGrid::NoSnap);
+ } else {
+ grid.setSnapTime(SnapGrid::SnapToUnit);
+ }
+ timeT newTime = grid.snapX(x - m_currentXOffset - m_xorigin,
+ SnapGrid::SnapEither);
+
+ int tcn = m_composition->getTempoChangeNumberAt(m_dragPreviousTime);
+ int ncn = m_composition->getTempoChangeNumberAt(newTime);
+ if (ncn > tcn || ncn < tcn - 1)
+ return ;
+ if (ncn >= 0 && ncn == tcn - 1) {
+ std::pair<timeT, tempoT> nc = m_composition->getTempoChange(ncn);
+ if (nc.first == newTime)
+ return ;
+ }
+
+ // std::cerr << " -> " << newTime << std::endl;
+
+ m_composition->removeTempoChange(tcn);
+ m_composition->addTempoAtTime(newTime,
+ m_dragStartTempo,
+ m_dragStartTarget);
+ showTextFloat(m_dragStartTempo, m_dragStartTarget, newTime, true);
+ m_dragPreviousTime = newTime;
+ update();
+
+ } else {
+
+ int x = e->x() + 1;
+ timeT t = m_rulerScale->getTimeForX(x - m_currentXOffset - m_xorigin);
+ int tcn = m_composition->getTempoChangeNumberAt(t);
+
+ if (tcn < 0 || tcn >= m_composition->getTempoChangeCount())
+ return ;
+
+ std::pair<timeT, tempoT> tc = m_composition->getTempoChange(tcn);
+ std::pair<bool, tempoT> tr = m_composition->getTempoRamping(tcn, true);
+
+ int bar, beat, fraction, remainder;
+ m_composition->getMusicalTimeForAbsoluteTime(tc.first, bar, beat,
+ fraction, remainder);
+ RG_DEBUG << "Tempo change: tempo " << m_composition->getTempoQpm(tc.second) << " at " << bar << ":" << beat << ":" << fraction << ":" << remainder << endl;
+
+ m_illuminate = tcn;
+ m_illuminatePoint = false;
+ m_illuminateTarget = false;
+ //!!! m_refreshLinesOnly = true;
+
+ //!!! merge this test with the one in mousePressEvent as
+ //isCloseToStart or equiv, and likewise for close to end
+
+ int px = m_rulerScale->getXForTime(tc.first) + m_currentXOffset + m_xorigin;
+ if (x >= px && x < px + 5) {
+ m_illuminatePoint = true;
+ } else {
+ timeT nt = m_composition->getEndMarker();
+ if (tcn < m_composition->getTempoChangeCount() - 1) {
+ nt = m_composition->getTempoChange(tcn + 1).first;
+ }
+ int nx = m_rulerScale->getXForTime(nt) + m_currentXOffset + m_xorigin;
+ if (x > px + 5 && x > nx - 5) {
+ m_illuminateTarget = true;
+ }
+
+ // std::cerr << "nt = " << nt << ", nx = " << nx << ", x = " << x << ", m_illuminateTarget = " << m_illuminateTarget << std::endl;
+ }
+
+ showTextFloat(tc.second, tr.first ? tr.second : -1,
+ tc.first, m_illuminatePoint);
+
+ update();
+ }
+}
+
+void
+TempoRuler::wheelEvent(QWheelEvent *e)
+{}
+
+void
+TempoRuler::enterEvent(QEvent *)
+{
+ setMouseTracking(true);
+}
+
+void
+TempoRuler::leaveEvent(QEvent *)
+{
+ if (!m_dragVert && !m_dragHoriz) {
+ setMouseTracking(false);
+ m_illuminate = -1;
+ m_illuminatePoint = false;
+ //!!! m_refreshLinesOnly = true;
+ m_textFloat->hide();
+ update();
+ }
+}
+
+void
+TempoRuler::showTextFloat(tempoT tempo, tempoT target,
+ timeT time, bool showTime)
+{
+ float qpm = m_composition->getTempoQpm(tempo);
+ int qi = int(qpm + 0.0001);
+ int q0 = int(qpm * 10 + 0.0001) % 10;
+ int q00 = int(qpm * 100 + 0.0001) % 10;
+
+ bool haveSet = false;
+
+ QString tempoText, timeText;
+
+ if (time >= 0) {
+
+ if (showTime) {
+ int bar, beat, fraction, remainder;
+ m_composition->getMusicalTimeForAbsoluteTime
+ (time, bar, beat, fraction, remainder);
+ RealTime rt = m_composition->getElapsedRealTime(time);
+
+ // blargh -- duplicated with TempoView::makeTimeString
+ timeText = 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);
+
+ timeText = QString("%1\n%2")
+ .arg(timeText)
+ // .arg(rt.toString().c_str());
+ .arg(rt.toText(true).c_str());
+ }
+
+ TimeSignature sig =
+ m_composition->getTimeSignatureAt(time);
+
+ if (sig.getBeatDuration() !=
+ Note(Note::Crotchet).getDuration()) {
+
+ float bpm =
+ (qpm *
+ Note(Note::Crotchet).getDuration())
+ / sig.getBeatDuration();
+ int bi = int(bpm + 0.0001);
+ int b0 = int(bpm * 10 + 0.0001) % 10;
+ int b00 = int(bpm * 100 + 0.0001) % 10;
+
+ tempoText = i18n("%1.%2%3 (%4.%5%6 bpm)")
+ .arg(qi).arg(q0).arg(q00)
+ .arg(bi).arg(b0).arg(b00);
+ haveSet = true;
+ }
+ }
+
+ if (!haveSet) {
+ tempoText = i18n("%1.%2%3 bpm").arg(qi).arg(q0).arg(q00);
+ }
+
+ if (target > 0 && target != tempo) {
+ float tq = m_composition->getTempoQpm(target);
+ int tqi = int(tq + 0.0001);
+ int tq0 = int(tq * 10 + 0.0001) % 10;
+ int tq00 = int(tq * 100 + 0.0001) % 10;
+ tempoText = i18n("%1 - %2.%3%4").arg(tempoText).arg(tqi).arg(tq0).arg(tq00);
+ }
+
+ if (showTime && time >= 0) {
+ m_textFloat->setText(QString("%1\n%2").arg(timeText).arg(tempoText));
+ } else {
+ m_textFloat->setText(tempoText);
+ }
+
+ QPoint cp = mapFromGlobal(QPoint(QCursor::pos()));
+ // std::cerr << "cp = " << cp.x() << "," << cp.y() << ", tempo = " << qpm << std::endl;
+ QPoint mp = cp + pos();
+
+ QWidget *parent = parentWidget();
+ while (parent->parentWidget() &&
+ !parent->isTopLevel() &&
+ !parent->isDialog()) {
+ mp += parent->pos();
+ parent = parent->parentWidget();
+ }
+
+ int yoff = cp.y() + m_textFloat->height() + 3;
+ mp = QPoint(mp.x() + 10, mp.y() > yoff ? mp.y() - yoff : 0);
+
+ m_textFloat->move(mp);
+ m_textFloat->show();
+}
+
+QSize
+TempoRuler::sizeHint() const
+{
+ double width =
+ m_rulerScale->getBarPosition(m_rulerScale->getLastVisibleBar()) +
+ m_rulerScale->getBarWidth(m_rulerScale->getLastVisibleBar()) +
+ m_xorigin;
+
+ QSize res(std::max(int(width), m_width), m_height);
+
+ return res;
+}
+
+QSize
+TempoRuler::minimumSizeHint() const
+{
+ double firstBarWidth = m_rulerScale->getBarWidth(0) + m_xorigin;
+ QSize res = QSize(int(firstBarWidth), m_height);
+ return res;
+}
+
+int
+TempoRuler::getYForTempo(tempoT tempo)
+{
+ int drawh = height() - 4;
+ int y = drawh / 2;
+
+ tempoT minTempo = m_composition->getMinTempo();
+ tempoT maxTempo = m_composition->getMaxTempo();
+
+ if (maxTempo > minTempo) {
+ y = drawh -
+ int((double(tempo - minTempo) / double(maxTempo - minTempo))
+ * drawh + 0.5);
+ }
+
+ return y;
+}
+
+tempoT
+TempoRuler::getTempoForY(int y)
+{
+ int drawh = height() - 4;
+
+ tempoT minTempo = m_composition->getMinTempo();
+ tempoT maxTempo = m_composition->getMaxTempo();
+
+ tempoT tempo = minTempo;
+
+ if (maxTempo > minTempo) {
+ tempo = (maxTempo - minTempo) *
+ (double(drawh - y) / double(drawh)) + minTempo + 0.5;
+ }
+
+ return tempo;
+}
+
+void
+TempoRuler::paintEvent(QPaintEvent* e)
+{
+ QRect clipRect = e->rect();
+
+ if (m_buffer.width() < width() || m_buffer.height() < height()) {
+ m_buffer = QPixmap(width(), height());
+ }
+
+ m_buffer.fill(GUIPalette::getColour
+ (GUIPalette::TextRulerBackground));
+
+ QPainter paint(&m_buffer);
+ paint.setPen(GUIPalette::getColour
+ (GUIPalette::TextRulerForeground));
+
+ paint.setClipRegion(e->region());
+ paint.setClipRect(clipRect);
+
+ if (m_xorigin > 0) {
+ paint.fillRect(0, 0, m_xorigin, height(), paletteBackgroundColor());
+ }
+
+ timeT from = m_rulerScale->getTimeForX
+ (clipRect.x() - m_currentXOffset - 100 - m_xorigin);
+ timeT to = m_rulerScale->getTimeForX
+ (clipRect.x() + clipRect.width() - m_currentXOffset + 100 - m_xorigin);
+
+ QRect boundsForHeight = m_fontMetrics.boundingRect("019");
+ int fontHeight = boundsForHeight.height();
+ int textY = fontHeight + 2;
+
+ double prevEndX = -1000.0;
+ double prevTempo = 0.0;
+ long prevBpm = 0;
+
+ typedef std::map<timeT, int> TimePoints;
+ int tempoChangeHere = 1;
+ int timeSigChangeHere = 2;
+ TimePoints timePoints;
+
+ for (int tempoNo = m_composition->getTempoChangeNumberAt(from);
+ tempoNo <= m_composition->getTempoChangeNumberAt(to) + 1; ++tempoNo) {
+
+ if (tempoNo >= 0 && tempoNo < m_composition->getTempoChangeCount()) {
+ timePoints.insert
+ (TimePoints::value_type
+ (m_composition->getTempoChange(tempoNo).first,
+ tempoChangeHere));
+ }
+ }
+
+ for (int sigNo = m_composition->getTimeSignatureNumberAt(from);
+ sigNo <= m_composition->getTimeSignatureNumberAt(to) + 1; ++sigNo) {
+
+ if (sigNo >= 0 && sigNo < m_composition->getTimeSignatureCount()) {
+ timeT time(m_composition->getTimeSignatureChange(sigNo).first);
+ if (timePoints.find(time) != timePoints.end()) {
+ timePoints[time] |= timeSigChangeHere;
+ } else {
+ timePoints.insert(TimePoints::value_type(time, timeSigChangeHere));
+ }
+ }
+ }
+
+ int lastx = 0, lasty = 0, lastx1 = 0;
+ bool haveSome = false;
+ // tempoT minTempo = m_composition->getMinTempo();
+ // tempoT maxTempo = m_composition->getMaxTempo();
+ bool illuminate = false;
+
+ if (m_illuminate >= 0) {
+ int tcn = m_composition->getTempoChangeNumberAt(from);
+ illuminate = (m_illuminate == tcn);
+ }
+
+ for (TimePoints::iterator i = timePoints.begin(); ; ++i) {
+
+ timeT t0, t1;
+
+ if (i == timePoints.begin()) {
+ t0 = from;
+ } else {
+ TimePoints::iterator j(i);
+ --j;
+ t0 = j->first;
+ }
+
+ if (i == timePoints.end()) {
+ t1 = to;
+ } else {
+ t1 = i->first;
+ }
+
+ if (t1 <= t0)
+ t1 = to;
+
+ int tcn = m_composition->getTempoChangeNumberAt(t0);
+ tempoT tempo = m_composition->getTempoAtTime(t0);
+
+ std::pair<bool, tempoT> ramping(false, tempo);
+ if (tcn > 0 && tcn < m_composition->getTempoChangeCount() + 1) {
+ ramping = m_composition->getTempoRamping(tcn - 1, true);
+ }
+
+ double x0, x1;
+ x0 = m_rulerScale->getXForTime(t0) + m_currentXOffset + m_xorigin;
+ x1 = m_rulerScale->getXForTime(t1) + m_currentXOffset + m_xorigin;
+ /*!!!
+ if (x0 > e->rect().x()) {
+ paint.fillRect(e->rect().x(), 0, x0 - e->rect().x(), height(),
+ paletteBackgroundColor());
+ }
+ */
+ QColor colour = TempoColour::getColour(m_composition->getTempoQpm(tempo));
+ paint.setPen(colour);
+ paint.setBrush(colour);
+
+ if (!m_refreshLinesOnly) {
+ // RG_DEBUG << "TempoRuler: draw rect from " << x0 << " to " << x1 << endl;
+ paint.drawRect(int(x0), 0, int(x1 - x0) + 1, height());
+ }
+
+ int y = getYForTempo(tempo);
+ /*!!!
+ int drawh = height() - 4;
+ int y = drawh / 2;
+ if (maxTempo > minTempo) {
+ y = drawh -
+ int((double(tempo - minTempo) / double(maxTempo - minTempo))
+ * drawh + 0.5);
+ }
+ */
+ y += 2;
+
+ if (haveSome) {
+
+ int x = int(x0) + 1;
+ int ry = lasty;
+
+ bool illuminateLine = (illuminate &&
+ !m_illuminatePoint && !m_illuminateTarget);
+
+ paint.setPen(illuminateLine ? Qt::white : Qt::black);
+
+ if (ramping.first) {
+ ry = getYForTempo(ramping.second);
+ ry += 2;
+ /*!!!
+ ry = drawh -
+ int((double(ramping.second - minTempo) /
+ double(maxTempo - minTempo))
+ * drawh + 0.5);
+ */
+ }
+
+ paint.drawLine(lastx + 1, lasty, x - 2, ry);
+
+ if (!illuminateLine && illuminate && m_illuminateTarget) {
+ if (x > lastx) {
+ paint.setPen(Qt::white);
+ paint.drawLine(x - 6, ry - ((ry - lasty) * 6) / (x - lastx),
+ x - 2, ry);
+ }
+ }
+
+ if (m_illuminate >= 0) {
+ illuminate = (m_illuminate == tcn);
+ }
+
+ bool illuminatePoint = (illuminate && m_illuminatePoint);
+
+ paint.setPen(illuminatePoint ? Qt::white : Qt::black);
+ paint.drawRect(x - 1, y - 1, 3, 3);
+
+ paint.setPen(illuminatePoint ? Qt::black : Qt::white);
+ paint.drawPoint(x, y);
+ }
+
+ lastx = int(x0) + 1;
+ lastx1 = int(x1) + 1;
+ lasty = y;
+ if (i == timePoints.end())
+ break;
+ haveSome = true;
+ }
+
+ if (lastx1 < e->rect().x() + e->rect().width()) {
+ /*!!!
+ paint.fillRect(lastx1, 0,
+ e->rect().x() + e->rect().width() - lastx1, height(),
+ paletteBackgroundColor());
+ */
+ }
+
+ if (haveSome) {
+ bool illuminateLine = (illuminate && !m_illuminatePoint);
+ paint.setPen(illuminateLine ? Qt::white : Qt::black);
+ paint.drawLine(lastx + 1, lasty, width(), lasty);
+ } else if (!m_refreshLinesOnly) {
+ tempoT tempo = m_composition->getTempoAtTime(from);
+ QColor colour = TempoColour::getColour(m_composition->getTempoQpm(tempo));
+ paint.setPen(colour);
+ paint.setBrush(colour);
+ paint.drawRect(e->rect());
+ }
+
+ paint.setPen(Qt::black);
+ paint.setBrush(Qt::black);
+ paint.drawLine(0, 0, width(), 0);
+
+ for (TimePoints::iterator i = timePoints.begin();
+ i != timePoints.end(); ++i) {
+
+ timeT time = i->first;
+ double x = m_rulerScale->getXForTime(time) + m_currentXOffset
+ + m_xorigin;
+
+ /*
+ paint.drawLine(static_cast<int>(x),
+ height() - (height()/4),
+ static_cast<int>(x),
+ height());
+ */
+
+ if ((i->second & timeSigChangeHere) && !m_refreshLinesOnly) {
+
+ TimeSignature sig =
+ m_composition->getTimeSignatureAt(time);
+
+ QString str = QString("%1/%2")
+ .arg(sig.getNumerator())
+ .arg(sig.getDenominator());
+
+ paint.setFont(m_boldFont);
+ paint.drawText(static_cast<int>(x) + 2, m_height - 2, str);
+ }
+
+ if ((i->second & tempoChangeHere) && !m_refreshLinesOnly) {
+
+ double tempo = m_composition->getTempoQpm(m_composition->getTempoAtTime(time));
+ long bpm = long(tempo);
+ // long frac = long(tempo * 100 + 0.001) - 100 * bpm;
+
+ QString tempoString = QString("%1").arg(bpm);
+
+ if (tempo == prevTempo) {
+ if (m_small)
+ continue;
+ tempoString = "=";
+ } else if (bpm == prevBpm) {
+ tempoString = (tempo > prevTempo ? "+" : "-");
+ } else {
+ if (m_small && (bpm != (bpm / 10 * 10))) {
+ if (bpm == prevBpm + 1)
+ tempoString = "+";
+ else if (bpm == prevBpm - 1)
+ tempoString = "-";
+ }
+ }
+ prevTempo = tempo;
+ prevBpm = bpm;
+
+ QRect bounds = m_fontMetrics.boundingRect(tempoString);
+
+ paint.setFont(m_font);
+ if (time > 0)
+ x -= bounds.width() / 2;
+ // if (x > bounds.width() / 2) x -= bounds.width() / 2;
+ if (prevEndX >= x - 3)
+ x = prevEndX + 3;
+ paint.drawText(static_cast<int>(x), textY, tempoString);
+ prevEndX = x + bounds.width();
+ }
+ }
+
+ paint.end();
+
+ QPainter dbpaint(this);
+ // dbpaint.drawPixmap(0, 0, m_buffer);
+ dbpaint.drawPixmap(clipRect.x(), clipRect.y(),
+ m_buffer,
+ clipRect.x(), clipRect.y(),
+ clipRect.width(), clipRect.height());
+
+ dbpaint.end();
+
+ m_refreshLinesOnly = false;
+}
+
+void
+TempoRuler::slotInsertTempoHere()
+{
+ SnapGrid grid(m_rulerScale);
+ grid.setSnapTime(SnapGrid::SnapToUnit);
+ timeT t = grid.snapX(m_clickX - m_currentXOffset - m_xorigin,
+ SnapGrid::SnapLeft);
+ tempoT tempo = Composition::getTempoForQpm(120.0);
+
+ int tcn = m_composition->getTempoChangeNumberAt(t);
+ if (tcn >= 0 && tcn < m_composition->getTempoChangeCount()) {
+ std::pair<timeT, tempoT> tc = m_composition->getTempoChange(tcn);
+ if (tc.first == t)
+ return ;
+ tempo = tc.second;
+ }
+
+ emit changeTempo(t, tempo, -1, TempoDialog::AddTempo);
+}
+
+void
+TempoRuler::slotInsertTempoAtPointer()
+{
+ timeT t = m_composition->getPosition();
+ tempoT tempo = Composition::getTempoForQpm(120.0);
+
+ int tcn = m_composition->getTempoChangeNumberAt(t);
+ if (tcn >= 0 && tcn < m_composition->getTempoChangeCount()) {
+ std::pair<timeT, tempoT> tc = m_composition->getTempoChange(tcn);
+ if (tc.first == t)
+ return ;
+ tempo = tc.second;
+ }
+
+ emit changeTempo(t, tempo, -1, TempoDialog::AddTempo);
+}
+
+void
+TempoRuler::slotDeleteTempoChange()
+{
+ timeT t = m_rulerScale->getTimeForX(m_clickX - m_currentXOffset - m_xorigin);
+ emit deleteTempo(t);
+}
+
+void
+TempoRuler::slotRampToNext()
+{
+ timeT t = m_rulerScale->getTimeForX(m_clickX - m_currentXOffset - m_xorigin);
+
+ int tcn = m_composition->getTempoChangeNumberAt(t);
+ if (tcn < 0 || tcn >= m_composition->getTempoChangeCount())
+ return ;
+
+ std::pair<timeT, tempoT> tc = m_composition->getTempoChange(tcn);
+
+ emit changeTempo(tc.first, tc.second, 0, TempoDialog::AddTempo);
+}
+
+void
+TempoRuler::slotUnramp()
+{
+ timeT t = m_rulerScale->getTimeForX(m_clickX - m_currentXOffset - m_xorigin);
+
+ int tcn = m_composition->getTempoChangeNumberAt(t);
+ if (tcn < 0 || tcn >= m_composition->getTempoChangeCount())
+ return ;
+
+ std::pair<timeT, tempoT> tc = m_composition->getTempoChange(tcn);
+
+ emit changeTempo(tc.first, tc.second, -1, TempoDialog::AddTempo);
+}
+
+void
+TempoRuler::slotEditTempo()
+{
+ timeT t = m_rulerScale->getTimeForX(m_clickX - m_currentXOffset - m_xorigin);
+ emit editTempo(t);
+}
+
+void
+TempoRuler::slotEditTimeSignature()
+{
+ timeT t = m_rulerScale->getTimeForX(m_clickX - m_currentXOffset - m_xorigin);
+ emit editTimeSignature(t);
+}
+
+void
+TempoRuler::slotEditTempos()
+{
+ timeT t = m_rulerScale->getTimeForX(m_clickX - m_currentXOffset - m_xorigin);
+ emit editTempos(t);
+}
+
+void
+TempoRuler::createMenu()
+{
+ setXMLFile("temporuler.rc");
+
+ KXMLGUIFactory* factory = m_parentMainWindow->factory();
+ factory->addClient(this);
+
+ QWidget* tmp = factory->container("tempo_ruler_menu", this);
+
+ m_menu = dynamic_cast<QPopupMenu*>(tmp);
+
+ if (!m_menu) {
+ RG_DEBUG << "MarkerRuler::createMenu() failed\n";
+ }
+}
+
+
+}
+#include "TempoRuler.moc"
diff --git a/src/gui/rulers/TempoRuler.h b/src/gui/rulers/TempoRuler.h
new file mode 100644
index 0000000..1d54e9d
--- /dev/null
+++ b/src/gui/rulers/TempoRuler.h
@@ -0,0 +1,180 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_TEMPORULER_H_
+#define _RG_TEMPORULER_H_
+
+#include "gui/dialogs/TempoDialog.h"
+#include <kxmlguiclient.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qpixmap.h>
+#include <qsize.h>
+#include <qwidget.h>
+#include "base/Event.h"
+
+
+class QWheelEvent;
+class QPopupMenu;
+class QPaintEvent;
+class QMouseEvent;
+class QEvent;
+class KMainWindow;
+
+
+namespace Rosegarden
+{
+
+class TextFloat;
+class RulerScale;
+class RosegardenGUIDoc;
+class Composition;
+
+
+/**
+ * TempoRuler is a widget that shows a strip of tempo values at
+ * x-coordinates corresponding to tempo changes in a Composition.
+ */
+
+class TempoRuler : public QWidget, public KXMLGUIClient
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Construct a TempoRuler that displays and allows editing of the
+ * tempo changes found in the given Composition, with positions
+ * calculated by the given RulerScale.
+ *
+ * The RulerScale will not be destroyed along with the TempoRuler.
+ */
+ TempoRuler(RulerScale *rulerScale,
+ RosegardenGUIDoc *doc,
+ KMainWindow *parentMainWindow,
+ double xorigin = 0.0,
+ int height = 0,
+ bool small = false,
+ QWidget* parent = 0,
+ const char *name = 0);
+
+ ~TempoRuler();
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ void setMinimumWidth(int width) { m_width = width; }
+
+ void connectSignals();
+
+signals:
+ void doubleClicked(timeT);
+
+ void changeTempo(timeT, // tempo change time
+ tempoT, // tempo value
+ tempoT, // tempo target
+ TempoDialog::TempoDialogAction); // tempo action
+
+ void moveTempo(timeT, // old time
+ timeT); // new time
+
+ void deleteTempo(timeT);
+
+ void editTempo(timeT);
+ void editTimeSignature(timeT);
+ void editTempos(timeT);
+
+public slots:
+ void slotScrollHoriz(int x);
+
+protected slots:
+ void slotInsertTempoHere();
+ void slotInsertTempoAtPointer();
+ void slotDeleteTempoChange();
+ void slotRampToNext();
+ void slotUnramp();
+ void slotEditTempo();
+ void slotEditTimeSignature();
+ void slotEditTempos();
+
+protected:
+ virtual void paintEvent(QPaintEvent *);
+ virtual void enterEvent(QEvent *);
+ virtual void leaveEvent(QEvent *);
+ virtual void mousePressEvent(QMouseEvent *);
+ virtual void mouseReleaseEvent(QMouseEvent *);
+ virtual void mouseMoveEvent(QMouseEvent *);
+ virtual void wheelEvent(QWheelEvent *);
+
+ void createMenu();
+
+private:
+ double m_xorigin;
+ int m_height;
+ int m_currentXOffset;
+ int m_width;
+ bool m_small;
+ int m_illuminate;
+ bool m_illuminatePoint;
+ bool m_illuminateTarget;
+ bool m_refreshLinesOnly;
+
+ bool m_dragVert;
+ bool m_dragTarget;
+ bool m_dragHoriz;
+ int m_dragStartY;
+ int m_dragStartX;
+ bool m_dragFine;
+ int m_clickX;
+
+ timeT m_dragStartTime;
+ timeT m_dragPreviousTime;
+ tempoT m_dragStartTempo;
+ tempoT m_dragStartTarget;
+ tempoT m_dragOriginalTempo;
+ tempoT m_dragOriginalTarget;
+
+ int getYForTempo(tempoT tempo);
+ tempoT getTempoForY(int y);
+ void showTextFloat(tempoT tempo,
+ tempoT target = -1,
+ timeT time = -1,
+ bool showTime = false);
+
+ Composition *m_composition;
+ RulerScale *m_rulerScale;
+ TextFloat *m_textFloat;
+ QPopupMenu *m_menu;
+ KMainWindow *m_parentMainWindow;
+
+ QFont m_font;
+ QFont m_boldFont;
+ QFontMetrics m_fontMetrics;
+ QPixmap m_buffer;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/TextRuler.cpp b/src/gui/rulers/TextRuler.cpp
new file mode 100644
index 0000000..0acb3ea
--- /dev/null
+++ b/src/gui/rulers/TextRuler.cpp
@@ -0,0 +1,157 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "TextRuler.h"
+
+#include "base/Event.h"
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/NotationTypes.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "gui/general/GUIPalette.h"
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qpainter.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+TextRuler::TextRuler(RulerScale *rulerScale,
+ Segment *segment,
+ int height,
+ QWidget *parent,
+ const char *name)
+ : QWidget(parent, name),
+ m_height(height),
+ m_currentXOffset(0),
+ m_width( -1),
+ m_segment(segment),
+ m_rulerScale(rulerScale),
+ m_font("helvetica", 12),
+ m_fontMetrics(m_font)
+{
+ m_mySegmentMaybe = (m_segment->getComposition() != 0);
+ setBackgroundColor(GUIPalette::getColour(GUIPalette::TextRulerBackground));
+
+ m_font.setPixelSize(10);
+}
+
+TextRuler::~TextRuler()
+{
+ if (m_mySegmentMaybe && !m_segment->getComposition()) {
+ delete m_segment;
+ }
+}
+
+void
+TextRuler::slotScrollHoriz(int x)
+{
+ int w = width(), h = height();
+ int dx = x - ( -m_currentXOffset);
+ m_currentXOffset = -x;
+
+ if (dx > w*3 / 4 || dx < -w*3 / 4) {
+ update();
+ return ;
+ }
+
+ if (dx > 0) { // moving right, so the existing stuff moves left
+ bitBlt(this, 0, 0, this, dx, 0, w - dx, h);
+ repaint(w - dx, 0, dx, h);
+ } else { // moving left, so the existing stuff moves right
+ bitBlt(this, -dx, 0, this, 0, 0, w + dx, h);
+ repaint(0, 0, -dx, h);
+ }
+}
+
+QSize
+TextRuler::sizeHint() const
+{
+ double width =
+ m_rulerScale->getBarPosition(m_rulerScale->getLastVisibleBar()) +
+ m_rulerScale->getBarWidth(m_rulerScale->getLastVisibleBar());
+
+ QSize res(std::max(int(width), m_width), m_height);
+
+ return res;
+}
+
+QSize
+TextRuler::minimumSizeHint() const
+{
+ double firstBarWidth = m_rulerScale->getBarWidth(0);
+ QSize res = QSize(int(firstBarWidth), m_height);
+ return res;
+}
+
+void
+TextRuler::paintEvent(QPaintEvent* e)
+{
+ QPainter paint(this);
+ paint.setPen(GUIPalette::getColour(GUIPalette::TextRulerForeground));
+
+ paint.setClipRegion(e->region());
+ paint.setClipRect(e->rect().normalize());
+
+ QRect clipRect = paint.clipRegion().boundingRect();
+
+ timeT from = m_rulerScale->getTimeForX
+ (clipRect.x() - m_currentXOffset - 100);
+ timeT to = m_rulerScale->getTimeForX
+ (clipRect.x() + clipRect.width() - m_currentXOffset + 100);
+
+ for (Segment::iterator i = m_segment->findTime(from);
+ i != m_segment->findTime(to) && i != m_segment->end(); ++i) {
+
+ if (!(*i)->isa(Text::EventType))
+ continue;
+
+ std::string text;
+ if (!(*i)->get
+ <String>(Text::TextPropertyName, text)) {
+ RG_DEBUG
+ << "Warning: TextRuler::paintEvent: No text in text event"
+ << endl;
+ continue;
+ }
+
+ QRect bounds = m_fontMetrics.boundingRect(strtoqstr(text));
+
+ double x = m_rulerScale->getXForTime((*i)->getAbsoluteTime()) +
+ m_currentXOffset - bounds.width() / 2;
+
+ int y = height() / 2 + bounds.height() / 2;
+
+ paint.drawText(static_cast<int>(x), y, strtoqstr(text));
+ }
+}
+
+}
+#include "TextRuler.moc"
diff --git a/src/gui/rulers/TextRuler.h b/src/gui/rulers/TextRuler.h
new file mode 100644
index 0000000..7d554cb
--- /dev/null
+++ b/src/gui/rulers/TextRuler.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_TEXTRULER_H_
+#define _RG_TEXTRULER_H_
+
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qsize.h>
+#include <qwidget.h>
+
+
+class QPaintEvent;
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class RulerScale;
+
+
+/**
+ * TextRuler is a widget that shows a strip of text strings at
+ * x-coordinates corresponding to specified times. The strings
+ * are obtained from a Segment, in which they are stored as
+ * text events. (The Segment does not have to be part of a
+ * Composition.)
+ *
+ * By design, this is more suitable for the display of single-purpose
+ * read-only data such as calculated chord names or (at a pinch)
+ * lyrics; it's not really suitable for displaying text data
+ * associated with a staff.
+ */
+
+class TextRuler : public QWidget
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Construct a TextRuler that displays the text events found
+ * in the given Segment at positions calculated by the given
+ * RulerScale.
+ *
+ * The Segment will be destroyed along with the TextRuler, if
+ * it was not in a Composition when the TextRuler was created
+ * and still is not when it's destroyed. If the Segment was
+ * found to be in a Composition on either occasion, it will be
+ * assumed to be someone else's responsibility.
+ *
+ * The RulerScale will not be destroyed along with the TextRuler.
+ */
+ TextRuler(RulerScale *rulerScale,
+ Segment *segment,
+ int height = 0,
+ QWidget* parent = 0,
+ const char *name = 0);
+
+ ~TextRuler();
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ void setMinimumWidth(int width) { m_width = width; }
+
+public slots:
+ void slotScrollHoriz(int x);
+
+protected:
+ virtual void paintEvent(QPaintEvent *);
+
+private:
+ int m_height;
+ int m_currentXOffset;
+ int m_width;
+
+ bool m_mySegmentMaybe;
+
+ Segment *m_segment;
+ RulerScale *m_rulerScale;
+
+ QFont m_font;
+ QFontMetrics m_fontMetrics;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/VelocityColour.cpp b/src/gui/rulers/VelocityColour.cpp
new file mode 100644
index 0000000..02a2f90
--- /dev/null
+++ b/src/gui/rulers/VelocityColour.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "VelocityColour.h"
+
+#include <qcolor.h>
+
+// #include <cassert>
+
+
+namespace Rosegarden
+{
+
+VelocityColour::VelocityColour(const QColor &loud,
+ const QColor &medium,
+ const QColor &quiet,
+ int maxValue,
+ int loudKnee,
+ int mediumKnee,
+ int quietKnee):
+ m_loudColour(loud),
+ m_mediumColour(medium),
+ m_quietColour(quiet),
+ m_loudKnee(loudKnee),
+ m_mediumKnee(mediumKnee),
+ m_quietKnee(quietKnee),
+ m_maxValue(maxValue),
+ m_mixedColour(QColor(0, 0, 0)), // black as default
+ m_multiplyFactor(1000)
+{
+
+// assert(maxValue > loudKnee);
+// assert(loudKnee > mediumKnee);
+// assert(mediumKnee > quietKnee);
+
+ // These are the colours for the first band from Quiet to Medium.
+ // We use a multiplication factor to keep ourselves in the realms
+ // of integer arithmetic as we can potentially be doing a lot of
+ // these calculations when playing.
+ //
+ //
+ m_loStartRed = m_quietColour.red() * m_multiplyFactor;
+ m_loStartGreen = m_quietColour.green() * m_multiplyFactor;
+ m_loStartBlue = m_quietColour.blue() * m_multiplyFactor;
+
+ m_loStepRed = ( m_mediumColour.red() * m_multiplyFactor
+ - m_loStartRed ) / m_mediumKnee;
+ m_loStepGreen = ( m_mediumColour.green() * m_multiplyFactor
+ - m_loStartGreen ) / m_mediumKnee;
+ m_loStepBlue = ( m_mediumColour.blue() * m_multiplyFactor
+ - m_loStartBlue ) / m_mediumKnee;
+
+ m_hiStartRed = m_mediumColour.red() * m_multiplyFactor;
+ m_hiStartGreen = m_mediumColour.green() * m_multiplyFactor;
+ m_hiStartBlue = m_mediumColour.blue() * m_multiplyFactor;
+
+ m_hiStepRed = ( m_loudColour.red() * m_multiplyFactor
+ - m_hiStartRed ) / m_mediumKnee;
+ m_hiStepGreen = ( m_loudColour.green() * m_multiplyFactor
+ - m_hiStartGreen ) / m_mediumKnee;
+ m_hiStepBlue = ( m_loudColour.blue() * m_multiplyFactor
+ - m_hiStartBlue ) / m_mediumKnee;
+
+}
+
+VelocityColour::~VelocityColour()
+{}
+
+const QColor&
+VelocityColour::getColour(int value)
+{
+ if (value > m_maxValue)
+ value = m_maxValue;
+
+ if (value < m_quietKnee) {
+ return m_quietColour;
+ } else if (value < m_mediumKnee) {
+ m_mixedColour.setRgb(
+ ( m_loStartRed + m_loStepRed * value ) / m_multiplyFactor,
+ ( m_loStartGreen + m_loStepGreen * value ) / m_multiplyFactor,
+ ( m_loStartBlue + m_loStepBlue * value ) / m_multiplyFactor);
+
+ } else if (value >= m_mediumKnee < m_loudKnee) {
+ int mixFactor = value - m_mediumKnee;
+
+ m_mixedColour.setRgb(
+ ( m_hiStartRed + m_hiStepRed * mixFactor ) / m_multiplyFactor,
+ ( m_hiStartGreen + m_hiStepGreen * mixFactor ) / m_multiplyFactor,
+ ( m_hiStartBlue + m_hiStepBlue * mixFactor ) / m_multiplyFactor);
+ } else {
+ return m_loudColour;
+ return m_loudColour;
+ }
+
+ return m_mixedColour;
+}
+
+}
diff --git a/src/gui/rulers/VelocityColour.h b/src/gui/rulers/VelocityColour.h
new file mode 100644
index 0000000..0e555e1
--- /dev/null
+++ b/src/gui/rulers/VelocityColour.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_VELOCITYCOLOUR_H_
+#define _RG_VELOCITYCOLOUR_H_
+
+#include <qcolor.h>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * Returns a QColour according to a formula. We provide three colours
+ * to mix, a maximum value and three knees at which points the
+ * intermediate colours max out. Play around to your satisfaction.
+ */
+class VelocityColour
+{
+
+public:
+ VelocityColour(const QColor &loud,
+ const QColor &medium,
+ const QColor &quiet,
+ int maxValue,
+ int loudKnee,
+ int mediumKnee,
+ int quietKnee);
+ ~VelocityColour();
+
+ const QColor& getColour(int value);
+
+ int getLoudKnee() const { return m_loudKnee; }
+ int getMediumKnee() const { return m_mediumKnee; }
+ int getQuietKnee() const { return m_quietKnee; }
+
+ QColor getLoudColour() const { return m_loudColour; }
+ QColor getMediumColour() const { return m_mediumColour; }
+ QColor getQuietColour() const { return m_quietColour; }
+
+ int getMaxValue() const { return m_maxValue; }
+
+private:
+
+ QColor m_loudColour;
+ QColor m_mediumColour;
+ QColor m_quietColour;
+ int m_loudKnee;
+ int m_mediumKnee;
+ int m_quietKnee;
+ int m_maxValue;
+
+ // the mixed colour that we can return
+ QColor m_mixedColour;
+
+
+ int m_loStartRed;
+ int m_loStartGreen;
+ int m_loStartBlue;
+
+ int m_loStepRed;
+ int m_loStepGreen;
+ int m_loStepBlue;
+
+ int m_hiStartRed;
+ int m_hiStartGreen;
+ int m_hiStartBlue;
+
+ int m_hiStepRed;
+ int m_hiStepGreen;
+ int m_hiStepBlue;
+
+
+ int m_multiplyFactor;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/rulers/ViewElementAdapter.cpp b/src/gui/rulers/ViewElementAdapter.cpp
new file mode 100644
index 0000000..550ddcf
--- /dev/null
+++ b/src/gui/rulers/ViewElementAdapter.cpp
@@ -0,0 +1,56 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ViewElementAdapter.h"
+
+namespace Rosegarden
+{
+
+ViewElementAdapter::ViewElementAdapter(ViewElement* el, const PropertyName& p)
+ : m_viewElement(el),
+ m_propertyName(p)
+{
+}
+
+bool ViewElementAdapter::getValue(long& val)
+{
+ return m_viewElement->event()->get<Rosegarden::Int>(m_propertyName, val);
+}
+
+void ViewElementAdapter::setValue(long val)
+{
+ m_viewElement->event()->set<Rosegarden::Int>(m_propertyName, val);
+}
+
+timeT ViewElementAdapter::getTime()
+{
+ return m_viewElement->getViewAbsoluteTime();
+}
+
+timeT ViewElementAdapter::getDuration()
+{
+ return m_viewElement->getViewDuration();
+}
+
+}
diff --git a/src/gui/rulers/ViewElementAdapter.h b/src/gui/rulers/ViewElementAdapter.h
new file mode 100644
index 0000000..1207522
--- /dev/null
+++ b/src/gui/rulers/ViewElementAdapter.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_VIEWELEMENTADAPTER_H_
+#define _RG_VIEWELEMENTADAPTER_H_
+
+#include "ElementAdapter.h"
+#include "base/ViewElement.h"
+
+namespace Rosegarden
+{
+
+class Event;
+
+class ViewElementAdapter : public ElementAdapter
+{
+public:
+ ViewElementAdapter(ViewElement*, const PropertyName&);
+
+ virtual bool getValue(long&);
+ virtual void setValue(long);
+ virtual timeT getTime();
+ virtual timeT getDuration();
+
+ virtual Event* getEvent() { return m_viewElement->event(); }
+ ViewElement* getViewElement() { return m_viewElement; }
+
+protected:
+
+ //--------------- Data members ---------------------------------
+
+ ViewElement* m_viewElement;
+ const PropertyName& m_propertyName;
+};
+
+}
+
+#endif /*VIEWELEMENTADAPTER_H_*/
diff --git a/src/gui/seqmanager/AudioSegmentMmapper.cpp b/src/gui/seqmanager/AudioSegmentMmapper.cpp
new file mode 100644
index 0000000..8933b39
--- /dev/null
+++ b/src/gui/seqmanager/AudioSegmentMmapper.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "AudioSegmentMmapper.h"
+
+#include "base/Event.h"
+#include "base/Composition.h"
+#include "base/RealTime.h"
+#include "base/Segment.h"
+#include "base/TriggerSegment.h"
+#include "document/RosegardenGUIDoc.h"
+#include "SegmentMmapper.h"
+#include "sound/MappedEvent.h"
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+AudioSegmentMmapper::AudioSegmentMmapper(RosegardenGUIDoc* doc, Segment* s,
+ const QString& fileName)
+ : SegmentMmapper(doc, s, fileName)
+{}
+
+void AudioSegmentMmapper::dump()
+{
+ Composition &comp = m_doc->getComposition();
+
+ RealTime eventTime;
+ Track* track = comp.getTrackById(m_segment->getTrack());
+
+ // Can't write out if no track
+ if (!track) {
+ std::cerr << "AudioSegmentMmapper::dump: ERROR: No track for segment!"
+ << std::endl;
+ return ;
+ }
+
+ timeT segmentStartTime = m_segment->getStartTime();
+ timeT segmentEndTime = m_segment->getEndMarkerTime();
+ timeT segmentDuration = segmentEndTime - segmentStartTime;
+ timeT repeatEndTime = segmentEndTime;
+
+ //!!! The repeat count is actually not quite right for audio
+ // segments -- it returns one too many for repeating segments,
+ // because in midi segments you want that (to deal with partial
+ // repeats). Here we really need to find a better way to deal
+ // with partial repeats...
+
+ int repeatCount = getSegmentRepeatCount();
+ if (repeatCount > 0)
+ repeatEndTime = m_segment->getRepeatEndTime();
+
+ MappedEvent* bufPos = m_mmappedEventBuffer;
+
+ for (int repeatNo = 0; repeatNo <= repeatCount; ++repeatNo) {
+
+ timeT playTime =
+ segmentStartTime + repeatNo * segmentDuration;
+ if (playTime >= repeatEndTime)
+ break;
+
+ playTime = playTime + m_segment->getDelay();
+ eventTime = comp.getElapsedRealTime(playTime);
+ eventTime = eventTime + m_segment->getRealTimeDelay();
+
+ RealTime audioStart = m_segment->getAudioStartTime();
+ RealTime audioDuration = m_segment->getAudioEndTime() - audioStart;
+ MappedEvent *mE =
+ new (bufPos) MappedEvent(track->getInstrument(), // send instrument for audio
+ m_segment->getAudioFileId(),
+ eventTime,
+ audioDuration,
+ audioStart);
+ mE->setTrackId(track->getId());
+ mE->setRuntimeSegmentId(m_segment->getRuntimeId());
+
+ // Send the autofade if required
+ //
+ if (m_segment->isAutoFading()) {
+ mE->setAutoFade(true);
+ mE->setFadeInTime(m_segment->getFadeInTime());
+ mE->setFadeOutTime(m_segment->getFadeOutTime());
+ std::cout << "AudioSegmentMmapper::dump - "
+ << "SETTING AUTOFADE "
+ << "in = " << m_segment->getFadeInTime()
+ << ", out = " << m_segment->getFadeOutTime()
+ << std::endl;
+ } else {
+ // std::cout << "AudioSegmentMmapper::dump - "
+ // << "NO AUTOFADE SET ON SEGMENT" << std::endl;
+ }
+
+ ++bufPos;
+ }
+
+ *(size_t *)m_mmappedRegion = repeatCount + 1;
+}
+
+size_t AudioSegmentMmapper::computeMmappedSize()
+{
+ if (!m_segment) return 0;
+
+ int repeatCount = getSegmentRepeatCount();
+
+ return (repeatCount + 1) * 1 * sizeof(MappedEvent);
+ // audio segments don't have events, we just need room for 1 MappedEvent
+}
+
+}
+
diff --git a/src/gui/seqmanager/AudioSegmentMmapper.h b/src/gui/seqmanager/AudioSegmentMmapper.h
new file mode 100644
index 0000000..bc4d798
--- /dev/null
+++ b/src/gui/seqmanager/AudioSegmentMmapper.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_AUDIOSEGMENTMMAPPER_H_
+#define _RG_AUDIOSEGMENTMMAPPER_H_
+
+#include "SegmentMmapper.h"
+
+
+class QString;
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class RosegardenGUIDoc;
+
+
+class AudioSegmentMmapper : public SegmentMmapper
+{
+ friend class SegmentMmapperFactory;
+
+protected:
+ AudioSegmentMmapper(RosegardenGUIDoc*, Segment*,
+ const QString& fileName);
+
+ virtual size_t computeMmappedSize();
+
+ /// dump all segment data in the file
+ virtual void dump();
+};
+
+//----------------------------------------
+
+
+}
+
+#endif
diff --git a/src/gui/seqmanager/CompositionMmapper.cpp b/src/gui/seqmanager/CompositionMmapper.cpp
new file mode 100644
index 0000000..094f562
--- /dev/null
+++ b/src/gui/seqmanager/CompositionMmapper.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "CompositionMmapper.h"
+#include "misc/Debug.h"
+
+#include <kstddirs.h>
+#include "base/Composition.h"
+#include "base/Segment.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/application/RosegardenApplication.h"
+#include "SegmentMmapperFactory.h"
+#include "SegmentMmapper.h"
+#include <kglobal.h>
+#include <qdir.h>
+#include <qfile.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <stdint.h>
+
+
+namespace Rosegarden
+{
+
+CompositionMmapper::CompositionMmapper(RosegardenGUIDoc *doc)
+ : m_doc(doc)
+{
+ cleanup();
+
+ SEQMAN_DEBUG << "CompositionMmapper() - doc = " << doc << endl;
+ Composition &comp = m_doc->getComposition();
+
+ for (Composition::iterator it = comp.begin(); it != comp.end(); it++) {
+
+ Track* track = comp.getTrackById((*it)->getTrack());
+
+ // check to see if track actually exists
+ //
+ if (track == 0)
+ continue;
+
+ mmapSegment(*it);
+ }
+}
+
+CompositionMmapper::~CompositionMmapper()
+{
+ SEQMAN_DEBUG << "~CompositionMmapper()\n";
+
+ //
+ // Clean up possible left-overs
+ //
+ cleanup();
+
+ for (segmentmmapers::iterator i = m_segmentMmappers.begin();
+ i != m_segmentMmappers.end(); ++i)
+ delete i->second;
+}
+
+void CompositionMmapper::cleanup()
+{
+ // In case the sequencer is still running, mapping some segments
+ //
+ rgapp->sequencerSend("closeAllSegments()");
+
+ // Erase all 'segment_*' files
+ //
+ QString tmpPath = KGlobal::dirs()->resourceDirs("tmp").last();
+
+ QDir segmentsDir(tmpPath, "segment_*");
+ for (unsigned int i = 0; i < segmentsDir.count(); ++i) {
+ QString segmentName = tmpPath + '/' + segmentsDir[i];
+ SEQMAN_DEBUG << "CompositionMmapper : cleaning up " << segmentName << endl;
+ QFile::remove
+ (segmentName);
+ }
+
+}
+
+bool CompositionMmapper::segmentModified(Segment* segment)
+{
+ SegmentMmapper* mmapper = m_segmentMmappers[segment];
+
+ if (!mmapper)
+ return false; // this can happen with the SegmentSplitCommand, where the new segment's transpose is set
+ // even though it's not mapped yet
+
+ SEQMAN_DEBUG << "CompositionMmapper::segmentModified(" << segment << ") - mmapper = "
+ << mmapper << endl;
+
+ return mmapper->refresh();
+}
+
+void CompositionMmapper::segmentAdded(Segment* segment)
+{
+ SEQMAN_DEBUG << "CompositionMmapper::segmentAdded(" << segment << ")\n";
+
+ mmapSegment(segment);
+}
+
+void CompositionMmapper::segmentDeleted(Segment* segment)
+{
+ SEQMAN_DEBUG << "CompositionMmapper::segmentDeleted(" << segment << ")\n";
+ SegmentMmapper* mmapper = m_segmentMmappers[segment];
+ m_segmentMmappers.erase(segment);
+ SEQMAN_DEBUG << "CompositionMmapper::segmentDeleted() : deleting SegmentMmapper " << mmapper << endl;
+
+ delete mmapper;
+}
+
+void CompositionMmapper::mmapSegment(Segment* segment)
+{
+ SEQMAN_DEBUG << "CompositionMmapper::mmapSegment(" << segment << ")\n";
+
+ SegmentMmapper* mmapper = SegmentMmapperFactory::makeMmapperForSegment(m_doc,
+ segment,
+ makeFileName(segment));
+
+ if (mmapper)
+ m_segmentMmappers[segment] = mmapper;
+}
+
+QString CompositionMmapper::makeFileName(Segment* segment)
+{
+ QStringList tmpDirs = KGlobal::dirs()->resourceDirs("tmp");
+
+ return QString("%1/segment_%2")
+ .arg(tmpDirs.last())
+ .arg((uintptr_t)segment, 0, 16);
+}
+
+QString CompositionMmapper::getSegmentFileName(Segment* s)
+{
+ SegmentMmapper* mmapper = m_segmentMmappers[s];
+
+ if (mmapper)
+ return mmapper->getFileName();
+ else
+ return QString::null;
+}
+
+size_t CompositionMmapper::getSegmentFileSize(Segment* s)
+{
+ SegmentMmapper* mmapper = m_segmentMmappers[s];
+
+ if (mmapper)
+ return mmapper->getFileSize();
+ else
+ return 0;
+}
+
+}
diff --git a/src/gui/seqmanager/CompositionMmapper.h b/src/gui/seqmanager/CompositionMmapper.h
new file mode 100644
index 0000000..8bf997d
--- /dev/null
+++ b/src/gui/seqmanager/CompositionMmapper.h
@@ -0,0 +1,75 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_COMPOSITIONMMAPPER_H_
+#define _RG_COMPOSITIONMMAPPER_H_
+
+#include <map>
+#include <qstring.h>
+
+
+
+
+namespace Rosegarden
+{
+
+class SegmentMmapper;
+class Segment;
+class RosegardenGUIDoc;
+
+
+class CompositionMmapper
+{
+ friend class SequenceManager;
+
+public:
+ CompositionMmapper(RosegardenGUIDoc *doc);
+ ~CompositionMmapper();
+
+ QString getSegmentFileName(Segment*);
+ size_t getSegmentFileSize(Segment*);
+
+ void cleanup();
+
+protected:
+ bool segmentModified(Segment*);
+ void segmentAdded(Segment*);
+ void segmentDeleted(Segment*);
+
+ void mmapSegment(Segment*);
+ QString makeFileName(Segment*);
+
+ //--------------- Data members ---------------------------------
+
+ RosegardenGUIDoc* m_doc;
+ typedef std::map<Segment*, SegmentMmapper*> segmentmmapers;
+
+ segmentmmapers m_segmentMmappers;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/seqmanager/ControlBlockMmapper.cpp b/src/gui/seqmanager/ControlBlockMmapper.cpp
new file mode 100644
index 0000000..90ab6c2
--- /dev/null
+++ b/src/gui/seqmanager/ControlBlockMmapper.cpp
@@ -0,0 +1,226 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ControlBlockMmapper.h"
+#include "misc/Debug.h"
+
+#include <kstddirs.h>
+#include "misc/Strings.h"
+#include "base/Composition.h"
+#include "base/Exception.h"
+#include "base/MidiProgram.h"
+#include "base/Track.h"
+#include "document/RosegardenGUIDoc.h"
+#include "sound/ControlBlock.h"
+#include <kglobal.h>
+#include <qfile.h>
+#include <qstring.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+namespace Rosegarden
+{
+
+ControlBlockMmapper::ControlBlockMmapper(RosegardenGUIDoc* doc)
+ : m_doc(doc),
+ m_fileName(createFileName()),
+ m_fd( -1),
+ m_mmappedBuffer(0),
+ m_mmappedSize(sizeof(ControlBlock)),
+ m_controlBlock(0)
+{
+ // just in case
+ QFile::remove
+ (m_fileName);
+
+ m_fd = ::open(m_fileName.latin1(), O_RDWR | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IWUSR);
+ if (m_fd < 0) {
+ SEQMAN_DEBUG << "ControlBlockMmapper : Couldn't open " << m_fileName
+ << endl;
+ throw Exception("Couldn't open " + qstrtostr(m_fileName));
+ }
+
+ setFileSize(m_mmappedSize);
+
+ //
+ // mmap() file for writing
+ //
+ m_mmappedBuffer = ::mmap(0, m_mmappedSize,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED, m_fd, 0);
+
+ if (m_mmappedBuffer == (void*) - 1) {
+ SEQMAN_DEBUG << QString("mmap failed : (%1) %2\n").arg(errno).arg(strerror(errno));
+ throw Exception("mmap failed");
+ }
+
+ SEQMAN_DEBUG << "ControlBlockMmapper : mmap size : " << m_mmappedSize
+ << " at " << (void*)m_mmappedBuffer << endl;
+
+ // Create new control block on file
+ initControlBlock();
+}
+
+ControlBlockMmapper::~ControlBlockMmapper()
+{
+ ::munmap(m_mmappedBuffer, m_mmappedSize);
+ ::close(m_fd);
+ QFile::remove
+ (m_fileName);
+}
+
+QString ControlBlockMmapper::createFileName()
+{
+ return KGlobal::dirs()->resourceDirs("tmp").last() + "/rosegarden_control_block";
+}
+
+void ControlBlockMmapper::updateTrackData(Track *t)
+{
+ m_controlBlock->updateTrackData(t);
+}
+
+void ControlBlockMmapper::setTrackDeleted(TrackId t)
+{
+ m_controlBlock->setTrackDeleted(t, true);
+}
+
+void ControlBlockMmapper::updateMidiFilters(MidiFilter thruFilter,
+ MidiFilter recordFilter)
+{
+ m_controlBlock->setThruFilter(thruFilter);
+ m_controlBlock->setRecordFilter(recordFilter);
+}
+
+void ControlBlockMmapper::updateMetronomeData(InstrumentId instId)
+{
+ m_controlBlock->setInstrumentForMetronome(instId);
+}
+
+void ControlBlockMmapper::updateMetronomeForPlayback()
+{
+ bool muted = !m_doc->getComposition().usePlayMetronome();
+ SEQMAN_DEBUG << "ControlBlockMmapper::updateMetronomeForPlayback: muted=" << muted << endl;
+ if (m_controlBlock->isMetronomeMuted() == muted)
+ return ;
+ m_controlBlock->setMetronomeMuted(muted);
+}
+
+void ControlBlockMmapper::updateMetronomeForRecord()
+{
+ bool muted = !m_doc->getComposition().useRecordMetronome();
+ SEQMAN_DEBUG << "ControlBlockMmapper::updateMetronomeForRecord: muted=" << muted << endl;
+ if (m_controlBlock->isMetronomeMuted() == muted)
+ return ;
+ m_controlBlock->setMetronomeMuted(muted);
+}
+
+bool ControlBlockMmapper::updateSoloData(bool solo,
+ TrackId selectedTrack)
+{
+ bool changed = false;
+
+ if (solo != m_controlBlock->isSolo()) {
+
+ changed = true;
+
+ } else if (solo &&
+ (selectedTrack != m_controlBlock->getSelectedTrack())) {
+
+ changed = true;
+ }
+
+ m_controlBlock->setSolo(solo);
+ m_controlBlock->setSelectedTrack(selectedTrack);
+
+ return changed;
+}
+
+void ControlBlockMmapper::setDocument(RosegardenGUIDoc* doc)
+{
+ SEQMAN_DEBUG << "ControlBlockMmapper::setDocument()\n";
+ m_doc = doc;
+ initControlBlock();
+}
+
+void ControlBlockMmapper::initControlBlock()
+{
+ SEQMAN_DEBUG << "ControlBlockMmapper::initControlBlock()\n";
+
+ m_controlBlock = new (m_mmappedBuffer) ControlBlock(m_doc->getComposition().getMaxTrackId());
+
+ Composition& comp = m_doc->getComposition();
+
+ for (Composition::trackiterator i = comp.getTracks().begin(); i != comp.getTracks().end(); ++i) {
+ Track* track = i->second;
+ if (track == 0)
+ continue;
+
+ m_controlBlock->updateTrackData(track);
+ }
+
+ m_controlBlock->setMetronomeMuted(!comp.usePlayMetronome());
+
+ m_controlBlock->setThruFilter(m_doc->getStudio().getMIDIThruFilter());
+ m_controlBlock->setRecordFilter(m_doc->getStudio().getMIDIRecordFilter());
+
+ ::msync(m_mmappedBuffer, m_mmappedSize, MS_ASYNC);
+}
+
+void ControlBlockMmapper::setFileSize(size_t size)
+{
+ SEQMAN_DEBUG << "ControlBlockMmapper : setting size of "
+ << m_fileName << " to " << size << endl;
+ // rewind
+ ::lseek(m_fd, 0, SEEK_SET);
+
+ //
+ // enlarge the file
+ // (seek() to wanted size, then write a byte)
+ //
+ if (::lseek(m_fd, size - 1, SEEK_SET) == -1) {
+ std::cerr << "WARNING: ControlBlockMmapper : Couldn't lseek in " << m_fileName
+ << " to " << size << std::endl;
+ throw Exception("lseek failed");
+ }
+
+ if (::write(m_fd, "\0", 1) != 1) {
+ std::cerr << "WARNING: ControlBlockMmapper : Couldn't write byte in "
+ << m_fileName << std::endl;
+ throw Exception("write failed");
+ }
+
+}
+
+void
+ControlBlockMmapper::enableMIDIThruRouting(bool state)
+{
+ m_controlBlock->setMidiRoutingEnabled(state);
+}
+
+}
diff --git a/src/gui/seqmanager/ControlBlockMmapper.h b/src/gui/seqmanager/ControlBlockMmapper.h
new file mode 100644
index 0000000..a96e742
--- /dev/null
+++ b/src/gui/seqmanager/ControlBlockMmapper.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_CONTROLBLOCKMMAPPER_H_
+#define _RG_CONTROLBLOCKMMAPPER_H_
+
+#include "base/MidiProgram.h"
+#include "base/Track.h"
+#include <qstring.h>
+
+
+
+
+namespace Rosegarden
+{
+
+class Track;
+class RosegardenGUIDoc;
+class ControlBlock;
+
+
+class ControlBlockMmapper
+{
+public:
+ ControlBlockMmapper(RosegardenGUIDoc*);
+ ~ControlBlockMmapper();
+
+ QString getFileName() { return m_fileName; }
+ void updateTrackData(Track*);
+ void setTrackDeleted(TrackId);
+ void updateMetronomeData(InstrumentId instId);
+ void updateMetronomeForPlayback();
+ void updateMetronomeForRecord();
+ bool updateSoloData(bool solo, TrackId selectedTrack);
+ void updateMidiFilters(MidiFilter thruFilter,
+ MidiFilter recordFilter);
+ void setDocument(RosegardenGUIDoc*);
+ void enableMIDIThruRouting(bool state);
+
+protected:
+ void initControlBlock();
+ void setFileSize(size_t);
+ QString createFileName();
+
+ //--------------- Data members ---------------------------------
+ RosegardenGUIDoc* m_doc;
+ QString m_fileName;
+ int m_fd;
+ void* m_mmappedBuffer;
+ size_t m_mmappedSize;
+ ControlBlock* m_controlBlock;
+};
+
+
+//----------------------------------------
+
+
+
+}
+
+#endif
diff --git a/src/gui/seqmanager/MetronomeMmapper.cpp b/src/gui/seqmanager/MetronomeMmapper.cpp
new file mode 100644
index 0000000..9ca9af7
--- /dev/null
+++ b/src/gui/seqmanager/MetronomeMmapper.cpp
@@ -0,0 +1,268 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "MetronomeMmapper.h"
+#include "misc/Debug.h"
+#include <kapplication.h>
+
+#include "sound/Midi.h"
+#include <kstddirs.h>
+#include "document/ConfigGroups.h"
+#include "base/Event.h"
+#include "base/MidiProgram.h"
+#include "base/NotationTypes.h"
+#include "base/RealTime.h"
+#include "base/Segment.h"
+#include "base/Studio.h"
+#include "base/TriggerSegment.h"
+#include "document/RosegardenGUIDoc.h"
+#include "SegmentMmapper.h"
+#include "sound/MappedEvent.h"
+#include <kconfig.h>
+#include <kglobal.h>
+#include <qstring.h>
+#include <algorithm>
+
+
+namespace Rosegarden
+{
+
+MetronomeMmapper::MetronomeMmapper(RosegardenGUIDoc* doc)
+ : SegmentMmapper(doc, 0, createFileName()),
+ m_metronome(0), // no metronome to begin with
+ m_tickDuration(0, 100000000)
+{
+ SEQMAN_DEBUG << "MetronomeMmapper ctor : " << this << endl;
+
+ // get metronome device
+ Studio &studio = m_doc->getStudio();
+ int device = studio.getMetronomeDevice();
+
+ const MidiMetronome *metronome =
+ m_doc->getStudio().getMetronomeFromDevice(device);
+
+ if (metronome) {
+
+ SEQMAN_DEBUG << "MetronomeMmapper: have metronome, it's on instrument " << metronome->getInstrument() << endl;
+
+ m_metronome = new MidiMetronome(*metronome);
+ } else {
+ m_metronome = new MidiMetronome
+ (SystemInstrumentBase);
+ SEQMAN_DEBUG << "MetronomeMmapper: no metronome for device " << device << endl;
+ }
+
+ Composition& c = m_doc->getComposition();
+ timeT t = c.getBarStart( -20); // somewhat arbitrary
+ int depth = m_metronome->getDepth();
+
+ if (depth > 0) {
+ while (t < c.getEndMarker()) {
+
+ TimeSignature sig = c.getTimeSignatureAt(t);
+ timeT barDuration = sig.getBarDuration();
+ std::vector<int> divisions;
+ if (depth > 0)
+ sig.getDivisions(depth - 1, divisions);
+ int ticks = 1;
+
+ for (int i = -1; i < (int)divisions.size(); ++i) {
+ if (i >= 0)
+ ticks *= divisions[i];
+
+ for (int tick = 0; tick < ticks; ++tick) {
+ if (i >= 0 && (tick % divisions[i] == 0))
+ continue;
+ timeT tickTime = t + (tick * barDuration) / ticks;
+ m_ticks.push_back(Tick(tickTime, i + 1));
+ }
+ }
+
+ t = c.getBarEndForTime(t);
+ }
+ }
+
+ KConfig *config = kapp->config();
+ config->setGroup(SequencerOptionsConfigGroup);
+ int midiClock = config->readNumEntry("midiclock", 0);
+ int mtcMode = config->readNumEntry("mtcmode", 0);
+
+ if (midiClock == 1) {
+ timeT quarterNote = Note(Note::Crotchet).getDuration();
+
+ // Insert 24 clocks per quarter note
+ //
+ for (timeT insertTime = c.getStartMarker();
+ insertTime < c.getEndMarker();
+ insertTime += quarterNote / 24) {
+ m_ticks.push_back(Tick(insertTime, 3));
+ }
+ }
+
+
+ if (mtcMode > 0) {
+ // do something
+ }
+
+ sortTicks();
+
+ if (m_ticks.size() == 0) {
+ SEQMAN_DEBUG << "MetronomeMmapper : WARNING no ticks generated\n";
+ }
+
+ // Done by init()
+
+ // m_mmappedSize = computeMmappedSize();
+ // if (m_mmappedSize > 0) {
+ // setFileSize(m_mmappedSize);
+ // doMmap();
+ // dump();
+ // }
+}
+
+MetronomeMmapper::~MetronomeMmapper()
+{
+ SEQMAN_DEBUG << "~MetronomeMmapper " << this << endl;
+ delete m_metronome;
+}
+
+InstrumentId MetronomeMmapper::getMetronomeInstrument()
+{
+ return m_metronome->getInstrument();
+}
+
+QString MetronomeMmapper::createFileName()
+{
+ return KGlobal::dirs()->resourceDirs("tmp").last() + "/rosegarden_metronome";
+}
+
+void MetronomeMmapper::dump()
+{
+ RealTime eventTime;
+ Composition& comp = m_doc->getComposition();
+
+ SEQMAN_DEBUG << "MetronomeMmapper::dump: instrument is " << m_metronome->getInstrument() << endl;
+
+ MappedEvent* bufPos = m_mmappedEventBuffer, *mE;
+
+ for (TickContainer::iterator i = m_ticks.begin(); i != m_ticks.end(); ++i) {
+
+ /*
+ SEQMAN_DEBUG << "MetronomeMmapper::dump: velocity = "
+ << int(velocity) << endl;
+ */
+
+ eventTime = comp.getElapsedRealTime(i->first);
+
+ if (i->second == 3) // MIDI Clock
+ {
+ mE = new (bufPos) MappedEvent(0, MappedEvent::MidiSystemMessage);
+ mE->setData1(MIDI_TIMING_CLOCK);
+ mE->setEventTime(eventTime);
+ } else {
+ MidiByte velocity;
+ MidiByte pitch;
+ switch (i->second) {
+ case 0:
+ velocity = m_metronome->getBarVelocity();
+ pitch = m_metronome->getBarPitch();
+ break;
+ case 1:
+ velocity = m_metronome->getBeatVelocity();
+ pitch = m_metronome->getBeatPitch();
+ break;
+ default:
+ velocity = m_metronome->getSubBeatVelocity();
+ pitch = m_metronome->getSubBeatPitch();
+ break;
+ }
+
+ new (bufPos) MappedEvent(m_metronome->getInstrument(),
+ MappedEvent::MidiNoteOneShot,
+ pitch,
+ velocity,
+ eventTime,
+ m_tickDuration,
+ RealTime::zeroTime);
+ }
+
+ ++bufPos;
+ }
+
+ // Store the number of events at the start of the shared memory region
+ *(size_t *)m_mmappedRegion = (bufPos - m_mmappedEventBuffer);
+
+ SEQMAN_DEBUG << "MetronomeMmapper::dump: - "
+ << "Total events written = " << *(size_t *)m_mmappedRegion
+ << endl;
+}
+
+void MetronomeMmapper::sortTicks()
+{
+ sort(m_ticks.begin(), m_ticks.end());
+}
+
+size_t MetronomeMmapper::computeMmappedSize()
+{
+ KConfig *config = kapp->config();
+ config->setGroup(Rosegarden::SequencerOptionsConfigGroup);
+ int midiClock = config->readNumEntry("midiclock", 0);
+ int mtcMode = config->readNumEntry("mtcmode", 0);
+
+ // base size for Metronome ticks
+ size_t size = m_ticks.size() * sizeof(MappedEvent);
+ Composition& comp = m_doc->getComposition();
+
+ if (midiClock == 1)
+ {
+ using Rosegarden::Note;
+
+ // Allow room for MIDI clocks
+ int clocks = ( 24 * ( comp.getEndMarker() - comp.getStartMarker() ) ) /
+ Note(Note::Crotchet).getDuration();
+
+ /*
+ SEQMAN_DEBUG << "MetronomeMmapper::computeMmappedSize - "
+ << "Number of clock events catered for = " << clocks
+ << endl;
+ */
+
+ size += clocks * sizeof(MappedEvent);
+ }
+
+ if (mtcMode > 0)
+ {
+ // Allow room for MTC timing messages (how?)
+ }
+
+ return size;
+}
+
+unsigned int MetronomeMmapper::getSegmentRepeatCount()
+{
+ return 1;
+}
+
+}
diff --git a/src/gui/seqmanager/MetronomeMmapper.h b/src/gui/seqmanager/MetronomeMmapper.h
new file mode 100644
index 0000000..1e18171
--- /dev/null
+++ b/src/gui/seqmanager/MetronomeMmapper.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_METRONOMEMMAPPER_H_
+#define _RG_METRONOMEMMAPPER_H_
+
+#include "base/MidiProgram.h"
+#include "base/RealTime.h"
+#include "SegmentMmapper.h"
+#include <qstring.h>
+#include <utility>
+#include <vector>
+#include "base/Event.h"
+
+
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+class MidiMetronome;
+
+
+class MetronomeMmapper : public SegmentMmapper
+{
+ friend class SegmentMmapperFactory;
+
+public:
+
+ virtual ~MetronomeMmapper();
+
+ InstrumentId getMetronomeInstrument();
+
+ // overrides from SegmentMmapper
+ virtual unsigned int getSegmentRepeatCount();
+
+protected:
+ MetronomeMmapper(RosegardenGUIDoc* doc);
+
+ virtual size_t computeMmappedSize();
+
+ void sortTicks();
+ QString createFileName();
+
+ // override from SegmentMmapper
+ virtual void dump();
+
+ //--------------- Data members ---------------------------------
+ typedef std::pair<timeT, int> Tick;
+ typedef std::vector<Tick> TickContainer;
+ friend bool operator<(Tick, Tick);
+
+ TickContainer m_ticks;
+ bool m_deleteMetronome;
+ const MidiMetronome* m_metronome;
+ RealTime m_tickDuration;
+};
+
+//----------------------------------------
+
+
+}
+
+#endif
diff --git a/src/gui/seqmanager/MidiFilterDialog.cpp b/src/gui/seqmanager/MidiFilterDialog.cpp
new file mode 100644
index 0000000..69d687a
--- /dev/null
+++ b/src/gui/seqmanager/MidiFilterDialog.cpp
@@ -0,0 +1,229 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "MidiFilterDialog.h"
+
+#include <klocale.h>
+#include "base/MidiProgram.h"
+#include "base/NotationTypes.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/seqmanager/SequenceManager.h"
+#include "sound/MappedEvent.h"
+#include <kdialogbase.h>
+#include <qbuttongroup.h>
+#include <qcheckbox.h>
+#include <qhbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+MidiFilterDialog::MidiFilterDialog(QWidget *parent,
+ RosegardenGUIDoc *doc):
+ KDialogBase(parent, 0, true, i18n("Modify MIDI filters..."),
+ Ok | Apply | Close | Help),
+ m_doc(doc),
+ m_modified(true)
+{
+ setHelp("studio-midi-filters");
+
+ QHBox *hBox = makeHBoxMainWidget();
+
+ m_thruBox =
+ new QButtonGroup(1,
+ Qt::Horizontal,
+ i18n("THRU events to ignore"), hBox);
+
+ QCheckBox *noteThru = new QCheckBox(i18n("Note"), m_thruBox);
+ QCheckBox *progThru = new QCheckBox(i18n("Program Change"), m_thruBox);
+ QCheckBox *keyThru = new QCheckBox(i18n("Key Pressure"), m_thruBox);
+ QCheckBox *chanThru = new QCheckBox(i18n("Channel Pressure"), m_thruBox);
+ QCheckBox *pitchThru = new QCheckBox(i18n("Pitch Bend"), m_thruBox);
+ QCheckBox *contThru = new QCheckBox(i18n("Controller"), m_thruBox);
+ QCheckBox *sysThru = new QCheckBox(i18n("System Exclusive"), m_thruBox);
+
+ MidiFilter thruFilter = m_doc->getStudio().getMIDIThruFilter();
+
+ if (thruFilter & MappedEvent::MidiNote)
+ noteThru->setChecked(true);
+
+ if (thruFilter & MappedEvent::MidiProgramChange)
+ progThru->setChecked(true);
+
+ if (thruFilter & MappedEvent::MidiKeyPressure)
+ keyThru->setChecked(true);
+
+ if (thruFilter & MappedEvent::MidiChannelPressure)
+ chanThru->setChecked(true);
+
+ if (thruFilter & MappedEvent::MidiPitchBend)
+ pitchThru->setChecked(true);
+
+ if (thruFilter & MappedEvent::MidiController)
+ contThru->setChecked(true);
+
+ if (thruFilter & MappedEvent::MidiSystemMessage)
+ sysThru->setChecked(true);
+
+ m_recordBox =
+ new QButtonGroup(1,
+ Qt::Horizontal,
+ i18n("RECORD events to ignore"), hBox);
+
+ QCheckBox *noteRecord = new QCheckBox(i18n("Note"), m_recordBox);
+ QCheckBox *progRecord = new QCheckBox(i18n("Program Change"), m_recordBox);
+ QCheckBox *keyRecord = new QCheckBox(i18n("Key Pressure"), m_recordBox);
+ QCheckBox *chanRecord = new QCheckBox(i18n("Channel Pressure"), m_recordBox);
+ QCheckBox *pitchRecord = new QCheckBox(i18n("Pitch Bend"), m_recordBox);
+ QCheckBox *contRecord = new QCheckBox(i18n("Controller"), m_recordBox);
+ QCheckBox *sysRecord = new QCheckBox(i18n("System Exclusive"), m_recordBox);
+
+ MidiFilter recordFilter =
+ m_doc->getStudio().getMIDIRecordFilter();
+
+ if (recordFilter & MappedEvent::MidiNote)
+ noteRecord->setChecked(true);
+
+ if (recordFilter & MappedEvent::MidiProgramChange)
+ progRecord->setChecked(true);
+
+ if (recordFilter & MappedEvent::MidiKeyPressure)
+ keyRecord->setChecked(true);
+
+ if (recordFilter & MappedEvent::MidiChannelPressure)
+ chanRecord->setChecked(true);
+
+ if (recordFilter & MappedEvent::MidiPitchBend)
+ pitchRecord->setChecked(true);
+
+ if (recordFilter & MappedEvent::MidiController)
+ contRecord->setChecked(true);
+
+ if (recordFilter & MappedEvent::MidiSystemMessage)
+ sysRecord->setChecked(true);
+
+
+ connect(m_thruBox, SIGNAL(released(int)),
+ this, SLOT(slotSetModified()));
+
+ connect(m_recordBox, SIGNAL(released(int)),
+ this, SLOT(slotSetModified()));
+
+ setModified(false);
+}
+
+void
+MidiFilterDialog::slotApply()
+{
+ MidiFilter thruFilter = 0,
+ recordFilter = 0;
+
+ if (dynamic_cast<QCheckBox*>(m_thruBox->find(0))->isChecked())
+ thruFilter |= MappedEvent::MidiNote;
+
+ if (dynamic_cast<QCheckBox*>(m_thruBox->find(1))->isChecked())
+ thruFilter |= MappedEvent::MidiProgramChange;
+
+ if (dynamic_cast<QCheckBox*>(m_thruBox->find(2))->isChecked())
+ thruFilter |= MappedEvent::MidiKeyPressure;
+
+ if (dynamic_cast<QCheckBox*>(m_thruBox->find(3))->isChecked())
+ thruFilter |= MappedEvent::MidiChannelPressure;
+
+ if (dynamic_cast<QCheckBox*>(m_thruBox->find(4))->isChecked())
+ thruFilter |= MappedEvent::MidiPitchBend;
+
+ if (dynamic_cast<QCheckBox*>(m_thruBox->find(5))->isChecked())
+ thruFilter |= MappedEvent::MidiController;
+
+ if (dynamic_cast<QCheckBox*>(m_thruBox->find(6))->isChecked())
+ thruFilter |= MappedEvent::MidiSystemMessage;
+
+ if (dynamic_cast<QCheckBox*>(m_recordBox->find(0))->isChecked())
+ recordFilter |= MappedEvent::MidiNote;
+
+ if (dynamic_cast<QCheckBox*>(m_recordBox->find(1))->isChecked())
+ recordFilter |= MappedEvent::MidiProgramChange;
+
+ if (dynamic_cast<QCheckBox*>(m_recordBox->find(2))->isChecked())
+ recordFilter |= MappedEvent::MidiKeyPressure;
+
+ if (dynamic_cast<QCheckBox*>(m_recordBox->find(3))->isChecked())
+ recordFilter |= MappedEvent::MidiChannelPressure;
+
+ if (dynamic_cast<QCheckBox*>(m_recordBox->find(4))->isChecked())
+ recordFilter |= MappedEvent::MidiPitchBend;
+
+ if (dynamic_cast<QCheckBox*>(m_recordBox->find(5))->isChecked())
+ recordFilter |= MappedEvent::MidiController;
+
+ if (dynamic_cast<QCheckBox*>(m_recordBox->find(6))->isChecked())
+ recordFilter |= MappedEvent::MidiSystemMessage;
+
+
+ //if (m_thruBox->
+
+ m_doc->getStudio().setMIDIThruFilter(thruFilter);
+ m_doc->getStudio().setMIDIRecordFilter(recordFilter);
+
+ if (m_doc->getSequenceManager()) {
+ m_doc->getSequenceManager()->filtersChanged(thruFilter, recordFilter);
+ }
+
+ setModified(false);
+}
+
+void
+MidiFilterDialog::slotOk()
+{
+ slotApply();
+ accept();
+}
+
+void
+MidiFilterDialog::slotSetModified()
+{
+ setModified(true);
+}
+
+void
+MidiFilterDialog::setModified(bool value)
+{
+ if (m_modified == value)
+ return ;
+
+ if (value) {
+ enableButtonApply(true);
+ } else {
+ enableButtonApply(false);
+ }
+
+ m_modified = value;
+
+}
+
+}
+#include "MidiFilterDialog.moc"
diff --git a/src/gui/seqmanager/MidiFilterDialog.h b/src/gui/seqmanager/MidiFilterDialog.h
new file mode 100644
index 0000000..06099ac
--- /dev/null
+++ b/src/gui/seqmanager/MidiFilterDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_MIDIFILTERDIALOG_H_
+#define _RG_MIDIFILTERDIALOG_H_
+
+#include <kdialogbase.h>
+
+
+class QWidget;
+class QButtonGroup;
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+
+
+class MidiFilterDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ MidiFilterDialog(QWidget *parent,
+ RosegardenGUIDoc *doc);
+
+ void setModified(bool value);
+
+public slots:
+
+ void slotOk();
+ void slotApply();
+ void slotSetModified();
+
+protected:
+
+ RosegardenGUIDoc *m_doc;
+
+ QButtonGroup *m_thruBox;
+ QButtonGroup *m_recordBox;
+
+ bool m_modified;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/seqmanager/SegmentMmapper.cpp b/src/gui/seqmanager/SegmentMmapper.cpp
new file mode 100644
index 0000000..5cf117d
--- /dev/null
+++ b/src/gui/seqmanager/SegmentMmapper.cpp
@@ -0,0 +1,562 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "SegmentMmapper.h"
+#include "misc/Debug.h"
+
+#include "misc/Strings.h"
+#include "base/BaseProperties.h"
+#include "base/Composition.h"
+#include "base/Event.h"
+#include "base/Exception.h"
+#include "base/NotationTypes.h"
+#include "base/RealTime.h"
+#include "base/Segment.h"
+#include "base/SegmentPerformanceHelper.h"
+#include "base/TriggerSegment.h"
+#include "document/RosegardenGUIDoc.h"
+#include "sound/MappedEvent.h"
+#include <qfile.h>
+#include <qstring.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+
+namespace Rosegarden
+{
+
+SegmentMmapper::SegmentMmapper(RosegardenGUIDoc* doc,
+ Segment* segment, const QString& fileName)
+ : m_doc(doc),
+ m_segment(segment),
+ m_fileName(fileName),
+ m_fd( -1),
+ m_mmappedSize(0),
+ m_mmappedRegion(0),
+ m_mmappedEventBuffer((MappedEvent*)0)
+{
+ SEQMAN_DEBUG << "SegmentMmapper : " << this
+ << " trying to mmap " << m_fileName
+ << endl;
+
+ m_fd = ::open(m_fileName.latin1(), O_RDWR | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IWUSR);
+ if (m_fd < 0) {
+ perror("SegmentMmapper::SegmentMmapper: Failed to open mmap file for writing");
+ SEQMAN_DEBUG << "SegmentMmapper : Couldn't open " << m_fileName
+ << endl;
+ throw Exception("Couldn't open " + qstrtostr(m_fileName));
+ }
+
+ // SEQMAN_DEBUG << "SegmentMmapper : mmap size = " << m_mmappedSize
+ // << endl;
+}
+
+void SegmentMmapper::init()
+{
+ m_mmappedSize = computeMmappedSize() + sizeof(size_t);
+
+ if (m_mmappedSize > 0) {
+ setFileSize(m_mmappedSize);
+ doMmap();
+ dump();
+ if (m_segment != 0) {
+ SEQMAN_DEBUG << "SegmentMmapper::init : mmap size = " << m_mmappedSize
+ << " for segment " << m_segment->getLabel() << endl;
+ }
+
+ } else {
+ SEQMAN_DEBUG << "SegmentMmapper::init : mmap size = 0 - skipping mmapping for now\n";
+ }
+}
+
+SegmentMmapper::~SegmentMmapper()
+{
+ SEQMAN_DEBUG << "~SegmentMmapper : " << this
+ << " unmapping " << (void*)m_mmappedRegion
+ << " of size " << m_mmappedSize
+ << endl;
+
+ if (m_mmappedRegion && m_mmappedSize)
+ ::munmap(m_mmappedRegion, m_mmappedSize);
+
+ ::close(m_fd);
+ SEQMAN_DEBUG << "~SegmentMmapper : removing " << m_fileName << endl;
+
+ QFile::remove
+ (m_fileName);
+}
+
+bool SegmentMmapper::refresh()
+{
+ bool res = false;
+
+ size_t newMmappedSize = computeMmappedSize() + sizeof(size_t);
+
+ SEQMAN_DEBUG << "SegmentMmapper::refresh() - " << getFileName()
+ << " - m_mmappedRegion = " << (void*)m_mmappedRegion
+ << " - m_mmappedEventBuffer = " << (void*)m_mmappedEventBuffer
+ << " - new size = " << newMmappedSize
+ << " - old size = " << m_mmappedSize
+ << endl;
+
+ // We can't zero out the buffer here because if the mmapped
+ // segment is being read from by the sequencer in the interval of
+ // time between the memset() and the dump(), the sequencer will go
+ // over all the zeros up to the end of the segment and reach its
+ // end, and therefore will stop playing it.
+ //
+
+ if (newMmappedSize != m_mmappedSize) {
+
+ res = true;
+
+ if (newMmappedSize == 0) {
+
+ // nothing to do, just msync and go
+ ::msync(m_mmappedRegion, m_mmappedSize, MS_ASYNC);
+ m_mmappedSize = 0;
+ return true;
+
+ } else {
+
+ setFileSize(newMmappedSize);
+ remap(newMmappedSize);
+ }
+ }
+
+ dump();
+
+ return res;
+}
+
+void SegmentMmapper::setFileSize(size_t size)
+{
+ SEQMAN_DEBUG << "SegmentMmapper::setFileSize() : setting size of "
+ << m_fileName << " to " << size
+ << " - current size = " << m_mmappedSize << endl;
+
+ if (size < m_mmappedSize) {
+
+ // Don't truncate the file here: that will cause trouble for
+ // the sequencer
+ // ftruncate(m_fd, size);
+
+ } else {
+
+ // On linux, ftruncate can enlarge a file, but this isn't specified by POSIX
+ // so go the safe way
+
+ if (size == 0) {
+ SEQMAN_DEBUG << "SegmentMmapper : size == 0 : no resize to do\n";
+ return ;
+ }
+
+ // rewind
+ ::lseek(m_fd, 0, SEEK_SET);
+
+ //
+ // enlarge the file
+ // (seek() to wanted size, then write a byte)
+ //
+ if (::lseek(m_fd, size - 1, SEEK_SET) == -1) {
+ std::cerr << "WARNING: SegmentMmapper : Couldn't lseek in "
+ << m_fileName << " to " << size << std::endl;
+ throw Exception("lseek failed");
+ }
+
+ if (::write(m_fd, "\0", 1) != 1) {
+ std::cerr << "WARNING: SegmentMmapper : Couldn't write byte in "
+ << m_fileName << std::endl;
+ throw Exception("write failed");
+ }
+
+ }
+
+
+}
+
+void SegmentMmapper::remap(size_t newsize)
+{
+ SEQMAN_DEBUG << "SegmentMmapper : remapping " << m_fileName
+ << " from size " << m_mmappedSize
+ << " to size " << newsize << endl;
+
+ if (!m_mmappedRegion) { // nothing to mremap, just mmap
+
+ SEQMAN_DEBUG << "SegmentMmapper : nothing to remap - mmap instead\n";
+ m_mmappedSize = newsize;
+ doMmap();
+
+ } else {
+
+#ifdef linux
+ void *oldBuffer = m_mmappedRegion;
+ m_mmappedRegion = (MappedEvent*)::mremap(m_mmappedRegion, m_mmappedSize,
+ newsize, MREMAP_MAYMOVE);
+ m_mmappedEventBuffer = (MappedEvent *)((size_t *)m_mmappedRegion + 1);
+
+ if (m_mmappedRegion != oldBuffer) {
+ SEQMAN_DEBUG << "NOTE: buffer moved from " << oldBuffer <<
+ " to " << (void *)m_mmappedRegion << endl;
+ }
+#else
+ ::munmap(m_mmappedRegion, m_mmappedSize);
+ m_mmappedRegion = ::mmap(0, newsize,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED, m_fd, 0);
+ m_mmappedEventBuffer = (MappedEvent *)((size_t *)m_mmappedRegion + 1);
+#endif
+
+ if (m_mmappedRegion == (void*) - 1) {
+ SEQMAN_DEBUG << QString("mremap failed : (%1) %2\n").arg(errno).arg(strerror(errno));
+ throw Exception("mremap failed");
+ }
+
+ m_mmappedSize = newsize;
+ }
+}
+
+void SegmentMmapper::doMmap()
+{
+ //
+ // mmap() file for writing
+ //
+ m_mmappedRegion = ::mmap(0, m_mmappedSize,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED, m_fd, 0);
+ m_mmappedEventBuffer = (MappedEvent *)((size_t *)m_mmappedRegion + 1);
+
+ if (m_mmappedRegion == (void*) - 1) {
+ SEQMAN_DEBUG << QString("mmap failed : (%1) %2\n").arg(errno).arg(strerror(errno));
+ throw Exception("mmap failed");
+ }
+
+ SEQMAN_DEBUG << "SegmentMmapper::doMmap() - mmap size : " << m_mmappedSize
+ << " at " << (void*)m_mmappedRegion << endl;
+
+}
+
+void SegmentMmapper::dump()
+{
+ Composition &comp = m_doc->getComposition();
+
+ RealTime eventTime;
+ RealTime duration;
+ Track* track = comp.getTrackById(m_segment->getTrack());
+
+ timeT segmentStartTime = m_segment->getStartTime();
+ timeT segmentEndTime = m_segment->getEndMarkerTime();
+ timeT segmentDuration = segmentEndTime - segmentStartTime;
+ timeT repeatEndTime = segmentEndTime;
+
+ int repeatCount = getSegmentRepeatCount();
+
+ if (repeatCount > 0)
+ repeatEndTime = m_segment->getRepeatEndTime();
+
+ MappedEvent* bufPos = m_mmappedEventBuffer;
+
+ for (int repeatNo = 0; repeatNo <= repeatCount; ++repeatNo) {
+
+ Segment *triggered = 0;
+ Segment::iterator *i = 0;
+
+ for (Segment::iterator j = m_segment->begin();
+ m_segment->isBeforeEndMarker(j) || (i && *i != triggered->end()); ) {
+
+ bool usingi = false;
+ Segment::iterator *k = &j;
+
+ if (i && *i != triggered->end() &&
+ (!m_segment->isBeforeEndMarker(j) ||
+ (**i)->getAbsoluteTime() < (*j)->getAbsoluteTime())) {
+ k = i;
+ usingi = true;
+ }
+
+ if (!usingi) { // don't permit nested triggered segments
+
+ long triggerId = -1;
+ (**k)->get
+ <Int>(BaseProperties::TRIGGER_SEGMENT_ID, triggerId);
+
+ if (triggerId >= 0) {
+
+ TriggerSegmentRec *rec =
+ comp.getTriggerSegmentRec(triggerId);
+
+ if (rec && rec->getSegment()) {
+ timeT performanceDuration =
+ SegmentPerformanceHelper(*m_segment).
+ getSoundingDuration(j);
+ if (performanceDuration > 0) {
+ mergeTriggerSegment(&triggered, *j,
+ performanceDuration, rec);
+ size_t sz = addMmappedSize(rec->getSegment());
+ size_t offset = bufPos - m_mmappedEventBuffer;
+ setFileSize(sz);
+ remap(sz);
+ bufPos = m_mmappedEventBuffer + offset;
+ }
+ }
+
+ if (triggered) {
+ if (i)
+ delete i;
+ i = new Segment::iterator
+ (triggered->findTime((*j)->getAbsoluteTime()));
+ }
+
+ // Use the next triggered event (presumably the
+ // first of the current triggered segment) instead
+ // of the one that triggered it
+
+ ++j; // whatever happens, we don't want to write this one
+
+ if (i && *i != triggered->end() &&
+ (!m_segment->isBeforeEndMarker(j) ||
+ ((**i)->getAbsoluteTime() < (*j)->getAbsoluteTime()))) {
+ k = i;
+ usingi = true;
+ } else {
+ // no joy at all
+ continue;
+ }
+ }
+ }
+
+ // Ignore rests
+ //
+ if (!(**k)->isa(Note::EventRestType)) {
+
+ SegmentPerformanceHelper helper
+ (usingi ? *triggered : *m_segment);
+
+ timeT playTime =
+ helper.getSoundingAbsoluteTime(*k) + repeatNo * segmentDuration;
+ if (playTime >= repeatEndTime)
+ break;
+
+ timeT playDuration = helper.getSoundingDuration(*k);
+
+ // Ignore notes without duration -- they're probably in a tied
+ // series but not as first note
+ //
+ if (playDuration > 0 || !(**k)->isa(Note::EventType)) {
+
+ if (playTime + playDuration > repeatEndTime)
+ playDuration = repeatEndTime - playTime;
+
+ playTime = playTime + m_segment->getDelay();
+ eventTime = comp.getElapsedRealTime(playTime);
+
+ // slightly quicker than calling helper.getRealSoundingDuration()
+ duration =
+ comp.getElapsedRealTime(playTime + playDuration) - eventTime;
+
+ eventTime = eventTime + m_segment->getRealTimeDelay();
+
+ try {
+ // Create mapped event in mmapped buffer. The
+ // instrument will be extracted from the ControlBlock
+ // by the sequencer, so we set it to zero here.
+ MappedEvent *mE = new (bufPos)
+ MappedEvent(0,
+ ***k, // three stars! what an accolade
+ eventTime,
+ duration);
+ mE->setTrackId(track->getId());
+
+ if (m_segment->getTranspose() != 0 &&
+ (**k)->isa(Note::EventType)) {
+ mE->setPitch(mE->getPitch() + m_segment->getTranspose());
+ }
+
+ ++bufPos;
+
+ } catch (...) {
+ SEQMAN_DEBUG << "SegmentMmapper::dump - caught exception while trying to create MappedEvent\n";
+ }
+ }
+ }
+
+ ++*k; // increment either i or j, whichever one we just used
+ }
+
+ delete i;
+ delete triggered;
+ }
+
+ // Store the number of events at the start of the shared memory region
+ *(size_t *)m_mmappedRegion = (bufPos - m_mmappedEventBuffer);
+
+ size_t coveredArea = (bufPos - m_mmappedEventBuffer) * sizeof(MappedEvent);
+ memset(bufPos, 0, m_mmappedSize - coveredArea - sizeof(size_t));
+
+ ::msync(m_mmappedRegion, m_mmappedSize, MS_ASYNC);
+}
+
+void
+SegmentMmapper::mergeTriggerSegment(Segment **target,
+ Event *trigger,
+ timeT evDuration,
+ TriggerSegmentRec *rec)
+{
+ if (!rec || !rec->getSegment() || rec->getSegment()->empty())
+ return ;
+ if (!*target)
+ *target = new Segment;
+
+ timeT evTime = trigger->getAbsoluteTime();
+ timeT trStart = rec->getSegment()->getStartTime();
+ timeT trEnd = rec->getSegment()->getEndMarkerTime();
+ timeT trDuration = trEnd - trStart;
+ if (trDuration == 0)
+ return ;
+
+ bool retune = false;
+ std::string timeAdjust = BaseProperties::TRIGGER_SEGMENT_ADJUST_NONE;
+
+ trigger->get
+ <Bool>
+ (BaseProperties::TRIGGER_SEGMENT_RETUNE, retune);
+
+ trigger->get
+ <String>
+ (BaseProperties::TRIGGER_SEGMENT_ADJUST_TIMES, timeAdjust);
+
+ long evPitch = rec->getBasePitch();
+ (void)trigger->get
+ <Int>(BaseProperties::PITCH, evPitch);
+ int pitchDiff = evPitch - rec->getBasePitch();
+
+ long evVelocity = rec->getBaseVelocity();
+ (void)trigger->get
+ <Int>(BaseProperties::VELOCITY, evVelocity);
+ int velocityDiff = evVelocity - rec->getBaseVelocity();
+
+ timeT offset = 0;
+ if (timeAdjust == BaseProperties::TRIGGER_SEGMENT_ADJUST_SYNC_END) {
+ offset = evDuration - trDuration;
+ }
+
+ for (Segment::iterator i = rec->getSegment()->begin();
+ rec->getSegment()->isBeforeEndMarker(i); ++i) {
+
+ timeT t = (*i)->getAbsoluteTime() - trStart;
+ timeT d = (*i)->getDuration();
+
+ if (evDuration != trDuration &&
+ timeAdjust == BaseProperties::TRIGGER_SEGMENT_ADJUST_SQUISH) {
+ t = timeT(double(t * evDuration) / double(trDuration));
+ d = timeT(double(d * evDuration) / double(trDuration));
+ }
+
+ t += evTime + offset;
+
+ if (t < evTime) {
+ if (t + d <= evTime)
+ continue;
+ else {
+ d -= (evTime - t);
+ t = evTime;
+ }
+ }
+
+ if (timeAdjust == BaseProperties::TRIGGER_SEGMENT_ADJUST_SYNC_START) {
+ if (t + d > evTime + evDuration) {
+ if (t >= evTime + evDuration)
+ continue;
+ else {
+ d = evTime + evDuration - t;
+ }
+ }
+ }
+
+ Event *newEvent = new Event(**i, t, d);
+
+ if (retune && newEvent->has(BaseProperties::PITCH)) {
+ int pitch = newEvent->get
+ <Int>(BaseProperties::PITCH) + pitchDiff;
+ if (pitch > 127)
+ pitch = 127;
+ if (pitch < 0)
+ pitch = 0;
+ newEvent->set
+ <Int>(BaseProperties::PITCH, pitch);
+ }
+
+ if (newEvent->has(BaseProperties::VELOCITY)) {
+ int velocity = newEvent->get
+ <Int>(BaseProperties::VELOCITY) + velocityDiff;
+ if (velocity > 127)
+ velocity = 127;
+ if (velocity < 0)
+ velocity = 0;
+ newEvent->set
+ <Int>(BaseProperties::VELOCITY, velocity);
+ }
+
+ (*target)->insert(newEvent);
+ }
+}
+
+unsigned int SegmentMmapper::getSegmentRepeatCount()
+{
+ int repeatCount = 0;
+
+ timeT segmentStartTime = m_segment->getStartTime();
+ timeT segmentEndTime = m_segment->getEndMarkerTime();
+ timeT segmentDuration = segmentEndTime - segmentStartTime;
+ timeT repeatEndTime = segmentEndTime;
+
+ if (m_segment->isRepeating() && segmentDuration > 0) {
+ repeatEndTime = m_segment->getRepeatEndTime();
+ repeatCount = 1 + (repeatEndTime - segmentEndTime) / segmentDuration;
+ }
+
+ return repeatCount;
+}
+
+size_t SegmentMmapper::addMmappedSize(Segment *s)
+{
+ int repeatCount = getSegmentRepeatCount();
+ return m_mmappedSize + (repeatCount + 1) * s->size() * sizeof(MappedEvent);
+}
+
+size_t SegmentMmapper::computeMmappedSize()
+{
+ if (!m_segment) return 0;
+
+ int repeatCount = getSegmentRepeatCount();
+
+ return (repeatCount + 1) * m_segment->size() * sizeof(MappedEvent);
+}
+
+}
diff --git a/src/gui/seqmanager/SegmentMmapper.h b/src/gui/seqmanager/SegmentMmapper.h
new file mode 100644
index 0000000..ab25aab
--- /dev/null
+++ b/src/gui/seqmanager/SegmentMmapper.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_SEGMENTMMAPPER_H_
+#define _RG_SEGMENTMMAPPER_H_
+
+#include <qstring.h>
+#include "base/Event.h"
+
+
+
+
+namespace Rosegarden
+{
+
+class TriggerSegmentRec;
+class Segment;
+class RosegardenGUIDoc;
+class MappedEvent;
+class Event;
+
+
+class SegmentMmapper
+{
+ friend class SegmentMmapperFactory;
+public:
+ virtual ~SegmentMmapper();
+
+ /**
+ * refresh the object after the segment has been modified
+ * returns true if size changed (and thus the sequencer
+ * needs to be told about it
+ */
+ bool refresh();
+
+ QString getFileName() { return m_fileName; }
+ size_t getFileSize() const { return m_mmappedSize; }
+
+ virtual unsigned int getSegmentRepeatCount();
+
+protected:
+ SegmentMmapper(RosegardenGUIDoc*, Segment*,
+ const QString& fileName);
+
+ virtual size_t computeMmappedSize();
+
+ virtual size_t addMmappedSize(Segment *);
+
+ /// actual setup, must be called after ctor, calls virtual methods
+ virtual void init();
+
+ /// set the size of the mmapped file
+ void setFileSize(size_t);
+
+ /// perform the mmap() of the file
+ void doMmap();
+
+ /// mremap() the file after a size change
+ void remap(size_t newsize);
+
+ /// dump all segment data in the file
+ virtual void dump();
+
+ void mergeTriggerSegment(Segment **target,
+ Event *trigger,
+ timeT performanceDuration,
+ TriggerSegmentRec *rec);
+
+ //--------------- Data members ---------------------------------
+ RosegardenGUIDoc* m_doc;
+ Segment* m_segment;
+ QString m_fileName;
+
+ int m_fd;
+ size_t m_mmappedSize;
+
+ // The shared memory region starts with a size_t value
+ // representing the number of MappedEvents that follow.
+ void *m_mmappedRegion;
+
+ // And this points to the next byte in the shared memory region.
+ MappedEvent* m_mmappedEventBuffer;
+};
+
+//----------------------------------------
+
+
+}
+
+#endif
diff --git a/src/gui/seqmanager/SegmentMmapperFactory.cpp b/src/gui/seqmanager/SegmentMmapperFactory.cpp
new file mode 100644
index 0000000..085d842
--- /dev/null
+++ b/src/gui/seqmanager/SegmentMmapperFactory.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "SegmentMmapperFactory.h"
+
+#include "base/Segment.h"
+#include "document/RosegardenGUIDoc.h"
+#include "misc/Debug.h"
+#include "MetronomeMmapper.h"
+#include "SegmentMmapper.h"
+#include "AudioSegmentMmapper.h"
+#include "TempoSegmentMmapper.h"
+#include "TimeSigSegmentMmapper.h"
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+SegmentMmapper* SegmentMmapperFactory::makeMmapperForSegment(RosegardenGUIDoc* doc,
+ Rosegarden::Segment* segment,
+ const QString& fileName)
+{
+ SegmentMmapper* mmapper = 0;
+
+ if (segment == 0) {
+ SEQMAN_DEBUG << "SegmentMmapperFactory::makeMmapperForSegment() segment == 0\n";
+ return 0;
+ }
+
+ switch (segment->getType()) {
+ case Segment::Internal :
+ mmapper = new SegmentMmapper(doc, segment, fileName);
+ break;
+ case Segment::Audio :
+ mmapper = new AudioSegmentMmapper(doc, segment, fileName);
+ break;
+ default:
+ SEQMAN_DEBUG << "SegmentMmapperFactory::makeMmapperForSegment(" << segment
+ << ") : can't map, unknown segment type " << segment->getType() << endl;
+ mmapper = 0;
+ }
+
+ if (mmapper)
+ mmapper->init();
+
+ return mmapper;
+}
+
+MetronomeMmapper* SegmentMmapperFactory::makeMetronome(RosegardenGUIDoc* doc)
+{
+ MetronomeMmapper* mmapper = new MetronomeMmapper(doc);
+ mmapper->init();
+
+ return mmapper;
+}
+
+TimeSigSegmentMmapper* SegmentMmapperFactory::makeTimeSig(RosegardenGUIDoc* doc)
+{
+ TimeSigSegmentMmapper* mmapper = new TimeSigSegmentMmapper(doc, "rosegarden_timesig");
+
+ mmapper->init();
+ return mmapper;
+}
+
+TempoSegmentMmapper* SegmentMmapperFactory::makeTempo(RosegardenGUIDoc* doc)
+{
+ TempoSegmentMmapper* mmapper = new TempoSegmentMmapper(doc, "rosegarden_tempo");
+
+ mmapper->init();
+ return mmapper;
+}
+
+}
diff --git a/src/gui/seqmanager/SegmentMmapperFactory.h b/src/gui/seqmanager/SegmentMmapperFactory.h
new file mode 100644
index 0000000..2970024
--- /dev/null
+++ b/src/gui/seqmanager/SegmentMmapperFactory.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_SEGMENTMMAPPERFACTORY_H_
+#define _RG_SEGMENTMMAPPERFACTORY_H_
+
+
+
+class QString;
+
+
+namespace Rosegarden
+{
+
+class TimeSigSegmentMmapper;
+class TempoSegmentMmapper;
+class SegmentMmapper;
+class Segment;
+class RosegardenGUIDoc;
+class MetronomeMmapper;
+
+
+class SegmentMmapperFactory
+{
+public:
+
+ static SegmentMmapper* makeMmapperForSegment(RosegardenGUIDoc*, Segment*,
+ const QString& fileName);
+
+ static MetronomeMmapper* makeMetronome(RosegardenGUIDoc*);
+ static TimeSigSegmentMmapper* makeTimeSig(RosegardenGUIDoc*);
+ static TempoSegmentMmapper* makeTempo(RosegardenGUIDoc*);
+};
+
+//----------------------------------------
+
+
+
+}
+
+#endif
diff --git a/src/gui/seqmanager/SequenceManager.cpp b/src/gui/seqmanager/SequenceManager.cpp
new file mode 100644
index 0000000..12334f6
--- /dev/null
+++ b/src/gui/seqmanager/SequenceManager.cpp
@@ -0,0 +1,2141 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "SequenceManager.h"
+
+#include "sound/Midi.h"
+#include "misc/Debug.h"
+#include "document/ConfigGroups.h"
+#include "base/Composition.h"
+#include "base/Device.h"
+#include "base/Exception.h"
+#include "base/Instrument.h"
+#include "base/MidiProgram.h"
+#include "base/RealTime.h"
+#include "base/Segment.h"
+#include "base/Studio.h"
+#include "base/Track.h"
+#include "base/TriggerSegment.h"
+#include "CompositionMmapper.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/MultiViewCommandHistory.h"
+#include "gui/application/RosegardenApplication.h"
+#include "gui/application/RosegardenGUIApp.h"
+#include "gui/application/RosegardenGUIView.h"
+#include "gui/dialogs/AudioManagerDialog.h"
+#include "gui/dialogs/CountdownDialog.h"
+#include "gui/dialogs/TransportDialog.h"
+#include "gui/kdeext/KStartupLogo.h"
+#include "gui/studio/StudioControl.h"
+#include "gui/widgets/CurrentProgressDialog.h"
+#include "MetronomeMmapper.h"
+#include "SegmentMmapperFactory.h"
+#include "SequencerMapper.h"
+#include "ControlBlockMmapper.h"
+#include "sound/AudioFile.h"
+#include "sound/MappedComposition.h"
+#include "sound/MappedEvent.h"
+#include "sound/MappedInstrument.h"
+#include "sound/SoundDriver.h"
+#include "TempoSegmentMmapper.h"
+#include "TimeSigSegmentMmapper.h"
+#include <klocale.h>
+#include <kstddirs.h>
+#include <kconfig.h>
+#include <kglobal.h>
+#include <kmessagebox.h>
+#include <qapplication.h>
+#include <qcstring.h>
+#include <qcursor.h>
+#include <qdatastream.h>
+#include <qevent.h>
+#include <qobject.h>
+#include <qpushbutton.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qtimer.h>
+#include <algorithm>
+
+
+namespace Rosegarden
+{
+
+SequenceManager::SequenceManager(RosegardenGUIDoc *doc,
+ TransportDialog *transport):
+ m_doc(doc),
+ m_compositionMmapper(new CompositionMmapper(m_doc)),
+ m_controlBlockMmapper(new ControlBlockMmapper(m_doc)),
+ m_metronomeMmapper(SegmentMmapperFactory::makeMetronome(m_doc)),
+ m_tempoSegmentMmapper(SegmentMmapperFactory::makeTempo(m_doc)),
+ m_timeSigSegmentMmapper(SegmentMmapperFactory::makeTimeSig(m_doc)),
+ m_transportStatus(STOPPED),
+ m_soundDriverStatus(NO_DRIVER),
+ m_transport(transport),
+ m_lastRewoundAt(clock()),
+ m_countdownDialog(0),
+ m_countdownTimer(new QTimer(m_doc)),
+ m_shownOverrunWarning(false),
+ m_recordTime(new QTime()),
+ m_compositionRefreshStatusId(m_doc->getComposition().getNewRefreshStatusId()),
+ m_updateRequested(true),
+ m_compositionMmapperResetTimer(new QTimer(m_doc)),
+ m_sequencerMapper(0),
+ m_reportTimer(new QTimer(m_doc)),
+ m_canReport(true),
+ m_lastLowLatencySwitchSent(false),
+ m_lastTransportStartPosition(0),
+ m_sampleRate(0)
+{
+ // Replaced this with a call to cleanup() from composition mmapper ctor:
+ // if done here, this removes the mmapped versions of any segments stored
+ // in the autoload (that have only just been mapped by the ctor!)
+ // m_compositionMmapper->cleanup();
+
+ m_countdownDialog = new CountdownDialog(dynamic_cast<QWidget*>
+ (m_doc->parent())->parentWidget());
+ // Connect these for use later
+ //
+ connect(m_countdownTimer, SIGNAL(timeout()),
+ this, SLOT(slotCountdownTimerTimeout()));
+
+ connect(m_reportTimer, SIGNAL(timeout()),
+ this, SLOT(slotAllowReport()));
+
+ connect(m_compositionMmapperResetTimer, SIGNAL(timeout()),
+ this, SLOT(slotScheduledCompositionMmapperReset()));
+
+
+ connect(doc->getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(update()));
+
+ m_doc->getComposition().addObserver(this);
+
+ // The owner of this sequence manager will need to call
+ // checkSoundDriverStatus on it to set up its status appropriately
+ // immediately after construction; we used to do it from here but
+ // we're not well placed to handle reporting to the user if it
+ // throws an exception (and we don't want to leave the object half
+ // constructed).
+
+ // Try to map the sequencer file
+ //
+ mapSequencer();
+}
+
+SequenceManager::~SequenceManager()
+{
+ m_doc->getComposition().removeObserver(this);
+
+ SEQMAN_DEBUG << "SequenceManager::~SequenceManager()\n";
+ delete m_compositionMmapper;
+ delete m_controlBlockMmapper;
+ delete m_metronomeMmapper;
+ delete m_tempoSegmentMmapper;
+ delete m_timeSigSegmentMmapper;
+ delete m_sequencerMapper;
+}
+
+void SequenceManager::setDocument(RosegardenGUIDoc* doc)
+{
+ SEQMAN_DEBUG << "SequenceManager::setDocument(" << doc << ")\n";
+
+ DataBlockRepository::clear();
+
+ m_doc->getComposition().removeObserver(this);
+ disconnect(m_doc->getCommandHistory(), SIGNAL(commandExecuted()));
+
+ m_segments.clear();
+ m_triggerSegments.clear();
+
+ m_doc = doc;
+ Composition &comp = m_doc->getComposition();
+
+ // Must recreate and reconnect the countdown timer and dialog
+ // (bug 729039)
+ //
+ delete m_countdownDialog;
+ delete m_countdownTimer;
+ delete m_compositionMmapperResetTimer;
+
+ m_countdownDialog = new CountdownDialog(dynamic_cast<QWidget*>
+ (m_doc->parent())->parentWidget());
+
+ // Bug 933041: no longer connect the CountdownDialog from
+ // SequenceManager; instead let the RosegardenGUIApp connect it to
+ // its own slotStop to ensure the right housekeeping is done
+
+ m_countdownTimer = new QTimer(m_doc);
+
+ // Connect this for use later
+ //
+ connect(m_countdownTimer, SIGNAL(timeout()),
+ this, SLOT(slotCountdownTimerTimeout()));
+
+ m_compositionRefreshStatusId = comp.getNewRefreshStatusId();
+ comp.addObserver(this);
+
+ connect(m_doc->getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(update()));
+
+ for (Composition::iterator i = comp.begin(); i != comp.end(); ++i) {
+
+ SEQMAN_DEBUG << "Adding segment with rid " << (*i)->getRuntimeId() << endl;
+
+ m_segments.insert(SegmentRefreshMap::value_type
+ (*i, (*i)->getNewRefreshStatusId()));
+ }
+
+ for (Composition::triggersegmentcontaineriterator i =
+ comp.getTriggerSegments().begin();
+ i != comp.getTriggerSegments().end(); ++i) {
+ m_triggerSegments.insert(SegmentRefreshMap::value_type
+ ((*i)->getSegment(),
+ (*i)->getSegment()->getNewRefreshStatusId()));
+ }
+
+
+ m_compositionMmapperResetTimer = new QTimer(m_doc);
+ connect(m_compositionMmapperResetTimer, SIGNAL(timeout()),
+ this, SLOT(slotScheduledCompositionMmapperReset()));
+
+ resetCompositionMmapper();
+
+ // Try to map the sequencer file
+ //
+ mapSequencer();
+}
+
+void
+SequenceManager::setTransportStatus(const TransportStatus &status)
+{
+ m_transportStatus = status;
+}
+
+void
+SequenceManager::mapSequencer()
+{
+ if (m_sequencerMapper)
+ return ;
+
+ try {
+ m_sequencerMapper = new SequencerMapper(
+ KGlobal::dirs()->resourceDirs("tmp").last() + "/rosegarden_sequencer_timing_block");
+ } catch (Exception) {
+ m_sequencerMapper = 0;
+ }
+}
+
+bool
+SequenceManager::play()
+{
+ mapSequencer();
+
+ Composition &comp = m_doc->getComposition();
+
+ // If already playing or recording then stop
+ //
+ if (m_transportStatus == PLAYING ||
+ m_transportStatus == RECORDING ) {
+ stopping();
+ return true;
+ }
+
+ // This check may throw an exception
+ checkSoundDriverStatus(false);
+
+ // Align Instrument lists and send initial program changes
+ //
+ preparePlayback();
+
+ m_lastTransportStartPosition = comp.getPosition();
+
+ // Update play metronome status
+ //
+ m_controlBlockMmapper->updateMetronomeData
+ (m_metronomeMmapper->getMetronomeInstrument());
+ m_controlBlockMmapper->updateMetronomeForPlayback();
+
+ // make sure we toggle the play button
+ //
+ m_transport->PlayButton()->setOn(true);
+
+ //!!! disable the record button, because recording while playing is horribly
+ // broken, and disabling it is less complicated than fixing it
+ // see #1223025 - DMM
+ // SEQMAN_DEBUG << "SequenceManager::play() - disabling record button, as we are playing\n";
+ // m_transport->RecordButton()->setEnabled(false);
+
+ if (comp.getCurrentTempo() == 0) {
+ comp.setCompositionDefaultTempo(comp.getTempoForQpm(120.0));
+
+ SEQMAN_DEBUG << "SequenceManager::play() - setting Tempo to Default value of 120.000\n";
+ } else {
+ SEQMAN_DEBUG << "SequenceManager::play() - starting to play\n";
+ }
+
+ // Send initial tempo
+ //
+ double qnD = 60.0 / comp.getTempoQpm(comp.getCurrentTempo());
+ RealTime qnTime =
+ RealTime(long(qnD),
+ long((qnD - double(long(qnD))) * 1000000000.0));
+ StudioControl::sendQuarterNoteLength(qnTime);
+
+ // set the tempo in the transport
+ m_transport->setTempo(comp.getCurrentTempo());
+
+ // The arguments for the Sequencer
+ RealTime startPos = comp.getElapsedRealTime(comp.getPosition());
+
+ // If we're looping then jump to loop start
+ if (comp.isLooping())
+ startPos = comp.getElapsedRealTime(comp.getLoopStart());
+
+ KConfig* config = kapp->config();
+ config->setGroup(SequencerOptionsConfigGroup);
+
+ bool lowLat = config->readBoolEntry("audiolowlatencymonitoring", true);
+
+ if (lowLat != m_lastLowLatencySwitchSent) {
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+ streamOut << lowLat;
+
+ rgapp->sequencerSend("setLowLatencyMode(bool)", data);
+ m_lastLowLatencySwitchSent = lowLat;
+ }
+
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ // playback start position
+ streamOut << (long)startPos.sec;
+ streamOut << (long)startPos.nsec;
+
+ // Apart from perhaps the small file size, I think with hindsight
+ // that these options are more easily set to reasonable defaults
+ // here than left to the user. Mostly.
+
+ //!!! need some cleverness somewhere to ensure the read-ahead
+ //is larger than the JACK period size
+
+ if (lowLat) {
+ streamOut << 0L; // read-ahead sec
+ streamOut << 160000000L; // read-ahead nsec
+ streamOut << 0L; // audio mix sec
+ streamOut << 60000000L; // audio mix nsec: ignored in lowlat mode
+ streamOut << 2L; // audio read sec
+ streamOut << 500000000L; // audio read nsec
+ streamOut << 4L; // audio write sec
+ streamOut << 0L; // audio write nsec
+ streamOut << 256L; // cacheable small file size in K
+ } else {
+ streamOut << 0L; // read-ahead sec
+ streamOut << 500000000L; // read-ahead nsec
+ streamOut << 0L; // audio mix sec
+ streamOut << 400000000L; // audio mix nsec
+ streamOut << 2L; // audio read sec
+ streamOut << 500000000L; // audio read nsec
+ streamOut << 4L; // audio write sec
+ streamOut << 0L; // audio write nsec
+ streamOut << 256L; // cacheable small file size in K
+ }
+
+ // Send Play to the Sequencer
+ if (!rgapp->sequencerCall("play(long int, long int, long int, long int, long int, long int, long int, long int, long int, long int, long int)",
+ replyType, replyData, data)) {
+ m_transportStatus = STOPPED;
+ return false;
+ }
+
+ // ensure the return type is ok
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ int result;
+ streamIn >> result;
+
+ if (result) {
+ // completed successfully
+ m_transportStatus = STARTING_TO_PLAY;
+ } else {
+ m_transportStatus = STOPPED;
+ std::cerr << "ERROR: SequenceManager::play(): Failed to start playback!" << std::endl;
+ }
+
+ return false;
+}
+
+void
+SequenceManager::stopping()
+{
+ if (m_countdownTimer)
+ m_countdownTimer->stop();
+ if (m_countdownDialog)
+ m_countdownDialog->hide();
+
+ // Do this here rather than in stop() to avoid any potential
+ // race condition (we use setPointerPosition() during stop()).
+ //
+ if (m_transportStatus == STOPPED) {
+ /*!!!
+ if (m_doc->getComposition().isLooping())
+ m_doc->slotSetPointerPosition(m_doc->getComposition().getLoopStart());
+ else
+ m_doc->slotSetPointerPosition(m_doc->getComposition().getStartMarker());
+ */
+ m_doc->slotSetPointerPosition(m_lastTransportStartPosition);
+
+ return ;
+ }
+
+ // Disarm recording and drop back to STOPPED
+ //
+ if (m_transportStatus == RECORDING_ARMED) {
+ m_transportStatus = STOPPED;
+ m_transport->RecordButton()->setOn(false);
+ m_transport->MetronomeButton()->
+ setOn(m_doc->getComposition().usePlayMetronome());
+ return ;
+ }
+
+ SEQMAN_DEBUG << "SequenceManager::stopping() - preparing to stop\n";
+
+ // SEQMAN_DEBUG << kdBacktrace() << endl;
+
+ stop();
+
+ m_shownOverrunWarning = false;
+}
+
+void
+SequenceManager::stop()
+{
+ // Toggle off the buttons - first record
+ //
+ if (m_transportStatus == RECORDING) {
+ m_transport->RecordButton()->setOn(false);
+ m_transport->MetronomeButton()->
+ setOn(m_doc->getComposition().usePlayMetronome());
+
+ // Remove the countdown dialog and stop the timer
+ //
+ m_countdownDialog->hide();
+ m_countdownTimer->stop();
+ }
+
+ // Now playback
+ m_transport->PlayButton()->setOn(false);
+
+ // re-enable the record button if it was previously disabled when
+ // going into play mode - DMM
+ // SEQMAN_DEBUG << "SequenceManager::stop() - re-enabling record button\n";
+ // m_transport->RecordButton()->setEnabled(true);
+
+
+ // "call" the sequencer with a stop so we get a synchronous
+ // response - then we can fiddle about with the audio file
+ // without worrying about the sequencer causing problems
+ // with access to the same audio files.
+ //
+
+ // wait cursor
+ //
+ QApplication::setOverrideCursor(QCursor(Qt::waitCursor));
+
+ QCString replyType;
+ QByteArray replyData;
+
+ bool failed = false;
+ if (!rgapp->sequencerCall("stop()", replyType, replyData)) {
+ failed = true;
+ }
+
+ // restore
+ QApplication::restoreOverrideCursor();
+
+ TransportStatus status = m_transportStatus;
+
+ // set new transport status first, so that if we're stopping
+ // recording we don't risk the record segment being restored by a
+ // timer while the document is busy trying to do away with it
+ m_transportStatus = STOPPED;
+
+ // if we're recording MIDI or Audio then tidy up the recording Segment
+ if (status == RECORDING) {
+ m_doc->stopRecordingMidi();
+ m_doc->stopRecordingAudio();
+
+ SEQMAN_DEBUG << "SequenceManager::stop() - stopped recording\n";
+ }
+
+ // always untoggle the play button at this stage
+ //
+ m_transport->PlayButton()->setOn(false);
+ SEQMAN_DEBUG << "SequenceManager::stop() - stopped playing\n";
+
+ // We don't reset controllers at this point - what happens with static
+ // controllers the next time we play otherwise? [rwb]
+ //resetControllers();
+
+ if (failed) {
+ throw(Exception("Failed to contact Rosegarden sequencer with stop command. Please save your composition and restart Rosegarden to continue."));
+ }
+}
+
+void
+SequenceManager::rewind()
+{
+ Composition &composition = m_doc->getComposition();
+
+ timeT position = composition.getPosition();
+ std::pair<timeT, timeT> barRange =
+ composition.getBarRangeForTime(position - 1);
+
+ if (m_transportStatus == PLAYING) {
+
+ // if we're playing and we had a rewind request less than 200ms
+ // ago and we're some way into the bar but less than half way
+ // through it, rewind two barlines instead of one
+
+ clock_t now = clock();
+ int elapsed = (now - m_lastRewoundAt) * 1000 / CLOCKS_PER_SEC;
+
+ SEQMAN_DEBUG << "That was " << m_lastRewoundAt << ", this is " << now << ", elapsed is " << elapsed << endl;
+
+ if (elapsed >= 0 && elapsed <= 200) {
+ if (position > barRange.first &&
+ position < barRange.second &&
+ position <= (barRange.first + (barRange.second -
+ barRange.first) / 2)) {
+ barRange = composition.getBarRangeForTime(barRange.first - 1);
+ }
+ }
+
+ m_lastRewoundAt = now;
+ }
+
+ if (barRange.first < composition.getStartMarker()) {
+ m_doc->slotSetPointerPosition(composition.getStartMarker());
+ } else {
+ m_doc->slotSetPointerPosition(barRange.first);
+ }
+}
+
+void
+SequenceManager::fastforward()
+{
+ Composition &composition = m_doc->getComposition();
+
+ timeT position = composition.getPosition() + 1;
+ timeT newPosition = composition.getBarRangeForTime(position).second;
+
+ // Don't skip past end marker
+ //
+ if (newPosition > composition.getEndMarker())
+ newPosition = composition.getEndMarker();
+
+ m_doc->slotSetPointerPosition(newPosition);
+
+}
+
+void
+SequenceManager::notifySequencerStatus(TransportStatus status)
+{
+ // for the moment we don't do anything fancy
+ m_transportStatus = status;
+
+}
+
+void
+SequenceManager::sendSequencerJump(const RealTime &time)
+{
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+ streamOut << (long)time.sec;
+ streamOut << (long)time.nsec;
+
+ rgapp->sequencerSend("jumpTo(long int, long int)", data);
+}
+
+void
+SequenceManager::record(bool toggled)
+{
+ mapSequencer();
+
+ SEQMAN_DEBUG << "SequenceManager::record(" << toggled << ")" << endl;
+
+ Composition &comp = m_doc->getComposition();
+ Studio &studio = m_doc->getStudio();
+ KConfig* config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+
+ bool punchIn = false; // are we punching in?
+
+ // If we have any audio tracks armed, then we need to check for
+ // a valid audio record path and a working audio subsystem before
+ // we go any further
+
+ const Composition::recordtrackcontainer &recordTracks =
+ comp.getRecordTracks();
+
+ for (Composition::recordtrackcontainer::const_iterator i =
+ recordTracks.begin();
+ i != recordTracks.end(); ++i) {
+
+ Track *track = comp.getTrackById(*i);
+ InstrumentId instrId = track->getInstrument();
+ Instrument *instr = studio.getInstrumentById(instrId);
+
+ if (instr && instr->getType() == Instrument::Audio) {
+ if (!m_doc || !(m_soundDriverStatus & AUDIO_OK)) {
+ m_transport->RecordButton()->setOn(false);
+ throw(Exception("Audio subsystem is not available - can't record audio"));
+ }
+ // throws BadAudioPathException if path is not valid:
+ m_doc->getAudioFileManager().testAudioPath();
+ break;
+ }
+ }
+
+ if (toggled) { // preparing record or punch-in record
+
+ if (m_transportStatus == RECORDING_ARMED) {
+ SEQMAN_DEBUG << "SequenceManager::record - unarming record\n";
+ m_transportStatus = STOPPED;
+
+ // Toggle the buttons
+ m_transport->MetronomeButton()->setOn(comp.usePlayMetronome());
+ m_transport->RecordButton()->setOn(false);
+
+ return ;
+ }
+
+ if (m_transportStatus == STOPPED) {
+ SEQMAN_DEBUG << "SequenceManager::record - armed record\n";
+ m_transportStatus = RECORDING_ARMED;
+
+ // Toggle the buttons
+ m_transport->MetronomeButton()->setOn(comp.useRecordMetronome());
+ m_transport->RecordButton()->setOn(true);
+
+ return ;
+ }
+
+ if (m_transportStatus == RECORDING) {
+ SEQMAN_DEBUG << "SequenceManager::record - stop recording and keep playing\n";
+
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+
+ // Send Record to the Sequencer to signal it to drop out of record mode
+ if (!rgapp->sequencerCall("punchOut()", replyType, replyData, data)) {
+ SEQMAN_DEBUG << "SequenceManager::record - the \"not very plausible\" code executed\n";
+ // #1797873 - set new transport status first, so that
+ // if we're stopping recording we don't risk the
+ // record segment being restored by a timer while the
+ // document is busy trying to do away with it
+ m_transportStatus = STOPPED;
+
+ m_doc->stopRecordingMidi();
+ m_doc->stopRecordingAudio();
+ return ;
+ }
+
+ // #1797873 - as above
+ m_transportStatus = PLAYING;
+
+ m_doc->stopRecordingMidi();
+ m_doc->stopRecordingAudio();
+
+ return ;
+ }
+
+ if (m_transportStatus == PLAYING) {
+ SEQMAN_DEBUG << "SequenceManager::record - punch in recording\n";
+ punchIn = true;
+ goto punchin;
+ }
+
+ } else {
+
+ m_lastTransportStartPosition = comp.getPosition();
+
+punchin:
+
+ // Get the record tracks and check we have a record instrument
+
+ bool haveInstrument = false;
+ bool haveAudioInstrument = false;
+ bool haveMIDIInstrument = false;
+ //TrackId recordMIDITrack = 0;
+
+ for (Composition::recordtrackcontainer::const_iterator i =
+ comp.getRecordTracks().begin();
+ i != comp.getRecordTracks().end(); ++i) {
+
+ InstrumentId iid =
+ comp.getTrackById(*i)->getInstrument();
+
+ Instrument *inst = studio.getInstrumentById(iid);
+ if (inst) {
+ haveInstrument = true;
+ if (inst->getType() == Instrument::Audio) {
+ haveAudioInstrument = true;
+ break;
+ } else { // soft synths count as MIDI for our purposes here
+ haveMIDIInstrument = true;
+ //recordMIDITrack = *i;
+ }
+ }
+ }
+
+ if (!haveInstrument) {
+ m_transport->RecordButton()->setDown(false);
+ throw(Exception("No Record instrument selected"));
+ }
+
+ // may throw an exception
+ checkSoundDriverStatus(false);
+
+ // toggle the Metronome button if it's in use
+ m_transport->MetronomeButton()->setOn(comp.useRecordMetronome());
+
+ // Update record metronome status
+ //
+ m_controlBlockMmapper->updateMetronomeData
+ (m_metronomeMmapper->getMetronomeInstrument());
+ m_controlBlockMmapper->updateMetronomeForRecord();
+
+ // If we are looping then jump to start of loop and start recording,
+ // if we're not take off the number of count-in bars and start
+ // recording.
+ //
+ if (comp.isLooping())
+ m_doc->slotSetPointerPosition(comp.getLoopStart());
+ else {
+ if (m_transportStatus != RECORDING_ARMED && punchIn == false) {
+ int startBar = comp.getBarNumber(comp.getPosition());
+ startBar -= config->readUnsignedNumEntry("countinbars", 0);
+ m_doc->slotSetPointerPosition(comp.getBarRange(startBar).first);
+ }
+ }
+
+ m_doc->setRecordStartTime(m_doc->getComposition().getPosition());
+
+ if (haveAudioInstrument) {
+ // Ask the document to update its record latencies so as to
+ // do latency compensation when we stop
+ m_doc->updateAudioRecordLatency();
+ }
+
+ if (haveMIDIInstrument) {
+ // Create the record MIDI segment now, so that the
+ // composition view has a real segment to display. It
+ // won't actually be added to the composition until the
+ // first recorded event arrives. We don't have to do this
+ // from here for audio, because for audio the sequencer
+ // calls back on createRecordAudioFiles so as to find out
+ // what files it needs to write to.
+ /*m_doc->addRecordMIDISegment(recordMIDITrack);*/
+ for (Composition::recordtrackcontainer::const_iterator i =
+ comp.getRecordTracks().begin(); i != comp.getRecordTracks().end(); ++i) {
+ InstrumentId iid = comp.getTrackById(*i)->getInstrument();
+ Instrument *inst = studio.getInstrumentById(iid);
+ if (inst && (inst->getType() != Instrument::Audio)) {
+ SEQMAN_DEBUG << "SequenceManager: mdoc->addRecordMIDISegment(" << *i << ")" << endl;
+ m_doc->addRecordMIDISegment(*i);
+ }
+ }
+ }
+
+ // set the buttons
+ m_transport->RecordButton()->setOn(true);
+ m_transport->PlayButton()->setOn(true);
+
+ if (comp.getCurrentTempo() == 0) {
+ SEQMAN_DEBUG << "SequenceManager::play() - setting Tempo to Default value of 120.000\n";
+ comp.setCompositionDefaultTempo(comp.getTempoForQpm(120.0));
+ } else {
+ SEQMAN_DEBUG << "SequenceManager::record() - starting to record\n";
+ }
+
+ // set the tempo in the transport
+ //
+ m_transport->setTempo(comp.getCurrentTempo());
+
+ // The arguments for the Sequencer - record is similar to playback,
+ // we must being playing to record.
+ //
+ RealTime startPos =
+ comp.getElapsedRealTime(comp.getPosition());
+
+ bool lowLat = config->readBoolEntry("audiolowlatencymonitoring", true);
+
+ if (lowLat != m_lastLowLatencySwitchSent) {
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+ streamOut << lowLat;
+
+ rgapp->sequencerSend("setLowLatencyMode(bool)", data);
+ m_lastLowLatencySwitchSent = lowLat;
+ }
+
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ // playback start position
+ streamOut << (long)startPos.sec;
+ streamOut << (long)startPos.nsec;
+
+ // Apart from perhaps the small file size, I think with hindsight
+ // that these options are more easily set to reasonable defaults
+ // here than left to the user. Mostly.
+
+ //!!! Duplicates code in play()
+
+ //!!! need some cleverness somewhere to ensure the read-ahead
+ //is larger than the JACK period size
+
+ if (lowLat) {
+ streamOut << 0L; // read-ahead sec
+ streamOut << 160000000L; // read-ahead nsec
+ streamOut << 0L; // audio mix sec
+ streamOut << 60000000L; // audio mix nsec: ignored in lowlat mode
+ streamOut << 2L; // audio read sec
+ streamOut << 500000000L; // audio read nsec
+ streamOut << 4L; // audio write sec
+ streamOut << 0L; // audio write nsec
+ streamOut << 256L; // cacheable small file size in K
+ } else {
+ streamOut << 0L; // read-ahead sec
+ streamOut << 500000000L; // read-ahead nsec
+ streamOut << 0L; // audio mix sec
+ streamOut << 400000000L; // audio mix nsec
+ streamOut << 2L; // audio read sec
+ streamOut << 500000000L; // audio read nsec
+ streamOut << 4L; // audio write sec
+ streamOut << 0L; // audio write nsec
+ streamOut << 256L; // cacheable small file size in K
+ }
+
+ // record type
+ streamOut << (long)STARTING_TO_RECORD;
+
+ // Send Play to the Sequencer
+ if (!rgapp->sequencerCall("record(long int, long int, long int, long int, long int, long int, long int, long int, long int, long int, long int, long int)",
+ replyType, replyData, data)) {
+ // failed
+ m_transportStatus = STOPPED;
+ return ;
+ }
+
+ // ensure the return type is ok
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ int result;
+ streamIn >> result;
+
+ if (result) {
+
+ // completed successfully
+ m_transportStatus = STARTING_TO_RECORD;
+
+ // Create the countdown timer dialog to show recording time
+ // remaining. (Note (dmm) this has changed, and it now reports
+ // the time remaining during both MIDI and audio recording.)
+ //
+ timeT p = comp.getPosition();
+ timeT d = comp.getEndMarker();
+ // end marker less current position == available duration
+ d -= p;
+
+ // set seconds to total possible time, initially
+ RealTime rtd = comp.getElapsedRealTime(d);
+ int seconds = rtd.sec;
+
+ // re-initialise
+ m_countdownDialog->setTotalTime(seconds);
+
+ // Create the timer
+ //
+ m_recordTime->start();
+
+ // Start an elapse timer for updating the dialog -
+ // it will fire every second.
+ //
+ m_countdownTimer->start(1000);
+
+ // Pop-up the dialog (don't use exec())
+ //
+ // bug #1505805, abolish recording countdown dialog
+ //m_countdownDialog->show();
+
+ } else {
+ // Stop immediately - turn off buttons in parent
+ //
+ m_transportStatus = STOPPED;
+
+ if (haveAudioInstrument) {
+ throw(Exception("Couldn't start recording audio.\nPlease set a valid file path in the Document Properties\n(Composition menu -> Edit Document Properties -> Audio)."));
+ }
+ }
+ }
+}
+
+void
+SequenceManager::processAsynchronousMidi(const MappedComposition &mC,
+ AudioManagerDialog *audioManagerDialog)
+{
+ static bool boolShowingWarning = false;
+ static bool boolShowingALSAWarning = false;
+ static long warningShownAt = 0;
+
+ if (m_doc == 0 || mC.size() == 0)
+ return ;
+
+ MappedComposition::const_iterator i;
+
+ // Thru filtering is done at the sequencer for the actual sound
+ // output, but here we need both filtered (for OUT display) and
+ // unfiltered (for insertable note callbacks) compositions, so
+ // we've received the unfiltered copy and will filter here
+ MappedComposition tempMC =
+ applyFiltering(mC,
+ MappedEvent::MappedEventType(
+ m_doc->getStudio().getMIDIThruFilter()));
+
+ // send to the MIDI labels (which can only hold one event at a time)
+ i = mC.begin();
+ if (i != mC.end()) {
+ m_transport->setMidiInLabel(*i);
+ }
+
+ i = tempMC.begin();
+ while (i != tempMC.end()) {
+ if ((*i)->getRecordedDevice() != Device::CONTROL_DEVICE) {
+ m_transport->setMidiOutLabel(*i);
+ break;
+ }
+ ++i;
+ }
+
+ for (i = mC.begin(); i != mC.end(); ++i ) {
+ if ((*i)->getType() >= MappedEvent::Audio) {
+ if ((*i)->getType() == MappedEvent::AudioStopped) {
+ /*
+ SEQMAN_DEBUG << "AUDIO FILE ID = "
+ << int((*i)->getData1())
+ << " - FILE STOPPED - "
+ << "INSTRUMENT = "
+ << (*i)->getInstrument()
+ << endl;
+ */
+
+ if (audioManagerDialog && (*i)->getInstrument() ==
+ m_doc->getStudio().getAudioPreviewInstrument()) {
+ audioManagerDialog->
+ closePlayingDialog(
+ AudioFileId((*i)->getData1()));
+ }
+ }
+
+ if ((*i)->getType() == MappedEvent::AudioLevel)
+ sendAudioLevel(*i);
+
+ if ((*i)->getType() ==
+ MappedEvent::AudioGeneratePreview) {
+ SEQMAN_DEBUG << "Received AudioGeneratePreview: data1 is " << int((*i)->getData1()) << ", data2 " << int((*i)->getData2()) << ", instrument is " << (*i)->getInstrument() << endl;
+
+ m_doc->finalizeAudioFile((int)(*i)->getData1() +
+ (int)(*i)->getData2() * 256);
+ }
+
+ if ((*i)->getType() ==
+ MappedEvent::SystemUpdateInstruments) {
+ // resync Devices and Instruments
+ //
+ m_doc->syncDevices();
+
+ /*KConfig* config = kapp->config();
+ config->setGroup(SequencerOptionsConfigGroup);
+ QString recordDeviceStr = config->readEntry("midirecorddevice");
+ sendMIDIRecordingDevice(recordDeviceStr);*/
+ restoreRecordSubscriptions();
+ }
+
+ if (m_transportStatus == PLAYING ||
+ m_transportStatus == RECORDING) {
+ if ((*i)->getType() == MappedEvent::SystemFailure) {
+
+ SEQMAN_DEBUG << "Failure of some sort..." << endl;
+
+ bool handling = true;
+
+ /* These are the ones that we always report or handle. */
+
+ if ((*i)->getData1() == MappedEvent::FailureJackDied) {
+
+ // Something horrible has happened to JACK or we got
+ // bumped out of the graph. Either way stop playback.
+ //
+ stopping();
+
+ } else if ((*i)->getData1() == MappedEvent::FailureJackRestartFailed) {
+
+ KMessageBox::error(
+ dynamic_cast<QWidget*>(m_doc->parent())->parentWidget(),
+ i18n("The JACK Audio subsystem has failed or it has stopped Rosegarden from processing audio.\nPlease restart Rosegarden to continue working with audio.\nQuitting other running applications may improve Rosegarden's performance."));
+
+ } else if ((*i)->getData1() == MappedEvent::FailureJackRestart) {
+
+ KMessageBox::error(
+ dynamic_cast<QWidget*>(m_doc->parent())->parentWidget(),
+ i18n("The JACK Audio subsystem has stopped Rosegarden from processing audio, probably because of a processing overload.\nAn attempt to restart the audio service has been made, but some problems may remain.\nQuitting other running applications may improve Rosegarden's performance."));
+
+ } else if ((*i)->getData1() == MappedEvent::FailureCPUOverload) {
+
+#define REPORT_CPU_OVERLOAD 1
+#ifdef REPORT_CPU_OVERLOAD
+
+ stopping();
+
+ KMessageBox::error(
+ dynamic_cast<QWidget*>(m_doc->parent())->parentWidget(),
+ i18n("Run out of processor power for real-time audio processing. Cannot continue."));
+
+#endif
+
+ } else {
+
+ handling = false;
+ }
+
+ if (handling)
+ continue;
+
+ if (!m_canReport) {
+ SEQMAN_DEBUG << "Not reporting it to user just yet"
+ << endl;
+ continue;
+ }
+
+ if ((*i)->getData1() == MappedEvent::FailureALSACallFailed) {
+
+ struct timeval tv;
+ (void)gettimeofday(&tv, 0);
+
+ if (tv.tv_sec - warningShownAt >= 5 &&
+ !boolShowingALSAWarning) {
+
+ QString message = i18n("A serious error has occurred in the ALSA MIDI subsystem. It may not be possible to continue sequencing. Please check console output for more information.");
+ boolShowingALSAWarning = true;
+
+ KMessageBox::information(0, message);
+ boolShowingALSAWarning = false;
+
+ (void)gettimeofday(&tv, 0);
+ warningShownAt = tv.tv_sec;
+ }
+
+ } else if ((*i)->getData1() == MappedEvent::FailureXRuns) {
+
+ //#define REPORT_XRUNS 1
+#ifdef REPORT_XRUNS
+
+ struct timeval tv;
+ (void)gettimeofday(&tv, 0);
+
+ if (tv.tv_sec - warningShownAt >= 5 &&
+ !boolShowingWarning) {
+
+ QString message = i18n("JACK Audio subsystem is losing sample frames.");
+ boolShowingWarning = true;
+
+ KMessageBox::information(0, message);
+ boolShowingWarning = false;
+
+ (void)gettimeofday(&tv, 0);
+ warningShownAt = tv.tv_sec;
+ }
+#endif
+
+ } else if (!m_shownOverrunWarning) {
+
+ QString message;
+
+ switch ((*i)->getData1()) {
+
+ case MappedEvent::FailureDiscUnderrun:
+ message = i18n("Failed to read audio data from disc in time to service the audio subsystem.");
+ break;
+
+ case MappedEvent::FailureDiscOverrun:
+ message = i18n("Failed to write audio data to disc fast enough to service the audio subsystem.");
+ break;
+
+ case MappedEvent::FailureBussMixUnderrun:
+ message = i18n("The audio mixing subsystem is failing to keep up.");
+ break;
+
+ case MappedEvent::FailureMixUnderrun:
+ message = i18n("The audio subsystem is failing to keep up.");
+ break;
+
+ default:
+ message = i18n("Unknown sequencer failure mode!");
+ break;
+ }
+
+ m_shownOverrunWarning = true;
+
+#ifdef REPORT_XRUNS
+
+ KMessageBox::information(0, message);
+#else
+
+ if ((*i)->getData1() == MappedEvent::FailureDiscOverrun) {
+ // the error you can't hear
+ KMessageBox::information(0, message);
+ } else {
+ std::cerr << message << std::endl;
+ }
+#endif
+
+ }
+
+ // Turn off the report flag and set off a one-shot
+ // timer for 5 seconds.
+ //
+ if (!m_reportTimer->isActive()) {
+ m_canReport = false;
+ m_reportTimer->start(5000, true);
+ }
+ }
+ } else {
+ KStartupLogo::hideIfStillThere();
+
+ if ((*i)->getType() == MappedEvent::SystemFailure) {
+
+ if ((*i)->getData1() == MappedEvent::FailureJackRestartFailed) {
+
+ KMessageBox::error(
+ dynamic_cast<QWidget*>(m_doc->parent()),
+ i18n("The JACK Audio subsystem has failed or it has stopped Rosegarden from processing audio.\nPlease restart Rosegarden to continue working with audio.\nQuitting other running applications may improve Rosegarden's performance."));
+
+ } else if ((*i)->getData1() == MappedEvent::FailureJackRestart) {
+
+ KMessageBox::error(
+ dynamic_cast<QWidget*>(m_doc->parent()),
+ i18n("The JACK Audio subsystem has stopped Rosegarden from processing audio, probably because of a processing overload.\nAn attempt to restart the audio service has been made, but some problems may remain.\nQuitting other running applications may improve Rosegarden's performance."));
+
+ } else if ((*i)->getData1() == MappedEvent::WarningImpreciseTimer &&
+ shouldWarnForImpreciseTimer()) {
+
+ std::cerr << "Rosegarden: WARNING: No accurate sequencer timer available" << std::endl;
+
+ KStartupLogo::hideIfStillThere();
+ CurrentProgressDialog::freeze();
+
+ RosegardenGUIApp::self()->awaitDialogClearance();
+
+ KMessageBox::information(
+ dynamic_cast<QWidget*>(m_doc->parent()),
+ i18n("<h3>System timer resolution is too low</h3><p>Rosegarden was unable to find a high-resolution timing source for MIDI performance.</p><p>This may mean you are using a Linux system with the kernel timer resolution set too low. Please contact your Linux distributor for more information.</p><p>Some Linux distributors already provide low latency kernels, see <a href=\"http://rosegarden.wiki.sourceforge.net/Low+latency+kernels\">http://rosegarden.wiki.sourceforge.net/Low+latency+kernels</a> for instructions.</p>"),
+ NULL, NULL,
+ KMessageBox::Notify + KMessageBox::AllowLink);
+
+ CurrentProgressDialog::thaw();
+
+ } else if ((*i)->getData1() == MappedEvent::WarningImpreciseTimerTryRTC &&
+ shouldWarnForImpreciseTimer()) {
+
+ std::cerr << "Rosegarden: WARNING: No accurate sequencer timer available" << std::endl;
+
+ KStartupLogo::hideIfStillThere();
+ CurrentProgressDialog::freeze();
+
+ RosegardenGUIApp::self()->awaitDialogClearance();
+
+ KMessageBox::information(
+ dynamic_cast<QWidget*>(m_doc->parent()),
+ i18n("<h3>System timer resolution is too low</h3><p>Rosegarden was unable to find a high-resolution timing source for MIDI performance.</p><p>You may be able to solve this problem by loading the RTC timer kernel module. To do this, try running <b>sudo modprobe snd-rtctimer</b> in a terminal window and then restarting Rosegarden.</p><p>Alternatively, check whether your Linux distributor provides a multimedia-optimized kernel. See <a href=\"http://rosegarden.wiki.sourceforge.net/Low+latency+kernels\">http://rosegarden.wiki.sourceforge.net/Low+latency+kernels</a> for notes about this.</p>"),
+ NULL, NULL,
+ KMessageBox::Notify + KMessageBox::AllowLink);
+
+ CurrentProgressDialog::thaw();
+ }
+ }
+ }
+ }
+ }
+
+ // if we aren't playing or recording, consider invoking any
+ // step-by-step clients (using unfiltered composition). send
+ // out any incoming external controller events
+
+ for (i = mC.begin(); i != mC.end(); ++i ) {
+ if (m_transportStatus == STOPPED ||
+ m_transportStatus == RECORDING_ARMED) {
+ if ((*i)->getType() == MappedEvent::MidiNote) {
+ if ((*i)->getVelocity() == 0) {
+ emit insertableNoteOffReceived((*i)->getPitch(), (*i)->getVelocity());
+ } else {
+ emit insertableNoteOnReceived((*i)->getPitch(), (*i)->getVelocity());
+ }
+ }
+ }
+ if ((*i)->getRecordedDevice() == Device::CONTROL_DEVICE) {
+ SEQMAN_DEBUG << "controllerDeviceEventReceived" << endl;
+ emit controllerDeviceEventReceived(*i);
+ }
+ }
+}
+
+void
+SequenceManager::rewindToBeginning()
+{
+ SEQMAN_DEBUG << "SequenceManager::rewindToBeginning()\n";
+ m_doc->slotSetPointerPosition(m_doc->getComposition().getStartMarker());
+}
+
+void
+SequenceManager::fastForwardToEnd()
+{
+ SEQMAN_DEBUG << "SequenceManager::fastForwardToEnd()\n";
+
+ Composition &comp = m_doc->getComposition();
+ m_doc->slotSetPointerPosition(comp.getDuration());
+}
+
+void
+SequenceManager::setLoop(const timeT &lhs, const timeT &rhs)
+{
+ // do not set a loop if JACK transport sync is enabled, because this is
+ // completely broken, and apparently broken due to a limitation of JACK
+ // transport itself. #1240039 - DMM
+ // KConfig* config = kapp->config();
+ // config->setGroup(SequencerOptionsConfigGroup);
+ // if (config->readBoolEntry("jacktransport", false))
+ // {
+ // //!!! message box should go here to inform user of why the loop was
+ // // not set, but I can't add it at the moment due to to the pre-release
+ // // freeze - DMM
+ // return;
+ // }
+
+ // Let the sequencer know about the loop markers
+ //
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ RealTime loopStart =
+ m_doc->getComposition().getElapsedRealTime(lhs);
+ RealTime loopEnd =
+ m_doc->getComposition().getElapsedRealTime(rhs);
+
+ streamOut << (long)loopStart.sec;
+ streamOut << (long)loopStart.nsec;
+ streamOut << (long)loopEnd.sec;
+ streamOut << (long)loopEnd.nsec;
+
+ rgapp->sequencerSend("setLoop(long int, long int, long int, long int)", data);
+}
+
+void
+SequenceManager::checkSoundDriverStatus(bool warnUser)
+{
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << QString(VERSION);
+
+ if (! rgapp->sequencerCall("getSoundDriverStatus(QString)",
+ replyType, replyData, data)) {
+
+ m_soundDriverStatus = NO_DRIVER;
+
+ } else {
+
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ unsigned int result;
+ streamIn >> result;
+ m_soundDriverStatus = result;
+ }
+
+ SEQMAN_DEBUG << "Sound driver status is: " << m_soundDriverStatus << endl;
+
+ if (!warnUser) return;
+
+#ifdef HAVE_LIBJACK
+ if ((m_soundDriverStatus & (AUDIO_OK | MIDI_OK | VERSION_OK)) ==
+ (AUDIO_OK | MIDI_OK | VERSION_OK)) return;
+#else
+ if ((m_soundDriverStatus & (MIDI_OK | VERSION_OK)) ==
+ (MIDI_OK | VERSION_OK)) return;
+#endif
+
+ KStartupLogo::hideIfStillThere();
+ CurrentProgressDialog::freeze();
+
+ QString text = "";
+
+ if (m_soundDriverStatus == NO_DRIVER) {
+ text = i18n("<p>Both MIDI and Audio subsystems have failed to initialize.</p><p>You may continue without the sequencer, but we suggest closing Rosegarden, running \"alsaconf\" as root, and starting Rosegarden again. If you wish to run with no sequencer by design, then use \"rosegarden --nosequencer\" to avoid seeing this error in the future.</p>");
+ } else if (!(m_soundDriverStatus & MIDI_OK)) {
+ text = i18n("<p>The MIDI subsystem has failed to initialize.</p><p>You may continue without the sequencer, but we suggest closing Rosegarden, running \"modprobe snd-seq-midi\" as root, and starting Rosegarden again. If you wish to run with no sequencer by design, then use \"rosegarden --nosequencer\" to avoid seeing this error in the future.</p>");
+ } else if (!(m_soundDriverStatus & VERSION_OK)) {
+ text = i18n("<p>The Rosegarden sequencer module version does not match the GUI module version.</p><p>You have probably mixed up files from two different versions of Rosegarden. Please check your installation.</p>");
+ }
+
+ if (text != "") {
+ RosegardenGUIApp::self()->awaitDialogClearance();
+ KMessageBox::error(RosegardenGUIApp::self(),
+ i18n("<h3>Sequencer startup failed</h3>%1").arg(text));
+ CurrentProgressDialog::thaw();
+ return;
+ }
+
+#ifdef HAVE_LIBJACK
+ if (!(m_soundDriverStatus & AUDIO_OK)) {
+ RosegardenGUIApp::self()->awaitDialogClearance();
+ KMessageBox::information(RosegardenGUIApp::self(), i18n("<h3>Failed to connect to JACK audio server.</h3><p>Rosegarden could not connect to the JACK audio server. This probably means the JACK server is not running.</p><p>If you want to be able to play or record audio files or use plugins, you should exit Rosegarden and start the JACK server before running Rosegarden again.</p>"),
+ i18n("Failed to connect to JACK"),
+ "startup-jack-failed");
+ }
+#endif
+ CurrentProgressDialog::thaw();
+}
+
+void
+SequenceManager::preparePlayback(bool forceProgramChanges)
+{
+ Studio &studio = m_doc->getStudio();
+ InstrumentList list = studio.getAllInstruments();
+ MappedComposition mC;
+ MappedEvent *mE;
+
+ std::set<InstrumentId> activeInstruments;
+
+ Composition &composition = m_doc->getComposition();
+
+ for (Composition::trackcontainer::const_iterator i =
+ composition.getTracks().begin();
+ i != composition.getTracks().end(); ++i) {
+
+ Track *track = i->second;
+ if (track) activeInstruments.insert(track->getInstrument());
+ }
+
+ // Send the MappedInstruments (minimal Instrument information
+ // required for Performance) to the Sequencer
+ //
+ InstrumentList::iterator it = list.begin();
+ for (; it != list.end(); it++) {
+
+ StudioControl::sendMappedInstrument(MappedInstrument(*it));
+
+ // Send program changes for MIDI Instruments
+ //
+ if ((*it)->getType() == Instrument::Midi) {
+
+ if (activeInstruments.find((*it)->getId()) ==
+ activeInstruments.end()) {
+// std::cerr << "SequenceManager::preparePlayback: instrument "
+// << (*it)->getId() << " is not in use" << std::endl;
+ continue;
+ }
+
+ // send bank select always before program change
+ //
+ if ((*it)->sendsBankSelect()) {
+ mE = new MappedEvent((*it)->getId(),
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_BANK_MSB,
+ (*it)->getMSB());
+ mC.insert(mE);
+
+ mE = new MappedEvent((*it)->getId(),
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_BANK_LSB,
+ (*it)->getLSB());
+ mC.insert(mE);
+ }
+
+ // send program change
+ //
+ if ((*it)->sendsProgramChange() || forceProgramChanges) {
+ RG_DEBUG << "SequenceManager::preparePlayback() : sending prg change for "
+ << (*it)->getPresentationName().c_str() << endl;
+
+ mE = new MappedEvent((*it)->getId(),
+ MappedEvent::MidiProgramChange,
+ (*it)->getProgramChange());
+ mC.insert(mE);
+ }
+
+ } else if ((*it)->getType() == Instrument::Audio ||
+ (*it)->getType() == Instrument::SoftSynth) {
+ } else {
+ RG_DEBUG << "SequenceManager::preparePlayback - "
+ << "unrecognised instrument type" << endl;
+ }
+
+
+ }
+
+ // Send the MappedComposition if it's got anything in it
+ showVisuals(mC);
+ StudioControl::sendMappedComposition(mC);
+}
+
+void
+SequenceManager::sendAudioLevel(MappedEvent *mE)
+{
+ RosegardenGUIView *v;
+ QList<RosegardenGUIView>& viewList = m_doc->getViewList();
+
+ for (v = viewList.first(); v != 0; v = viewList.next()) {
+ v->showVisuals(mE);
+ }
+
+}
+
+void
+SequenceManager::resetControllers()
+{
+ SEQMAN_DEBUG << "SequenceManager::resetControllers - resetting\n";
+
+ // Should do all Midi Instrument - not just guess like this is doing
+ // currently.
+
+ InstrumentList list = m_doc->getStudio().getPresentationInstruments();
+ InstrumentList::iterator it;
+
+ MappedComposition mC;
+
+ for (it = list.begin(); it != list.end(); it++) {
+ if ((*it)->getType() == Instrument::Midi) {
+ MappedEvent *mE = new MappedEvent((*it)->getId(),
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_RESET,
+ 0);
+ mC.insert(mE);
+ }
+ }
+
+ StudioControl::sendMappedComposition(mC);
+ //showVisuals(mC);
+}
+
+void
+SequenceManager::resetMidiNetwork()
+{
+ SEQMAN_DEBUG << "SequenceManager::resetMidiNetwork - resetting\n";
+ MappedComposition mC;
+
+ // Should do all Midi Instrument - not just guess like this is doing
+ // currently.
+
+ for (unsigned int i = 0; i < 16; i++) {
+ MappedEvent *mE =
+ new MappedEvent(MidiInstrumentBase + i,
+ MappedEvent::MidiController,
+ MIDI_SYSTEM_RESET,
+ 0);
+
+ mC.insert(mE);
+ }
+ showVisuals(mC);
+ StudioControl::sendMappedComposition(mC);
+}
+
+void
+SequenceManager::sendMIDIRecordingDevice(const QString recordDeviceStr)
+{
+
+ if (recordDeviceStr) {
+ int recordDevice = recordDeviceStr.toInt();
+
+ if (recordDevice >= 0) {
+ MappedEvent mE(MidiInstrumentBase, // InstrumentId
+ MappedEvent::SystemRecordDevice,
+ MidiByte(recordDevice),
+ MidiByte(true));
+
+ StudioControl::sendMappedEvent(mE);
+ SEQMAN_DEBUG << "set MIDI record device to "
+ << recordDevice << endl;
+ }
+ }
+}
+
+void
+SequenceManager::restoreRecordSubscriptions()
+{
+ KConfig* config = kapp->config();
+ config->setGroup(SequencerOptionsConfigGroup);
+ //QString recordDeviceStr = config->readEntry("midirecorddevice");
+ QStringList devList = config->readListEntry("midirecorddevice");
+
+ for ( QStringList::ConstIterator it = devList.begin();
+ it != devList.end(); ++it) {
+ sendMIDIRecordingDevice(*it);
+ }
+
+}
+
+void
+SequenceManager::reinitialiseSequencerStudio()
+{
+ KConfig* config = kapp->config();
+ config->setGroup(SequencerOptionsConfigGroup);
+ //QString recordDeviceStr = config->readEntry("midirecorddevice");
+
+ //sendMIDIRecordingDevice(recordDeviceStr);
+ restoreRecordSubscriptions();
+
+ // Toggle JACK audio ports appropriately
+ //
+ bool submasterOuts = config->readBoolEntry("audiosubmasterouts", false);
+ bool faderOuts = config->readBoolEntry("audiofaderouts", false);
+ unsigned int audioFileFormat = config->readUnsignedNumEntry("audiorecordfileformat", 1);
+
+ MidiByte ports = 0;
+ if (faderOuts) {
+ ports |= MappedEvent::FaderOuts;
+ }
+ if (submasterOuts) {
+ ports |= MappedEvent::SubmasterOuts;
+ }
+ MappedEvent mEports
+ (MidiInstrumentBase,
+ MappedEvent::SystemAudioPorts,
+ ports);
+
+ StudioControl::sendMappedEvent(mEports);
+
+ MappedEvent mEff
+ (MidiInstrumentBase,
+ MappedEvent::SystemAudioFileFormat,
+ audioFileFormat);
+ StudioControl::sendMappedEvent(mEff);
+
+
+ // Set the studio from the current document
+ //
+ m_doc->initialiseStudio();
+}
+
+void
+SequenceManager::panic()
+{
+ SEQMAN_DEBUG << "panic button\n";
+
+ stopping();
+
+ MappedEvent mE(MidiInstrumentBase, MappedEvent::Panic, 0, 0);
+ emit setProgress(40);
+ StudioControl::sendMappedEvent(mE);
+ emit setProgress(100);
+
+ // Studio &studio = m_doc->getStudio();
+ //
+ // InstrumentList list = studio.getPresentationInstruments();
+ // InstrumentList::iterator it;
+ //
+ // int maxDevices = 0, device = 0;
+ // for (it = list.begin(); it != list.end(); it++)
+ // if ((*it)->getType() == Instrument::Midi)
+ // maxDevices++;
+ //
+ // emit setProgress(40);
+ //
+ // for (it = list.begin(); it != list.end(); it++)
+ // {
+ // if ((*it)->getType() == Instrument::Midi)
+ // {
+ // for (unsigned int i = 0; i < 128; i++)
+ // {
+ // MappedEvent
+ // mE((*it)->getId(),
+ // MappedEvent::MidiNote,
+ // i,
+ // 0);
+ //
+ // StudioControl::sendMappedEvent(mE);
+ // }
+ //
+ // device++;
+ // }
+ //
+ // emit setProgress(int(90.0 * (double(device) / double(maxDevices))));
+ // }
+ //
+ // resetControllers();
+}
+
+void
+SequenceManager::showVisuals(const MappedComposition &mC)
+{
+ MappedComposition::const_iterator it = mC.begin();
+ if (it != mC.end())
+ m_transport->setMidiOutLabel(*it);
+}
+
+MappedComposition
+SequenceManager::applyFiltering(const MappedComposition &mC,
+ MappedEvent::MappedEventType filter)
+{
+ MappedComposition retMc;
+ MappedComposition::const_iterator it = mC.begin();
+
+ for (; it != mC.end(); it++) {
+ if (!((*it)->getType() & filter))
+ retMc.insert(new MappedEvent(*it));
+ }
+
+ return retMc;
+}
+
+void SequenceManager::resetCompositionMmapper()
+{
+ SEQMAN_DEBUG << "SequenceManager::resetCompositionMmapper()\n";
+ delete m_compositionMmapper;
+ m_compositionMmapper = new CompositionMmapper(m_doc);
+
+ resetMetronomeMmapper();
+ resetTempoSegmentMmapper();
+ resetTimeSigSegmentMmapper();
+ resetControlBlockMmapper();
+}
+
+void SequenceManager::resetMetronomeMmapper()
+{
+ SEQMAN_DEBUG << "SequenceManager::resetMetronomeMmapper()\n";
+
+ delete m_metronomeMmapper;
+ m_metronomeMmapper = SegmentMmapperFactory::makeMetronome(m_doc);
+}
+
+void SequenceManager::resetTempoSegmentMmapper()
+{
+ SEQMAN_DEBUG << "SequenceManager::resetTempoSegmentMmapper()\n";
+
+ delete m_tempoSegmentMmapper;
+ m_tempoSegmentMmapper = SegmentMmapperFactory::makeTempo(m_doc);
+}
+
+void SequenceManager::resetTimeSigSegmentMmapper()
+{
+ SEQMAN_DEBUG << "SequenceManager::resetTimeSigSegmentMmapper()\n";
+
+ delete m_timeSigSegmentMmapper;
+ m_timeSigSegmentMmapper = SegmentMmapperFactory::makeTimeSig(m_doc);
+}
+
+void SequenceManager::resetControlBlockMmapper()
+{
+ SEQMAN_DEBUG << "SequenceManager::resetControlBlockMmapper()\n";
+
+ m_controlBlockMmapper->setDocument(m_doc);
+}
+
+bool SequenceManager::event(QEvent *e)
+{
+ if (e->type() == QEvent::User) {
+ SEQMAN_DEBUG << "SequenceManager::event() with user event\n";
+ if (m_updateRequested) {
+ SEQMAN_DEBUG << "SequenceManager::event(): update requested\n";
+ checkRefreshStatus();
+ m_updateRequested = false;
+ }
+ return true;
+ } else {
+ return QObject::event(e);
+ }
+}
+
+void SequenceManager::update()
+{
+ SEQMAN_DEBUG << "SequenceManager::update()\n";
+ // schedule a refresh-status check for the next event loop
+ QEvent *e = new QEvent(QEvent::User);
+ m_updateRequested = true;
+ QApplication::postEvent(this, e);
+}
+
+void SequenceManager::checkRefreshStatus()
+{
+ SEQMAN_DEBUG << "SequenceManager::checkRefreshStatus()\n";
+
+ // Look at trigger segments first: if one of those has changed, we'll
+ // need to be aware of it when scanning segments subsequently
+
+ TriggerSegmentRec::SegmentRuntimeIdSet ridset;
+ Composition &comp = m_doc->getComposition();
+ SegmentRefreshMap newTriggerMap;
+
+ for (Composition::triggersegmentcontaineriterator i =
+ comp.getTriggerSegments().begin();
+ i != comp.getTriggerSegments().end(); ++i) {
+
+ Segment *s = (*i)->getSegment();
+
+ if (m_triggerSegments.find(s) == m_triggerSegments.end()) {
+ newTriggerMap[s] = s->getNewRefreshStatusId();
+ } else {
+ newTriggerMap[s] = m_triggerSegments[s];
+ }
+
+ if (s->getRefreshStatus(newTriggerMap[s]).needsRefresh()) {
+ TriggerSegmentRec::SegmentRuntimeIdSet &thisSet = (*i)->getReferences();
+ ridset.insert(thisSet.begin(), thisSet.end());
+ s->getRefreshStatus(newTriggerMap[s]).setNeedsRefresh(false);
+ }
+ }
+
+ m_triggerSegments = newTriggerMap;
+
+ SEQMAN_DEBUG << "SequenceManager::checkRefreshStatus: segments modified by changes to trigger segments are:" << endl;
+ int x = 0;
+ for (TriggerSegmentRec::SegmentRuntimeIdSet::iterator i = ridset.begin();
+ i != ridset.end(); ++i) {
+ SEQMAN_DEBUG << x << ": " << *i << endl;
+ ++x;
+ }
+
+ std::vector<Segment*>::iterator i;
+
+ // Check removed segments first
+ for (i = m_removedSegments.begin(); i != m_removedSegments.end(); ++i) {
+ processRemovedSegment(*i);
+ }
+ m_removedSegments.clear();
+
+ SEQMAN_DEBUG << "SequenceManager::checkRefreshStatus: we have "
+ << m_segments.size() << " segments" << endl;
+
+ // then the ones which are still there
+ for (SegmentRefreshMap::iterator i = m_segments.begin();
+ i != m_segments.end(); ++i) {
+ if (i->first->getRefreshStatus(i->second).needsRefresh() ||
+ ridset.find(i->first->getRuntimeId()) != ridset.end()) {
+ segmentModified(i->first);
+ i->first->getRefreshStatus(i->second).setNeedsRefresh(false);
+ }
+ }
+
+ // then added ones
+ for (i = m_addedSegments.begin(); i != m_addedSegments.end(); ++i) {
+ processAddedSegment(*i);
+ }
+ m_addedSegments.clear();
+}
+
+void SequenceManager::segmentModified(Segment* s)
+{
+ SEQMAN_DEBUG << "SequenceManager::segmentModified(" << s << ")\n";
+
+ bool sizeChanged = m_compositionMmapper->segmentModified(s);
+
+ SEQMAN_DEBUG << "SequenceManager::segmentModified() : size changed = "
+ << sizeChanged << endl;
+
+ if ((m_transportStatus == PLAYING) && sizeChanged) {
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (QString)m_compositionMmapper->getSegmentFileName(s);
+ streamOut << (size_t)m_compositionMmapper->getSegmentFileSize(s);
+
+ SEQMAN_DEBUG << "SequenceManager::segmentModified() : DCOP-call sequencer remapSegment"
+ << m_compositionMmapper->getSegmentFileName(s) << endl;
+
+ rgapp->sequencerSend("remapSegment(QString, size_t)", data);
+ }
+}
+
+void SequenceManager::segmentAdded(const Composition*, Segment* s)
+{
+ SEQMAN_DEBUG << "SequenceManager::segmentAdded(" << s << ")\n";
+ m_addedSegments.push_back(s);
+}
+
+void SequenceManager::segmentRemoved(const Composition*, Segment* s)
+{
+ SEQMAN_DEBUG << "SequenceManager::segmentRemoved(" << s << ")\n";
+ m_removedSegments.push_back(s);
+ std::vector<Segment*>::iterator i = find(m_addedSegments.begin(), m_addedSegments.end(), s);
+ if (i != m_addedSegments.end())
+ m_addedSegments.erase(i);
+}
+
+void SequenceManager::segmentRepeatChanged(const Composition*, Segment* s, bool repeat)
+{
+ SEQMAN_DEBUG << "SequenceManager::segmentRepeatChanged(" << s << ", " << repeat << ")\n";
+ segmentModified(s);
+}
+
+void SequenceManager::segmentRepeatEndChanged(const Composition*, Segment* s, timeT newEndTime)
+{
+ SEQMAN_DEBUG << "SequenceManager::segmentRepeatEndChanged(" << s << ", " << newEndTime << ")\n";
+ segmentModified(s);
+}
+
+void SequenceManager::segmentEventsTimingChanged(const Composition*, Segment * s, timeT t, RealTime)
+{
+ SEQMAN_DEBUG << "SequenceManager::segmentEventsTimingChanged(" << s << ", " << t << ")\n";
+ segmentModified(s);
+ if (s && s->getType() == Segment::Audio && m_transportStatus == PLAYING) {
+ QByteArray data;
+ rgapp->sequencerSend("remapTracks()", data);
+ }
+}
+
+void SequenceManager::segmentTransposeChanged(const Composition*, Segment *s, int transpose)
+{
+ SEQMAN_DEBUG << "SequenceManager::segmentTransposeChanged(" << s << ", " << transpose << ")\n";
+ segmentModified(s);
+}
+
+void SequenceManager::segmentTrackChanged(const Composition*, Segment *s, TrackId id)
+{
+ SEQMAN_DEBUG << "SequenceManager::segmentTrackChanged(" << s << ", " << id << ")\n";
+ segmentModified(s);
+ if (s && s->getType() == Segment::Audio && m_transportStatus == PLAYING) {
+ QByteArray data;
+ rgapp->sequencerSend("remapTracks()", data);
+ }
+}
+
+void SequenceManager::segmentEndMarkerChanged(const Composition*, Segment *s, bool)
+{
+ SEQMAN_DEBUG << "SequenceManager::segmentEndMarkerChanged(" << s << ")\n";
+ segmentModified(s);
+}
+
+void SequenceManager::processAddedSegment(Segment* s)
+{
+ SEQMAN_DEBUG << "SequenceManager::processAddedSegment(" << s << ")\n";
+
+ m_compositionMmapper->segmentAdded(s);
+
+ if (m_transportStatus == PLAYING) {
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << m_compositionMmapper->getSegmentFileName(s);
+
+ if (!rgapp->sequencerSend("addSegment(QString)", data)) {
+ m_transportStatus = STOPPED;
+ }
+ }
+
+ // Add to segments map
+ int id = s->getNewRefreshStatusId();
+ m_segments.insert(SegmentRefreshMap::value_type(s, id));
+
+}
+
+void SequenceManager::processRemovedSegment(Segment* s)
+{
+ SEQMAN_DEBUG << "SequenceManager::processRemovedSegment(" << s << ")\n";
+
+ QString filename = m_compositionMmapper->getSegmentFileName(s);
+ m_compositionMmapper->segmentDeleted(s);
+
+ if (m_transportStatus == PLAYING) {
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << filename;
+
+ if (!rgapp->sequencerSend("deleteSegment(QString)", data)) {
+ // failed
+ m_transportStatus = STOPPED;
+ }
+ }
+
+ // Remove from segments map
+ m_segments.erase(s);
+}
+
+void SequenceManager::endMarkerTimeChanged(const Composition *, bool /*shorten*/)
+{
+ SEQMAN_DEBUG << "SequenceManager::endMarkerTimeChanged()\n";
+ m_compositionMmapperResetTimer->start(500, true); // schedule a composition mmapper reset in 0.5s
+}
+
+void SequenceManager::timeSignatureChanged(const Composition *)
+{
+ resetMetronomeMmapper();
+}
+
+void SequenceManager::trackChanged(const Composition *, Track* t)
+{
+ SEQMAN_DEBUG << "SequenceManager::trackChanged(" << t << ", " << (t ? t->getPosition() : -1) << ")\n";
+ m_controlBlockMmapper->updateTrackData(t);
+
+ if (m_transportStatus == PLAYING) {
+ QByteArray data;
+ rgapp->sequencerSend("remapTracks()", data);
+ }
+}
+
+void SequenceManager::trackDeleted(const Composition *, TrackId t)
+{
+ m_controlBlockMmapper->setTrackDeleted(t);
+}
+
+void SequenceManager::metronomeChanged(InstrumentId id,
+ bool regenerateTicks)
+{
+ // This method is called when the user has changed the
+ // metronome instrument, pitch etc
+
+ SEQMAN_DEBUG << "SequenceManager::metronomeChanged (simple)"
+ << ", instrument = "
+ << id
+ << endl;
+
+ if (regenerateTicks)
+ resetMetronomeMmapper();
+
+ m_controlBlockMmapper->updateMetronomeData(id);
+ if (m_transportStatus == PLAYING) {
+ m_controlBlockMmapper->updateMetronomeForPlayback();
+ } else {
+ m_controlBlockMmapper->updateMetronomeForRecord();
+ }
+
+ m_metronomeMmapper->refresh();
+ m_timeSigSegmentMmapper->refresh();
+ m_tempoSegmentMmapper->refresh();
+}
+
+void SequenceManager::metronomeChanged(const Composition *)
+{
+ // This method is called when the muting status in the composition
+ // has changed -- the metronome itself has not actually changed
+
+ SEQMAN_DEBUG << "SequenceManager::metronomeChanged "
+ << ", instrument = "
+ << m_metronomeMmapper->getMetronomeInstrument()
+ << endl;
+
+ m_controlBlockMmapper->updateMetronomeData
+ (m_metronomeMmapper->getMetronomeInstrument());
+
+ if (m_transportStatus == PLAYING) {
+ m_controlBlockMmapper->updateMetronomeForPlayback();
+ } else {
+ m_controlBlockMmapper->updateMetronomeForRecord();
+ }
+}
+
+void SequenceManager::filtersChanged(MidiFilter thruFilter,
+ MidiFilter recordFilter)
+{
+ m_controlBlockMmapper->updateMidiFilters(thruFilter, recordFilter);
+}
+
+void SequenceManager::soloChanged(const Composition *, bool solo, TrackId selectedTrack)
+{
+ if (m_controlBlockMmapper->updateSoloData(solo, selectedTrack)) {
+ if (m_transportStatus == PLAYING) {
+ QByteArray data;
+ rgapp->sequencerSend("remapTracks()", data);
+ }
+ }
+}
+
+void SequenceManager::tempoChanged(const Composition *c)
+{
+ SEQMAN_DEBUG << "SequenceManager::tempoChanged()\n";
+
+ // Refresh all segments
+ //
+ for (SegmentRefreshMap::iterator i = m_segments.begin();
+ i != m_segments.end(); ++i) {
+ segmentModified(i->first);
+ }
+
+ // and metronome, time sig and tempo
+ //
+ m_metronomeMmapper->refresh();
+ m_timeSigSegmentMmapper->refresh();
+ m_tempoSegmentMmapper->refresh();
+
+ if (c->isLooping())
+ setLoop(c->getLoopStart(), c->getLoopEnd());
+ else if (m_transportStatus == PLAYING) {
+ // If the tempo changes during playback, reset the pointer
+ // position because the sequencer keeps track of position in
+ // real time and we want to maintain the same position in
+ // musical time. Turn off play tracking while this happens,
+ // so that we don't jump about in the main window while the
+ // user's trying to drag the tempo in it. (That doesn't help
+ // for matrix or notation though, sadly)
+ bool tracking = RosegardenGUIApp::self()->isTrackEditorPlayTracking();
+ if (tracking)
+ RosegardenGUIApp::self()->slotToggleTracking();
+ m_doc->slotSetPointerPosition(c->getPosition());
+ if (tracking)
+ RosegardenGUIApp::self()->slotToggleTracking();
+ }
+}
+
+void
+SequenceManager::sendTransportControlStatuses()
+{
+ KConfig* config = kapp->config();
+ config->setGroup(SequencerOptionsConfigGroup);
+
+ // Get the config values
+ //
+ bool jackTransport = config->readBoolEntry("jacktransport", false);
+ bool jackMaster = config->readBoolEntry("jackmaster", false);
+
+ int mmcMode = config->readNumEntry("mmcmode", 0);
+ int mtcMode = config->readNumEntry("mtcmode", 0);
+
+ int midiClock = config->readNumEntry("midiclock", 0);
+ bool midiSyncAuto = config->readBoolEntry("midisyncautoconnect", false);
+
+ // Send JACK transport
+ //
+ int jackValue = 0;
+ if (jackTransport && jackMaster)
+ jackValue = 2;
+ else {
+ if (jackTransport)
+ jackValue = 1;
+ else
+ jackValue = 0;
+ }
+
+ MappedEvent mEjackValue(MidiInstrumentBase, // InstrumentId
+ MappedEvent::SystemJackTransport,
+ MidiByte(jackValue));
+ StudioControl::sendMappedEvent(mEjackValue);
+
+
+ // Send MMC transport
+ //
+ MappedEvent mEmmcValue(MidiInstrumentBase, // InstrumentId
+ MappedEvent::SystemMMCTransport,
+ MidiByte(mmcMode));
+
+ StudioControl::sendMappedEvent(mEmmcValue);
+
+
+ // Send MTC transport
+ //
+ MappedEvent mEmtcValue(MidiInstrumentBase, // InstrumentId
+ MappedEvent::SystemMTCTransport,
+ MidiByte(mtcMode));
+
+ StudioControl::sendMappedEvent(mEmtcValue);
+
+
+ // Send MIDI Clock
+ //
+ MappedEvent mEmidiClock(MidiInstrumentBase, // InstrumentId
+ MappedEvent::SystemMIDIClock,
+ MidiByte(midiClock));
+
+ StudioControl::sendMappedEvent(mEmidiClock);
+
+
+ // Send MIDI Sync Auto-Connect
+ //
+ MappedEvent mEmidiSyncAuto(MidiInstrumentBase, // InstrumentId
+ MappedEvent::SystemMIDISyncAuto,
+ MidiByte(midiSyncAuto ? 1 : 0));
+
+ StudioControl::sendMappedEvent(mEmidiSyncAuto);
+
+}
+
+void
+SequenceManager::slotCountdownTimerTimeout()
+{
+ // Set the elapsed time in seconds
+ //
+ m_countdownDialog->setElapsedTime(m_recordTime->elapsed() / 1000);
+}
+
+void
+SequenceManager::slotFoundMountPoint(const QString&,
+ unsigned long kBSize,
+ unsigned long /*kBUsed*/,
+ unsigned long kBAvail)
+{
+ m_gotDiskSpaceResult = true;
+ m_diskSpaceKBAvail = kBAvail;
+}
+
+void
+SequenceManager::enableMIDIThruRouting(bool state)
+{
+ m_controlBlockMmapper->enableMIDIThruRouting(state);
+}
+
+int
+SequenceManager::getSampleRate()
+{
+ if (m_sampleRate != 0) return m_sampleRate;
+
+ QCString replyType;
+ QByteArray replyData;
+ if (rgapp->sequencerCall("getSampleRate()", replyType, replyData)) {
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ unsigned int result;
+ streamIn >> m_sampleRate;
+ }
+
+ return m_sampleRate;
+}
+
+bool
+SequenceManager::shouldWarnForImpreciseTimer()
+{
+ kapp->config()->setGroup(SequencerOptionsConfigGroup);
+ QString timer = kapp->config()->readEntry("timer");
+ if (timer == "(auto)" || timer == "") return true;
+ else return false; // if the user has chosen the timer, leave them alone
+}
+
+}
+#include "SequenceManager.moc"
diff --git a/src/gui/seqmanager/SequenceManager.h b/src/gui/seqmanager/SequenceManager.h
new file mode 100644
index 0000000..792ec01
--- /dev/null
+++ b/src/gui/seqmanager/SequenceManager.h
@@ -0,0 +1,322 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_SEQUENCEMANAGER_H_
+#define _RG_SEQUENCEMANAGER_H_
+
+#include "base/Composition.h"
+#include "base/Event.h"
+#include "base/MidiProgram.h"
+#include "base/RealTime.h"
+#include "base/Track.h"
+#include "gui/application/RosegardenDCOP.h"
+#include "sound/MappedComposition.h"
+#include "sound/MappedEvent.h"
+#include <qobject.h>
+#include <qstring.h>
+#include <vector>
+#include <map>
+
+
+class QTimer;
+class QTime;
+class QEvent;
+
+
+namespace Rosegarden
+{
+
+class TransportDialog;
+class Track;
+class TimeSigSegmentMmapper;
+class TempoSegmentMmapper;
+class SequencerMapper;
+class Segment;
+class RosegardenGUIDoc;
+class MetronomeMmapper;
+class CountdownDialog;
+class ControlBlockMmapper;
+class CompositionMmapper;
+class Composition;
+class AudioManagerDialog;
+
+
+class SequenceManager : public QObject, public CompositionObserver
+{
+ Q_OBJECT
+public:
+ SequenceManager(RosegardenGUIDoc *doc,
+ TransportDialog *transport);
+ ~SequenceManager();
+
+ /**
+ * Replaces the internal document
+ */
+ void setDocument(RosegardenGUIDoc*);
+
+ /**
+ * Return the current internal document
+ */
+ RosegardenGUIDoc* getDocument();
+
+ //
+ // Transport controls
+ //
+
+ /// returns true if the call actually paused playback
+ bool play();
+
+ // We don't call stop() directly - using stopping() and then
+ // call stop().
+ //
+ void stop();
+
+ void stopping();
+ void rewind();
+ void fastforward();
+ void record(bool countIn);
+ void rewindToBeginning();
+ void fastForwardToEnd();
+
+ void setLoop(const timeT &lhs, const timeT &rhs);
+ void notifySequencerStatus(TransportStatus status);
+ void sendSequencerJump(const RealTime &time);
+
+ // Events coming in
+ void processAsynchronousMidi(const MappedComposition &mC,
+ AudioManagerDialog *aMD);
+
+ // Before playing and recording. If warnUser is true, show the
+ // user a warning dialog if there is a problem with the setup.
+ //
+ void checkSoundDriverStatus(bool warnUser);
+
+ /**
+ * Send program changes and align Instrument lists before playback
+ * starts.
+ * Also called at document loading (with arg set to true) to reset all instruments
+ * (fix for bug 820174)
+ *
+ * @arg forceProgramChanges if true, always send program changes even if the instrument is
+ * set not to send any.
+ */
+ void preparePlayback(bool forceProgramChanges = false);
+
+ /// Check and set sequencer status
+ void setTransportStatus(const TransportStatus &status);
+ TransportStatus getTransportStatus() const { return m_transportStatus; }
+
+ /**
+ * Suspend the sequencer to allow for a safe DCOP call() i.e. one
+ * when we don't hang both clients 'cos they're blocking on each
+ * other.
+ */
+ void suspendSequencer(bool value);
+
+ /// Send the audio level to VU meters
+ void sendAudioLevel(MappedEvent *mE);
+
+ /// Find what has been initialised and what hasn't
+ unsigned int getSoundDriverStatus() { return m_soundDriverStatus; }
+
+ /// Reset MIDI controllers
+ void resetControllers();
+
+ /// Reset MIDI network
+ void resetMidiNetwork();
+
+ /// Reinitialise the studio
+ void reinitialiseSequencerStudio();
+
+ /// Send JACK and MMC transport control statuses
+ void sendTransportControlStatuses();
+
+ /// Send all note offs and resets to MIDI devices
+ void panic();
+
+ /// Send an MC to the view
+ void showVisuals(const MappedComposition &mC);
+
+ /// Apply in-situ filtering to a MappedComposition
+ MappedComposition
+ applyFiltering(const MappedComposition &mC,
+ MappedEvent::MappedEventType filter);
+
+ CountdownDialog* getCountdownDialog() { return m_countdownDialog; }
+
+
+ //
+ // CompositionObserver interface
+ //
+ virtual void segmentAdded (const Composition*, Segment*);
+ virtual void segmentRemoved (const Composition*, Segment*);
+ virtual void segmentRepeatChanged (const Composition*, Segment*, bool);
+ virtual void segmentRepeatEndChanged (const Composition*, Segment*, timeT);
+ virtual void segmentEventsTimingChanged(const Composition*, Segment *, timeT delay, RealTime rtDelay);
+ virtual void segmentTransposeChanged (const Composition*, Segment *, int transpose);
+ virtual void segmentTrackChanged (const Composition*, Segment *, TrackId id);
+ virtual void segmentEndMarkerChanged (const Composition*, Segment *, bool);
+ virtual void endMarkerTimeChanged (const Composition*, bool shorten);
+ virtual void trackChanged (const Composition*, Track*);
+ virtual void trackDeleted (const Composition*, TrackId);
+ virtual void timeSignatureChanged (const Composition*);
+ virtual void metronomeChanged (const Composition*);
+ virtual void soloChanged (const Composition*, bool solo, TrackId selectedTrack);
+ virtual void tempoChanged (const Composition*);
+
+ void processAddedSegment(Segment*);
+ void processRemovedSegment(Segment*);
+ void segmentModified(Segment*);
+
+ virtual bool event(QEvent *e);
+
+ /// for the gui to call to indicate that the metronome needs to be remapped
+ void metronomeChanged(InstrumentId id, bool regenerateTicks);
+
+ /// for the gui to call to indicate that a MIDI filter needs to be remapped
+ void filtersChanged(MidiFilter thruFilter,
+ MidiFilter recordFilter);
+
+ /// Return the current sequencer memory mapped file
+ SequencerMapper* getSequencerMapper() { return m_sequencerMapper; }
+
+ /// Ensure that the sequencer file is mapped
+ void mapSequencer();
+
+ void setTransport(TransportDialog* t) { m_transport = t; }
+
+ void enableMIDIThruRouting(bool state);
+
+ int getSampleRate(); // may return 0 if sequencer uncontactable
+
+public slots:
+
+ void update();
+
+signals:
+ void setProgress(int);
+ void incrementProgress(int);
+
+ void insertableNoteOnReceived(int pitch, int velocity);
+ void insertableNoteOffReceived(int pitch, int velocity);
+ void controllerDeviceEventReceived(MappedEvent *ev);
+
+protected slots:
+ void slotCountdownTimerTimeout();
+
+ // Activated by timer to allow a message to be reported to
+ // the user - we use this mechanism so that the user isn't
+ // bombarded with dialogs in the event of lots of failures.
+ //
+ void slotAllowReport() { m_canReport = true; }
+
+ void slotFoundMountPoint(const QString&,
+ unsigned long kBSize,
+ unsigned long kBUsed,
+ unsigned long kBAvail);
+
+ void slotScheduledCompositionMmapperReset() { resetCompositionMmapper(); }
+
+protected:
+
+ void resetCompositionMmapper();
+ void resetControlBlockMmapper();
+ void resetMetronomeMmapper();
+ void resetTempoSegmentMmapper();
+ void resetTimeSigSegmentMmapper();
+ void checkRefreshStatus();
+ void sendMIDIRecordingDevice(const QString recordDeviceStr);
+ void restoreRecordSubscriptions();
+ bool shouldWarnForImpreciseTimer();
+
+ //--------------- Data members ---------------------------------
+
+ MappedComposition m_mC;
+ RosegardenGUIDoc *m_doc;
+ CompositionMmapper *m_compositionMmapper;
+ ControlBlockMmapper *m_controlBlockMmapper;
+ MetronomeMmapper *m_metronomeMmapper;
+ TempoSegmentMmapper *m_tempoSegmentMmapper;
+ TimeSigSegmentMmapper *m_timeSigSegmentMmapper;
+
+ std::vector<Segment*> m_addedSegments;
+ std::vector<Segment*> m_removedSegments;
+ bool m_metronomeNeedsRefresh;
+
+ // statuses
+ TransportStatus m_transportStatus;
+ unsigned int m_soundDriverStatus;
+
+ // pointer to the transport dialog
+ TransportDialog *m_transport;
+
+ clock_t m_lastRewoundAt;
+
+ CountdownDialog *m_countdownDialog;
+ QTimer *m_countdownTimer;
+
+ bool m_shownOverrunWarning;
+
+ // Keep a track of elapsed record time with this object
+ //
+ QTime *m_recordTime;
+
+ typedef std::map<Segment *, int> SegmentRefreshMap;
+ SegmentRefreshMap m_segments; // map to refresh status id
+ SegmentRefreshMap m_triggerSegments;
+ unsigned int m_compositionRefreshStatusId;
+ bool m_updateRequested;
+
+ // used to schedule a composition mmapper reset when the composition end time marker changes
+ // this can be caused by a window resize, and since the reset is potentially expensive we want to collapse
+ // several following requests into one.
+ QTimer *m_compositionMmapperResetTimer;
+
+ // Information that the sequencer is providing to us - for the moment
+ // it's only the position pointer.
+ //
+ SequencerMapper *m_sequencerMapper;
+
+ // Just to make sure we don't bother the user too often
+ //
+ QTimer *m_reportTimer;
+ bool m_canReport;
+
+ bool m_gotDiskSpaceResult;
+ unsigned long m_diskSpaceKBAvail;
+
+ bool m_lastLowLatencySwitchSent;
+
+ timeT m_lastTransportStartPosition;
+
+ int m_sampleRate;
+};
+
+
+
+
+}
+
+#endif
diff --git a/src/gui/seqmanager/SequencerMapper.cpp b/src/gui/seqmanager/SequencerMapper.cpp
new file mode 100644
index 0000000..5d8acf3
--- /dev/null
+++ b/src/gui/seqmanager/SequencerMapper.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "SequencerMapper.h"
+
+#include "misc/Debug.h"
+#include "base/Exception.h"
+#include "base/MidiProgram.h"
+#include "base/RealTime.h"
+#include "base/Track.h"
+#include "sound/MappedComposition.h"
+#include "sound/MappedEvent.h"
+#include "sound/SequencerDataBlock.h"
+#include <qfileinfo.h>
+#include <qstring.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <errno.h>
+
+
+namespace Rosegarden
+{
+
+SequencerMapper::SequencerMapper(const QString filename)
+ : m_fd( -1),
+ m_mmappedSize(0),
+ m_mmappedBuffer((void *)0),
+ m_filename(filename)
+{
+ RG_DEBUG << "SequencerMapper::SequencerMapper - mmapping " << filename << endl;
+ map();
+}
+
+SequencerMapper::~SequencerMapper()
+{
+ unmap();
+}
+
+void
+SequencerMapper::map()
+{
+ QFileInfo fInfo(m_filename);
+ if (!fInfo.exists()) {
+ RG_DEBUG << "SequencerMapper::map() : file " << m_filename << " doesn't exist\n";
+ throw Exception("file not found");
+ }
+
+ m_fd = ::open(m_filename.latin1(), O_RDWR);
+
+ if (m_fd < 0) {
+ RG_DEBUG << "SequencerMapper::map() : Couldn't open " << m_filename
+ << endl;
+ throw Exception("Couldn't open " + std::string(m_filename.data()));
+ }
+
+ m_mmappedSize = sizeof(SequencerDataBlock);
+ m_mmappedBuffer = (long*)::mmap(0, m_mmappedSize, PROT_READ, MAP_SHARED, m_fd, 0);
+
+ if (m_mmappedBuffer == (void*) - 1) {
+
+ RG_DEBUG << QString("mmap failed : (%1) %2\n").
+ arg(errno).arg(strerror(errno));
+
+ throw Exception("mmap failed");
+ }
+
+ RG_DEBUG << "SequencerMapper::map() : "
+ << (void*)m_mmappedBuffer << "," << m_mmappedSize << endl;
+
+ m_sequencerDataBlock = new (m_mmappedBuffer)
+ SequencerDataBlock(false);
+}
+
+void
+SequencerMapper::unmap()
+{
+ ::munmap(m_mmappedBuffer, m_mmappedSize);
+ ::close(m_fd);
+}
+
+}
diff --git a/src/gui/seqmanager/SequencerMapper.h b/src/gui/seqmanager/SequencerMapper.h
new file mode 100644
index 0000000..59d217f
--- /dev/null
+++ b/src/gui/seqmanager/SequencerMapper.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_SEQUENCERMAPPER_H_
+#define _RG_SEQUENCERMAPPER_H_
+
+#include "base/MidiProgram.h"
+#include "base/RealTime.h"
+#include "base/Track.h"
+#include "sound/SequencerDataBlock.h"
+#include <qstring.h>
+
+
+class LevelInfo;
+
+
+namespace Rosegarden
+{
+
+class MappedEvent;
+class MappedComposition;
+
+
+class SequencerMapper
+{
+public:
+ SequencerMapper(const QString filename);
+ ~SequencerMapper();
+
+ RealTime getPositionPointer() const {
+ return m_sequencerDataBlock->getPositionPointer();
+ }
+
+ bool getVisual(MappedEvent &ev) const {
+ return m_sequencerDataBlock->getVisual(ev);
+ }
+
+ int getRecordedEvents(MappedComposition &mC) const {
+ return m_sequencerDataBlock->getRecordedEvents(mC);
+ }
+
+ bool getTrackLevel(TrackId track,
+ LevelInfo &info) const {
+ return m_sequencerDataBlock->getTrackLevel(track, info);
+ }
+
+ bool getInstrumentLevel(InstrumentId id,
+ LevelInfo &info) const {
+ return m_sequencerDataBlock->getInstrumentLevel(id, info);
+ }
+
+ bool getInstrumentLevelForMixer(InstrumentId id,
+ LevelInfo &info) const {
+ return m_sequencerDataBlock->getInstrumentLevelForMixer(id, info);
+ }
+
+ bool getInstrumentRecordLevel(InstrumentId id,
+ LevelInfo &info) const {
+ return m_sequencerDataBlock->getInstrumentRecordLevel(id, info);
+ }
+
+ bool getInstrumentRecordLevelForMixer(InstrumentId id,
+ LevelInfo &info) const {
+ return m_sequencerDataBlock->getInstrumentRecordLevelForMixer(id, info);
+ }
+
+ bool getSubmasterLevel(int submaster,
+ LevelInfo &info) const {
+ return m_sequencerDataBlock->getSubmasterLevel(submaster, info);
+ }
+
+ bool getMasterLevel(LevelInfo &info) const {
+ return m_sequencerDataBlock->getMasterLevel(info);
+ }
+
+protected:
+ void map();
+ void unmap();
+
+ int m_fd;
+ size_t m_mmappedSize;
+ void* m_mmappedBuffer;
+ QString m_filename;
+ SequencerDataBlock *m_sequencerDataBlock;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/seqmanager/SpecialSegmentMmapper.cpp b/src/gui/seqmanager/SpecialSegmentMmapper.cpp
new file mode 100644
index 0000000..5926f11
--- /dev/null
+++ b/src/gui/seqmanager/SpecialSegmentMmapper.cpp
@@ -0,0 +1,56 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "SpecialSegmentMmapper.h"
+
+#include <kstddirs.h>
+#include "base/Event.h"
+#include "base/Segment.h"
+#include "base/TriggerSegment.h"
+#include "document/RosegardenGUIDoc.h"
+#include "SegmentMmapper.h"
+#include <kglobal.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+SpecialSegmentMmapper::SpecialSegmentMmapper(RosegardenGUIDoc* doc,
+ QString baseFileName)
+ : SegmentMmapper(doc, 0, createFileName(baseFileName))
+{}
+
+QString SpecialSegmentMmapper::createFileName(QString baseFileName)
+{
+ return KGlobal::dirs()->resourceDirs("tmp").last() + "/" + baseFileName;
+}
+
+unsigned int SpecialSegmentMmapper::getSegmentRepeatCount()
+{
+ return 1;
+}
+
+}
diff --git a/src/gui/seqmanager/SpecialSegmentMmapper.h b/src/gui/seqmanager/SpecialSegmentMmapper.h
new file mode 100644
index 0000000..e16c7b5
--- /dev/null
+++ b/src/gui/seqmanager/SpecialSegmentMmapper.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_SPECIALSEGMENTMMAPPER_H_
+#define _RG_SPECIALSEGMENTMMAPPER_H_
+
+#include "SegmentMmapper.h"
+#include <qstring.h>
+
+
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+
+
+class SpecialSegmentMmapper : public SegmentMmapper
+{
+public:
+ // overrides from SegmentMmapper
+ virtual unsigned int getSegmentRepeatCount();
+
+protected:
+ SpecialSegmentMmapper(RosegardenGUIDoc* doc,
+ QString baseFileName);
+
+ QString createFileName(QString baseFileName);
+};
+
+//----------------------------------------
+
+
+}
+
+#endif
diff --git a/src/gui/seqmanager/TempoSegmentMmapper.cpp b/src/gui/seqmanager/TempoSegmentMmapper.cpp
new file mode 100644
index 0000000..1c53922
--- /dev/null
+++ b/src/gui/seqmanager/TempoSegmentMmapper.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "TempoSegmentMmapper.h"
+
+#include "base/Event.h"
+#include "base/RealTime.h"
+#include "base/Segment.h"
+#include "base/TriggerSegment.h"
+#include "document/RosegardenGUIDoc.h"
+#include "SegmentMmapper.h"
+#include "sound/MappedEvent.h"
+#include "SpecialSegmentMmapper.h"
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+void TempoSegmentMmapper::dump()
+{
+ RealTime eventTime;
+
+ Composition& comp = m_doc->getComposition();
+ MappedEvent* bufPos = m_mmappedEventBuffer;
+
+ for (int i = 0; i < comp.getTempoChangeCount(); ++i) {
+
+ std::pair<timeT, tempoT> tempoChange = comp.getTempoChange(i);
+
+ eventTime = comp.getElapsedRealTime(tempoChange.first);
+ MappedEvent* mappedEvent = new (bufPos) MappedEvent();
+ mappedEvent->setType(MappedEvent::Tempo);
+ mappedEvent->setEventTime(eventTime);
+
+ // Nasty hack -- we use the instrument ID to pass through the
+ // raw tempo value, as it has the appropriate range (unlike
+ // e.g. tempo1 + tempo2). These events are not actually used
+ // on the sequencer side yet, so this may change to something
+ // nicer at some point.
+ mappedEvent->setInstrument(tempoChange.second);
+
+ ++bufPos;
+ }
+
+ // Store the number of events at the start of the shared memory region
+ *(size_t *)m_mmappedRegion = (bufPos - m_mmappedEventBuffer);
+}
+
+size_t TempoSegmentMmapper::computeMmappedSize()
+{
+ return m_doc->getComposition().getTempoChangeCount() * sizeof(MappedEvent);
+}
+
+}
diff --git a/src/gui/seqmanager/TempoSegmentMmapper.h b/src/gui/seqmanager/TempoSegmentMmapper.h
new file mode 100644
index 0000000..08c8ebf
--- /dev/null
+++ b/src/gui/seqmanager/TempoSegmentMmapper.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_TEMPOSEGMENTMMAPPER_H_
+#define _RG_TEMPOSEGMENTMMAPPER_H_
+
+#include "SpecialSegmentMmapper.h"
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+
+
+class TempoSegmentMmapper : public SpecialSegmentMmapper
+{
+ friend class SegmentMmapperFactory;
+
+protected:
+ TempoSegmentMmapper(RosegardenGUIDoc* doc,
+ QString baseFileName)
+ : SpecialSegmentMmapper(doc, baseFileName) {}
+
+ // overrides from SegmentMmapper
+ virtual size_t computeMmappedSize();
+
+ // override from SegmentMmapper
+ virtual void dump();
+};
+
+//----------------------------------------
+
+
+}
+
+#endif
diff --git a/src/gui/seqmanager/TimeSigSegmentMmapper.cpp b/src/gui/seqmanager/TimeSigSegmentMmapper.cpp
new file mode 100644
index 0000000..98beab3
--- /dev/null
+++ b/src/gui/seqmanager/TimeSigSegmentMmapper.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "TimeSigSegmentMmapper.h"
+
+#include "base/Event.h"
+#include "base/RealTime.h"
+#include "base/Segment.h"
+#include "base/TriggerSegment.h"
+#include "document/RosegardenGUIDoc.h"
+#include "SegmentMmapper.h"
+#include "sound/MappedEvent.h"
+#include "SpecialSegmentMmapper.h"
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+void TimeSigSegmentMmapper::dump()
+{
+ RealTime eventTime;
+
+ Composition& comp = m_doc->getComposition();
+ MappedEvent* bufPos = m_mmappedEventBuffer;
+
+ for (int i = 0; i < comp.getTimeSignatureCount(); ++i) {
+
+ std::pair<timeT, TimeSignature> timeSigChange = comp.getTimeSignatureChange(i);
+
+ eventTime = comp.getElapsedRealTime(timeSigChange.first);
+ MappedEvent* mappedEvent = new (bufPos) MappedEvent();
+ mappedEvent->setType(MappedEvent::TimeSignature);
+ mappedEvent->setEventTime(eventTime);
+ mappedEvent->setData1(timeSigChange.second.getNumerator());
+ mappedEvent->setData2(timeSigChange.second.getDenominator());
+
+ ++bufPos;
+ }
+
+ // Store the number of events at the start of the shared memory region
+ *(size_t *)m_mmappedRegion = (bufPos - m_mmappedEventBuffer);
+}
+
+size_t TimeSigSegmentMmapper::computeMmappedSize()
+{
+ return m_doc->getComposition().getTimeSignatureCount() * sizeof(MappedEvent);
+}
+
+}
diff --git a/src/gui/seqmanager/TimeSigSegmentMmapper.h b/src/gui/seqmanager/TimeSigSegmentMmapper.h
new file mode 100644
index 0000000..20e0474
--- /dev/null
+++ b/src/gui/seqmanager/TimeSigSegmentMmapper.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_TIMESIGSEGMENTMMAPPER_H_
+#define _RG_TIMESIGSEGMENTMMAPPER_H_
+
+#include "SpecialSegmentMmapper.h"
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+class RosegardenGUIDoc;
+
+
+class TimeSigSegmentMmapper : public SpecialSegmentMmapper
+{
+ friend class SegmentMmapperFactory;
+
+public:
+
+protected:
+ TimeSigSegmentMmapper(RosegardenGUIDoc* doc,
+ QString baseFileName)
+ : SpecialSegmentMmapper(doc, baseFileName) {}
+
+ // overrides from SegmentMmapper
+ virtual size_t computeMmappedSize();
+
+ // override from SegmentMmapper
+ virtual void dump();
+};
+
+//----------------------------------------
+
+
+}
+
+#endif
diff --git a/src/gui/studio/AudioMixerWindow.cpp b/src/gui/studio/AudioMixerWindow.cpp
new file mode 100644
index 0000000..e8d09b3
--- /dev/null
+++ b/src/gui/studio/AudioMixerWindow.cpp
@@ -0,0 +1,1734 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "AudioMixerWindow.h"
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include "AudioPlugin.h"
+#include "AudioPluginManager.h"
+#include "MixerWindow.h"
+#include "StudioControl.h"
+#include "sound/Midi.h"
+#include "misc/Debug.h"
+#include "gui/application/RosegardenDCOP.h"
+#include "base/AudioLevel.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 "document/RosegardenGUIDoc.h"
+#include "gui/editors/notation/NotePixmapFactory.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/seqmanager/SequencerMapper.h"
+#include "gui/seqmanager/SequenceManager.h"
+#include "gui/widgets/AudioRouteMenu.h"
+#include "gui/widgets/AudioVUMeter.h"
+#include "gui/widgets/Fader.h"
+#include "gui/widgets/Rotary.h"
+#include "gui/widgets/VUMeter.h"
+#include "sound/MappedCommon.h"
+#include "sound/MappedEvent.h"
+#include "sound/MappedStudio.h"
+#include <klocale.h>
+#include <kstddirs.h>
+#include <kaction.h>
+#include <kglobal.h>
+#include <kmainwindow.h>
+#include <kstdaction.h>
+#include <qaccel.h>
+#include <qcolor.h>
+#include <qfont.h>
+#include <qframe.h>
+#include <qhbox.h>
+#include <qiconset.h>
+#include <qlabel.h>
+#include <qobject.h>
+#include <qpalette.h>
+#include <qpixmap.h>
+#include <qpushbutton.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+// We define these such that the default of no-bits-set for the
+// studio's mixer display options produces the most sensible result
+static const unsigned int MIXER_OMIT_FADERS = 1 << 0;
+static const unsigned int MIXER_OMIT_SUBMASTERS = 1 << 1;
+static const unsigned int MIXER_OMIT_PLUGINS = 1 << 2;
+static const unsigned int MIXER_SHOW_UNASSIGNED_FADERS = 1 << 3;
+static const unsigned int MIXER_OMIT_SYNTH_FADERS = 1 << 4;
+
+
+AudioMixerWindow::AudioMixerWindow(QWidget *parent,
+ RosegardenGUIDoc *document):
+ MixerWindow(parent, document),
+ m_mainBox (0)
+{
+ populate();
+
+ KStdAction::close(this,
+ SLOT(slotClose()),
+ actionCollection());
+
+ QIconSet 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-record")));
+ new KAction(i18n("&Record"), icon, 0, this,
+ SIGNAL(record()), actionCollection(),
+ "record");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-panic")));
+ new KAction(i18n("Panic"), icon, Key_P + CTRL + ALT, this,
+ SIGNAL(panic()), actionCollection(),
+ "panic");
+
+ unsigned int mixerOptions = m_studio->getMixerDisplayOptions();
+
+ (new KToggleAction(i18n("Show Audio &Faders"), 0, this,
+ SLOT(slotToggleFaders()), actionCollection(),
+ "show_audio_faders"))->setChecked
+ (!(mixerOptions & MIXER_OMIT_FADERS));
+
+ (new KToggleAction(i18n("Show Synth &Faders"), 0, this,
+ SLOT(slotToggleSynthFaders()), actionCollection(),
+ "show_synth_faders"))->setChecked
+ (!(mixerOptions & MIXER_OMIT_SYNTH_FADERS));
+
+ (new KToggleAction(i18n("Show &Submasters"), 0, this,
+ SLOT(slotToggleSubmasters()), actionCollection(),
+ "show_audio_submasters"))->setChecked
+ (!(mixerOptions & MIXER_OMIT_SUBMASTERS));
+
+ (new KToggleAction(i18n("Show &Plugin Buttons"), 0, this,
+ SLOT(slotTogglePluginButtons()), actionCollection(),
+ "show_plugin_buttons"))->setChecked
+ (!(mixerOptions & MIXER_OMIT_PLUGINS));
+
+ (new KToggleAction(i18n("Show &Unassigned Faders"), 0, this,
+ SLOT(slotToggleUnassignedFaders()), actionCollection(),
+ "show_unassigned_faders"))->setChecked
+ (mixerOptions & MIXER_SHOW_UNASSIGNED_FADERS);
+
+ KRadioAction *action = 0;
+
+ for (int i = 1; i <= 16; i *= 2) {
+ action =
+ new KRadioAction(i18n("1 Input", "%n Inputs", i),
+ 0, this,
+ SLOT(slotSetInputCountFromAction()), actionCollection(),
+ QString("inputs_%1").arg(i));
+ action->setExclusiveGroup("inputs");
+ if (i == int(m_studio->getRecordIns().size()))
+ action->setChecked(true);
+ }
+
+ action = new KRadioAction
+ (i18n("No Submasters"),
+ 0, this,
+ SLOT(slotSetSubmasterCountFromAction()), actionCollection(),
+ QString("submasters_0"));
+ action->setExclusiveGroup("submasters");
+ action->setChecked(true);
+
+ for (int i = 2; i <= 8; i *= 2) {
+ action = new KRadioAction
+ (i18n("1 Submaster", "%n Submasters", i),
+ 0, this,
+ SLOT(slotSetSubmasterCountFromAction()), actionCollection(),
+ QString("submasters_%1").arg(i));
+ action->setExclusiveGroup("submasters");
+ if (i == int(m_studio->getBusses().size()) - 1)
+ action->setChecked(true);
+ }
+
+ createGUI("mixer.rc");
+}
+
+AudioMixerWindow::~AudioMixerWindow()
+{
+ RG_DEBUG << "AudioMixerWindow::~AudioMixerWindow\n";
+ depopulate();
+}
+
+void
+AudioMixerWindow::depopulate()
+{
+ if (!m_mainBox)
+ return ;
+
+ // All the widgets will be deleted when the main box goes,
+ // except that we need to delete the AudioRouteMenus first
+ // because they aren't actually widgets but do contain them
+
+ for (FaderMap::iterator i = m_faders.begin();
+ i != m_faders.end(); ++i) {
+ delete i->second.m_input;
+ delete i->second.m_output;
+ }
+
+ m_faders.clear();
+ m_submasters.clear();
+
+ delete m_mainBox;
+ m_mainBox = 0;
+}
+
+void
+AudioMixerWindow::populate()
+{
+ if (m_mainBox) {
+
+ depopulate();
+
+ } else {
+
+ m_surroundBox = new QHBox(this);
+ setCentralWidget(m_surroundBox);
+ }
+
+ QFont font;
+ font.setPointSize(font.pointSize() * 8 / 10);
+ font.setBold(false);
+ setFont(font);
+
+ QFont boldFont(font);
+ boldFont.setBold(true);
+
+ m_mainBox = new QFrame(m_surroundBox);
+
+ InstrumentList instruments = m_studio->getPresentationInstruments();
+ BussList busses = m_studio->getBusses();
+
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ m_monoPixmap.load(QString("%1/misc/mono.xpm").arg(pixmapDir));
+ m_stereoPixmap.load(QString("%1/misc/stereo.xpm").arg(pixmapDir));
+
+ // Total cols: is 2 for each fader, submaster or master, plus 1
+ // for each spacer.
+ QGridLayout *mainLayout = new QGridLayout
+ (m_mainBox, (instruments.size() + busses.size()) * 3, 7);
+
+ setCaption(i18n("Audio Mixer"));
+
+ int count = 1;
+ int col = 0;
+
+ unsigned int mixerOptions = m_studio->getMixerDisplayOptions();
+
+ bool showUnassigned = (mixerOptions & MIXER_SHOW_UNASSIGNED_FADERS);
+
+ for (InstrumentList::iterator i = instruments.begin();
+ i != instruments.end(); ++i) {
+
+ if ((*i)->getType() != Instrument::Audio &&
+ (*i)->getType() != Instrument::SoftSynth)
+ continue;
+
+ FaderRec rec;
+
+ if (!showUnassigned) {
+ // Do any tracks use this instrument?
+ if (!isInstrumentAssigned((*i)->getId())) {
+ continue;
+ }
+ }
+ rec.m_populated = true;
+
+ if ((*i)->getType() == Instrument::Audio) {
+ rec.m_input = new AudioRouteMenu(m_mainBox,
+ AudioRouteMenu::In,
+ AudioRouteMenu::Compact,
+ m_studio, *i);
+ QToolTip::add
+ (rec.m_input->getWidget(), i18n("Record input source"));
+ rec.m_input->getWidget()->setMaximumWidth(45);
+ } else {
+ rec.m_input = 0;
+ }
+
+ rec.m_output = new AudioRouteMenu(m_mainBox,
+ AudioRouteMenu::Out,
+ AudioRouteMenu::Compact,
+ m_studio, *i);
+ QToolTip::add
+ (rec.m_output->getWidget(), i18n("Output destination"));
+ rec.m_output->getWidget()->setMaximumWidth(45);
+
+ rec.m_pan = new Rotary
+ (m_mainBox, -100.0, 100.0, 1.0, 5.0, 0.0, 20,
+ Rotary::NoTicks, false, true);
+
+ if ((*i)->getType() == Instrument::Audio) {
+ rec.m_pan->setKnobColour(GUIPalette::getColour(GUIPalette::RotaryPastelGreen));
+ } else {
+ rec.m_pan->setKnobColour(GUIPalette::getColour(GUIPalette::RotaryPastelYellow));
+ }
+
+ QToolTip::add
+ (rec.m_pan, i18n("Pan"));
+
+ rec.m_fader = new Fader
+ (AudioLevel::LongFader, 20, 240, m_mainBox);
+ rec.m_meter = new AudioVUMeter
+ (m_mainBox, VUMeter::AudioPeakHoldIECLong, true, rec.m_input != 0,
+ 20, 240);
+
+ QToolTip::add
+ (rec.m_fader, i18n("Audio level"));
+ QToolTip::add
+ (rec.m_meter, i18n("Audio level"));
+
+ rec.m_stereoButton = new QPushButton(m_mainBox);
+ rec.m_stereoButton->setPixmap(m_monoPixmap);
+ rec.m_stereoButton->setFixedSize(20, 20);
+ rec.m_stereoButton->setFlat(true);
+ rec.m_stereoness = false;
+ QToolTip::add
+ (rec.m_stereoButton, i18n("Mono or stereo"));
+
+ rec.m_muteButton = new QPushButton(m_mainBox);
+ rec.m_muteButton->setText("M");
+ rec.m_muteButton->setToggleButton(true);
+ rec.m_muteButton->setFixedWidth(rec.m_stereoButton->width());
+ rec.m_muteButton->setFixedHeight(rec.m_stereoButton->height());
+ rec.m_muteButton->setFlat(true);
+ QToolTip::add
+ (rec.m_muteButton, i18n("Mute"));
+
+ rec.m_soloButton = new QPushButton(m_mainBox);
+ rec.m_soloButton->setText("S");
+ rec.m_soloButton->setToggleButton(true);
+ rec.m_soloButton->setFixedWidth(rec.m_stereoButton->width());
+ rec.m_soloButton->setFixedHeight(rec.m_stereoButton->height());
+ rec.m_soloButton->setFlat(true);
+ QToolTip::add
+ (rec.m_soloButton, i18n("Solo"));
+
+ rec.m_recordButton = new QPushButton(m_mainBox);
+ rec.m_recordButton->setText("R");
+ rec.m_recordButton->setToggleButton(true);
+ rec.m_recordButton->setFixedWidth(rec.m_stereoButton->width());
+ rec.m_recordButton->setFixedHeight(rec.m_stereoButton->height());
+ rec.m_recordButton->setFlat(true);
+ QToolTip::add
+ (rec.m_recordButton, i18n("Arm recording"));
+
+ rec.m_pluginBox = new QVBox(m_mainBox);
+
+ for (int p = 0; p < 5; ++p) {
+ QPushButton *plugin = new QPushButton(rec.m_pluginBox, "pluginButton");
+ plugin->setText(i18n("<none>"));
+ plugin->setMaximumWidth(45);
+ QToolTip::add
+ (plugin, i18n("Audio plugin button"));
+ rec.m_plugins.push_back(plugin);
+ connect(plugin, SIGNAL(clicked()),
+ this, SLOT(slotSelectPlugin()));
+ }
+
+ QLabel *idLabel;
+ QString idString;
+ if ((*i)->getType() == Instrument::Audio) {
+ idString = i18n("Audio %1").arg((*i)->getId() -
+ AudioInstrumentBase + 1);
+ idLabel = new QLabel(idString, m_mainBox, "audioIdLabel");
+ } else {
+ idString = i18n("Synth %1").arg((*i)->getId() -
+ SoftSynthInstrumentBase + 1);
+ idLabel = new QLabel(idString, m_mainBox, "synthIdLabel");
+ }
+ idLabel->setFont(boldFont);
+
+ if (rec.m_input) {
+ mainLayout->addMultiCellWidget(rec.m_input->getWidget(), 1, 1, col, col + 1);
+ }
+ mainLayout->addMultiCellWidget(rec.m_output->getWidget(), 2, 2, col, col + 1);
+ // mainLayout->addWidget(idLabel, 2, col, Qt::AlignCenter);
+ // mainLayout->addWidget(rec.m_pan, 2, col+1, Qt::AlignLeft);
+
+ mainLayout->addMultiCellWidget(idLabel, 0, 0, col, col + 1, Qt::AlignCenter);
+ mainLayout->addWidget(rec.m_pan, 5, col, Qt::AlignCenter);
+
+ mainLayout->addWidget(rec.m_fader, 3, col, Qt::AlignCenter);
+ mainLayout->addWidget(rec.m_meter, 3, col + 1, Qt::AlignCenter);
+
+ // commented out until implemented
+ // mainLayout->addWidget(rec.m_muteButton, 4, col);
+ // mainLayout->addWidget(rec.m_soloButton, 4, col+1);
+ rec.m_muteButton->hide();
+ rec.m_soloButton->hide();
+
+ // mainLayout->addWidget(rec.m_recordButton, 5, col);
+ // mainLayout->addWidget(rec.m_stereoButton, 5, col+1);
+
+ rec.m_recordButton->hide();
+ mainLayout->addWidget(rec.m_stereoButton, 5, col + 1);
+
+ if (rec.m_pluginBox) {
+ mainLayout->addMultiCellWidget(rec.m_pluginBox, 6, 6, col, col + 1);
+ }
+
+ m_faders[(*i)->getId()] = rec;
+ updateFader((*i)->getId());
+ updateRouteButtons((*i)->getId());
+ updateStereoButton((*i)->getId());
+ updatePluginButtons((*i)->getId());
+
+ if (rec.m_input) {
+ connect(rec.m_input, SIGNAL(changed()),
+ this, SLOT(slotInputChanged()));
+ }
+
+ connect(rec.m_output, SIGNAL(changed()),
+ this, SLOT(slotOutputChanged()));
+
+ connect(rec.m_fader, SIGNAL(faderChanged(float)),
+ this, SLOT(slotFaderLevelChanged(float)));
+
+ connect(rec.m_pan, SIGNAL(valueChanged(float)),
+ this, SLOT(slotPanChanged(float)));
+
+ connect(rec.m_soloButton, SIGNAL(clicked()),
+ this, SLOT(slotSoloChanged()));
+
+ connect(rec.m_muteButton, SIGNAL(clicked()),
+ this, SLOT(slotMuteChanged()));
+
+ connect(rec.m_stereoButton, SIGNAL(clicked()),
+ this, SLOT(slotChannelsChanged()));
+
+ connect(rec.m_recordButton, SIGNAL(clicked()),
+ this, SLOT(slotRecordChanged()));
+
+ ++count;
+
+ mainLayout->addMultiCell(new QSpacerItem(2, 0), 0, 6, col + 2, col + 2);
+
+ col += 3;
+ }
+
+ count = 1;
+
+ for (BussList::iterator i = busses.begin();
+ i != busses.end(); ++i) {
+
+ if (i == busses.begin())
+ continue; // that one's the master
+
+ FaderRec rec;
+ rec.m_populated = true;
+
+ rec.m_pan = new Rotary
+ (m_mainBox, -100.0, 100.0, 1.0, 5.0, 0.0, 20,
+ Rotary::NoTicks, false, true);
+ rec.m_pan->setKnobColour(GUIPalette::getColour(GUIPalette::RotaryPastelBlue));
+
+ QToolTip::add
+ (rec.m_pan, i18n("Pan"));
+
+ rec.m_fader = new Fader
+ (AudioLevel::LongFader, 20, 240, m_mainBox);
+ rec.m_meter = new AudioVUMeter
+ (m_mainBox, VUMeter::AudioPeakHoldIECLong, true, false, 20, 240);
+
+ QToolTip::add
+ (rec.m_fader, i18n("Audio level"));
+ QToolTip::add
+ (rec.m_meter, i18n("Audio level"));
+
+ rec.m_muteButton = new QPushButton(m_mainBox);
+ rec.m_muteButton->setText("M");
+ rec.m_muteButton->setToggleButton(true);
+ rec.m_muteButton->setFlat(true);
+
+ QToolTip::add
+ (rec.m_muteButton, i18n("Mute"));
+
+ rec.m_pluginBox = new QVBox(m_mainBox);
+
+ for (int p = 0; p < 5; ++p) {
+ QPushButton *plugin = new QPushButton(rec.m_pluginBox, "pluginButton");
+ plugin->setText(i18n("<none>"));
+ plugin->setMaximumWidth(45);
+ QToolTip::add
+ (plugin, i18n("Audio plugin button"));
+ rec.m_plugins.push_back(plugin);
+ connect(plugin, SIGNAL(clicked()),
+ this, SLOT(slotSelectPlugin()));
+ }
+
+ QLabel *idLabel = new QLabel(i18n("Sub %1").arg(count), m_mainBox, "subMaster");
+ idLabel->setFont(boldFont);
+
+ // mainLayout->addWidget(idLabel, 2, col, Qt::AlignCenter);
+ mainLayout->addMultiCellWidget(idLabel, 0, 0, col, col + 1, Qt::AlignCenter);
+
+ // mainLayout->addWidget(rec.m_pan, 2, col+1, Qt::AlignLeft);
+ mainLayout->addMultiCellWidget(rec.m_pan, 5, 5, col, col + 1, Qt::AlignCenter);
+
+ mainLayout->addWidget(rec.m_fader, 3, col, Qt::AlignCenter);
+ mainLayout->addWidget(rec.m_meter, 3, col + 1, Qt::AlignCenter);
+
+ // mainLayout->addMultiCellWidget(rec.m_muteButton, 4, 4, col, col+1);
+ rec.m_muteButton->hide();
+
+ if (rec.m_pluginBox) {
+ mainLayout->addMultiCellWidget(rec.m_pluginBox, 6, 6, col, col + 1);
+ }
+
+ m_submasters.push_back(rec);
+ updateFader(count);
+ updatePluginButtons(count);
+
+ connect(rec.m_fader, SIGNAL(faderChanged(float)),
+ this, SLOT(slotFaderLevelChanged(float)));
+
+ connect(rec.m_pan, SIGNAL(valueChanged(float)),
+ this, SLOT(slotPanChanged(float)));
+
+ connect(rec.m_muteButton, SIGNAL(clicked()),
+ this, SLOT(slotMuteChanged()));
+
+ ++count;
+
+ mainLayout->addMultiCell(new QSpacerItem(2, 0), 0, 6, col + 2, col + 2);
+
+ col += 3;
+ }
+
+ if (busses.size() > 0) {
+
+ FaderRec rec;
+ rec.m_populated = true;
+
+ rec.m_fader = new Fader
+ (AudioLevel::LongFader, 20, 240, m_mainBox);
+ rec.m_meter = new AudioVUMeter
+ (m_mainBox, VUMeter::AudioPeakHoldIEC, true, false, 20, 240);
+
+ QToolTip::add
+ (rec.m_fader, i18n("Audio master output level"));
+ QToolTip::add
+ (rec.m_meter, i18n("Audio master output level"));
+
+ rec.m_muteButton = new QPushButton(m_mainBox);
+ rec.m_muteButton->setText("M");
+ rec.m_muteButton->setToggleButton(true);
+ rec.m_muteButton->setFlat(true);
+
+ QLabel *idLabel = new QLabel(i18n("Master"), m_mainBox);
+ idLabel->setFont(boldFont);
+
+ mainLayout->addMultiCellWidget(idLabel, 0, 0, col, col + 1, Qt::AlignCenter);
+ mainLayout->addWidget(rec.m_fader, 3, col, Qt::AlignCenter);
+ mainLayout->addWidget(rec.m_meter, 3, col + 1, Qt::AlignCenter);
+
+ // mainLayout->addMultiCellWidget(rec.m_muteButton, 4, 4, col, col+1);
+ rec.m_muteButton->hide();
+
+ mainLayout->addMultiCell(new QSpacerItem(2, 0), 0, 6, col + 2, col + 2);
+
+ m_master = rec;
+ updateFader(0);
+
+ connect(rec.m_fader, SIGNAL(faderChanged(float)),
+ this, SLOT(slotFaderLevelChanged(float)));
+
+ connect(rec.m_muteButton, SIGNAL(clicked()),
+ this, SLOT(slotMuteChanged()));
+ }
+
+ m_mainBox->show();
+
+ slotUpdateFaderVisibility();
+ slotUpdateSynthFaderVisibility();
+ slotUpdateSubmasterVisibility();
+ slotUpdatePluginButtonVisibility();
+
+ adjustSize();
+}
+
+bool
+AudioMixerWindow::isInstrumentAssigned(InstrumentId id)
+{
+ Composition::trackcontainer &tracks =
+ m_document->getComposition().getTracks();
+
+ for (Composition::trackcontainer::iterator ti =
+ tracks.begin(); ti != tracks.end(); ++ti) {
+ if (ti->second->getInstrument() == id) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+AudioMixerWindow::slotTrackAssignmentsChanged()
+{
+ for (FaderMap::iterator i = m_faders.begin(); i != m_faders.end(); ++i) {
+
+ InstrumentId id = i->first;
+ bool assigned = isInstrumentAssigned(id);
+
+ if (assigned != i->second.m_populated) {
+ // found an inconsistency
+ populate();
+ return ;
+ }
+ }
+}
+
+void
+AudioMixerWindow::slotUpdateInstrument(InstrumentId id)
+{
+ RG_DEBUG << "AudioMixerWindow::slotUpdateInstrument(" << id << ")" << endl;
+
+ blockSignals(true);
+
+ updateFader(id);
+ updateStereoButton(id);
+ updateRouteButtons(id);
+ updatePluginButtons(id);
+ updateMiscButtons(id);
+
+ blockSignals(false);
+}
+
+void
+AudioMixerWindow::slotPluginSelected(InstrumentId id,
+ int index, int plugin)
+{
+ if (id >= (int)AudioInstrumentBase) {
+
+ FaderRec &rec = m_faders[id];
+ if (!rec.m_populated || !rec.m_pluginBox)
+ return ;
+
+ // nowhere to display synth plugin info yet
+ if (index >= rec.m_plugins.size())
+ return ;
+
+ if (plugin == -1) {
+
+ rec.m_plugins[index]->setText(i18n("<none>"));
+ QToolTip::add
+ (rec.m_plugins[index], i18n("<no plugin>"));
+
+ rec.m_plugins[index]->setPaletteBackgroundColor
+ (kapp->palette().
+ color(QPalette::Active, QColorGroup::Button));
+
+ } else {
+
+ AudioPlugin *pluginClass
+ = m_document->getPluginManager()->getPlugin(plugin);
+
+ QColor pluginBgColour =
+ kapp->palette().color(QPalette::Active, QColorGroup::Light);
+
+ if (pluginClass) {
+ rec.m_plugins[index]->
+ setText(pluginClass->getLabel());
+ QToolTip::add
+ (rec.m_plugins[index], pluginClass->getLabel());
+
+ pluginBgColour = pluginClass->getColour();
+ }
+
+
+ rec.m_plugins[index]->setPaletteForegroundColor(Qt::white);
+ rec.m_plugins[index]->setPaletteBackgroundColor(pluginBgColour);
+ }
+ } else if (id > 0 && id <= m_submasters.size()) {
+
+ FaderRec &rec = m_submasters[id - 1];
+ if (!rec.m_populated || !rec.m_pluginBox)
+ return ;
+ if (index >= rec.m_plugins.size())
+ return ;
+
+ if (plugin == -1) {
+
+ rec.m_plugins[index]->setText(i18n("<none>"));
+ QToolTip::add
+ (rec.m_plugins[index], i18n("<no plugin>"));
+
+ rec.m_plugins[index]->setPaletteBackgroundColor
+ (kapp->palette().
+ color(QPalette::Active, QColorGroup::Button));
+
+ } else {
+
+ AudioPlugin *pluginClass
+ = m_document->getPluginManager()->getPlugin(plugin);
+
+ QColor pluginBgColour =
+ kapp->palette().color(QPalette::Active, QColorGroup::Light);
+
+ if (pluginClass) {
+ rec.m_plugins[index]->
+ setText(pluginClass->getLabel());
+ QToolTip::add
+ (rec.m_plugins[index], pluginClass->getLabel());
+
+ pluginBgColour = pluginClass->getColour();
+ }
+
+
+ rec.m_plugins[index]->setPaletteForegroundColor(Qt::white);
+ rec.m_plugins[index]->setPaletteBackgroundColor(pluginBgColour);
+ }
+ }
+}
+
+void
+AudioMixerWindow::slotPluginBypassed(InstrumentId instrumentId,
+ int , bool )
+{
+ RG_DEBUG << "AudioMixerWindow::slotPluginBypassed(" << instrumentId << ")" << endl;
+
+ updatePluginButtons(instrumentId);
+}
+
+void
+AudioMixerWindow::updateFader(int id)
+{
+ if (id == -1) {
+
+ // This used to be the special code for updating the monitor fader.
+ return ;
+
+ } else if (id >= (int)AudioInstrumentBase) {
+
+ FaderRec &rec = m_faders[id];
+ if (!rec.m_populated)
+ return ;
+ Instrument *instrument = m_studio->getInstrumentById(id);
+
+ rec.m_fader->blockSignals(true);
+ rec.m_fader->setFader(instrument->getLevel());
+ rec.m_fader->blockSignals(false);
+
+ rec.m_pan->blockSignals(true);
+ rec.m_pan->setPosition(instrument->getPan() - 100);
+ rec.m_pan->blockSignals(false);
+
+ } else {
+
+ FaderRec &rec = (id == 0 ? m_master : m_submasters[id - 1]);
+ BussList busses = m_studio->getBusses();
+ Buss *buss = busses[id];
+
+ rec.m_fader->blockSignals(true);
+ rec.m_fader->setFader(buss->getLevel());
+ rec.m_fader->blockSignals(false);
+
+ if (rec.m_pan) {
+ rec.m_pan->blockSignals(true);
+ rec.m_pan->setPosition(buss->getPan() - 100);
+ rec.m_pan->blockSignals(false);
+ }
+ }
+}
+
+void
+AudioMixerWindow::updateRouteButtons(int id)
+{
+ if (id >= (int)AudioInstrumentBase) {
+ FaderRec &rec = m_faders[id];
+ if (!rec.m_populated)
+ return ;
+ if (rec.m_input)
+ rec.m_input->slotRepopulate();
+ rec.m_output->slotRepopulate();
+ }
+}
+
+void
+AudioMixerWindow::updateStereoButton(int id)
+{
+ if (id >= (int)AudioInstrumentBase) {
+
+ FaderRec &rec = m_faders[id];
+ if (!rec.m_populated)
+ return ;
+ Instrument *i = m_studio->getInstrumentById(id);
+
+ bool stereo = (i->getAudioChannels() > 1);
+ if (stereo == rec.m_stereoness)
+ return ;
+
+ rec.m_stereoness = stereo;
+
+ if (stereo)
+ rec.m_stereoButton->setPixmap(m_stereoPixmap);
+ else
+ rec.m_stereoButton->setPixmap(m_monoPixmap);
+ }
+}
+
+void
+AudioMixerWindow::updateMiscButtons(int )
+{
+ //... complications here, because the mute/solo status is actually
+ // per-track rather than per-instrument... doh.
+}
+
+void
+AudioMixerWindow::updatePluginButtons(int id)
+{
+ FaderRec *rec = 0;
+ PluginContainer *container = 0;
+
+ if (id >= (int)AudioInstrumentBase) {
+
+ container = m_studio->getInstrumentById(id);
+ rec = &m_faders[id];
+ if (!rec->m_populated || !rec->m_pluginBox)
+ return ;
+
+ } else {
+
+ BussList busses = m_studio->getBusses();
+ if (busses.size() > id) {
+ container = busses[id];
+ }
+ rec = &m_submasters[id - 1];
+ if (!rec->m_populated || !rec->m_pluginBox)
+ return ;
+ }
+
+ if (rec && container) {
+
+ for (unsigned int i = 0; i < rec->m_plugins.size(); i++) {
+
+ bool used = false;
+ bool bypass = false;
+ QColor pluginBgColour =
+ kapp->palette().color(QPalette::Active, QColorGroup::Light);
+
+ rec->m_plugins[i]->show();
+
+ AudioPluginInstance *inst = container->getPlugin(i);
+
+ if (inst && inst->isAssigned()) {
+
+ AudioPlugin *pluginClass
+ = m_document->getPluginManager()->getPlugin(
+ m_document->getPluginManager()->
+ getPositionByIdentifier(inst->getIdentifier().c_str()));
+
+ if (pluginClass) {
+ rec->m_plugins[i]->setText(pluginClass->getLabel());
+ QToolTip::add
+ (rec->m_plugins[i], pluginClass->getLabel());
+
+ pluginBgColour = pluginClass->getColour();
+ }
+
+ used = true;
+ bypass = inst->isBypassed();
+
+ } else {
+
+ rec->m_plugins[i]->setText(i18n("<none>"));
+ QToolTip::add
+ (rec->m_plugins[i], i18n("<no plugin>"));
+
+ if (inst)
+ bypass = inst->isBypassed();
+ }
+
+ if (bypass) {
+
+ rec->m_plugins[i]->setPaletteForegroundColor
+ (kapp->palette().
+ color(QPalette::Active, QColorGroup::Button));
+
+ rec->m_plugins[i]->setPaletteBackgroundColor
+ (kapp->palette().
+ color(QPalette::Active, QColorGroup::ButtonText));
+
+ } else if (used) {
+
+ rec->m_plugins[i]->setPaletteForegroundColor(Qt::white);
+ rec->m_plugins[i]->setPaletteBackgroundColor(pluginBgColour);
+
+
+ } else {
+
+ rec->m_plugins[i]->setPaletteForegroundColor
+ (kapp->palette().
+ color(QPalette::Active, QColorGroup::ButtonText));
+
+ rec->m_plugins[i]->setPaletteBackgroundColor
+ (kapp->palette().
+ color(QPalette::Active, QColorGroup::Button));
+ }
+ }
+ }
+}
+
+void
+AudioMixerWindow::slotSelectPlugin()
+{
+ const QObject *s = sender();
+
+ for (FaderMap::iterator i = m_faders.begin();
+ i != m_faders.end(); ++i) {
+
+ int index = 0;
+ if (!i->second.m_populated || !i->second.m_pluginBox)
+ continue;
+
+ for (std::vector<QPushButton *>::iterator pli = i->second.m_plugins.begin();
+ pli != i->second.m_plugins.end(); ++pli) {
+
+ if (*pli == s) {
+
+ emit selectPlugin(this, i->first, index);
+ return ;
+ }
+
+ ++index;
+ }
+ }
+
+
+ int b = 1;
+
+ for (FaderVector::iterator i = m_submasters.begin();
+ i != m_submasters.end(); ++i) {
+
+ int index = 0;
+ if (!i->m_populated || !i->m_pluginBox)
+ continue;
+
+ for (std::vector<QPushButton *>::iterator pli = i->m_plugins.begin();
+ pli != i->m_plugins.end(); ++pli) {
+
+ if (*pli == s) {
+
+ emit selectPlugin(this, b, index);
+ return ;
+ }
+
+ ++index;
+ }
+
+ ++b;
+ }
+}
+
+void
+AudioMixerWindow::slotInputChanged()
+{
+ const QObject *s = sender();
+
+ for (FaderMap::iterator i = m_faders.begin();
+ i != m_faders.end(); ++i) {
+
+ if (i->second.m_input == s)
+ emit instrumentParametersChanged(i->first);
+ }
+}
+
+void
+AudioMixerWindow::slotOutputChanged()
+{
+ const QObject *s = sender();
+
+ for (FaderMap::iterator i = m_faders.begin();
+ i != m_faders.end(); ++i) {
+
+ if (i->second.m_output == s)
+ emit instrumentParametersChanged(i->first);
+ }
+}
+
+void
+AudioMixerWindow::sendControllerRefresh()
+{
+ //!!! really want some notification of whether we have an external controller!
+ int controllerChannel = 0;
+
+ for (FaderMap::iterator i = m_faders.begin(); i != m_faders.end(); ++i) {
+
+ if (controllerChannel >= 16)
+ break;
+
+ Instrument *instrument =
+ m_studio->getInstrumentById(i->first);
+
+ int value = AudioLevel::dB_to_fader
+ (instrument->getLevel(), 127, AudioLevel::LongFader);
+ MappedEvent mE(instrument->getId(),
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_VOLUME,
+ MidiByte(value));
+ mE.setRecordedChannel(controllerChannel);
+ mE.setRecordedDevice(Device::CONTROL_DEVICE);
+ StudioControl::sendMappedEvent(mE);
+
+ int ipan = (int(instrument->getPan()) * 64) / 100;
+ if (ipan < 0)
+ ipan = 0;
+ if (ipan > 127)
+ ipan = 127;
+ MappedEvent mEp(instrument->getId(),
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_PAN,
+ MidiByte(ipan));
+ mEp.setRecordedChannel(controllerChannel);
+ mEp.setRecordedDevice(Device::CONTROL_DEVICE);
+ StudioControl::sendMappedEvent(mEp);
+
+ ++controllerChannel;
+ }
+}
+
+void
+AudioMixerWindow::slotFaderLevelChanged(float dB)
+{
+ const QObject *s = sender();
+
+ BussList busses = m_studio->getBusses();
+
+ if (m_master.m_fader == s) {
+
+ if (busses.size() > 0) {
+ StudioControl::setStudioObjectProperty
+ (MappedObjectId(busses[0]->getMappedId()),
+ MappedAudioBuss::Level,
+ MappedObjectValue(dB));
+ busses[0]->setLevel(dB);
+ }
+
+ return ;
+ }
+
+ int index = 1;
+
+ for (FaderVector::iterator i = m_submasters.begin();
+ i != m_submasters.end(); ++i) {
+
+ if (i->m_fader == s) {
+ if ((int)busses.size() > index) {
+ StudioControl::setStudioObjectProperty
+ (MappedObjectId(busses[index]->getMappedId()),
+ MappedAudioBuss::Level,
+ MappedObjectValue(dB));
+ busses[index]->setLevel(dB);
+ }
+
+ return ;
+ }
+
+ ++index;
+ }
+
+ int controllerChannel = 0;
+
+ for (FaderMap::iterator i = m_faders.begin();
+ i != m_faders.end(); ++i) {
+
+ if (i->second.m_fader == s) {
+
+ Instrument *instrument =
+ m_studio->getInstrumentById(i->first);
+
+ if (instrument) {
+ StudioControl::setStudioObjectProperty
+ (MappedObjectId
+ (instrument->getMappedId()),
+ MappedAudioFader::FaderLevel,
+ MappedObjectValue(dB));
+ instrument->setLevel(dB);
+ }
+
+ // send out to external controllers as well.
+ //!!! really want some notification of whether we have any!
+ if (controllerChannel < 16) {
+ int value = AudioLevel::dB_to_fader
+ (dB, 127, AudioLevel::LongFader);
+ MappedEvent mE(instrument->getId(),
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_VOLUME,
+ MidiByte(value));
+ mE.setRecordedChannel(controllerChannel);
+ mE.setRecordedDevice(Device::CONTROL_DEVICE);
+ StudioControl::sendMappedEvent(mE);
+ }
+
+ emit instrumentParametersChanged(i->first);
+ }
+
+ ++controllerChannel;
+ }
+}
+
+void
+AudioMixerWindow::slotPanChanged(float pan)
+{
+ const QObject *s = sender();
+
+ BussList busses = m_studio->getBusses();
+
+ int index = 1;
+
+ for (FaderVector::iterator i = m_submasters.begin();
+ i != m_submasters.end(); ++i) {
+
+ if (i->m_pan == s) {
+ if ((int)busses.size() > index) {
+ StudioControl::setStudioObjectProperty
+ (MappedObjectId(busses[index]->getMappedId()),
+ MappedAudioBuss::Pan,
+ MappedObjectValue(pan));
+ busses[index]->setPan(MidiByte(pan + 100.0));
+ }
+ return ;
+ }
+
+ ++index;
+ }
+
+ int controllerChannel = 0;
+
+ for (FaderMap::iterator i = m_faders.begin();
+ i != m_faders.end(); ++i) {
+
+ if (i->second.m_pan == s) {
+
+ Instrument *instrument =
+ m_studio->getInstrumentById(i->first);
+
+ if (instrument) {
+ StudioControl::setStudioObjectProperty
+ (instrument->getMappedId(),
+ MappedAudioFader::Pan,
+ MappedObjectValue(pan));
+ instrument->setPan(MidiByte(pan + 100.0));
+ }
+
+ // send out to external controllers as well.
+ //!!! really want some notification of whether we have any!
+ if (controllerChannel < 16) {
+ int ipan = (int(instrument->getPan()) * 64) / 100;
+ if (ipan < 0)
+ ipan = 0;
+ if (ipan > 127)
+ ipan = 127;
+ MappedEvent mE(instrument->getId(),
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_PAN,
+ MidiByte(ipan));
+ mE.setRecordedChannel(controllerChannel);
+ mE.setRecordedDevice(Device::CONTROL_DEVICE);
+ StudioControl::sendMappedEvent(mE);
+ }
+
+ emit instrumentParametersChanged(i->first);
+ }
+
+ ++controllerChannel;
+ }
+}
+
+void
+AudioMixerWindow::slotChannelsChanged()
+{
+ const QObject *s = sender();
+
+ // channels are only switchable on instruments
+
+ //!!! need to reconnect input, or change input channel number anyway
+
+
+ for (FaderMap::iterator i = m_faders.begin();
+ i != m_faders.end(); ++i) {
+
+ if (s == i->second.m_stereoButton) {
+
+ Instrument *instrument =
+ m_studio->getInstrumentById(i->first);
+
+ if (instrument) {
+ instrument->setAudioChannels
+ ((instrument->getAudioChannels() > 1) ? 1 : 2);
+ updateStereoButton(instrument->getId());
+ updateRouteButtons(instrument->getId());
+
+ emit instrumentParametersChanged(i->first);
+
+ return ;
+ }
+ }
+ }
+}
+
+void
+AudioMixerWindow::slotSoloChanged()
+{
+ //...
+}
+
+void
+AudioMixerWindow::slotMuteChanged()
+{
+ //...
+}
+
+void
+AudioMixerWindow::slotRecordChanged()
+{
+ //...
+}
+
+void
+AudioMixerWindow::updateMeters(SequencerMapper *mapper)
+{
+ for (FaderMap::iterator i = m_faders.begin(); i != m_faders.end(); ++i) {
+
+ InstrumentId id = i->first;
+ FaderRec &rec = i->second;
+ if (!rec.m_populated)
+ continue;
+
+ LevelInfo info;
+
+ if (mapper->getInstrumentLevelForMixer(id, info)) {
+
+ // The values passed through are long-fader values
+ float dBleft = AudioLevel::fader_to_dB
+ (info.level, 127, AudioLevel::LongFader);
+
+ if (rec.m_stereoness) {
+ float dBright = AudioLevel::fader_to_dB
+ (info.levelRight, 127, AudioLevel::LongFader);
+
+ rec.m_meter->setLevel(dBleft, dBright);
+
+ } else {
+ rec.m_meter->setLevel(dBleft);
+ }
+ }
+ }
+
+ for (unsigned int i = 0; i < m_submasters.size(); ++i) {
+
+ FaderRec &rec = m_submasters[i];
+
+ LevelInfo info;
+ if (!mapper->getSubmasterLevel(i, info))
+ continue;
+
+ // The values passed through are long-fader values
+ float dBleft = AudioLevel::fader_to_dB
+ (info.level, 127, AudioLevel::LongFader);
+ float dBright = AudioLevel::fader_to_dB
+ (info.levelRight, 127, AudioLevel::LongFader);
+
+ rec.m_meter->setLevel(dBleft, dBright);
+ }
+
+ updateMonitorMeters(mapper);
+
+ LevelInfo masterInfo;
+ if (mapper->getMasterLevel(masterInfo)) {
+
+ float dBleft = AudioLevel::fader_to_dB
+ (masterInfo.level, 127, AudioLevel::LongFader);
+ float dBright = AudioLevel::fader_to_dB
+ (masterInfo.levelRight, 127, AudioLevel::LongFader);
+
+ m_master.m_meter->setLevel(dBleft, dBright);
+ }
+}
+
+void
+AudioMixerWindow::updateMonitorMeters(SequencerMapper *mapper)
+{
+ // only show monitor levels when quiescent or when recording (as
+ // record levels)
+ if (m_document->getSequenceManager() &&
+ m_document->getSequenceManager()->getTransportStatus() == PLAYING) {
+ return ;
+ }
+
+ Composition &comp = m_document->getComposition();
+ Composition::trackcontainer &tracks = comp.getTracks();
+
+ for (FaderMap::iterator i = m_faders.begin(); i != m_faders.end(); ++i) {
+
+ InstrumentId id = i->first;
+ FaderRec &rec = i->second;
+ if (!rec.m_populated)
+ continue;
+
+ LevelInfo info;
+
+ if (mapper->getInstrumentRecordLevelForMixer(id, info)) {
+
+ bool armed = false;
+
+ for (Composition::trackcontainer::iterator ti =
+ tracks.begin(); ti != tracks.end(); ++ti) {
+ if (ti->second->getInstrument() == id) {
+ if (comp.isTrackRecording(ti->second->getId())) {
+ armed = true;
+ break;
+ }
+ }
+ }
+
+ if (!armed)
+ continue;
+
+ // The values passed through are long-fader values
+ float dBleft = AudioLevel::fader_to_dB
+ (info.level, 127, AudioLevel::LongFader);
+
+ if (rec.m_stereoness) {
+ float dBright = AudioLevel::fader_to_dB
+ (info.levelRight, 127, AudioLevel::LongFader);
+
+ rec.m_meter->setRecordLevel(dBleft, dBright);
+
+ } else {
+ rec.m_meter->setRecordLevel(dBleft);
+ }
+ }
+ }
+}
+
+void
+AudioMixerWindow::slotControllerDeviceEventReceived(MappedEvent *e,
+ const void *preferredCustomer)
+{
+ if (preferredCustomer != this)
+ return ;
+ RG_DEBUG << "AudioMixerWindow::slotControllerDeviceEventReceived: this one's for me" << endl;
+ raise();
+
+ // get channel number n from event
+ // update instrument for nth fader in m_faders
+
+ if (e->getType() != MappedEvent::MidiController)
+ return ;
+ unsigned int channel = e->getRecordedChannel();
+ MidiByte controller = e->getData1();
+ MidiByte value = e->getData2();
+
+ int count = 0;
+ for (FaderMap::iterator i = m_faders.begin(); i != m_faders.end(); ++i) {
+
+ if (count < channel) {
+ ++count;
+ continue;
+ }
+
+ Instrument *instrument =
+ m_studio->getInstrumentById(i->first);
+ if (!instrument)
+ continue;
+
+ switch (controller) {
+
+ case MIDI_CONTROLLER_VOLUME: {
+ float level = AudioLevel::fader_to_dB
+ (value, 127, AudioLevel::LongFader);
+
+ StudioControl::setStudioObjectProperty
+ (instrument->getMappedId(),
+ MappedAudioFader::FaderLevel,
+ MappedObjectValue(level));
+
+ instrument->setLevel(level);
+ break;
+ }
+
+ case MIDI_CONTROLLER_PAN: {
+ MidiByte ipan = MidiByte((value / 64.0) * 100.0 + 0.01);
+
+ StudioControl::setStudioObjectProperty
+ (instrument->getMappedId(),
+ MappedAudioFader::Pan,
+ MappedObjectValue(float(ipan) - 100.0));
+
+ instrument->setPan(ipan);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ slotUpdateInstrument(i->first);
+ emit instrumentParametersChanged(i->first);
+
+ break;
+ }
+}
+
+void
+AudioMixerWindow::slotSetInputCountFromAction()
+{
+ const QObject *s = sender();
+ QString name = s->name();
+
+ if (name.left(7) == "inputs_") {
+
+ int count = name.right(name.length() - 7).toInt();
+
+ RecordInList ins = m_studio->getRecordIns();
+ int current = ins.size();
+
+ if (count == current)
+ return ;
+
+ m_studio->clearRecordIns(); // leaves the default 1
+
+ for (int i = 1; i < count; ++i) {
+ m_studio->addRecordIn(new RecordIn());
+ }
+ }
+
+ m_document->initialiseStudio();
+
+ for (FaderMap::iterator i = m_faders.begin();
+ i != m_faders.end(); ++i) {
+ updateRouteButtons(i->first);
+ }
+}
+
+void
+AudioMixerWindow::slotSetSubmasterCountFromAction()
+{
+ const QObject *s = sender();
+ QString name = s->name();
+
+ if (name.left(11) == "submasters_") {
+
+ int count = name.right(name.length() - 11).toInt();
+
+ BussList busses = m_studio->getBusses();
+ int current = busses.size();
+
+ // offset by 1 generally to take into account the fact that
+ // the first buss in the studio is the master, not a submaster
+
+ if (count + 1 == current)
+ return ;
+
+ BussList dups;
+ for (int i = 0; i < count; ++i) {
+ if (i + 1 < int(busses.size())) {
+ dups.push_back(new Buss(*busses[i + 1]));
+ } else {
+ dups.push_back(new Buss(i + 1));
+ }
+ }
+
+ m_studio->clearBusses();
+
+ for (BussList::iterator i = dups.begin();
+ i != dups.end(); ++i) {
+ m_studio->addBuss(*i);
+ }
+ }
+
+ m_document->initialiseStudio();
+
+ populate();
+}
+
+void AudioMixerWindow::FaderRec::setVisible(bool visible)
+{
+ if (visible) {
+ if (m_input)
+ m_input->getWidget()->show();
+ if (m_output)
+ m_output->getWidget()->show();
+ if (m_pan)
+ m_pan->show();
+ if (m_fader)
+ m_fader->show();
+ if (m_meter)
+ m_meter->show();
+ // commented out until implemented
+ // if (m_muteButton) m_muteButton->show();
+ // if (m_soloButton) m_soloButton->show();
+ // if (m_recordButton) m_recordButton->show();
+ if (m_stereoButton)
+ m_stereoButton->show();
+
+ } else {
+
+ if (m_input)
+ m_input->getWidget()->hide();
+ if (m_output)
+ m_output->getWidget()->hide();
+ if (m_pan)
+ m_pan->hide();
+ if (m_fader)
+ m_fader->hide();
+ if (m_meter)
+ m_meter->hide();
+ // commented out until implemented
+ // if (m_muteButton) m_muteButton->hide();
+ // if (m_soloButton) m_soloButton->hide();
+ // if (m_recordButton) m_recordButton->hide();
+ if (m_stereoButton)
+ m_stereoButton->hide();
+ }
+
+ setPluginButtonsVisible(visible);
+
+}
+
+void
+AudioMixerWindow::FaderRec::setPluginButtonsVisible(bool visible)
+{
+ if (!m_pluginBox)
+ return ;
+
+ if (visible) {
+ m_pluginBox->show();
+ } else {
+ m_pluginBox->hide();
+ }
+}
+
+void
+AudioMixerWindow::slotToggleFaders()
+{
+ m_studio->setMixerDisplayOptions(m_studio->getMixerDisplayOptions() ^
+ MIXER_OMIT_FADERS);
+
+ slotUpdateFaderVisibility();
+}
+
+void
+AudioMixerWindow::slotUpdateFaderVisibility()
+{
+ bool d = !(m_studio->getMixerDisplayOptions() & MIXER_OMIT_FADERS);
+
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("show_audio_faders"));
+ if (action) {
+ action->setChecked(d);
+ }
+
+ RG_DEBUG << "AudioMixerWindow::slotUpdateFaderVisibility: visiblility is " << d << " (options " << m_studio->getMixerDisplayOptions() << ")" << endl;
+
+ for (FaderMap::iterator i = m_faders.begin(); i != m_faders.end(); ++i) {
+ if (i->first < SoftSynthInstrumentBase) {
+ FaderRec rec = i->second;
+ rec.setVisible(d);
+ }
+ }
+
+ toggleNamedWidgets(d, "audioIdLabel");
+
+ adjustSize();
+}
+
+void
+AudioMixerWindow::slotToggleSynthFaders()
+{
+ m_studio->setMixerDisplayOptions(m_studio->getMixerDisplayOptions() ^
+ MIXER_OMIT_SYNTH_FADERS);
+
+ slotUpdateSynthFaderVisibility();
+}
+
+void
+AudioMixerWindow::slotUpdateSynthFaderVisibility()
+{
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("show_synth_faders"));
+ if (!action)
+ return ;
+
+ action->setChecked(!(m_studio->getMixerDisplayOptions() &
+ MIXER_OMIT_SYNTH_FADERS));
+
+ for (FaderMap::iterator i = m_faders.begin(); i != m_faders.end(); ++i) {
+ if (i->first >= SoftSynthInstrumentBase) {
+ FaderRec rec = i->second;
+ rec.setVisible(action->isChecked());
+ }
+ }
+
+ toggleNamedWidgets(action->isChecked(), "synthIdLabel");
+
+ adjustSize();
+}
+
+void
+AudioMixerWindow::slotToggleSubmasters()
+{
+ m_studio->setMixerDisplayOptions(m_studio->getMixerDisplayOptions() ^
+ MIXER_OMIT_SUBMASTERS);
+
+ slotUpdateSubmasterVisibility();
+}
+
+void
+AudioMixerWindow::slotUpdateSubmasterVisibility()
+{
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("show_audio_submasters"));
+ if (!action)
+ return ;
+
+ action->setChecked(!(m_studio->getMixerDisplayOptions() &
+ MIXER_OMIT_SUBMASTERS));
+
+ for (FaderVector::iterator i = m_submasters.begin(); i != m_submasters.end(); ++i) {
+ FaderRec rec = *i;
+ rec.setVisible(action->isChecked());
+ }
+
+ toggleNamedWidgets(action->isChecked(), "subMaster");
+
+ adjustSize();
+}
+
+void
+AudioMixerWindow::slotTogglePluginButtons()
+{
+ m_studio->setMixerDisplayOptions(m_studio->getMixerDisplayOptions() ^
+ MIXER_OMIT_PLUGINS);
+
+ slotUpdatePluginButtonVisibility();
+}
+
+void
+AudioMixerWindow::slotUpdatePluginButtonVisibility()
+{
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("show_plugin_buttons"));
+ if (!action)
+ return ;
+
+ action->setChecked(!(m_studio->getMixerDisplayOptions() &
+ MIXER_OMIT_PLUGINS));
+
+ for (FaderMap::iterator i = m_faders.begin(); i != m_faders.end(); ++i) {
+ FaderRec rec = i->second;
+ rec.setPluginButtonsVisible(action->isChecked());
+ }
+
+ adjustSize();
+}
+
+void
+AudioMixerWindow::slotToggleUnassignedFaders()
+{
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("show_unassigned_faders"));
+ if (!action)
+ return ;
+
+ m_studio->setMixerDisplayOptions(m_studio->getMixerDisplayOptions() ^
+ MIXER_SHOW_UNASSIGNED_FADERS);
+
+ action->setChecked(m_studio->getMixerDisplayOptions() &
+ MIXER_SHOW_UNASSIGNED_FADERS);
+
+ populate();
+}
+
+void
+AudioMixerWindow::toggleNamedWidgets(bool show, const char* const name)
+{
+ QLayoutIterator it = m_mainBox->layout()->iterator();
+ QLayoutItem *child;
+ while ( (child = it.current()) != 0 ) {
+ QWidget * widget = child->widget();
+ if (widget && widget->name() && !strcmp(widget->name(), name)) {
+ if (show)
+ widget->show();
+ else
+ widget->hide();
+ }
+
+ ++it;
+ }
+
+}
+
+}
+#include "AudioMixerWindow.moc"
diff --git a/src/gui/studio/AudioMixerWindow.h b/src/gui/studio/AudioMixerWindow.h
new file mode 100644
index 0000000..99829de
--- /dev/null
+++ b/src/gui/studio/AudioMixerWindow.h
@@ -0,0 +1,191 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_AUDIOMIXERWINDOW_H_
+#define _RG_AUDIOMIXERWINDOW_H_
+
+#include "base/MidiProgram.h"
+#include <map>
+#include "MixerWindow.h"
+#include <qpixmap.h>
+#include <vector>
+
+
+class QWidget;
+class QVBox;
+class QPushButton;
+class QHBox;
+class QFrame;
+
+
+namespace Rosegarden
+{
+
+class SequencerMapper;
+class Rotary;
+class RosegardenGUIDoc;
+class MappedEvent;
+class Fader;
+class AudioVUMeter;
+class AudioRouteMenu;
+
+
+class AudioMixerWindow : public MixerWindow
+{
+ Q_OBJECT
+
+public:
+ AudioMixerWindow(QWidget *parent, RosegardenGUIDoc *document);
+ ~AudioMixerWindow();
+
+ void updateMeters(SequencerMapper *mapper);
+ void updateMonitorMeters(SequencerMapper *mapper);
+
+public slots:
+ void slotControllerDeviceEventReceived(MappedEvent *,
+ const void *);
+
+signals:
+ void selectPlugin(QWidget *, InstrumentId id, int index);
+
+ void play();
+ void stop();
+ void fastForwardPlayback();
+ void rewindPlayback();
+ void fastForwardPlaybackToEnd();
+ void rewindPlaybackToBeginning();
+ void record();
+ void panic();
+
+ // to be redirected to the instrument parameter box if necessary
+ void instrumentParametersChanged(InstrumentId);
+
+protected slots:
+ void slotFaderLevelChanged(float level);
+ void slotPanChanged(float value);
+ void slotInputChanged();
+ void slotOutputChanged();
+ void slotChannelsChanged();
+ void slotSoloChanged();
+ void slotMuteChanged();
+ void slotRecordChanged();
+ void slotSelectPlugin();
+
+ // to be called if something changes in an instrument parameter box
+ void slotUpdateInstrument(InstrumentId);
+
+ void slotTrackAssignmentsChanged();
+
+ // from Plugin dialog
+ void slotPluginSelected(InstrumentId id, int index, int plugin);
+ void slotPluginBypassed(InstrumentId id, int pluginIndex, bool bp);
+
+ void slotSetInputCountFromAction();
+ void slotSetSubmasterCountFromAction();
+
+ void slotToggleFaders();
+ void slotToggleSynthFaders();
+ void slotToggleSubmasters();
+ void slotTogglePluginButtons();
+ void slotToggleUnassignedFaders();
+
+ void slotUpdateFaderVisibility();
+ void slotUpdateSynthFaderVisibility();
+ void slotUpdateSubmasterVisibility();
+ void slotUpdatePluginButtonVisibility();
+
+protected:
+ virtual void sendControllerRefresh();
+
+private:
+
+ void toggleNamedWidgets(bool show, const char* const);
+
+
+ // manage the various bits of it in horizontal/vertical slices
+ // with other faders:
+
+ struct FaderRec {
+
+ FaderRec() :
+ m_populated(false),
+ m_input(0), m_output(0), m_pan(0), m_fader(0), m_meter(0),
+ m_muteButton(0), m_soloButton(0), m_recordButton(0),
+ m_stereoButton(0), m_stereoness(false), m_pluginBox(0)
+ { }
+
+ void setVisible(bool);
+ void setPluginButtonsVisible(bool);
+
+ bool m_populated;
+
+ AudioRouteMenu *m_input;
+ AudioRouteMenu *m_output;
+
+ Rotary *m_pan;
+ Fader *m_fader;
+ AudioVUMeter *m_meter;
+
+ QPushButton *m_muteButton;
+ QPushButton *m_soloButton;
+ QPushButton *m_recordButton;
+ QPushButton *m_stereoButton;
+ bool m_stereoness;
+
+ QVBox *m_pluginBox;
+ std::vector<QPushButton *> m_plugins;
+ };
+
+ QHBox *m_surroundBox;
+ QFrame *m_mainBox;
+
+ typedef std::map<InstrumentId, FaderRec> FaderMap;
+ FaderMap m_faders;
+
+ typedef std::vector<FaderRec> FaderVector;
+ FaderVector m_submasters;
+ FaderRec m_monitor;
+ FaderRec m_master;
+
+ void depopulate();
+ void populate();
+
+ bool isInstrumentAssigned(InstrumentId id);
+
+ void updateFader(int id); // instrument id if large enough, monitor if -1, master/sub otherwise
+ void updateRouteButtons(int id);
+ void updateStereoButton(int id);
+ void updatePluginButtons(int id);
+ void updateMiscButtons(int id);
+
+ QPixmap m_monoPixmap;
+ QPixmap m_stereoPixmap;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/studio/AudioPlugin.cpp b/src/gui/studio/AudioPlugin.cpp
new file mode 100644
index 0000000..2cf3db2
--- /dev/null
+++ b/src/gui/studio/AudioPlugin.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "AudioPlugin.h"
+
+#include "misc/Strings.h"
+#include "base/AudioPluginInstance.h"
+#include <qcolor.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+AudioPlugin::AudioPlugin(const QString &identifier,
+ const QString &name,
+ unsigned long uniqueId,
+ const QString &label,
+ const QString &author,
+ const QString &copyright,
+ bool isSynth,
+ bool isGrouped,
+ const QString &category):
+ m_identifier(identifier),
+ m_name(name),
+ m_uniqueId(uniqueId),
+ m_label(label),
+ m_author(author),
+ m_copyright(copyright),
+ m_isSynth(isSynth),
+ m_isGrouped(isGrouped),
+ m_category(category),
+ m_colour(Qt::darkRed)
+{}
+
+void
+AudioPlugin::addPort(int number,
+ const QString &name,
+ PluginPort::PortType type,
+ PluginPort::PortDisplayHint hint,
+ PortData lowerBound,
+ PortData upperBound,
+ PortData defaultValue)
+{
+ PluginPort *port = new PluginPort(number,
+ qstrtostr(name),
+ type,
+ hint,
+ lowerBound,
+ upperBound,
+ defaultValue);
+ m_ports.push_back(port);
+
+}
+
+}
diff --git a/src/gui/studio/AudioPlugin.h b/src/gui/studio/AudioPlugin.h
new file mode 100644
index 0000000..591a43b
--- /dev/null
+++ b/src/gui/studio/AudioPlugin.h
@@ -0,0 +1,117 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_AUDIOPLUGIN_H_
+#define _RG_AUDIOPLUGIN_H_
+
+#include "base/AudioPluginInstance.h"
+#include <qcolor.h>
+#include <qstring.h>
+#include <vector>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+class AudioPlugin
+{
+public:
+ AudioPlugin(const QString &identifier,
+ const QString &name,
+ unsigned long uniqueId,
+ const QString &label,
+ const QString &author,
+ const QString &copyright,
+ bool isSynth,
+ bool isGrouped,
+ const QString &category);
+
+ QString getIdentifier() const { return m_identifier; }
+
+ QString getName() const { return m_name; }
+ unsigned long getUniqueId() const { return m_uniqueId; }
+ QString getLabel() const { return m_label; }
+ QString getAuthor() const { return m_author; }
+ QString getCopyright() const { return m_copyright; }
+ bool isSynth() const { return m_isSynth; }
+ bool isEffect() const { // true if >0 audio inputs
+ for (unsigned int i = 0; i < m_ports.size(); ++i) {
+ if ((m_ports[i]->getType() & PluginPort::Input) &&
+ (m_ports[i]->getType() & PluginPort::Audio)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ bool isGrouped() const { return m_isGrouped; }
+ QString getCategory() const { return m_category; }
+
+ void addPort(int number,
+ const QString &name,
+ PluginPort::PortType type,
+ PluginPort::PortDisplayHint hint,
+ PortData lowerBound,
+ PortData upperBound,
+ PortData defaultVale);
+
+ typedef std::vector<PluginPort*>::iterator PortIterator;
+
+ PortIterator begin() { return m_ports.begin(); }
+ PortIterator end() { return m_ports.end(); }
+
+ QColor getColour() const { return m_colour; }
+ void setColour(const QColor &colour) { m_colour = colour; }
+
+protected:
+
+ QString m_identifier;
+
+ QString m_name;
+ unsigned long m_uniqueId;
+ QString m_label;
+ QString m_author;
+ QString m_copyright;
+ bool m_isSynth;
+ bool m_isGrouped;
+ QString m_category;
+
+ // our ports and associated hints
+ std::vector<PluginPort*> m_ports;
+
+ // Colour of this activated plugin
+ //
+ QColor m_colour;
+};
+
+typedef std::vector<AudioPlugin*>::iterator PluginIterator;
+
+
+}
+
+#endif
diff --git a/src/gui/studio/AudioPluginClipboard.cpp b/src/gui/studio/AudioPluginClipboard.cpp
new file mode 100644
index 0000000..54f5612
--- /dev/null
+++ b/src/gui/studio/AudioPluginClipboard.cpp
@@ -0,0 +1,32 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "AudioPluginClipboard.h"
+
+
+
+namespace Rosegarden
+{
+}
diff --git a/src/gui/studio/AudioPluginClipboard.h b/src/gui/studio/AudioPluginClipboard.h
new file mode 100644
index 0000000..e31ed90
--- /dev/null
+++ b/src/gui/studio/AudioPluginClipboard.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_AUDIOPLUGINCLIPBOARD_H_
+#define _RG_AUDIOPLUGINCLIPBOARD_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+struct AudioPluginClipboard
+{
+ int m_pluginNumber;
+ std::map<std::string, std::string> m_configuration;
+ std::string m_program;
+ std::vector<float> m_controlValues;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/AudioPluginManager.cpp b/src/gui/studio/AudioPluginManager.cpp
new file mode 100644
index 0000000..6b64085
--- /dev/null
+++ b/src/gui/studio/AudioPluginManager.cpp
@@ -0,0 +1,307 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "AudioPluginManager.h"
+
+#include "misc/Debug.h"
+#include "AudioPluginClipboard.h"
+#include "AudioPlugin.h"
+#include "base/AudioPluginInstance.h"
+#include "gui/application/RosegardenApplication.h"
+#include "sound/PluginFactory.h"
+#include "sound/PluginIdentifier.h"
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <qmutex.h>
+#include <qstring.h>
+#include <qthread.h>
+
+
+namespace Rosegarden
+{
+
+AudioPluginManager::AudioPluginManager() :
+ m_sampleRate(0),
+ m_enumerator(this)
+{
+// std::cerr << "AudioPluginManager[" << this << "]::AudioPluginManager - "
+// << "trace is ";
+// std::cerr << kdBacktrace() << std::endl;
+
+
+ // fetch from sequencer
+ fetchSampleRate();
+
+ // Clear the plugin clipboard
+ //
+ m_pluginClipboard.m_pluginNumber = -1;
+ m_pluginClipboard.m_program = "";
+ m_pluginClipboard.m_controlValues.clear();
+
+ m_enumerator.start();
+}
+
+AudioPluginManager::Enumerator::Enumerator(AudioPluginManager *manager) :
+ m_manager(manager),
+ m_done(false)
+{}
+
+void
+AudioPluginManager::Enumerator::run()
+{
+ QMutexLocker locker(&(m_manager->m_mutex));
+ MappedObjectPropertyList rawPlugins;
+
+ RG_DEBUG << "\n\nAudioPluginManager::Enumerator::run()\n\n" << endl;
+
+ if (!rgapp->noSequencerMode()) {
+ // We only waste the time looking for plugins here if we
+ // know we're actually going to be able to use them.
+ PluginFactory::enumerateAllPlugins(rawPlugins);
+ }
+
+ unsigned int i = 0;
+
+ while (i < rawPlugins.size()) {
+
+ QString identifier = rawPlugins[i++];
+ QString name = rawPlugins[i++];
+ unsigned long uniqueId = rawPlugins[i++].toLong();
+ QString label = rawPlugins[i++];
+ QString author = rawPlugins[i++];
+ QString copyright = rawPlugins[i++];
+ bool isSynth = ((rawPlugins[i++]).lower() == "true");
+ bool isGrouped = ((rawPlugins[i++]).lower() == "true");
+ QString category = rawPlugins[i++];
+ unsigned int portCount = rawPlugins[i++].toInt();
+
+ // std::cerr << "PLUGIN: " << i << ": " << (identifier ? identifier : "(null)") << " unique id " << uniqueId << " / CATEGORY: \"" << (category ? category : "(null)") << "\"" << std::endl;
+
+ AudioPlugin *aP = m_manager->addPlugin(identifier,
+ name,
+ uniqueId,
+ label,
+ author,
+ copyright,
+ isSynth,
+ isGrouped,
+ category);
+
+ for (unsigned int j = 0; j < portCount; j++) {
+
+ int number = rawPlugins[i++].toInt();
+ name = rawPlugins[i++];
+ PluginPort::PortType type =
+ PluginPort::PortType(rawPlugins[i++].toInt());
+ PluginPort::PortDisplayHint hint =
+ PluginPort::PortDisplayHint(rawPlugins[i++].toInt());
+ PortData lowerBound = rawPlugins[i++].toFloat();
+ PortData upperBound = rawPlugins[i++].toFloat();
+ PortData defaultValue = rawPlugins[i++].toFloat();
+
+ aP->addPort(number,
+ name,
+ type,
+ hint,
+ lowerBound,
+ upperBound,
+ defaultValue);
+ }
+ }
+
+ m_done = true;
+
+ RG_DEBUG << "\n\nAudioPluginManager::Enumerator::run() - done\n\n" << endl;
+}
+
+AudioPlugin*
+AudioPluginManager::addPlugin(const QString &identifier,
+ const QString &name,
+ unsigned long uniqueId,
+ const QString &label,
+ const QString &author,
+ const QString &copyright,
+ bool isSynth,
+ bool isGrouped,
+ const QString &category)
+{
+ AudioPlugin *newPlugin = new AudioPlugin(identifier,
+ name,
+ uniqueId,
+ label,
+ author,
+ copyright,
+ isSynth,
+ isGrouped,
+ category);
+ m_plugins.push_back(newPlugin);
+
+ return newPlugin;
+}
+
+bool
+AudioPluginManager::removePlugin(const QString &identifier)
+{
+ std::vector<AudioPlugin*>::iterator it = m_plugins.begin();
+
+ for (; it != m_plugins.end(); ++it) {
+ if ((*it)->getIdentifier() == identifier) {
+ delete *it;
+ m_plugins.erase(it);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+std::vector<QString>
+AudioPluginManager::getPluginNames()
+{
+ awaitEnumeration();
+
+ std::vector<QString> names;
+
+ PluginIterator it = m_plugins.begin();
+
+ for (; it != m_plugins.end(); ++it)
+ names.push_back((*it)->getName());
+
+ return names;
+}
+
+AudioPlugin*
+AudioPluginManager::getPlugin(int number)
+{
+ awaitEnumeration();
+
+ if (number < 0 || number > (int(m_plugins.size()) - 1))
+ return 0;
+
+ return m_plugins[number];
+}
+
+int
+AudioPluginManager::getPositionByIdentifier(QString identifier)
+{
+ awaitEnumeration();
+
+ int pos = 0;
+ PluginIterator it = m_plugins.begin();
+
+ for (; it != m_plugins.end(); ++it) {
+ if ((*it)->getIdentifier() == identifier)
+ return pos;
+
+ pos++;
+ }
+
+ pos = 0;
+ it = m_plugins.begin();
+ for (; it != m_plugins.end(); ++it) {
+ if (PluginIdentifier::areIdentifiersSimilar((*it)->getIdentifier(), identifier))
+ return pos;
+
+ pos++;
+ }
+
+ return -1;
+}
+
+AudioPlugin*
+AudioPluginManager::getPluginByIdentifier(QString identifier)
+{
+ awaitEnumeration();
+
+ PluginIterator it = m_plugins.begin();
+ for (; it != m_plugins.end(); ++it) {
+ if ((*it)->getIdentifier() == identifier)
+ return (*it);
+ }
+
+ it = m_plugins.begin();
+ for (; it != m_plugins.end(); ++it) {
+ if (PluginIdentifier::areIdentifiersSimilar((*it)->getIdentifier(), identifier))
+ return (*it);
+ }
+
+ return 0;
+}
+
+AudioPlugin*
+AudioPluginManager::getPluginByUniqueId(unsigned long uniqueId)
+{
+ awaitEnumeration();
+
+ PluginIterator it = m_plugins.begin();
+ for (; it != m_plugins.end(); ++it) {
+ if ((*it)->getUniqueId() == uniqueId)
+ return (*it);
+ }
+
+ return 0;
+}
+
+PluginIterator
+AudioPluginManager::begin()
+{
+ awaitEnumeration();
+ return m_plugins.begin();
+}
+
+PluginIterator
+AudioPluginManager::end()
+{
+ awaitEnumeration();
+ return m_plugins.end();
+}
+
+void
+AudioPluginManager::awaitEnumeration()
+{
+ while (!m_enumerator.isDone()) {
+ RG_DEBUG << "\n\nAudioPluginManager::awaitEnumeration() - waiting\n\n" << endl;
+// m_mutex.lock();
+ usleep(100000);
+// m_mutex.unlock();
+ }
+}
+
+void
+AudioPluginManager::fetchSampleRate()
+{
+ QCString replyType;
+ QByteArray replyData;
+
+ if (rgapp->sequencerCall("getSampleRate()", replyType, replyData)) {
+
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ unsigned int result;
+ streamIn >> result;
+ m_sampleRate = result;
+ }
+}
+
+}
diff --git a/src/gui/studio/AudioPluginManager.h b/src/gui/studio/AudioPluginManager.h
new file mode 100644
index 0000000..f8574f8
--- /dev/null
+++ b/src/gui/studio/AudioPluginManager.h
@@ -0,0 +1,118 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_AUDIOPLUGINMANAGER_H_
+#define _RG_AUDIOPLUGINMANAGER_H_
+
+#include "AudioPluginClipboard.h"
+#include <qmutex.h>
+#include <qstring.h>
+#include <qthread.h>
+#include <vector>
+#include "AudioPlugin.h"
+
+
+
+
+namespace Rosegarden
+{
+
+class AudioPlugin;
+
+
+class AudioPluginManager
+{
+public:
+ AudioPluginManager();
+
+ // Get a straight list of names
+ //
+ std::vector<QString> getPluginNames();
+
+ // Some useful members
+ //
+ AudioPlugin* getPlugin(int number);
+
+ AudioPlugin* getPluginByIdentifier(QString identifier);
+ int getPositionByIdentifier(QString identifier);
+
+ // Deprecated -- the GUI shouldn't be using unique ID because it's
+ // bound to a particular plugin type (and not necessarily unique
+ // anyway). It should use the identifier instead, which is a
+ // structured string managed by the sequencer. Keep this in only
+ // for compatibility with old .rg files.
+ //
+ AudioPlugin* getPluginByUniqueId(unsigned long uniqueId);
+
+ PluginIterator begin();
+ PluginIterator end();
+
+ // Sample rate
+ //
+ void setSampleRate(unsigned int rate) { m_sampleRate = rate; }
+ unsigned int getSampleRate() const { return m_sampleRate; }
+
+ AudioPluginClipboard* getPluginClipboard() { return &m_pluginClipboard; }
+
+protected:
+ AudioPlugin* addPlugin(const QString &identifier,
+ const QString &name,
+ unsigned long uniqueId,
+ const QString &label,
+ const QString &author,
+ const QString &copyright,
+ bool isSynth,
+ bool isGrouped,
+ const QString &category);
+
+ bool removePlugin(const QString &identifier);
+
+ class Enumerator : public QThread
+ {
+ public:
+ Enumerator(AudioPluginManager *);
+ virtual void run();
+ bool isDone() const { return m_done; }
+
+ protected:
+ AudioPluginManager *m_manager;
+ bool m_done;
+ };
+
+ void awaitEnumeration();
+ void fetchSampleRate();
+
+ std::vector<AudioPlugin*> m_plugins;
+ unsigned int m_sampleRate;
+ AudioPluginClipboard m_pluginClipboard;
+ Enumerator m_enumerator;
+ QMutex m_mutex;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/studio/AudioPluginOSCGUI.cpp b/src/gui/studio/AudioPluginOSCGUI.cpp
new file mode 100644
index 0000000..106cbbe
--- /dev/null
+++ b/src/gui/studio/AudioPluginOSCGUI.cpp
@@ -0,0 +1,234 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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.
+*/
+
+#ifdef HAVE_LIBLO
+
+#include "AudioPluginOSCGUI.h"
+
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/AudioPluginInstance.h"
+#include "base/Exception.h"
+#include "sound/PluginIdentifier.h"
+#include <kprocess.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <qstring.h>
+#include <algorithm>
+
+
+namespace Rosegarden
+{
+
+AudioPluginOSCGUI::AudioPluginOSCGUI(AudioPluginInstance *instance,
+ QString serverURL, QString friendlyName) :
+ m_gui(0),
+ m_address(0),
+ m_basePath(""),
+ m_serverUrl(serverURL)
+{
+ QString identifier = strtoqstr(instance->getIdentifier());
+
+ QString filePath = getGUIFilePath(identifier);
+ if (!filePath) {
+ throw Exception("No GUI found");
+ }
+
+ QString type, soName, label;
+ PluginIdentifier::parseIdentifier(identifier, type, soName, label);
+ QFileInfo soInfo(soName);
+
+ // arguments: osc url, dll name, label, instance tag
+
+ m_gui = new KProcess();
+
+ *m_gui << filePath
+ << m_serverUrl
+ << soInfo.fileName()
+ << label
+ << friendlyName;
+
+ RG_DEBUG << "AudioPluginOSCGUI::AudioPluginOSCGUI: Starting process "
+ << filePath << " " << m_serverUrl << " "
+ << soInfo.fileName() << " " << label << " " << friendlyName << endl;
+
+ if (!m_gui->start(KProcess::NotifyOnExit, KProcess::NoCommunication)) {
+ RG_DEBUG << "AudioPluginOSCGUI::AudioPluginOSCGUI: Couldn't start process " << filePath << endl;
+ delete m_gui;
+ m_gui = 0;
+ throw Exception("Failed to start GUI");
+ }
+}
+
+AudioPluginOSCGUI::~AudioPluginOSCGUI()
+{
+ quit();
+}
+
+QString
+AudioPluginOSCGUI::getGUIFilePath(QString identifier)
+{
+ QString type, soName, label;
+ PluginIdentifier::parseIdentifier(identifier, type, soName, label);
+
+ RG_DEBUG << "AudioPluginOSCGUI::getGUIFilePath(" << identifier << ")" << endl;
+
+ QFileInfo soInfo(soName);
+ if (soInfo.isRelative()) {
+ //!!!
+ RG_DEBUG << "AudioPluginOSCGUI::AudioPluginOSCGUI: Unable to deal with relative .so path \"" << soName << "\" in identifier \"" << identifier << "\" yet" << endl;
+ throw Exception("Can't deal with relative .soname");
+ }
+
+ QDir dir(soInfo.dir());
+ QString fileBase(soInfo.baseName(TRUE));
+
+ if (!dir.cd(fileBase)) {
+ RG_DEBUG << "AudioPluginOSCGUI::AudioPluginOSCGUI: No GUI subdir for plugin .so " << soName << endl;
+ throw Exception("No GUI subdir available");
+ }
+
+ const QFileInfoList *list = dir.entryInfoList();
+
+ // in order of preference:
+ const char *suffixes[] = { "_rg", "_kde", "_qt", "_gtk2", "_gtk", "_x11", "_gui"
+ };
+ int nsuffixes = sizeof(suffixes) / sizeof(suffixes[0]);
+
+ for (int k = 0; k <= nsuffixes; ++k) {
+
+ for (int fuzzy = 0; fuzzy <= 1; ++fuzzy) {
+
+ QFileInfoListIterator i(*list);
+ QFileInfo *info;
+
+ while ((info = i.current()) != 0) {
+
+ RG_DEBUG << "Looking at " << info->fileName() << " in path "
+ << info->filePath() << " for suffix " << (k == nsuffixes ? "(none)" : suffixes[k]) << ", fuzzy " << fuzzy << endl;
+
+ ++i;
+
+ if (!(info->isFile() || info->isSymLink())
+ || !info->isExecutable()) {
+ RG_DEBUG << "(not executable)" << endl;
+ continue;
+ }
+
+ if (fuzzy) {
+ if (info->fileName().left(fileBase.length()) != fileBase)
+ continue;
+ RG_DEBUG << "(is file base)" << endl;
+ } else {
+ if (info->fileName().left(label.length()) != label)
+ continue;
+ RG_DEBUG << "(is label)" << endl;
+ }
+
+ if (k == nsuffixes || info->fileName().lower().endsWith(suffixes[k])) {
+ RG_DEBUG << "(ends with suffix " << (k == nsuffixes ? "(none)" : suffixes[k]) << " or out of suffixes)" << endl;
+ return info->filePath();
+ }
+ RG_DEBUG << "(doesn't end with suffix " << (k == nsuffixes ? "(none)" : suffixes[k]) << ")" << endl;
+ }
+ }
+ }
+
+ return QString();
+}
+
+void
+AudioPluginOSCGUI::setGUIUrl(QString url)
+{
+ if (m_address)
+ lo_address_free(m_address);
+
+ char *host = lo_url_get_hostname(url);
+ char *port = lo_url_get_port(url);
+ m_address = lo_address_new(host, port);
+ free(host);
+ free(port);
+
+ m_basePath = lo_url_get_path(url);
+}
+
+void
+AudioPluginOSCGUI::show()
+{
+ RG_DEBUG << "AudioPluginOSCGUI::show" << endl;
+
+ if (!m_address)
+ return ;
+ QString path = m_basePath + "/show";
+ lo_send(m_address, path, "");
+}
+
+void
+AudioPluginOSCGUI::hide()
+{
+ if (!m_address)
+ return ;
+ QString path = m_basePath + "/hide";
+ lo_send(m_address, path, "");
+}
+
+void
+AudioPluginOSCGUI::quit()
+{
+ if (!m_address)
+ return ;
+ QString path = m_basePath + "/quit";
+ lo_send(m_address, path, "");
+}
+
+void
+AudioPluginOSCGUI::sendProgram(int bank, int program)
+{
+ if (!m_address)
+ return ;
+ QString path = m_basePath + "/program";
+ lo_send(m_address, path, "ii", bank, program);
+}
+
+void
+AudioPluginOSCGUI::sendPortValue(int port, float value)
+{
+ if (!m_address)
+ return ;
+ QString path = m_basePath + "/control";
+ lo_send(m_address, path, "if", port, value);
+}
+
+void
+AudioPluginOSCGUI::sendConfiguration(QString key, QString value)
+{
+ if (!m_address)
+ return ;
+ QString path = m_basePath + "/configure";
+ lo_send(m_address, path, "ss", key.data(), value.data());
+}
+
+}
+
+#endif
diff --git a/src/gui/studio/AudioPluginOSCGUI.h b/src/gui/studio/AudioPluginOSCGUI.h
new file mode 100644
index 0000000..d1982f4
--- /dev/null
+++ b/src/gui/studio/AudioPluginOSCGUI.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_AUDIOPLUGINOSCGUI_H_
+#define _RG_AUDIOPLUGINOSCGUI_H_
+
+#ifdef HAVE_LIBLO
+
+#include <lo/lo.h>
+
+#include <qstring.h>
+
+
+class KProcess;
+
+
+namespace Rosegarden
+{
+
+class AudioPluginInstance;
+
+
+class AudioPluginOSCGUI
+{
+public:
+ AudioPluginOSCGUI(AudioPluginInstance *instance,
+ QString serverURL, QString friendlyName);
+ virtual ~AudioPluginOSCGUI();
+
+ void setGUIUrl(QString url);
+
+ void show();
+ void hide();
+ void quit();
+ void sendProgram(int bank, int program);
+ void sendPortValue(int port, float value);
+ void sendConfiguration(QString key, QString value);
+
+ static QString getGUIFilePath(QString identifier);
+
+protected:
+ KProcess *m_gui;
+ lo_address m_address;
+ QString m_basePath;
+ QString m_serverUrl;
+};
+
+
+
+}
+
+
+#endif
+
+#endif
diff --git a/src/gui/studio/AudioPluginOSCGUIManager.cpp b/src/gui/studio/AudioPluginOSCGUIManager.cpp
new file mode 100644
index 0000000..54c23d7
--- /dev/null
+++ b/src/gui/studio/AudioPluginOSCGUIManager.cpp
@@ -0,0 +1,711 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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.
+*/
+
+#ifdef HAVE_LIBLO
+
+#include <lo/lo.h>
+
+#include "AudioPluginOSCGUIManager.h"
+
+#include "sound/Midi.h"
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "AudioPluginOSCGUI.h"
+#include "base/AudioPluginInstance.h"
+#include "base/Exception.h"
+#include "base/Instrument.h"
+#include "base/MidiProgram.h"
+#include "base/RealTime.h"
+#include "base/Studio.h"
+#include "gui/application/RosegardenGUIApp.h"
+#include "OSCMessage.h"
+#include "sound/MappedEvent.h"
+#include "sound/PluginIdentifier.h"
+#include "StudioControl.h"
+#include "TimerCallbackAssistant.h"
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+static void osc_error(int num, const char *msg, const char *path)
+{
+ std::cerr << "Rosegarden: ERROR: liblo server error " << num
+ << " in path " << path << ": " << msg << std::endl;
+}
+
+static int osc_message_handler(const char *path, const char *types, lo_arg **argv,
+ int argc, lo_message, void *user_data)
+{
+ AudioPluginOSCGUIManager *manager = (AudioPluginOSCGUIManager *)user_data;
+
+ InstrumentId instrument;
+ int position;
+ QString method;
+
+ if (!manager->parseOSCPath(path, instrument, position, method)) {
+ return 1;
+ }
+
+ OSCMessage *message = new OSCMessage();
+ message->setTarget(instrument);
+ message->setTargetData(position);
+ message->setMethod(qstrtostr(method));
+
+ int arg = 0;
+ while (types && arg < argc && types[arg]) {
+ message->addArg(types[arg], argv[arg]);
+ ++arg;
+ }
+
+ manager->postMessage(message);
+ return 0;
+}
+
+AudioPluginOSCGUIManager::AudioPluginOSCGUIManager(RosegardenGUIApp *app) :
+ m_app(app),
+ m_studio(0),
+ m_haveOSCThread(false),
+ m_oscBuffer(1023),
+ m_dispatchTimer(0)
+{}
+
+AudioPluginOSCGUIManager::~AudioPluginOSCGUIManager()
+{
+ delete m_dispatchTimer;
+
+ for (TargetGUIMap::iterator i = m_guis.begin(); i != m_guis.end(); ++i) {
+ for (IntGUIMap::iterator j = i->second.begin(); j != i->second.end();
+ ++j) {
+ delete j->second;
+ }
+ }
+ m_guis.clear();
+
+#ifdef HAVE_LIBLO_THREADSTOP
+
+ if (m_haveOSCThread)
+ lo_server_thread_stop(m_serverThread);
+#endif
+}
+
+void
+AudioPluginOSCGUIManager::checkOSCThread()
+{
+ if (m_haveOSCThread)
+ return ;
+
+ m_serverThread = lo_server_thread_new(NULL, osc_error);
+
+ lo_server_thread_add_method(m_serverThread, NULL, NULL,
+ osc_message_handler, this);
+
+ lo_server_thread_start(m_serverThread);
+
+ RG_DEBUG << "AudioPluginOSCGUIManager: Base OSC URL is "
+ << lo_server_thread_get_url(m_serverThread) << endl;
+
+ m_dispatchTimer = new TimerCallbackAssistant(20, timerCallback, this);
+
+ m_haveOSCThread = true;
+}
+
+bool
+AudioPluginOSCGUIManager::hasGUI(InstrumentId instrument, int position)
+{
+ PluginContainer *container = 0;
+ container = m_studio->getContainerById(instrument);
+ if (!container) return false;
+
+ AudioPluginInstance *pluginInstance = container->getPlugin(position);
+ if (!pluginInstance) return false;
+
+ try {
+ QString filePath = AudioPluginOSCGUI::getGUIFilePath
+ (strtoqstr(pluginInstance->getIdentifier()));
+ return (filePath && filePath != "");
+ } catch (Exception e) { // that's OK
+ return false;
+ }
+}
+
+void
+AudioPluginOSCGUIManager::startGUI(InstrumentId instrument, int position)
+{
+ RG_DEBUG << "AudioPluginOSCGUIManager::startGUI: " << instrument << "," << position
+ << endl;
+
+ checkOSCThread();
+
+ if (m_guis.find(instrument) != m_guis.end() &&
+ m_guis[instrument].find(position) != m_guis[instrument].end()) {
+ RG_DEBUG << "stopping GUI first" << endl;
+ stopGUI(instrument, position);
+ }
+
+ // check the label
+ PluginContainer *container = 0;
+ container = m_studio->getContainerById(instrument);
+ if (!container) {
+ RG_DEBUG << "AudioPluginOSCGUIManager::startGUI: no such instrument or buss as "
+ << instrument << endl;
+ return;
+ }
+
+ AudioPluginInstance *pluginInstance = container->getPlugin(position);
+ if (!pluginInstance) {
+ RG_DEBUG << "AudioPluginOSCGUIManager::startGUI: no plugin at position "
+ << position << " for instrument " << instrument << endl;
+ return ;
+ }
+
+ try {
+ AudioPluginOSCGUI *gui =
+ new AudioPluginOSCGUI(pluginInstance,
+ getOSCUrl(instrument,
+ position,
+ strtoqstr(pluginInstance->getIdentifier())),
+ getFriendlyName(instrument,
+ position,
+ strtoqstr(pluginInstance->getIdentifier())));
+ m_guis[instrument][position] = gui;
+
+ } catch (Exception e) {
+
+ RG_DEBUG << "AudioPluginOSCGUIManager::startGUI: failed to start GUI: "
+ << e.getMessage() << endl;
+ }
+}
+
+void
+AudioPluginOSCGUIManager::showGUI(InstrumentId instrument, int position)
+{
+ RG_DEBUG << "AudioPluginOSCGUIManager::showGUI: " << instrument << "," << position
+ << endl;
+
+ if (m_guis.find(instrument) != m_guis.end() &&
+ m_guis[instrument].find(position) != m_guis[instrument].end()) {
+ m_guis[instrument][position]->show();
+ } else {
+ startGUI(instrument, position);
+ }
+}
+
+void
+AudioPluginOSCGUIManager::stopGUI(InstrumentId instrument, int position)
+{
+ if (m_guis.find(instrument) != m_guis.end() &&
+ m_guis[instrument].find(position) != m_guis[instrument].end()) {
+ delete m_guis[instrument][position];
+ m_guis[instrument].erase(position);
+ if (m_guis[instrument].empty())
+ m_guis.erase(instrument);
+ }
+}
+
+void
+AudioPluginOSCGUIManager::stopAllGUIs()
+{
+ while (!m_guis.empty()) {
+ while (!m_guis.begin()->second.empty()) {
+ delete (m_guis.begin()->second.begin()->second);
+ m_guis.begin()->second.erase(m_guis.begin()->second.begin());
+ }
+ m_guis.erase(m_guis.begin());
+ }
+}
+
+void
+AudioPluginOSCGUIManager::postMessage(OSCMessage *message)
+{
+ RG_DEBUG << "AudioPluginOSCGUIManager::postMessage" << endl;
+ m_oscBuffer.write(&message, 1);
+}
+
+void
+AudioPluginOSCGUIManager::updateProgram(InstrumentId instrument, int position)
+{
+ RG_DEBUG << "AudioPluginOSCGUIManager::updateProgram(" << instrument << ","
+ << position << ")" << endl;
+
+ if (m_guis.find(instrument) == m_guis.end() ||
+ m_guis[instrument].find(position) == m_guis[instrument].end())
+ return ;
+
+ PluginContainer *container = 0;
+ container = m_studio->getContainerById(instrument);
+ if (!container) return;
+
+ AudioPluginInstance *pluginInstance = container->getPlugin(position);
+ if (!pluginInstance) return;
+
+ unsigned long rv = StudioControl::getPluginProgram
+ (pluginInstance->getMappedId(),
+ strtoqstr(pluginInstance->getProgram()));
+
+ int bank = rv >> 16;
+ int program = rv - (bank << 16);
+
+ RG_DEBUG << "AudioPluginOSCGUIManager::updateProgram(" << instrument << ","
+ << position << "): rv " << rv << ", bank " << bank << ", program " << program << endl;
+
+ m_guis[instrument][position]->sendProgram(bank, program);
+}
+
+void
+AudioPluginOSCGUIManager::updatePort(InstrumentId instrument, int position,
+ int port)
+{
+ RG_DEBUG << "AudioPluginOSCGUIManager::updatePort(" << instrument << ","
+ << position << "," << port << ")" << endl;
+
+ if (m_guis.find(instrument) == m_guis.end() ||
+ m_guis[instrument].find(position) == m_guis[instrument].end())
+ return ;
+
+ PluginContainer *container = 0;
+ container = m_studio->getContainerById(instrument);
+ if (!container) return;
+
+ AudioPluginInstance *pluginInstance = container->getPlugin(position);
+ if (!pluginInstance)
+ return ;
+
+ PluginPortInstance *porti = pluginInstance->getPort(port);
+ if (!porti)
+ return ;
+
+ RG_DEBUG << "AudioPluginOSCGUIManager::updatePort(" << instrument << ","
+ << position << "," << port << "): value " << porti->value << endl;
+
+ m_guis[instrument][position]->sendPortValue(port, porti->value);
+}
+
+void
+AudioPluginOSCGUIManager::updateConfiguration(InstrumentId instrument, int position,
+ QString key)
+{
+ RG_DEBUG << "AudioPluginOSCGUIManager::updateConfiguration(" << instrument << ","
+ << position << "," << key << ")" << endl;
+
+ if (m_guis.find(instrument) == m_guis.end() ||
+ m_guis[instrument].find(position) == m_guis[instrument].end())
+ return ;
+
+ PluginContainer *container = m_studio->getContainerById(instrument);
+ if (!container) return;
+
+ AudioPluginInstance *pluginInstance = container->getPlugin(position);
+ if (!pluginInstance) return;
+
+ QString value = strtoqstr(pluginInstance->getConfigurationValue(qstrtostr(key)));
+
+ RG_DEBUG << "AudioPluginOSCGUIManager::updatePort(" << instrument << ","
+ << position << "," << key << "): value " << value << endl;
+
+ m_guis[instrument][position]->sendConfiguration(key, value);
+}
+
+QString
+AudioPluginOSCGUIManager::getOSCUrl(InstrumentId instrument, int position,
+ QString identifier)
+{
+ // OSC URL will be of the form
+ // osc.udp://localhost:54343/plugin/dssi/<instrument>/<position>/<label>
+ // where <position> will be "synth" for synth plugins
+
+ QString type, soName, label;
+ PluginIdentifier::parseIdentifier(identifier, type, soName, label);
+
+ QString baseUrl = lo_server_thread_get_url(m_serverThread);
+ if (!baseUrl.endsWith("/"))
+ baseUrl += '/';
+
+ QString url = QString("%1%2/%3/%4/%5/%6")
+ .arg(baseUrl)
+ .arg("plugin")
+ .arg(type)
+ .arg(instrument);
+
+ if (position == int(Instrument::SYNTH_PLUGIN_POSITION)) {
+ url = url.arg("synth");
+ } else {
+ url = url.arg(position);
+ }
+
+ url = url.arg(label);
+
+ return url;
+}
+
+bool
+AudioPluginOSCGUIManager::parseOSCPath(QString path, InstrumentId &instrument,
+ int &position, QString &method)
+{
+ RG_DEBUG << "AudioPluginOSCGUIManager::parseOSCPath(" << path << ")" << endl;
+ if (!m_studio)
+ return false;
+
+ QString pluginStr("/plugin/");
+
+ if (path.startsWith("//")) {
+ path = path.right(path.length() - 1);
+ }
+
+ if (!path.startsWith(pluginStr)) {
+ RG_DEBUG << "AudioPluginOSCGUIManager::parseOSCPath: malformed path "
+ << path << endl;
+ return false;
+ }
+
+ path = path.right(path.length() - pluginStr.length());
+
+ QString type = path.section('/', 0, 0);
+ QString instrumentStr = path.section('/', 1, 1);
+ QString positionStr = path.section('/', 2, 2);
+ QString label = path.section('/', 3, -2);
+ method = path.section('/', -1, -1);
+
+ if (!instrumentStr || !positionStr) {
+ RG_DEBUG << "AudioPluginOSCGUIManager::parseOSCPath: no instrument or position in " << path << endl;
+ return false;
+ }
+
+ instrument = instrumentStr.toUInt();
+
+ if (positionStr == "synth") {
+ position = Instrument::SYNTH_PLUGIN_POSITION;
+ } else {
+ position = positionStr.toInt();
+ }
+
+ // check the label
+ PluginContainer *container = m_studio->getContainerById(instrument);
+ if (!container) {
+ RG_DEBUG << "AudioPluginOSCGUIManager::parseOSCPath: no such instrument or buss as "
+ << instrument << " in path " << path << endl;
+ return false;
+ }
+
+ AudioPluginInstance *pluginInstance = container->getPlugin(position);
+ if (!pluginInstance) {
+ RG_DEBUG << "AudioPluginOSCGUIManager::parseOSCPath: no plugin at position "
+ << position << " for instrument " << instrument << " in path "
+ << path << endl;
+ return false;
+ }
+
+ QString identifier = strtoqstr(pluginInstance->getIdentifier());
+ QString iType, iSoName, iLabel;
+ PluginIdentifier::parseIdentifier(identifier, iType, iSoName, iLabel);
+ if (iLabel != label) {
+ RG_DEBUG << "AudioPluginOSCGUIManager::parseOSCPath: wrong label for plugin"
+ << " at position " << position << " for instrument " << instrument
+ << " in path " << path << " (actual label is " << iLabel
+ << ")" << endl;
+ return false;
+ }
+
+ RG_DEBUG << "AudioPluginOSCGUIManager::parseOSCPath: good path " << path
+ << ", got mapped id " << pluginInstance->getMappedId() << endl;
+
+ return true;
+}
+
+QString
+AudioPluginOSCGUIManager::getFriendlyName(InstrumentId instrument, int position,
+ QString)
+{
+ PluginContainer *container = m_studio->getContainerById(instrument);
+ if (!container)
+ return i18n("Rosegarden Plugin");
+ else {
+ if (position == int(Instrument::SYNTH_PLUGIN_POSITION)) {
+ return i18n("Rosegarden: %1").arg(strtoqstr(container->getPresentationName()));
+ } else {
+ return i18n("Rosegarden: %1: %2").arg(strtoqstr(container->getPresentationName()))
+ .arg(i18n("Plugin slot %1").arg(position));
+ }
+ }
+}
+
+void
+AudioPluginOSCGUIManager::timerCallback(void *data)
+{
+ AudioPluginOSCGUIManager *manager = (AudioPluginOSCGUIManager *)data;
+ manager->dispatch();
+}
+
+void
+AudioPluginOSCGUIManager::dispatch()
+{
+ if (!m_studio)
+ return ;
+
+ while (m_oscBuffer.getReadSpace() > 0) {
+
+ OSCMessage *message = 0;
+ m_oscBuffer.read(&message, 1);
+
+ int instrument = message->getTarget();
+ int position = message->getTargetData();
+
+ PluginContainer *container = m_studio->getContainerById(instrument);
+ if (!container) continue;
+
+ AudioPluginInstance *pluginInstance = container->getPlugin(position);
+ if (!pluginInstance) continue;
+
+ AudioPluginOSCGUI *gui = 0;
+
+ if (m_guis.find(instrument) == m_guis.end()) {
+ RG_DEBUG << "AudioPluginOSCGUIManager: no GUI for instrument "
+ << instrument << endl;
+ } else if (m_guis[instrument].find(position) == m_guis[instrument].end()) {
+ RG_DEBUG << "AudioPluginOSCGUIManager: no GUI for instrument "
+ << instrument << ", position " << position << endl;
+ } else {
+ gui = m_guis[instrument][position];
+ }
+
+ std::string method = message->getMethod();
+
+ char type;
+ const lo_arg *arg;
+
+ // These generally call back on the RosegardenGUIApp. We'd
+ // like to emit signals, but making AudioPluginOSCGUIManager a
+ // QObject is problematic if it's only conditionally compiled.
+
+ if (method == "control") {
+
+ if (message->getArgCount() != 2) {
+ RG_DEBUG << "AudioPluginOSCGUIManager: wrong number of args ("
+ << message->getArgCount() << ") for control method"
+ << endl;
+ goto done;
+ }
+ if (!(arg = message->getArg(0, type)) || type != 'i') {
+ RG_DEBUG << "AudioPluginOSCGUIManager: failed to get port number"
+ << endl;
+ goto done;
+ }
+ int port = arg->i;
+ if (!(arg = message->getArg(1, type)) || type != 'f') {
+ RG_DEBUG << "AudioPluginOSCGUIManager: failed to get port value"
+ << endl;
+ goto done;
+ }
+ float value = arg->f;
+
+ RG_DEBUG << "AudioPluginOSCGUIManager: setting port " << port
+ << " to value " << value << endl;
+
+ m_app->slotChangePluginPort(instrument, position, port, value);
+
+ } else if (method == "program") {
+
+ if (message->getArgCount() != 2) {
+ RG_DEBUG << "AudioPluginOSCGUIManager: wrong number of args ("
+ << message->getArgCount() << ") for program method"
+ << endl;
+ goto done;
+ }
+ if (!(arg = message->getArg(0, type)) || type != 'i') {
+ RG_DEBUG << "AudioPluginOSCGUIManager: failed to get bank number"
+ << endl;
+ goto done;
+ }
+ int bank = arg->i;
+ if (!(arg = message->getArg(1, type)) || type != 'i') {
+ RG_DEBUG << "AudioPluginOSCGUIManager: failed to get program number"
+ << endl;
+ goto done;
+ }
+ int program = arg->i;
+
+ QString programName = StudioControl::getPluginProgram
+ (pluginInstance->getMappedId(), bank, program);
+
+ m_app->slotChangePluginProgram(instrument, position, programName);
+
+ } else if (method == "update") {
+
+ if (message->getArgCount() != 1) {
+ RG_DEBUG << "AudioPluginOSCGUIManager: wrong number of args ("
+ << message->getArgCount() << ") for update method"
+ << endl;
+ goto done;
+ }
+ if (!(arg = message->getArg(0, type)) || type != 's') {
+ RG_DEBUG << "AudioPluginOSCGUIManager: failed to get GUI URL"
+ << endl;
+ goto done;
+ }
+ QString url = &arg->s;
+
+ if (!gui) {
+ RG_DEBUG << "AudioPluginOSCGUIManager: no GUI for update method"
+ << endl;
+ goto done;
+ }
+
+ gui->setGUIUrl(url);
+
+ for (AudioPluginInstance::ConfigMap::const_iterator i =
+ pluginInstance->getConfiguration().begin();
+ i != pluginInstance->getConfiguration().end(); ++i) {
+
+ QString key = strtoqstr(i->first);
+ QString value = strtoqstr(i->second);
+
+#ifdef DSSI_PROJECT_DIRECTORY_KEY
+
+ if (key == PluginIdentifier::RESERVED_PROJECT_DIRECTORY_KEY) {
+ key = DSSI_PROJECT_DIRECTORY_KEY;
+ }
+#endif
+
+ RG_DEBUG << "update: configuration: " << key << " -> "
+ << value << endl;
+
+ gui->sendConfiguration(key, value);
+ }
+
+ unsigned long rv = StudioControl::getPluginProgram
+ (pluginInstance->getMappedId(), strtoqstr(pluginInstance->getProgram()));
+
+ int bank = rv >> 16;
+ int program = rv - (bank << 16);
+ gui->sendProgram(bank, program);
+
+ int controlCount = 0;
+ for (PortInstanceIterator i = pluginInstance->begin();
+ i != pluginInstance->end(); ++i) {
+ gui->sendPortValue((*i)->number, (*i)->value);
+ /* Avoid overloading the GUI if there are lots and lots of ports */
+ if (++controlCount % 50 == 0)
+ usleep(300000);
+ }
+
+ gui->show();
+
+ } else if (method == "configure") {
+
+ if (message->getArgCount() != 2) {
+ RG_DEBUG << "AudioPluginOSCGUIManager: wrong number of args ("
+ << message->getArgCount() << ") for configure method"
+ << endl;
+ goto done;
+ }
+
+ if (!(arg = message->getArg(0, type)) || type != 's') {
+ RG_DEBUG << "AudioPluginOSCGUIManager: failed to get configure key"
+ << endl;
+ goto done;
+ }
+ QString key = &arg->s;
+
+ if (!(arg = message->getArg(1, type)) || type != 's') {
+ RG_DEBUG << "AudioPluginOSCGUIManager: failed to get configure value"
+ << endl;
+ goto done;
+ }
+ QString value = &arg->s;
+
+#ifdef DSSI_RESERVED_CONFIGURE_PREFIX
+
+ if (key.startsWith(DSSI_RESERVED_CONFIGURE_PREFIX) ||
+ key == PluginIdentifier::RESERVED_PROJECT_DIRECTORY_KEY) {
+ RG_DEBUG << "AudioPluginOSCGUIManager: illegal reserved configure call from gui: " << key << " -> " << value << endl;
+ goto done;
+ }
+#endif
+
+ RG_DEBUG << "AudioPluginOSCGUIManager: configure(" << key << "," << value
+ << ")" << endl;
+
+ m_app->slotChangePluginConfiguration(instrument, position,
+#ifdef DSSI_GLOBAL_CONFIGURE_PREFIX
+ key.startsWith(DSSI_GLOBAL_CONFIGURE_PREFIX),
+#else
+ false,
+#endif
+ key, value);
+
+ } else if (method == "midi") {
+
+ if (message->getArgCount() != 1) {
+ RG_DEBUG << "AudioPluginOSCGUIManager: wrong number of args ("
+ << message->getArgCount() << ") for midi method"
+ << endl;
+ goto done;
+ }
+ if (!(arg = message->getArg(0, type)) || type != 'm') {
+ RG_DEBUG << "AudioPluginOSCGUIManager: failed to get MIDI event"
+ << endl;
+ goto done;
+ }
+
+ RG_DEBUG << "AudioPluginOSCGUIManager: handling MIDI message" << endl;
+
+ // let's only handle note on and note off
+
+ int eventCode = arg->m[1];
+ int eventType = eventCode & MIDI_MESSAGE_TYPE_MASK;
+ if (eventType == MIDI_NOTE_ON ||
+ eventType == MIDI_NOTE_OFF) {
+ MappedEvent ev(instrument,
+ MappedEvent::MidiNote,
+ MidiByte(arg->m[2]),
+ MidiByte(arg->m[3]),
+ RealTime::zeroTime,
+ RealTime::zeroTime,
+ RealTime::zeroTime);
+ if (eventType == MIDI_NOTE_OFF)
+ ev.setVelocity(0);
+ StudioControl::sendMappedEvent(ev);
+ }
+
+ } else if (method == "exiting") {
+
+ RG_DEBUG << "AudioPluginOSCGUIManager: GUI exiting" << endl;
+ stopGUI(instrument, position);
+ m_app->slotPluginGUIExited(instrument, position);
+
+ } else {
+
+ RG_DEBUG << "AudioPluginOSCGUIManager: unknown method " << method << endl;
+ }
+
+done:
+ delete message;
+ }
+}
+
+}
+
+#endif
diff --git a/src/gui/studio/AudioPluginOSCGUIManager.h b/src/gui/studio/AudioPluginOSCGUIManager.h
new file mode 100644
index 0000000..0bef2a2
--- /dev/null
+++ b/src/gui/studio/AudioPluginOSCGUIManager.h
@@ -0,0 +1,104 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_AUDIOPLUGINOSCGUIMANAGER_H_
+#define _RG_AUDIOPLUGINOSCGUIMANAGER_H_
+
+#ifdef HAVE_LIBLO
+
+#include <lo/lo.h>
+#include "base/MidiProgram.h"
+#include <map>
+#include "sound/RingBuffer.h"
+#include <qstring.h>
+
+
+
+namespace Rosegarden
+{
+
+class TimerCallbackAssistant;
+class Studio;
+class RosegardenGUIApp;
+class OSCMessage;
+class AudioPluginOSCGUI;
+
+
+class AudioPluginOSCGUIManager
+{
+public:
+ AudioPluginOSCGUIManager(RosegardenGUIApp *app);
+ virtual ~AudioPluginOSCGUIManager();
+
+ void setStudio(Studio *studio) { m_studio = studio; }
+
+ bool hasGUI(InstrumentId id, int position);
+ void startGUI(InstrumentId id, int position);
+ void showGUI(InstrumentId id, int position);
+ void stopGUI(InstrumentId id, int position);
+ void stopAllGUIs();
+
+ void postMessage(OSCMessage *message); // I take over ownership of message
+ void dispatch();
+
+ void updateProgram(InstrumentId id, int position);
+ void updatePort(InstrumentId id, int position, int port);
+ void updateConfiguration(InstrumentId id, int position,
+ QString key);
+
+ QString getOSCUrl(InstrumentId instrument, int position,
+ QString identifier);
+ QString getFriendlyName(InstrumentId instrument, int position,
+ QString identifier);
+ bool parseOSCPath(QString path, InstrumentId &instrument, int &position,
+ QString &method);
+
+ static void timerCallback(void *data);
+ static void guiExitedCallback(void *data);
+
+protected:
+ RosegardenGUIApp *m_app;
+ Studio *m_studio;
+
+ bool m_haveOSCThread;
+ void checkOSCThread();
+
+ lo_server_thread m_serverThread;
+ RingBuffer<OSCMessage *> m_oscBuffer;
+
+ typedef std::map<int, AudioPluginOSCGUI *> IntGUIMap;
+ typedef std::map<int, IntGUIMap> TargetGUIMap;
+ TargetGUIMap m_guis;
+
+ TimerCallbackAssistant *m_dispatchTimer;
+};
+
+
+
+}
+
+#endif
+
+#endif
diff --git a/src/gui/studio/BankEditorDialog.cpp b/src/gui/studio/BankEditorDialog.cpp
new file mode 100644
index 0000000..20aaf2d
--- /dev/null
+++ b/src/gui/studio/BankEditorDialog.cpp
@@ -0,0 +1,1713 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "BankEditorDialog.h"
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include <klocale.h>
+#include <kstddirs.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/Device.h"
+#include "base/MidiDevice.h"
+#include "base/MidiProgram.h"
+#include "base/NotationTypes.h"
+#include "base/Studio.h"
+#include "commands/studio/ModifyDeviceCommand.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "gui/dialogs/ExportDeviceDialog.h"
+#include "gui/dialogs/ImportDeviceDialog.h"
+#include "MidiBankListViewItem.h"
+#include "MidiDeviceListViewItem.h"
+#include "MidiKeyMapListViewItem.h"
+#include "MidiKeyMappingEditor.h"
+#include "MidiProgramsEditor.h"
+#include <kaction.h>
+#include <kcombobox.h>
+#include <kcommand.h>
+#include <kfiledialog.h>
+#include <kglobal.h>
+#include <klistview.h>
+#include <kmainwindow.h>
+#include <kmessagebox.h>
+#include <kstdaccel.h>
+#include <kstdaction.h>
+#include <kxmlguiclient.h>
+#include <qcheckbox.h>
+#include <qdialog.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qpushbutton.h>
+#include <qsizepolicy.h>
+#include <qsplitter.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qvgroupbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+BankEditorDialog::BankEditorDialog(QWidget *parent,
+ RosegardenGUIDoc *doc,
+ DeviceId defaultDevice):
+ KMainWindow(parent, "bankeditordialog"),
+ m_studio(&doc->getStudio()),
+ m_doc(doc),
+ m_copyBank(Device::NO_DEVICE, -1),
+ m_modified(false),
+ m_keepBankList(false),
+ m_deleteAllReally(false),
+ m_lastDevice(Device::NO_DEVICE),
+ m_updateDeviceList(false)
+{
+ QVBox* mainFrame = new QVBox(this);
+ setCentralWidget(mainFrame);
+
+ setCaption(i18n("Manage MIDI Banks and Programs"));
+
+ QSplitter* splitter = new QSplitter(mainFrame);
+
+ QFrame* btnBox = new QFrame(mainFrame);
+
+ btnBox->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
+
+ QHBoxLayout* layout = new QHBoxLayout(btnBox, 4, 10);
+
+ m_closeButton = new QPushButton(btnBox);
+ m_applyButton = new QPushButton(i18n("Apply"), btnBox);
+ m_resetButton = new QPushButton(i18n("Reset"), btnBox);
+
+ layout->addStretch(10);
+ layout->addWidget(m_applyButton);
+ layout->addWidget(m_resetButton);
+ layout->addSpacing(15);
+ layout->addWidget(m_closeButton);
+ layout->addSpacing(5);
+
+ connect(m_applyButton, SIGNAL(clicked()),
+ this, SLOT(slotApply()));
+ connect(m_resetButton, SIGNAL(clicked()),
+ this, SLOT(slotReset()));
+
+ //
+ // Left-side list view
+ //
+ QVBox* leftPart = new QVBox(splitter);
+ m_listView = new KListView(leftPart);
+ m_listView->addColumn(i18n("MIDI Device"));
+ m_listView->addColumn(i18n("Type"));
+ m_listView->addColumn(i18n("MSB"));
+ m_listView->addColumn(i18n("LSB"));
+ m_listView->setRootIsDecorated(true);
+ m_listView->setShowSortIndicator(true);
+ m_listView->setItemsRenameable(true);
+ m_listView->restoreLayout(kapp->config(), BankEditorConfigGroup);
+
+ QFrame *bankBox = new QFrame(leftPart);
+ QGridLayout *gridLayout = new QGridLayout(bankBox, 4, 2, 6, 6);
+
+ m_addBank = new QPushButton(i18n("Add Bank"), bankBox);
+ m_addKeyMapping = new QPushButton(i18n("Add Key Mapping"), bankBox);
+ m_delete = new QPushButton(i18n("Delete"), bankBox);
+ m_deleteAll = new QPushButton(i18n("Delete All"), bankBox);
+ gridLayout->addWidget(m_addBank, 0, 0);
+ gridLayout->addWidget(m_addKeyMapping, 0, 1);
+ gridLayout->addWidget(m_delete, 1, 0);
+ gridLayout->addWidget(m_deleteAll, 1, 1);
+
+ // Tips
+ //
+ QToolTip::add
+ (m_addBank,
+ i18n("Add a Bank to the current device"));
+
+ QToolTip::add
+ (m_addKeyMapping,
+ i18n("Add a Percussion Key Mapping to the current device"));
+
+ QToolTip::add
+ (m_delete,
+ i18n("Delete the current Bank or Key Mapping"));
+
+ QToolTip::add
+ (m_deleteAll,
+ i18n("Delete all Banks and Key Mappings from the current Device"));
+
+ m_importBanks = new QPushButton(i18n("Import..."), bankBox);
+ m_exportBanks = new QPushButton(i18n("Export..."), bankBox);
+ gridLayout->addWidget(m_importBanks, 2, 0);
+ gridLayout->addWidget(m_exportBanks, 2, 1);
+
+ // Tips
+ //
+ QToolTip::add
+ (m_importBanks,
+ i18n("Import Bank and Program data from a Rosegarden file to the current Device"));
+ QToolTip::add
+ (m_exportBanks,
+ i18n("Export all Device and Bank information to a Rosegarden format interchange file"));
+
+ m_copyPrograms = new QPushButton(i18n("Copy"), bankBox);
+ m_pastePrograms = new QPushButton(i18n("Paste"), bankBox);
+ gridLayout->addWidget(m_copyPrograms, 3, 0);
+ gridLayout->addWidget(m_pastePrograms, 3, 1);
+
+ // Tips
+ //
+ QToolTip::add
+ (m_copyPrograms,
+ i18n("Copy all Program names from current Bank to clipboard"));
+
+ QToolTip::add
+ (m_pastePrograms,
+ i18n("Paste Program names from clipboard to current Bank"));
+
+ connect(m_listView, SIGNAL(currentChanged(QListViewItem*)),
+ this, SLOT(slotPopulateDevice(QListViewItem*)));
+
+ QFrame *vbox = new QFrame(splitter);
+ QVBoxLayout *vboxLayout = new QVBoxLayout(vbox, 8, 6);
+
+ m_programEditor = new MidiProgramsEditor(this, vbox);
+ vboxLayout->addWidget(m_programEditor);
+
+ m_keyMappingEditor = new MidiKeyMappingEditor(this, vbox);
+ vboxLayout->addWidget(m_keyMappingEditor);
+ m_keyMappingEditor->hide();
+
+ m_programEditor->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred));
+ m_keyMappingEditor->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred));
+
+ m_optionBox = new QVGroupBox(i18n("Options"), vbox);
+ vboxLayout->addWidget(m_optionBox);
+
+ QHBox *variationBox = new QHBox(m_optionBox);
+ m_variationToggle = new QCheckBox(i18n("Show Variation list based on "), variationBox);
+ m_variationCombo = new KComboBox(variationBox);
+ m_variationCombo->insertItem(i18n("LSB"));
+ m_variationCombo->insertItem(i18n("MSB"));
+
+ // device/bank modification
+ connect(m_listView, SIGNAL(itemRenamed (QListViewItem*, const QString&, int)),
+ this, SLOT(slotModifyDeviceOrBankName(QListViewItem*, const QString&, int)));
+
+ connect(m_addBank, SIGNAL(clicked()),
+ this, SLOT(slotAddBank()));
+
+ connect(m_addKeyMapping, SIGNAL(clicked()),
+ this, SLOT(slotAddKeyMapping()));
+
+ connect(m_delete, SIGNAL(clicked()),
+ this, SLOT(slotDelete()));
+
+ connect(m_deleteAll, SIGNAL(clicked()),
+ this, SLOT(slotDeleteAll()));
+
+ connect(m_importBanks, SIGNAL(clicked()),
+ this, SLOT(slotImport()));
+
+ connect(m_exportBanks, SIGNAL(clicked()),
+ this, SLOT(slotExport()));
+
+ connect(m_copyPrograms, SIGNAL(clicked()),
+ this, SLOT(slotEditCopy()));
+
+ connect(m_pastePrograms, SIGNAL(clicked()),
+ this, SLOT(slotEditPaste()));
+
+ connect(m_variationToggle, SIGNAL(clicked()),
+ this, SLOT(slotVariationToggled()));
+
+ connect(m_variationCombo, SIGNAL(activated(int)),
+ this, SLOT(slotVariationChanged(int)));
+
+ setupActions();
+
+ m_doc->getCommandHistory()->attachView(actionCollection());
+ connect(m_doc->getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(slotUpdate()));
+
+ // Initialise the dialog
+ //
+ initDialog();
+ setModified(false);
+
+ // Check for no Midi devices and disable everything
+ //
+ DeviceList *devices = m_studio->getDevices();
+ DeviceListIterator it;
+ bool haveMidiPlayDevice = false;
+ for (it = devices->begin(); it != devices->end(); ++it) {
+ MidiDevice *md =
+ dynamic_cast<MidiDevice *>(*it);
+ if (md && md->getDirection() == MidiDevice::Play) {
+ haveMidiPlayDevice = true;
+ break;
+ }
+ }
+ if (!haveMidiPlayDevice) {
+ leftPart->setDisabled(true);
+ m_programEditor->setDisabled(true);
+ m_keyMappingEditor->setDisabled(true);
+ m_optionBox->setDisabled(true);
+ }
+
+ if (defaultDevice != Device::NO_DEVICE) {
+ setCurrentDevice(defaultDevice);
+ }
+
+ setAutoSaveSettings(BankEditorConfigGroup, true);
+}
+
+BankEditorDialog::~BankEditorDialog()
+{
+ RG_DEBUG << "~BankEditorDialog()\n";
+
+ m_listView->saveLayout(kapp->config(), BankEditorConfigGroup);
+
+ if (m_doc) // see slotFileClose() for an explanation on why we need to test m_doc
+ m_doc->getCommandHistory()->detachView(actionCollection());
+}
+
+void
+BankEditorDialog::setupActions()
+{
+ KAction* close = KStdAction::close (this, SLOT(slotFileClose()), actionCollection());
+
+ m_closeButton->setText(close->text());
+ connect(m_closeButton, SIGNAL(clicked()),
+ this, SLOT(slotFileClose()));
+
+ KStdAction::copy (this, SLOT(slotEditCopy()), actionCollection());
+ KStdAction::paste (this, SLOT(slotEditPaste()), actionCollection());
+
+ // 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("bankeditor.rc");
+}
+
+void
+BankEditorDialog::initDialog()
+{
+ // Clear down
+ //
+ m_deviceNameMap.clear();
+ m_listView->clear();
+
+ // Fill list view
+ //
+ DeviceList *devices = m_studio->getDevices();
+ DeviceListIterator it;
+
+ for (it = devices->begin(); it != devices->end(); ++it) {
+ if ((*it)->getType() == Device::Midi) {
+ MidiDevice* midiDevice =
+ dynamic_cast<MidiDevice*>(*it);
+ if (!midiDevice)
+ continue;
+
+ // skip read-only devices
+ if (midiDevice->getDirection() == MidiDevice::Record)
+ continue;
+
+ m_deviceNameMap[midiDevice->getId()] = midiDevice->getName();
+ QString itemName = strtoqstr(midiDevice->getName());
+
+ RG_DEBUG << "BankEditorDialog::initDialog - adding "
+ << itemName << endl;
+
+ QListViewItem* deviceItem = new MidiDeviceListViewItem
+ (midiDevice->getId(), m_listView, itemName);
+ deviceItem->setOpen(true);
+
+ populateDeviceItem(deviceItem, midiDevice);
+ }
+ }
+
+ // Select the first Device
+ //
+ populateDevice(m_listView->firstChild());
+ m_listView->setSelected(m_listView->firstChild(), true);
+
+}
+
+void
+BankEditorDialog::updateDialog()
+{
+ // Update list view
+ //
+ DeviceList *devices = m_studio->getDevices();
+ DeviceListIterator it;
+ bool deviceLabelUpdate = false;
+
+ for (it = devices->begin(); it != devices->end(); ++it) {
+
+ if ((*it)->getType() != Device::Midi)
+ continue;
+
+ MidiDevice* midiDevice =
+ dynamic_cast<MidiDevice*>(*it);
+ if (!midiDevice)
+ continue;
+
+ // skip read-only devices
+ if (midiDevice->getDirection() == MidiDevice::Record)
+ continue;
+
+ if (m_deviceNameMap.find(midiDevice->getId()) != m_deviceNameMap.end()) {
+ // Device already displayed but make sure the label is up to date
+ //
+ QListViewItem* currentItem = m_listView->currentItem();
+
+ if (currentItem) {
+ MidiDeviceListViewItem* deviceItem =
+ getParentDeviceItem(currentItem);
+
+ if (deviceItem &&
+ deviceItem->getDeviceId() == midiDevice->getId()) {
+ if (deviceItem->text(0) != strtoqstr(midiDevice->getName())) {
+ deviceItem->setText(0,
+ strtoqstr(midiDevice->getName()));
+ m_deviceNameMap[midiDevice->getId()] =
+ midiDevice->getName();
+
+ /*
+ cout << "NEW TEXT FOR DEVICE " << midiDevice->getId()
+ << " IS " << midiDevice->getName() << endl;
+ cout << "LIST ITEM ID = "
+ << deviceItem->getDeviceId() << endl;
+ */
+
+ deviceLabelUpdate = true;
+ }
+
+ QListViewItem *child = deviceItem->firstChild();
+
+ while (child) {
+
+ MidiBankListViewItem *bankItem =
+ dynamic_cast<MidiBankListViewItem *>(child);
+
+ if (bankItem) {
+ bool percussion = bankItem->isPercussion();
+ int msb = bankItem->text(2).toInt();
+ int lsb = bankItem->text(3).toInt();
+ std::string bankName =
+ midiDevice->getBankName
+ (MidiBank(percussion, msb, lsb));
+ if (bankName != "" &&
+ bankItem->text(0) != strtoqstr(bankName)) {
+ bankItem->setText(0, strtoqstr(bankName));
+ }
+ }
+
+ child = child->nextSibling();
+ }
+ }
+ }
+
+ continue;
+ }
+
+ m_deviceNameMap[midiDevice->getId()] = midiDevice->getName();
+ QString itemName = strtoqstr(midiDevice->getName());
+
+ RG_DEBUG << "BankEditorDialog::updateDialog - adding "
+ << itemName << endl;
+
+ QListViewItem* deviceItem = new MidiDeviceListViewItem
+ (midiDevice->getId(), m_listView, itemName);
+ deviceItem->setOpen(true);
+
+ populateDeviceItem(deviceItem, midiDevice);
+ }
+
+ // delete items whose corresponding devices are no longer present,
+ // and update the other ones
+ //
+ std::vector<MidiDeviceListViewItem*> itemsToDelete;
+
+ MidiDeviceListViewItem* sibling = dynamic_cast<MidiDeviceListViewItem*>
+ (m_listView->firstChild());
+
+ while (sibling) {
+
+ if (m_deviceNameMap.find(sibling->getDeviceId()) == m_deviceNameMap.end())
+ itemsToDelete.push_back(sibling);
+ else
+ updateDeviceItem(sibling);
+
+ sibling = dynamic_cast<MidiDeviceListViewItem*>(sibling->nextSibling());
+ }
+
+ for (unsigned int i = 0; i < itemsToDelete.size(); ++i)
+ delete itemsToDelete[i];
+
+ m_listView->sort();
+
+ if (deviceLabelUpdate)
+ emit deviceNamesChanged();
+}
+
+void
+BankEditorDialog::setCurrentDevice(DeviceId device)
+{
+ for (QListViewItem *item = m_listView->firstChild(); item;
+ item = item->nextSibling()) {
+ MidiDeviceListViewItem * deviceItem =
+ dynamic_cast<MidiDeviceListViewItem *>(item);
+ if (deviceItem && deviceItem->getDeviceId() == device) {
+ m_listView->setSelected(item, true);
+ break;
+ }
+ }
+}
+
+void
+BankEditorDialog::populateDeviceItem(QListViewItem* deviceItem, MidiDevice* midiDevice)
+{
+ clearItemChildren(deviceItem);
+
+ QString itemName = strtoqstr(midiDevice->getName());
+
+ BankList banks = midiDevice->getBanks();
+ // add banks for this device
+ for (unsigned int i = 0; i < banks.size(); ++i) {
+ RG_DEBUG << "BankEditorDialog::populateDeviceItem - adding "
+ << itemName << " - " << strtoqstr(banks[i].getName())
+ << endl;
+ new MidiBankListViewItem(midiDevice->getId(), i, deviceItem,
+ strtoqstr(banks[i].getName()),
+ banks[i].isPercussion(),
+ banks[i].getMSB(), banks[i].getLSB());
+ }
+
+ const KeyMappingList &mappings = midiDevice->getKeyMappings();
+ for (unsigned int i = 0; i < mappings.size(); ++i) {
+ RG_DEBUG << "BankEditorDialog::populateDeviceItem - adding key mapping "
+ << itemName << " - " << strtoqstr(mappings[i].getName())
+ << endl;
+ new MidiKeyMapListViewItem(midiDevice->getId(), deviceItem,
+ strtoqstr(mappings[i].getName()));
+ }
+}
+
+void
+BankEditorDialog::updateDeviceItem(MidiDeviceListViewItem* deviceItem)
+{
+ MidiDevice* midiDevice = getMidiDevice(deviceItem->getDeviceId());
+ if (!midiDevice) {
+ RG_DEBUG << "BankEditorDialog::updateDeviceItem : WARNING no midi device for this item\n";
+ return ;
+ }
+
+ QString itemName = strtoqstr(midiDevice->getName());
+
+ BankList banks = midiDevice->getBanks();
+ KeyMappingList keymaps = midiDevice->getKeyMappings();
+
+ // add missing banks for this device
+ //
+ for (unsigned int i = 0; i < banks.size(); ++i) {
+ if (deviceItemHasBank(deviceItem, i))
+ continue;
+
+ RG_DEBUG << "BankEditorDialog::updateDeviceItem - adding "
+ << itemName << " - " << strtoqstr(banks[i].getName())
+ << endl;
+ new MidiBankListViewItem(midiDevice->getId(), i, deviceItem,
+ strtoqstr(banks[i].getName()),
+ banks[i].isPercussion(),
+ banks[i].getMSB(), banks[i].getLSB());
+ }
+
+ for (unsigned int i = 0; i < keymaps.size(); ++i) {
+
+ QListViewItem *child = deviceItem->firstChild();
+ bool have = false;
+
+ while (child) {
+ MidiKeyMapListViewItem *keyItem =
+ dynamic_cast<MidiKeyMapListViewItem*>(child);
+ if (keyItem) {
+ if (keyItem->getName() == strtoqstr(keymaps[i].getName())) {
+ have = true;
+ }
+ }
+ child = child->nextSibling();
+ }
+
+ if (have)
+ continue;
+
+ RG_DEBUG << "BankEditorDialog::updateDeviceItem - adding "
+ << itemName << " - " << strtoqstr(keymaps[i].getName())
+ << endl;
+ new MidiKeyMapListViewItem(midiDevice->getId(), deviceItem,
+ strtoqstr(keymaps[i].getName()));
+ }
+
+ // delete banks which are no longer present
+ //
+ std::vector<QListViewItem*> childrenToDelete;
+
+ QListViewItem* child = deviceItem->firstChild();
+
+ while (child) {
+
+ MidiBankListViewItem *bankItem =
+ dynamic_cast<MidiBankListViewItem *>(child);
+ if (bankItem) {
+ if (bankItem->getBank() >= int(banks.size()))
+ childrenToDelete.push_back(child);
+ else { // update the banks MSB/LSB which might have changed
+ bankItem->setPercussion(banks[bankItem->getBank()].isPercussion());
+ bankItem->setMSB(banks[bankItem->getBank()].getMSB());
+ bankItem->setLSB(banks[bankItem->getBank()].getLSB());
+ }
+ }
+
+ MidiKeyMapListViewItem *keyItem =
+ dynamic_cast<MidiKeyMapListViewItem *>(child);
+ if (keyItem) {
+ if (!midiDevice->getKeyMappingByName(qstrtostr(keyItem->getName()))) {
+ childrenToDelete.push_back(child);
+ }
+ }
+
+ child = child->nextSibling();
+ }
+
+ for (unsigned int i = 0; i < childrenToDelete.size(); ++i)
+ delete childrenToDelete[i];
+}
+
+bool
+BankEditorDialog::deviceItemHasBank(MidiDeviceListViewItem* deviceItem, int bankNb)
+{
+ QListViewItem *child = deviceItem->firstChild();
+
+ while (child) {
+ MidiBankListViewItem *bankItem =
+ dynamic_cast<MidiBankListViewItem*>(child);
+ if (bankItem) {
+ if (bankItem->getBank() == bankNb)
+ return true;
+ }
+ child = child->nextSibling();
+ }
+
+ return false;
+}
+
+void
+BankEditorDialog::clearItemChildren(QListViewItem* item)
+{
+ QListViewItem* child = 0;
+
+ while ((child = item->firstChild()))
+ delete child;
+}
+
+MidiDevice*
+BankEditorDialog::getCurrentMidiDevice()
+{
+ return getMidiDevice(m_listView->currentItem());
+}
+
+void
+BankEditorDialog::checkModified()
+{
+ if (!m_modified)
+ return ;
+
+ setModified(false);
+
+ // // then ask if we want to apply the changes
+
+ // int reply = KMessageBox::questionYesNo(this,
+ // i18n("Apply pending changes?"));
+
+ ModifyDeviceCommand *command = 0;
+ MidiDevice *device = getMidiDevice(m_lastDevice);
+ if (!device) {
+ RG_DEBUG << "%%% WARNING : BankEditorDialog::checkModified() - NO MIDI DEVICE for device "
+ << m_lastDevice << endl;
+ return ;
+ }
+
+ if (m_bankList.size() == 0 && m_programList.size() == 0) {
+
+ command = new ModifyDeviceCommand(m_studio,
+ m_lastDevice,
+ m_deviceNameMap[m_lastDevice],
+ device->getLibrarianName(),
+ device->getLibrarianEmail()); // rename
+
+ command->clearBankAndProgramList();
+
+ } else {
+
+ MidiDevice::VariationType variation =
+ MidiDevice::NoVariations;
+ if (m_variationToggle->isChecked()) {
+ if (m_variationCombo->currentItem() == 0) {
+ variation = MidiDevice::VariationFromLSB;
+ } else {
+ variation = MidiDevice::VariationFromMSB;
+ }
+ }
+
+ command = new ModifyDeviceCommand(m_studio,
+ m_lastDevice,
+ m_deviceNameMap[m_lastDevice],
+ device->getLibrarianName(),
+ device->getLibrarianEmail());
+
+ command->setVariation(variation);
+ command->setBankList(m_bankList);
+ command->setProgramList(m_programList);
+ }
+
+ addCommandToHistory(command);
+
+ setModified(false);
+}
+
+void
+BankEditorDialog::slotPopulateDevice(QListViewItem* item)
+{
+ RG_DEBUG << "BankEditorDialog::slotPopulateDevice" << endl;
+
+ if (!item)
+ return ;
+
+ checkModified();
+
+ populateDevice(item);
+}
+
+void
+BankEditorDialog::populateDevice(QListViewItem* item)
+{
+ RG_DEBUG << "BankEditorDialog::populateDevice\n";
+
+ if (!item)
+ return ;
+
+ MidiKeyMapListViewItem *keyItem = dynamic_cast<MidiKeyMapListViewItem *>(item);
+
+ if (keyItem) {
+
+ stateChanged("on_key_item");
+ stateChanged("on_bank_item", KXMLGUIClient::StateReverse);
+
+ m_delete->setEnabled(true);
+
+ MidiDevice *device = getMidiDevice(keyItem->getDeviceId());
+ if (!device)
+ return ;
+
+ setProgramList(device);
+
+ m_keyMappingEditor->populate(item);
+
+ m_programEditor->hide();
+ m_keyMappingEditor->show();
+
+ m_lastDevice = keyItem->getDeviceId();
+
+ return ;
+ }
+
+ MidiBankListViewItem* bankItem = dynamic_cast<MidiBankListViewItem*>(item);
+
+ if (bankItem) {
+
+ stateChanged("on_bank_item");
+ stateChanged("on_key_item", KXMLGUIClient::StateReverse);
+
+ m_delete->setEnabled(true);
+ m_copyPrograms->setEnabled(true);
+
+ if (m_copyBank.first != Device::NO_DEVICE)
+ m_pastePrograms->setEnabled(true);
+
+ MidiDevice *device = getMidiDevice(bankItem->getDeviceId());
+ if (!device)
+ return ;
+
+ if (!m_keepBankList || m_bankList.size() == 0)
+ m_bankList = device->getBanks();
+ else
+ m_keepBankList = false;
+
+ setProgramList(device);
+
+ m_variationToggle->setChecked(device->getVariationType() !=
+ MidiDevice::NoVariations);
+ m_variationCombo->setEnabled(m_variationToggle->isChecked());
+ m_variationCombo->setCurrentItem
+ (device->getVariationType() ==
+ MidiDevice::VariationFromLSB ? 0 : 1);
+
+ m_lastBank = m_bankList[bankItem->getBank()];
+
+ m_programEditor->populate(item);
+
+ m_keyMappingEditor->hide();
+ m_programEditor->show();
+
+ m_lastDevice = bankItem->getDeviceId();
+
+ return ;
+ }
+
+ // Device, not bank or key mapping
+ // Ensure we fill these lists for the new device
+ //
+ MidiDeviceListViewItem* deviceItem = getParentDeviceItem(item);
+
+ m_lastDevice = deviceItem->getDeviceId();
+
+ MidiDevice *device = getMidiDevice(deviceItem);
+ if (!device) {
+ RG_DEBUG << "BankEditorDialog::populateDevice - no device for this item\n";
+ return ;
+ }
+
+ m_bankList = device->getBanks();
+ setProgramList(device);
+
+ RG_DEBUG << "BankEditorDialog::populateDevice : not a bank item - disabling" << endl;
+ m_delete->setEnabled(false);
+ m_copyPrograms->setEnabled(false);
+ m_pastePrograms->setEnabled(false);
+
+ m_variationToggle->setChecked(device->getVariationType() !=
+ MidiDevice::NoVariations);
+ m_variationCombo->setEnabled(m_variationToggle->isChecked());
+ m_variationCombo->setCurrentItem
+ (device->getVariationType() ==
+ MidiDevice::VariationFromLSB ? 0 : 1);
+
+ stateChanged("on_bank_item", KXMLGUIClient::StateReverse);
+ stateChanged("on_key_item", KXMLGUIClient::StateReverse);
+ m_programEditor->clearAll();
+ m_keyMappingEditor->clearAll();
+}
+
+void
+BankEditorDialog::slotApply()
+{
+ RG_DEBUG << "BankEditorDialog::slotApply()\n";
+
+ ModifyDeviceCommand *command = 0;
+
+ MidiDevice *device = getMidiDevice(m_lastDevice);
+
+ // Make sure that we don't delete all the banks and programs
+ // if we've not populated them here yet.
+ //
+ if (m_bankList.size() == 0 && m_programList.size() == 0 &&
+ m_deleteAllReally == false) {
+ RG_DEBUG << "BankEditorDialog::slotApply() : m_bankList size = 0\n";
+
+ command = new ModifyDeviceCommand(m_studio,
+ m_lastDevice,
+ m_deviceNameMap[m_lastDevice],
+ device->getLibrarianName(),
+ device->getLibrarianEmail());
+
+ command->clearBankAndProgramList();
+ } else {
+ MidiDevice::VariationType variation =
+ MidiDevice::NoVariations;
+ if (m_variationToggle->isChecked()) {
+ if (m_variationCombo->currentItem() == 0) {
+ variation = MidiDevice::VariationFromLSB;
+ } else {
+ variation = MidiDevice::VariationFromMSB;
+ }
+ }
+
+ RG_DEBUG << "BankEditorDialog::slotApply() : m_bankList size = "
+ << m_bankList.size() << endl;
+
+ command = new ModifyDeviceCommand(m_studio,
+ m_lastDevice,
+ m_deviceNameMap[m_lastDevice],
+ device->getLibrarianName(),
+ device->getLibrarianEmail());
+
+ MidiKeyMapListViewItem *keyItem = dynamic_cast<MidiKeyMapListViewItem*>
+ (m_listView->currentItem());
+ if (keyItem) {
+ KeyMappingList kml(device->getKeyMappings());
+ for (int i = 0; i < kml.size(); ++i) {
+ if (kml[i].getName() == qstrtostr(keyItem->getName())) {
+ kml[i] = m_keyMappingEditor->getMapping();
+ break;
+ }
+ }
+ command->setKeyMappingList(kml);
+ }
+
+ command->setVariation(variation);
+ command->setBankList(m_bankList);
+ command->setProgramList(m_programList);
+ }
+
+ addCommandToHistory(command);
+
+ // Our freaky fudge to update instrument/device names externally
+ //
+ if (m_updateDeviceList) {
+ emit deviceNamesChanged();
+ m_updateDeviceList = false;
+ }
+
+ setModified(false);
+}
+
+void
+BankEditorDialog::slotReset()
+{
+ resetProgramList();
+
+ m_programEditor->reset();
+ m_programEditor->populate(m_listView->currentItem());
+ m_keyMappingEditor->reset();
+ m_keyMappingEditor->populate(m_listView->currentItem());
+
+ MidiDeviceListViewItem* deviceItem = getParentDeviceItem
+ (m_listView->currentItem());
+
+ if (deviceItem) {
+ MidiDevice *device = getMidiDevice(deviceItem);
+ m_variationToggle->setChecked(device->getVariationType() !=
+ MidiDevice::NoVariations);
+ m_variationCombo->setEnabled(m_variationToggle->isChecked());
+ m_variationCombo->setCurrentItem
+ (device->getVariationType() ==
+ MidiDevice::VariationFromLSB ? 0 : 1);
+ }
+
+ updateDialog();
+
+ setModified(false);
+}
+
+void
+BankEditorDialog::resetProgramList()
+{
+ m_programList = m_oldProgramList;
+}
+
+void
+BankEditorDialog::setProgramList(MidiDevice *device)
+{
+ m_programList = device->getPrograms();
+ m_oldProgramList = m_programList;
+}
+
+void
+BankEditorDialog::slotUpdate()
+{
+ updateDialog();
+}
+
+MidiDeviceListViewItem*
+BankEditorDialog::getParentDeviceItem(QListViewItem* item)
+{
+ if (!item)
+ return 0;
+
+ if (dynamic_cast<MidiBankListViewItem*>(item))
+ // go up to the parent device item
+ item = item->parent();
+
+ if (dynamic_cast<MidiKeyMapListViewItem*>(item))
+ // go up to the parent device item
+ item = item->parent();
+
+ if (!item) {
+ RG_DEBUG << "BankEditorDialog::getParentDeviceItem : missing parent device item for bank item - this SHOULD NOT HAPPEN" << endl;
+ return 0;
+ }
+
+ return dynamic_cast<MidiDeviceListViewItem*>(item);
+}
+
+void
+BankEditorDialog::slotAddBank()
+{
+ if (!m_listView->currentItem())
+ return ;
+
+ QListViewItem* currentItem = m_listView->currentItem();
+
+ MidiDeviceListViewItem* deviceItem = getParentDeviceItem(currentItem);
+ MidiDevice *device = getMidiDevice(currentItem);
+
+ if (device) {
+ // If the bank and program lists are empty then try to
+ // populate them.
+ //
+ if (m_bankList.size() == 0 && m_programList.size() == 0) {
+ m_bankList = device->getBanks();
+ setProgramList(device);
+ }
+
+ std::pair<int, int> bank = getFirstFreeBank(m_listView->currentItem());
+
+ MidiBank newBank(false,
+ bank.first, bank.second,
+ qstrtostr(i18n("<new bank>")));
+ m_bankList.push_back(newBank);
+
+ QListViewItem* newBankItem =
+ new MidiBankListViewItem(deviceItem->getDeviceId(),
+ m_bankList.size() - 1,
+ deviceItem,
+ strtoqstr(newBank.getName()),
+ newBank.isPercussion(),
+ newBank.getMSB(), newBank.getLSB());
+ keepBankListForNextPopulate();
+ m_listView->setCurrentItem(newBankItem);
+
+ slotApply();
+ selectDeviceItem(device);
+ }
+}
+
+void
+BankEditorDialog::slotAddKeyMapping()
+{
+ if (!m_listView->currentItem())
+ return ;
+
+ QListViewItem* currentItem = m_listView->currentItem();
+
+ MidiDeviceListViewItem* deviceItem = getParentDeviceItem(currentItem);
+ MidiDevice *device = getMidiDevice(currentItem);
+
+ if (device) {
+
+ QString name = "";
+ int n = 0;
+ while (name == "" || device->getKeyMappingByName(qstrtostr(name)) != 0) {
+ ++n;
+ if (n == 1)
+ name = i18n("<new mapping>");
+ else
+ name = i18n("<new mapping %1>").arg(n);
+ }
+
+ MidiKeyMapping newKeyMapping(qstrtostr(name));
+
+ ModifyDeviceCommand *command = new ModifyDeviceCommand
+ (m_studio,
+ device->getId(),
+ device->getName(),
+ device->getLibrarianName(),
+ device->getLibrarianEmail());
+
+ KeyMappingList kml;
+ kml.push_back(newKeyMapping);
+ command->setKeyMappingList(kml);
+ command->setOverwrite(false);
+ command->setRename(false);
+
+ addCommandToHistory(command);
+
+ updateDialog();
+ selectDeviceItem(device);
+ }
+}
+
+void
+BankEditorDialog::slotDelete()
+{
+ if (!m_listView->currentItem())
+ return ;
+
+ QListViewItem* currentItem = m_listView->currentItem();
+
+ MidiBankListViewItem* bankItem = dynamic_cast<MidiBankListViewItem*>(currentItem);
+
+ MidiDevice *device = getMidiDevice(currentItem);
+
+ if (device && bankItem) {
+ int currentBank = bankItem->getBank();
+
+ int reply =
+ KMessageBox::warningYesNo(this, i18n("Really delete this bank?"));
+
+ if (reply == KMessageBox::Yes) {
+ MidiBank bank = m_bankList[currentBank];
+
+ // Copy across all programs that aren't in the doomed bank
+ //
+ ProgramList::iterator it;
+ ProgramList tempList;
+ for (it = m_programList.begin(); it != m_programList.end(); it++)
+ if (!(it->getBank() == bank))
+ tempList.push_back(*it);
+
+ // Erase the bank and repopulate
+ //
+ BankList::iterator er =
+ m_bankList.begin();
+ er += currentBank;
+ m_bankList.erase(er);
+ m_programList = tempList;
+ keepBankListForNextPopulate();
+
+ // the listview automatically selects a new current item
+ m_listView->blockSignals(true);
+ delete currentItem;
+ m_listView->blockSignals(false);
+
+ // Don't allow pasting from this defunct device
+ //
+ if (m_copyBank.first == bankItem->getDeviceId() &&
+ m_copyBank.second == bankItem->getBank()) {
+ m_pastePrograms->setEnabled(false);
+ m_copyBank = std::pair<DeviceId, int>
+ (Device::NO_DEVICE, -1);
+ }
+
+ slotApply();
+ selectDeviceItem(device);
+ }
+
+ return ;
+ }
+
+ MidiKeyMapListViewItem* keyItem = dynamic_cast<MidiKeyMapListViewItem*>(currentItem);
+
+ if (keyItem && device) {
+
+ int reply =
+ KMessageBox::warningYesNo(this, i18n("Really delete this key mapping?"));
+
+ if (reply == KMessageBox::Yes) {
+
+ std::string keyMappingName = qstrtostr(keyItem->getName());
+
+ ModifyDeviceCommand *command = new ModifyDeviceCommand
+ (m_studio,
+ device->getId(),
+ device->getName(),
+ device->getLibrarianName(),
+ device->getLibrarianEmail());
+
+ KeyMappingList kml = device->getKeyMappings();
+
+ for (KeyMappingList::iterator i = kml.begin();
+ i != kml.end(); ++i) {
+ if (i->getName() == keyMappingName) {
+ RG_DEBUG << "erasing " << keyMappingName << endl;
+ kml.erase(i);
+ break;
+ }
+ }
+
+ RG_DEBUG << " setting " << kml.size() << " key mappings to device " << endl;
+
+ command->setKeyMappingList(kml);
+ command->setOverwrite(true);
+
+ addCommandToHistory(command);
+
+ RG_DEBUG << " device has " << device->getKeyMappings().size() << " key mappings now " << endl;
+
+ updateDialog();
+ }
+
+ return ;
+ }
+}
+
+void
+BankEditorDialog::slotDeleteAll()
+{
+ if (!m_listView->currentItem())
+ return ;
+
+ QListViewItem* currentItem = m_listView->currentItem();
+ MidiDeviceListViewItem* deviceItem = getParentDeviceItem(currentItem);
+ MidiDevice *device = getMidiDevice(deviceItem);
+
+ QString question = i18n("Really delete all banks for ") +
+ strtoqstr(device->getName()) + QString(" ?");
+
+ int reply = KMessageBox::warningYesNo(this, question);
+
+ if (reply == KMessageBox::Yes) {
+
+ // erase all bank items
+ QListViewItem* child = 0;
+ while ((child = deviceItem->firstChild()))
+ delete child;
+
+ m_bankList.clear();
+ m_programList.clear();
+
+ // Don't allow pasting from this defunct device
+ //
+ if (m_copyBank.first == deviceItem->getDeviceId()) {
+ m_pastePrograms->setEnabled(false);
+ m_copyBank = std::pair<DeviceId, int>
+ (Device::NO_DEVICE, -1);
+ }
+
+ // Urgh, we have this horrible flag that we're using to frig this.
+ // (we might not need this anymore but I'm too scared to remove it
+ // now).
+ //
+ m_deleteAllReally = true;
+ slotApply();
+ m_deleteAllReally = false;
+
+ selectDeviceItem(device);
+
+ }
+}
+
+MidiDevice*
+BankEditorDialog::getMidiDevice(DeviceId id)
+{
+ Device *device = m_studio->getDevice(id);
+ MidiDevice *midiDevice =
+ dynamic_cast<MidiDevice *>(device);
+
+ /*
+ if (device) {
+ if (!midiDevice) {
+ std::cerr << "ERROR: BankEditorDialog::getMidiDevice: device "
+ << id << " is not a MIDI device" << std::endl;
+ }
+ } else {
+ std::cerr
+ << "ERROR: BankEditorDialog::getMidiDevice: no such device as "
+ << id << std::endl;
+ }
+ */
+
+ return midiDevice;
+}
+
+MidiDevice*
+BankEditorDialog::getMidiDevice(QListViewItem* item)
+{
+ MidiDeviceListViewItem* deviceItem =
+ dynamic_cast<MidiDeviceListViewItem*>(item);
+ if (!deviceItem)
+ return 0;
+
+ return getMidiDevice(deviceItem->getDeviceId());
+}
+
+std::pair<int, int>
+BankEditorDialog::getFirstFreeBank(QListViewItem* item)
+{
+ //!!! percussion? this is actually only called in the expectation
+ // that percussion==false at the moment
+
+ for (int msb = 0; msb < 128; ++msb) {
+ for (int lsb = 0; lsb < 128; ++lsb) {
+ BankList::iterator i = m_bankList.begin();
+ for ( ; i != m_bankList.end(); ++i) {
+ if (i->getLSB() == lsb && i->getMSB() == msb) {
+ break;
+ }
+ }
+ if (i == m_bankList.end())
+ return std::pair<int, int>(msb, lsb);
+ }
+ }
+
+ return std::pair<int, int>(0, 0);
+}
+
+void
+BankEditorDialog::slotModifyDeviceOrBankName(QListViewItem* item, const QString &label, int)
+{
+ RG_DEBUG << "BankEditorDialog::slotModifyDeviceOrBankName" << endl;
+
+ MidiDeviceListViewItem* deviceItem =
+ dynamic_cast<MidiDeviceListViewItem*>(item);
+ MidiBankListViewItem* bankItem =
+ dynamic_cast<MidiBankListViewItem*>(item);
+ MidiKeyMapListViewItem *keyItem =
+ dynamic_cast<MidiKeyMapListViewItem*>(item);
+
+ if (bankItem) {
+
+ // renaming a bank item
+
+ RG_DEBUG << "BankEditorDialog::slotModifyDeviceOrBankName - "
+ << "modify bank name to " << label << endl;
+
+ if (m_bankList[bankItem->getBank()].getName() != qstrtostr(label)) {
+ m_bankList[bankItem->getBank()].setName(qstrtostr(label));
+ setModified(true);
+ }
+
+ } else if (keyItem) {
+
+ RG_DEBUG << "BankEditorDialog::slotModifyDeviceOrBankName - "
+ << "modify key mapping name to " << label << endl;
+
+ QString oldName = keyItem->getName();
+
+ QListViewItem* currentItem = m_listView->currentItem();
+ MidiDevice *device = getMidiDevice(currentItem);
+
+ if (device) {
+
+ ModifyDeviceCommand *command = new ModifyDeviceCommand
+ (m_studio,
+ device->getId(),
+ device->getName(),
+ device->getLibrarianName(),
+ device->getLibrarianEmail());
+
+ KeyMappingList kml = device->getKeyMappings();
+
+ for (KeyMappingList::iterator i = kml.begin();
+ i != kml.end(); ++i) {
+ if (i->getName() == qstrtostr(oldName)) {
+ i->setName(qstrtostr(label));
+ break;
+ }
+ }
+
+ command->setKeyMappingList(kml);
+ command->setOverwrite(true);
+
+ addCommandToHistory(command);
+
+ updateDialog();
+ }
+
+ } else if (deviceItem) { // must be last, as the others are subclasses
+
+ // renaming a device item
+
+ RG_DEBUG << "BankEditorDialog::slotModifyDeviceOrBankName - "
+ << "modify device name to " << label << endl;
+
+ if (m_deviceNameMap[deviceItem->getDeviceId()] != qstrtostr(label)) {
+ m_deviceNameMap[deviceItem->getDeviceId()] = qstrtostr(label);
+ setModified(true);
+
+ m_updateDeviceList = true;
+ }
+
+ }
+
+}
+
+void
+BankEditorDialog::selectDeviceItem(MidiDevice *device)
+{
+ QListViewItem *child = m_listView->firstChild();
+ MidiDeviceListViewItem *midiDeviceItem;
+ MidiDevice *midiDevice;
+
+ do {
+ midiDeviceItem = dynamic_cast<MidiDeviceListViewItem*>(child);
+
+ if (midiDeviceItem) {
+ midiDevice = getMidiDevice(midiDeviceItem);
+
+ if (midiDevice == device) {
+ m_listView->setSelected(child, true);
+ return ;
+ }
+ }
+
+ } while ((child = child->nextSibling()));
+}
+
+void
+BankEditorDialog::selectDeviceBankItem(DeviceId deviceId,
+ int bank)
+{
+ QListViewItem *deviceChild = m_listView->firstChild();
+ QListViewItem *bankChild;
+ int deviceCount = 0, bankCount = 0;
+
+ do {
+ bankChild = deviceChild->firstChild();
+
+ MidiDeviceListViewItem *midiDeviceItem =
+ dynamic_cast<MidiDeviceListViewItem*>(deviceChild);
+
+ if (midiDeviceItem && bankChild) {
+ do {
+ if (deviceId == midiDeviceItem->getDeviceId() &
+ bank == bankCount) {
+ m_listView->setSelected(bankChild, true);
+ return ;
+ }
+ bankCount++;
+
+ } while ((bankChild = bankChild->nextSibling()));
+ }
+
+ deviceCount++;
+ bankCount = 0;
+ } while ((deviceChild = deviceChild->nextSibling()));
+}
+
+void
+BankEditorDialog::slotVariationToggled()
+{
+ setModified(true);
+ m_variationCombo->setEnabled(m_variationToggle->isChecked());
+}
+
+void
+BankEditorDialog::slotVariationChanged(int)
+{
+ setModified(true);
+}
+
+void
+BankEditorDialog::setModified(bool modified)
+{
+ RG_DEBUG << "BankEditorDialog::setModified("
+ << modified << ")" << endl;
+
+ if (modified) {
+
+ m_applyButton->setEnabled(true);
+ m_resetButton->setEnabled(true);
+ m_closeButton->setEnabled(false);
+ m_listView->setEnabled(false);
+
+ } else {
+
+ m_applyButton->setEnabled(false);
+ m_resetButton->setEnabled(false);
+ m_closeButton->setEnabled(true);
+ m_listView->setEnabled(true);
+
+ }
+
+ m_modified = modified;
+}
+
+void
+BankEditorDialog::addCommandToHistory(KCommand *command)
+{
+ getCommandHistory()->addCommand(command);
+ setModified(false);
+}
+
+MultiViewCommandHistory*
+BankEditorDialog::getCommandHistory()
+{
+ return m_doc->getCommandHistory();
+}
+
+void
+BankEditorDialog::slotImport()
+{
+ QString deviceDir = KGlobal::dirs()->findResource("appdata", "library/");
+ QDir dir(deviceDir);
+ if (!dir.exists()) {
+ deviceDir = ":ROSEGARDENDEVICE";
+ } else {
+ deviceDir = "file://" + deviceDir;
+ }
+
+ KURL url = KFileDialog::getOpenURL
+ (deviceDir,
+ "audio/x-rosegarden-device audio/x-rosegarden audio/x-soundfont",
+ this, i18n("Import Banks from Device in File"));
+
+ if (url.isEmpty())
+ return ;
+
+ ImportDeviceDialog *dialog = new ImportDeviceDialog(this, url);
+ if (dialog->doImport() && dialog->exec() == QDialog::Accepted) {
+
+ MidiDeviceListViewItem* deviceItem =
+ dynamic_cast<MidiDeviceListViewItem*>
+ (m_listView->selectedItem());
+
+ if (!deviceItem) {
+ KMessageBox::error(this, "Some internal error: cannot locate selected device");
+ return ;
+ }
+
+ ModifyDeviceCommand *command = 0;
+
+ BankList banks(dialog->getBanks());
+ ProgramList programs(dialog->getPrograms());
+ ControlList controls(dialog->getControllers());
+ KeyMappingList keyMappings(dialog->getKeyMappings());
+ MidiDevice::VariationType variation(dialog->getVariationType());
+ std::string librarianName(dialog->getLibrarianName());
+ std::string librarianEmail(dialog->getLibrarianEmail());
+
+ // don't record the librarian when
+ // merging banks -- it's misleading.
+ // (also don't use variation type)
+ if (!dialog->shouldOverwriteBanks()) {
+ librarianName = "";
+ librarianEmail = "";
+ }
+
+ command = new ModifyDeviceCommand(m_studio,
+ deviceItem->getDeviceId(),
+ dialog->getDeviceName(),
+ librarianName,
+ librarianEmail);
+
+ if (dialog->shouldOverwriteBanks()) {
+ command->setVariation(variation);
+ }
+ if (dialog->shouldImportBanks()) {
+ command->setBankList(banks);
+ command->setProgramList(programs);
+ }
+ if (dialog->shouldImportControllers()) {
+ command->setControlList(controls);
+ }
+ if (dialog->shouldImportKeyMappings()) {
+ command->setKeyMappingList(keyMappings);
+ }
+ command->setOverwrite(dialog->shouldOverwriteBanks());
+ command->setRename(dialog->shouldRename());
+
+ addCommandToHistory(command);
+
+ // No need to redraw the dialog, this is done by
+ // slotUpdate, signalled by the MultiViewCommandHistory
+ MidiDevice *device = getMidiDevice(deviceItem);
+ if (device)
+ selectDeviceItem(device);
+ }
+
+ delete dialog;
+ updateDialog();
+}
+
+void
+BankEditorDialog::slotEditCopy()
+{
+ MidiBankListViewItem* bankItem
+ = dynamic_cast<MidiBankListViewItem*>(m_listView->currentItem());
+
+ if (bankItem) {
+ m_copyBank = std::pair<DeviceId, int>(bankItem->getDeviceId(),
+ bankItem->getBank());
+ m_pastePrograms->setEnabled(true);
+ }
+}
+
+void
+BankEditorDialog::slotEditPaste()
+{
+ MidiBankListViewItem* bankItem
+ = dynamic_cast<MidiBankListViewItem*>(m_listView->currentItem());
+
+ if (bankItem) {
+ // Get the full program and bank list for the source device
+ //
+ MidiDevice *device = getMidiDevice(m_copyBank.first);
+ std::vector<MidiBank> tempBank = device->getBanks();
+
+ ProgramList::iterator it;
+ std::vector<MidiProgram> tempProg;
+
+ // Remove programs that will be overwritten
+ //
+ for (it = m_programList.begin(); it != m_programList.end(); it++) {
+ if (!(it->getBank() == m_lastBank))
+ tempProg.push_back(*it);
+ }
+ m_programList = tempProg;
+
+ // Now get source list and msb/lsb
+ //
+ tempProg = device->getPrograms();
+ MidiBank sourceBank = tempBank[m_copyBank.second];
+
+ // Add the new programs
+ //
+ for (it = tempProg.begin(); it != tempProg.end(); it++) {
+ if (it->getBank() == sourceBank) {
+ // Insert with new MSB and LSB
+ //
+ MidiProgram copyProgram(m_lastBank,
+ it->getProgram(),
+ it->getName());
+
+ m_programList.push_back(copyProgram);
+ }
+ }
+
+ // Save these for post-apply
+ //
+ DeviceId devPos = bankItem->getDeviceId();
+ int bankPos = bankItem->getBank();
+
+ slotApply();
+
+ // Select same bank
+ //
+ selectDeviceBankItem(devPos, bankPos);
+ }
+}
+
+void
+BankEditorDialog::slotExport()
+{
+ QString extension = "rgd";
+
+ QString name =
+ KFileDialog::getSaveFileName(":ROSEGARDEN",
+ (extension.isEmpty() ? QString("*") : ("*." + extension)),
+ this,
+ i18n("Export Device as..."));
+
+ // Check for the existence of the name
+ if (name.isEmpty())
+ return ;
+
+ // Append extension if we don't have one
+ //
+ if (!extension.isEmpty()) {
+ if (!name.endsWith("." + extension)) {
+ name += "." + extension;
+ }
+ }
+
+ QFileInfo info(name);
+
+ if (info.isDir()) {
+ KMessageBox::sorry(this, i18n("You have specified a directory"));
+ return ;
+ }
+
+ if (info.exists()) {
+ int overwrite = KMessageBox::questionYesNo
+ (this, i18n("The specified file exists. Overwrite?"));
+
+ if (overwrite != KMessageBox::Yes)
+ return ;
+
+ }
+
+ MidiDeviceListViewItem* deviceItem =
+ dynamic_cast<MidiDeviceListViewItem*>
+ (m_listView->selectedItem());
+
+ std::vector<DeviceId> devices;
+ MidiDevice *md = getMidiDevice(deviceItem);
+
+ if (md) {
+ ExportDeviceDialog *ed = new ExportDeviceDialog
+ (this, strtoqstr(md->getName()));
+ if (ed->exec() != QDialog::Accepted)
+ return ;
+ if (ed->getExportType() == ExportDeviceDialog::ExportOne) {
+ devices.push_back(md->getId());
+ }
+ }
+
+ m_doc->exportStudio(name, devices);
+}
+
+void
+BankEditorDialog::slotFileClose()
+{
+ RG_DEBUG << "BankEditorDialog::slotFileClose()\n";
+
+ // We need to do this because we might be here due to a
+ // documentAboutToChange signal, in which case the document won't
+ // be valid by the time we reach the dtor, since it will be
+ // triggered when the closeEvent is actually processed.
+ //
+ m_doc->getCommandHistory()->detachView(actionCollection());
+ m_doc = 0;
+ close();
+}
+
+void
+BankEditorDialog::closeEvent(QCloseEvent *e)
+{
+ if (m_modified) {
+
+ int res = KMessageBox::warningYesNoCancel(this,
+ i18n("There are unsaved changes.\n"
+ "Do you want to apply the changes before exiting "
+ "the Bank Editor or discard the changes ?"),
+ i18n("Unsaved Changes"),
+ i18n("&Apply"),
+ i18n("&Discard"));
+ if (res == KMessageBox::Yes) {
+
+ slotApply();
+
+ } else if (res == KMessageBox::Cancel)
+ return ;
+ }
+
+ emit closing();
+ KMainWindow::closeEvent(e);
+}
+
+}
+#include "BankEditorDialog.moc"
diff --git a/src/gui/studio/BankEditorDialog.h b/src/gui/studio/BankEditorDialog.h
new file mode 100644
index 0000000..0e49430
--- /dev/null
+++ b/src/gui/studio/BankEditorDialog.h
@@ -0,0 +1,211 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_BANKEDITORDIALOG_H_
+#define _RG_BANKEDITORDIALOG_H_
+
+#include "base/Device.h"
+#include "base/MidiProgram.h"
+#include <map>
+#include <string>
+#include <kmainwindow.h>
+#include <utility>
+
+
+class QWidget;
+class QString;
+class QPushButton;
+class QListViewItem;
+class QGroupBox;
+class QCloseEvent;
+class QCheckBox;
+class KListView;
+class KCommand;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class Studio;
+class RosegardenGUIDoc;
+class MultiViewCommandHistory;
+class MidiProgramsEditor;
+class MidiKeyMappingEditor;
+class MidiDeviceListViewItem;
+class MidiDevice;
+
+
+class BankEditorDialog : public KMainWindow
+{
+ Q_OBJECT
+
+public:
+ BankEditorDialog(QWidget *parent,
+ RosegardenGUIDoc *doc,
+ DeviceId defaultDevice =
+ Device::NO_DEVICE);
+
+ ~BankEditorDialog();
+
+ // Initialise the devices/banks and programs - the whole lot
+ //
+ void initDialog();
+
+ std::pair<int, int> getFirstFreeBank(QListViewItem*);
+
+ void addCommandToHistory(KCommand *command);
+ MultiViewCommandHistory* getCommandHistory();
+
+ void setCurrentDevice(DeviceId device);
+
+ // Get a MidiDevice from an index number
+ //
+ MidiDevice* getMidiDevice(DeviceId);
+ MidiDevice* getMidiDevice(QListViewItem*);
+ MidiDevice* getCurrentMidiDevice();
+ BankList& getBankList() { return m_bankList; }
+ ProgramList&getProgramList() { return m_programList; }
+
+ Studio *getStudio() { return m_studio; }
+
+ void setModified(bool value);
+
+ void checkModified();
+
+ // Set the listview to select a certain device - used after adding
+ // or deleting banks.
+ //
+ void selectDeviceItem(MidiDevice *device);
+
+ // Select a device/bank combination
+ //
+ void selectDeviceBankItem(DeviceId device, int bank);
+
+public slots:
+ void slotPopulateDevice(QListViewItem*);
+
+ void slotApply();
+ void slotReset();
+
+ void slotUpdate();
+
+ void slotAddBank();
+ void slotAddKeyMapping();
+ void slotDelete();
+ void slotDeleteAll();
+
+ void slotImport();
+ void slotExport();
+
+ void slotModifyDeviceOrBankName(QListViewItem*, const QString&,int);
+
+ void slotFileClose();
+
+ void slotEditCopy();
+ void slotEditPaste();
+
+ void slotVariationToggled();
+ void slotVariationChanged(int);
+
+signals:
+ void closing();
+ void deviceNamesChanged();
+
+protected:
+ virtual void closeEvent(QCloseEvent*);
+
+ void resetProgramList();
+ void setProgramList(MidiDevice *device);
+
+ void updateDialog();
+
+ void populateDeviceItem(QListViewItem* deviceItem,
+ MidiDevice* midiDevice);
+
+ void updateDeviceItem(MidiDeviceListViewItem* deviceItem);
+
+ bool deviceItemHasBank(MidiDeviceListViewItem* deviceItem, int bankNb);
+
+ void clearItemChildren(QListViewItem* deviceItem);
+
+ MidiDeviceListViewItem* getParentDeviceItem(QListViewItem*);
+ void keepBankListForNextPopulate() { m_keepBankList = true; }
+
+ void populateDevice(QListViewItem*);
+
+ void setupActions();
+
+ //--------------- Data members ---------------------------------
+ Studio *m_studio;
+ RosegardenGUIDoc *m_doc;
+
+ MidiProgramsEditor *m_programEditor;
+ MidiKeyMappingEditor *m_keyMappingEditor;
+ KListView *m_listView;
+
+ QGroupBox *m_optionBox;
+ QCheckBox *m_variationToggle;
+ KComboBox *m_variationCombo;
+
+ QPushButton *m_closeButton;
+ QPushButton *m_resetButton;
+ QPushButton *m_applyButton;
+
+ QPushButton *m_addBank;
+ QPushButton *m_addKeyMapping;
+ QPushButton *m_delete;
+ QPushButton *m_deleteAll;
+
+ QPushButton *m_importBanks;
+ QPushButton *m_exportBanks;
+
+ QPushButton *m_copyPrograms;
+ QPushButton *m_pastePrograms;
+ std::pair<DeviceId, int> m_copyBank;
+
+ std::map<DeviceId, std::string> m_deviceNameMap;
+ BankList m_bankList;
+ ProgramList m_programList;
+ ProgramList m_oldProgramList;
+
+ bool m_modified;
+ bool m_keepBankList;
+ bool m_deleteAllReally;
+
+ DeviceId m_lastDevice;
+ MidiBank m_lastBank;
+
+ bool m_updateDeviceList;
+};
+
+// ----------------------- RemapInstrumentDialog ------------------------
+//
+//
+
+
+}
+
+#endif
diff --git a/src/gui/studio/ChangeRecordDeviceCommand.cpp b/src/gui/studio/ChangeRecordDeviceCommand.cpp
new file mode 100644
index 0000000..a5a3947
--- /dev/null
+++ b/src/gui/studio/ChangeRecordDeviceCommand.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ChangeRecordDeviceCommand.h"
+#include "StudioControl.h"
+#include "document/ConfigGroups.h"
+#include "sound/MappedEvent.h"
+#include <kapplication.h>
+#include <kconfig.h>
+
+namespace Rosegarden
+{
+
+void
+ChangeRecordDeviceCommand::swap()
+ {
+
+ KConfig *config = kapp->config();
+ config->setGroup(Rosegarden::SequencerOptionsConfigGroup);
+ QStringList devList = config->readListEntry("midirecorddevice");
+ QString sdevice = QString::number(m_deviceId);
+ if (m_action)
+ {
+ if(!devList.contains(sdevice))
+ devList.append(sdevice);
+ }
+ else
+ {
+ if(devList.contains(sdevice))
+ devList.remove(sdevice);
+ }
+ config->writeEntry("midirecorddevice", devList);
+
+ // send the selected device to the sequencer
+ Rosegarden::MappedEvent mEdevice
+ (Rosegarden::MidiInstrumentBase,
+ Rosegarden::MappedEvent::SystemRecordDevice,
+ Rosegarden::MidiByte(m_deviceId),
+ Rosegarden::MidiByte(m_action));
+ Rosegarden::StudioControl::sendMappedEvent(mEdevice);
+
+ m_action = !m_action;
+}
+
+}
diff --git a/src/gui/studio/ChangeRecordDeviceCommand.h b/src/gui/studio/ChangeRecordDeviceCommand.h
new file mode 100644
index 0000000..70819db
--- /dev/null
+++ b/src/gui/studio/ChangeRecordDeviceCommand.h
@@ -0,0 +1,54 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_CHANGERECORDDEVICECOMMAND_H_
+#define _RG_CHANGERECORDDEVICECOMMAND_H_
+
+#include "base/Studio.h"
+#include <klocale.h>
+#include <kcommand.h>
+
+namespace Rosegarden
+{
+
+class ChangeRecordDeviceCommand : public KNamedCommand
+{
+public:
+ ChangeRecordDeviceCommand(Rosegarden::DeviceId deviceId, bool action) :
+ KNamedCommand(i18n("Change Record Device")),
+ m_deviceId(deviceId), m_action(action) { }
+
+ virtual void execute() { swap(); }
+ virtual void unexecute() { swap(); }
+
+private:
+ Rosegarden::DeviceId m_deviceId;
+ bool m_action;
+ void swap();
+
+};
+
+}
+
+#endif /*CHANGERECORDDEVICECOMMAND_H_*/
diff --git a/src/gui/studio/DeviceEditorDialog.cpp b/src/gui/studio/DeviceEditorDialog.cpp
new file mode 100644
index 0000000..29c9dd4
--- /dev/null
+++ b/src/gui/studio/DeviceEditorDialog.cpp
@@ -0,0 +1,406 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "DeviceEditorDialog.h"
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/Device.h"
+#include "base/MidiDevice.h"
+#include "base/Studio.h"
+#include "commands/studio/CreateOrDeleteDeviceCommand.h"
+#include "commands/studio/ReconnectDeviceCommand.h"
+#include "commands/studio/RenameDeviceCommand.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/MultiViewCommandHistory.h"
+#include "gui/application/RosegardenApplication.h"
+#include <kdialogbase.h>
+#include <kmessagebox.h>
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <qhbox.h>
+#include <qpushbutton.h>
+#include <qregexp.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qtable.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <algorithm>
+
+
+namespace Rosegarden
+{
+
+DeviceEditorDialog::DeviceEditorDialog(QWidget *parent,
+ RosegardenGUIDoc *document) :
+ KDialogBase(parent, "deviceeditordialog", true,
+ i18n("Manage MIDI Devices"), Ok | Apply | Close, Ok, true),
+ m_document(document),
+ m_studio(&document->getStudio()),
+ m_modified(false)
+{
+ QVBox *mainBox = makeVBoxMainWidget();
+
+ m_table = new QTable(0, 4, mainBox);
+ m_table->setSorting(false);
+ m_table->setRowMovingEnabled(false);
+ m_table->setColumnMovingEnabled(false);
+ m_table->setShowGrid(false);
+ m_table->horizontalHeader()->setLabel(0, i18n("Device"));
+ m_table->horizontalHeader()->setLabel(1, i18n("Name"));
+ m_table->horizontalHeader()->setLabel(2, i18n("Type"));
+ m_table->horizontalHeader()->setLabel(3, i18n("Connection"));
+ m_table->horizontalHeader()->show();
+ m_table->verticalHeader()->hide();
+ m_table->setLeftMargin(0);
+ m_table->setSelectionMode(QTable::SingleRow);
+ m_table->setColumnReadOnly(0, true);
+ m_table->setColumnReadOnly(2, true);
+
+ makeConnectionList((unsigned int)MidiDevice::Play,
+ m_playConnections);
+ makeConnectionList((unsigned int)MidiDevice::Record,
+ m_recordConnections);
+
+ populate();
+
+ QHBox *hbox = new QHBox(mainBox);
+ QPushButton *addButton = new QPushButton(i18n("Add Play Device"), hbox);
+ QPushButton *addRButton = new QPushButton(i18n("Add Record Device"), hbox);
+ QPushButton *deleteButton = new QPushButton(i18n("Delete Device"), hbox);
+ connect(addButton, SIGNAL(clicked()), this, SLOT(slotAddPlayDevice()));
+ connect(addRButton, SIGNAL(clicked()), this, SLOT(slotAddRecordDevice()));
+ connect(deleteButton, SIGNAL(clicked()), this, SLOT(slotDeleteDevice()));
+ connect(m_table, SIGNAL(valueChanged(int, int)),
+ this, SLOT(slotValueChanged (int, int)));
+
+ setMinimumHeight(250);
+
+ enableButtonOK(false);
+ enableButtonApply(false);
+}
+
+DeviceEditorDialog::~DeviceEditorDialog()
+{
+ // nothing -- don't need to clear device list (the devices were
+ // just aliases for those in the studio)
+}
+
+void
+DeviceEditorDialog::populate()
+{
+ DeviceList *devices = m_studio->getDevices();
+ DeviceListIterator it;
+ m_devices.clear();
+
+ for (it = devices->begin(); it != devices->end(); ++it) {
+ if ((*it)->getType() == Device::Midi) {
+ MidiDevice *md =
+ dynamic_cast<MidiDevice *>(*it);
+ if (md)
+ m_devices.push_back(md);
+ }
+ }
+
+ while (m_table->numRows() > 0) {
+ m_table->removeRow(m_table->numRows() - 1);
+ }
+
+ int deviceCount = 0;
+
+#define NAME_COL 0
+#define LABEL_COL 1
+#define DIRECTION_COL 2
+#define CONNECTION_COL 3
+
+ for (it = m_devices.begin(); it != m_devices.end(); ++it) {
+
+ m_table->insertRows(deviceCount, 1);
+
+ // we know we only put MidiDevices in m_devices
+ MidiDevice *md = static_cast<MidiDevice *>(*it);
+
+ // if you change this string ("Device %1"), change test in slotApply
+ QString deviceName = i18n("Device %1").arg(md->getId() + 1);
+ QString deviceLabel = strtoqstr(md->getName());
+ QString connectionName = strtoqstr(md->getConnection());
+
+ m_table->setText(deviceCount, NAME_COL, deviceName);
+ m_table->setText(deviceCount, LABEL_COL, deviceLabel);
+ m_table->setText(deviceCount, DIRECTION_COL,
+ (md->getDirection() == MidiDevice::Play ?
+ i18n("Play") : i18n("Record")));
+
+ QStringList &list(md->getDirection() == MidiDevice::Play ?
+ m_playConnections : m_recordConnections);
+ int currentConnectionIndex = list.size() - 1;
+ for (unsigned int i = 0; i < list.size(); ++i) {
+ if (list[i] == connectionName)
+ currentConnectionIndex = i;
+ }
+
+ QComboTableItem *item = new QComboTableItem(m_table, list, false);
+ item->setCurrentItem(currentConnectionIndex);
+ m_table->setItem(deviceCount, CONNECTION_COL, item);
+
+ m_table->adjustRow(deviceCount);
+ ++deviceCount;
+ }
+
+ int minColumnWidths[] = { 80, 120, 100, 250 };
+ for (int i = 0; i < 4; ++i) {
+ m_table->adjustColumn(i);
+ if (m_table->columnWidth(i) < minColumnWidths[i])
+ m_table->setColumnWidth(i, minColumnWidths[i]);
+ }
+}
+
+void
+DeviceEditorDialog::makeConnectionList(unsigned int direction,
+ QStringList &list)
+{
+ QByteArray data;
+ QByteArray replyData;
+ QCString replyType;
+ QDataStream arg(data, IO_WriteOnly);
+ arg << (int)Device::Midi;
+ arg << direction;
+
+ if (!rgapp->sequencerCall("getConnections(int, unsigned int)", replyType, replyData, data)) {
+ RG_DEBUG << "DeviceEditorDialog: can't call Sequencer" << endl;
+ list.append(i18n("No connection"));
+ return ;
+ }
+
+ QDataStream reply(replyData, IO_ReadOnly);
+ unsigned int connections = 0;
+ if (replyType == "unsigned int")
+ reply >> connections;
+
+ for (unsigned int i = 0; i < connections; ++i) {
+
+ QByteArray data;
+ QByteArray replyData;
+ QCString replyType;
+ QDataStream arg(data, IO_WriteOnly);
+ arg << (int)Device::Midi;
+ arg << direction;
+ arg << i;
+
+
+ if (!rgapp->sequencerCall("getConnection(int, unsigned int, unsigned int)",
+ replyType, replyData, data)) {
+ RG_DEBUG << "DeviceEditorDialog: can't call Sequencer" << endl;
+ list.append(i18n("No connection"));
+ return ;
+ }
+
+ QDataStream reply(replyData, IO_ReadOnly);
+ QString connection;
+ if (replyType == "QString") {
+ reply >> connection;
+ list.append(connection);
+ }
+ }
+
+ list.append(i18n("No connection"));
+}
+
+void
+DeviceEditorDialog::setModified(bool m)
+{
+ if (m_modified == m)
+ return ;
+ enableButtonOK(m);
+ enableButtonApply(m);
+ m_modified = m;
+}
+
+void
+DeviceEditorDialog::slotOk()
+{
+ slotApply();
+ accept();
+}
+
+void
+DeviceEditorDialog::slotClose()
+{
+ if (m_modified) {
+
+ int reply = KMessageBox::questionYesNo(this,
+ i18n("Apply pending changes?"));
+
+ if (reply == KMessageBox::Yes)
+ slotApply();
+ }
+
+ reject();
+}
+
+void
+DeviceEditorDialog::slotApply()
+{
+ KMacroCommand *command = new KMacroCommand("Edit Devices");
+
+ // first delete deleted devices, in reverse order of id (so that
+ // if we undo this command we'll get the original ids back... probably)
+
+ std::vector<DeviceId> ids;
+
+ for (DeviceListIterator i = m_devices.begin();
+ i != m_devices.end(); ++i) {
+ if (m_deletedDevices.find((*i)->getId()) != m_deletedDevices.end()) {
+ ids.push_back((*i)->getId());
+ }
+ }
+
+ std::sort(ids.begin(), ids.end());
+
+ for (int i = ids.size() - 1; i >= 0; --i) {
+ command->addCommand(new CreateOrDeleteDeviceCommand(m_studio, ids[i]));
+ }
+
+ // create the new devices, and rename and/or set connections for
+ // any others that have changed
+
+ for (int i = 0; i < m_table->numRows(); ++i) {
+ int deviceId = getDeviceIdAt(i);
+ if (deviceId < 0) { // new device
+ command->addCommand(new CreateOrDeleteDeviceCommand
+ (m_studio,
+ qstrtostr(m_table->text(i, LABEL_COL)),
+ Device::Midi,
+ m_table->text(i, DIRECTION_COL) == "Play" ?
+ MidiDevice::Play :
+ MidiDevice::Record,
+ qstrtostr(m_table->text(i, CONNECTION_COL))));
+ } else { // existing device
+ Device *device = m_studio->getDevice(deviceId);
+ if (!device) {
+ /*
+ std::cerr <<
+ "WARNING: DeviceEditorDialog::slotApply(): device at row "
+ << i << " (id " << deviceId
+ << ") claims not to be new, but isn't in the studio"
+ << std::endl;
+ */
+ } else {
+ std::string name = qstrtostr(m_table->text(i, LABEL_COL));
+ std::string conn = qstrtostr(m_table->text(i, CONNECTION_COL));
+ if (device->getName() != name) {
+ command->addCommand(new RenameDeviceCommand
+ (m_studio, deviceId, name));
+ }
+ if (device->getConnection() != conn) {
+ command->addCommand(new ReconnectDeviceCommand
+ (m_studio, deviceId, conn));
+ }
+ }
+ }
+ }
+
+ m_document->getCommandHistory()->addCommand(command);
+
+ m_deletedDevices.clear();
+
+ populate();
+ setModified(false);
+}
+
+int
+DeviceEditorDialog::getDeviceIdAt(int row) // -1 for new device w/o an id yet
+{
+ QString t(m_table->text(row, 0));
+
+ QRegExp re("^.*(\\d+).*$");
+ re.search(t);
+
+ QString number = re.cap(1);
+ int id = -1;
+
+ if (number && number != "")
+ {
+ id = number.toInt() - 1; // displayed device numbers are 1-based
+ }
+
+ return id;
+}
+
+void
+DeviceEditorDialog::slotAddPlayDevice()
+{
+ int n = m_table->numRows();
+ m_table->insertRows(n, 1);
+ m_table->setText(n, 0, i18n("<new device>"));
+ m_table->setText(n, 1, i18n("New Device"));
+ m_table->setText(n, 2, i18n("Play"));
+
+ QComboTableItem *item =
+ new QComboTableItem(m_table, m_playConnections, false);
+ item->setCurrentItem(m_playConnections.size() - 1);
+ m_table->setItem(n, 3, item);
+ m_table->adjustRow(n);
+
+ setModified(true);
+}
+
+void
+DeviceEditorDialog::slotAddRecordDevice()
+{
+ int n = m_table->numRows();
+ m_table->insertRows(n, 1);
+ m_table->setText(n, 0, i18n("<new device>"));
+ m_table->setText(n, 1, i18n("New Device"));
+ m_table->setText(n, 2, i18n("Record"));
+
+ QComboTableItem *item =
+ new QComboTableItem(m_table, m_recordConnections, false);
+ item->setCurrentItem(m_recordConnections.size() - 1);
+ m_table->setItem(n, 3, item);
+ m_table->adjustRow(n);
+
+ setModified(true);
+}
+
+void
+DeviceEditorDialog::slotDeleteDevice()
+{
+ int n = m_table->currentRow();
+ m_deletedDevices.insert(getDeviceIdAt(n));
+ m_table->removeRow(n);
+ setModified(true);
+}
+
+void
+DeviceEditorDialog::slotValueChanged(int, int)
+{
+ setModified(true);
+}
+
+}
+#include "DeviceEditorDialog.moc"
diff --git a/src/gui/studio/DeviceEditorDialog.h b/src/gui/studio/DeviceEditorDialog.h
new file mode 100644
index 0000000..2bde025
--- /dev/null
+++ b/src/gui/studio/DeviceEditorDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_DEVICEEDITORDIALOG_H_
+#define _RG_DEVICEEDITORDIALOG_H_
+
+#include <base/Studio.h>
+#include <kdialogbase.h>
+#include <qstringlist.h>
+#include <set>
+
+
+class QWidget;
+class QTable;
+
+
+namespace Rosegarden
+{
+
+class Studio;
+class RosegardenGUIDoc;
+
+
+class DeviceEditorDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ DeviceEditorDialog(QWidget *parent, RosegardenGUIDoc *document);
+ ~DeviceEditorDialog();
+
+ void setModified(bool value);
+
+protected slots:
+ void slotOk();
+ void slotApply();
+ void slotClose();
+
+ void slotAddPlayDevice();
+ void slotAddRecordDevice();
+ void slotDeleteDevice();
+ void slotValueChanged(int row, int col);
+
+private:
+ RosegardenGUIDoc *m_document;
+ Studio *m_studio;
+
+ QStringList m_playConnections;
+ QStringList m_recordConnections;
+ void makeConnectionList(unsigned int direction, QStringList &list);
+
+ QTable *m_table;
+
+ DeviceList m_devices;
+ std::set<DeviceId> m_deletedDevices;
+
+ void populate();
+ int getDeviceIdAt(int row); // -1 for new device without an id yet
+
+ bool m_modified;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/DeviceManagerDialog.cpp b/src/gui/studio/DeviceManagerDialog.cpp
new file mode 100644
index 0000000..8f2fa6b
--- /dev/null
+++ b/src/gui/studio/DeviceManagerDialog.cpp
@@ -0,0 +1,833 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "DeviceManagerDialog.h"
+
+#include "ChangeRecordDeviceCommand.h"
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "base/Device.h"
+#include "base/Event.h"
+#include "base/Instrument.h"
+#include "base/MidiDevice.h"
+#include "base/MidiTypes.h"
+#include "base/Studio.h"
+#include "commands/studio/CreateOrDeleteDeviceCommand.h"
+#include "commands/studio/ModifyDeviceCommand.h"
+#include "commands/studio/ReconnectDeviceCommand.h"
+#include "commands/studio/RenameDeviceCommand.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "gui/application/RosegardenApplication.h"
+#include "gui/dialogs/ExportDeviceDialog.h"
+#include "gui/dialogs/ImportDeviceDialog.h"
+#include <kapplication.h>
+#include <klocale.h>
+#include <kstddirs.h>
+#include <kaction.h>
+#include <kfiledialog.h>
+#include <kglobal.h>
+#include <kmainwindow.h>
+#include <kmessagebox.h>
+#include <kstdaccel.h>
+#include <kstdaction.h>
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <qdialog.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <qframe.h>
+#include <qgrid.h>
+#include <qgroupbox.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qsizepolicy.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qtable.h>
+#include <qtooltip.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+static const int PLAY_NAME_COL = 0;
+static const int PLAY_CONNECTION_COL = 1;
+
+static const int RECORD_NAME_COL = 0;
+static const int RECORD_CURRENT_COL = 1;
+static const int RECORD_CONNECTION_COL = 2;
+
+
+DeviceManagerDialog::DeviceManagerDialog(QWidget *parent,
+ RosegardenGUIDoc *document) :
+ KMainWindow(parent, "deviceeditordialog"),
+ m_document(document),
+ m_studio(&document->getStudio())
+ {
+ QFrame * mainBox = new QFrame(this);
+ setCentralWidget(mainBox);
+ QVBoxLayout *mainLayout = new QVBoxLayout(mainBox, 10, 10);
+
+ setCaption(i18n("Manage MIDI Devices"));
+
+ QGroupBox *groupBox = new QGroupBox(2, Horizontal, i18n("Play devices"), mainBox);
+
+ m_playTable = new QTable(0, 2, groupBox);
+ m_playTable->setSorting(false);
+ m_playTable->setRowMovingEnabled(false);
+ m_playTable->setColumnMovingEnabled(false);
+ m_playTable->setShowGrid(false);
+ m_playTable->horizontalHeader()->setLabel(PLAY_NAME_COL, i18n("Device"));
+ m_playTable->horizontalHeader()->setLabel(PLAY_CONNECTION_COL, i18n("Connection"));
+ m_playTable->horizontalHeader()->show();
+ m_playTable->verticalHeader()->hide();
+ m_playTable->setLeftMargin(0);
+ m_playTable->setSelectionMode(QTable::SingleRow);
+
+ QFrame *frame = new QFrame(groupBox);
+ QVBoxLayout *vlayout = new QVBoxLayout(frame);
+ QGrid *buttons = new QGrid(2, Horizontal, frame);
+ QPushButton *addButton = new QPushButton(i18n("New"), buttons);
+ m_deletePlayButton = new QPushButton(i18n("Delete"), buttons);
+ m_importButton = new QPushButton(i18n("Import..."), buttons);
+ m_exportButton = new QPushButton(i18n("Export..."), buttons);
+ m_banksButton = new QPushButton(i18n("Banks..."), buttons);
+ m_controllersButton = new QPushButton(i18n("Control Events..."), buttons);
+ vlayout->addWidget(buttons);
+ vlayout->addStretch(10);
+
+ QToolTip::add
+ (addButton,
+ i18n("Create a new Play device"));
+ QToolTip::add
+ (m_deletePlayButton,
+ i18n("Delete the selected device"));
+ QToolTip::add
+ (m_importButton,
+ i18n("Import Bank, Program and Controller data from a Rosegarden file to the selected device"));
+ QToolTip::add
+ (m_exportButton,
+ i18n("Export Bank and Controller data to a Rosegarden interchange file"));
+ QToolTip::add
+ (m_banksButton,
+ i18n("View and edit Banks and Programs for the selected device"));
+ QToolTip::add
+ (m_controllersButton,
+ i18n("View and edit Control Events for the selected device - these are special Event types that you can define against your device and control through Control Rulers or the Instrument Parameter Box "));
+
+ connect(addButton, SIGNAL(clicked()), this, SLOT(slotAddPlayDevice()));
+ connect(m_deletePlayButton, SIGNAL(clicked()), this, SLOT(slotDeletePlayDevice()));
+ connect(m_importButton, SIGNAL(clicked()), this, SLOT(slotImport()));
+ connect(m_exportButton, SIGNAL(clicked()), this, SLOT(slotExport()));
+ connect(m_banksButton, SIGNAL(clicked()), this, SLOT(slotSetBanks()));
+ connect(m_controllersButton, SIGNAL(clicked()), this, SLOT(slotSetControllers()));
+
+ connect(m_playTable, SIGNAL(valueChanged(int, int)),
+ this, SLOT(slotPlayValueChanged (int, int)));
+ connect(m_playTable, SIGNAL(currentChanged(int, int)),
+ this, SLOT(slotPlayDeviceSelected (int, int)));
+
+ mainLayout->addWidget(groupBox);
+ groupBox = new QGroupBox(2, Horizontal, i18n("Record devices"), mainBox);
+
+ m_recordTable = new QTable(0, 3, groupBox);
+ m_recordTable->setSorting(false);
+ m_recordTable->setRowMovingEnabled(false);
+ m_recordTable->setColumnMovingEnabled(false);
+ m_recordTable->setShowGrid(false);
+ m_recordTable->horizontalHeader()->setLabel(RECORD_NAME_COL, i18n("Device"));
+ m_recordTable->horizontalHeader()->setLabel(RECORD_CURRENT_COL, i18n("Current"));
+ m_recordTable->horizontalHeader()->setLabel(RECORD_CONNECTION_COL, i18n("Connection"));
+ m_recordTable->horizontalHeader()->show();
+ m_recordTable->verticalHeader()->hide();
+ m_recordTable->setLeftMargin(0);
+ m_recordTable->setSelectionMode(QTable::SingleRow);
+
+ frame = new QFrame(groupBox);
+ vlayout = new QVBoxLayout(frame);
+ buttons = new QGrid(2, Horizontal, frame);
+ addButton = new QPushButton(i18n("New"), buttons);
+ m_deleteRecordButton = new QPushButton(i18n("Delete"), buttons);
+ vlayout->addWidget(buttons);
+ vlayout->addStretch(10);
+
+ QToolTip::add
+ (addButton,
+ i18n("Create a new Record device"));
+ QToolTip::add
+ (m_deleteRecordButton,
+ i18n("Delete the selected device"));
+
+ connect(addButton, SIGNAL(clicked()), this, SLOT(slotAddRecordDevice()));
+ connect(m_deleteRecordButton, SIGNAL(clicked()), this, SLOT(slotDeleteRecordDevice()));
+
+ connect(m_recordTable, SIGNAL(currentChanged(int, int)),
+ this, SLOT(slotRecordDeviceSelected (int, int)));
+ connect(m_recordTable, SIGNAL(valueChanged(int, int)),
+ this, SLOT(slotRecordValueChanged (int, int)));
+
+ connect(document, SIGNAL(devicesResyncd()), this, SLOT(slotDevicesResyncd()));
+
+ m_noConnectionString = i18n("No connection");
+
+ slotDevicesResyncd();
+
+ setMinimumHeight(400);
+ setMinimumWidth(600);
+
+ mainLayout->addWidget(groupBox);
+
+ QFrame* btnBox = new QFrame(mainBox);
+
+ btnBox->setSizePolicy(
+ QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
+
+ QPushButton *closeButton = new QPushButton(i18n("Close"), btnBox);
+
+ QHBoxLayout* layout = new QHBoxLayout(btnBox, 0, 10);
+ layout->addStretch(10);
+ layout->addWidget(closeButton);
+ layout->addSpacing(5);
+
+ KAction* close = KStdAction::close(this,
+ SLOT(slotClose()),
+ actionCollection());
+
+ closeButton->setText(close->text());
+ connect(closeButton, SIGNAL(clicked()), this, SLOT(slotClose()));
+
+ mainLayout->addWidget(btnBox);
+
+ // some adjustments
+ new KToolBarPopupAction(i18n("Und&o"),
+ "undo",
+ KStdAccel::shortcut(KStdAccel::Undo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Undo));
+
+ new KToolBarPopupAction(i18n("Re&do"),
+ "redo",
+ KStdAccel::shortcut(KStdAccel::Redo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Redo));
+
+ createGUI("devicemanager.rc");
+
+ m_document->getCommandHistory()->attachView(actionCollection());
+ connect(m_document->getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(populate()));
+
+ m_playTable->setCurrentCell( -1, 0);
+ m_recordTable->setCurrentCell( -1, 0);
+
+ setAutoSaveSettings(DeviceManagerConfigGroup, true);
+
+ // enableButtonOK(false);
+ // enableButtonApply(false);
+
+ }
+
+ DeviceManagerDialog::~DeviceManagerDialog()
+ {
+ if (m_document) {
+ m_document->getCommandHistory()->detachView(actionCollection());
+ m_document = 0;
+ }
+
+ RG_DEBUG << "\n*** DeviceManagerDialog::~DeviceManagerDialog\n" << endl;
+ }
+
+ void
+ DeviceManagerDialog::slotClose()
+ {
+ if (m_document) {
+ m_document->getCommandHistory()->detachView(actionCollection());
+ m_document = 0;
+ }
+
+ close();
+ }
+
+ void
+ DeviceManagerDialog::slotDevicesResyncd()
+ {
+ makeConnectionList((unsigned int)MidiDevice::Play,
+ m_playConnections);
+ makeConnectionList((unsigned int)MidiDevice::Record,
+ m_recordConnections);
+
+ populate();
+ }
+
+ void
+ DeviceManagerDialog::populate()
+ {
+ DeviceList *devices = m_studio->getDevices();
+
+ //KConfig *config = kapp->config();
+ //config->setGroup(SequencerOptionsConfigGroup);
+ //DeviceId recordDevice =
+ //config->readUnsignedNumEntry("midirecorddevice");
+
+ m_playDevices.clear();
+ m_recordDevices.clear();
+
+ for (DeviceList::iterator it = devices->begin();
+ it != devices->end(); ++it) {
+ if ((*it)->getType() == Device::Midi) {
+ MidiDevice *md =
+ dynamic_cast<MidiDevice *>(*it);
+ if (md) {
+ if (md->getDirection() == MidiDevice::Play) {
+ m_playDevices.push_back(md);
+ } else {
+ m_recordDevices.push_back(md);
+ }
+ }
+ }
+ }
+
+ while (m_playTable->numRows() > 0) {
+ m_playTable->removeRow(m_playTable->numRows() - 1);
+ }
+ while (m_recordTable->numRows() > 0) {
+ m_recordTable->removeRow(m_recordTable->numRows() - 1);
+ }
+
+ int deviceCount = 0;
+
+ for (MidiDeviceList::iterator it = m_playDevices.begin();
+ it != m_playDevices.end(); ++it) {
+
+ m_playTable->insertRows(deviceCount, 1);
+
+ QString deviceName = i18n("%1").arg(deviceCount + 1);
+ QString connectionName = strtoqstr((*it)->getConnection());
+
+ m_playTable->setText(deviceCount, PLAY_NAME_COL,
+ strtoqstr((*it)->getName()));
+
+ int currentConnectionIndex = m_playConnections.size() - 1;
+ for (unsigned int i = 0; i < m_playConnections.size(); ++i) {
+ if (m_playConnections[i] == connectionName)
+ currentConnectionIndex = i;
+ }
+
+ QComboTableItem *item = new QComboTableItem(m_playTable, m_playConnections, false);
+ item->setCurrentItem(currentConnectionIndex);
+ m_playTable->setItem(deviceCount, PLAY_CONNECTION_COL, item);
+
+ m_playTable->adjustRow(deviceCount);
+ ++deviceCount;
+ }
+
+ int minPlayColumnWidths[] = { 250, 270 };
+ for (int i = 0; i < 2; ++i) {
+ m_playTable->adjustColumn(i);
+ if (m_playTable->columnWidth(i) < minPlayColumnWidths[i])
+ m_playTable->setColumnWidth(i, minPlayColumnWidths[i]);
+ }
+
+ deviceCount = 0;
+
+ for (MidiDeviceList::iterator it = m_recordDevices.begin();
+ it != m_recordDevices.end(); ++it) {
+
+ m_recordTable->insertRows(deviceCount, 1);
+
+ QString deviceName = i18n("%1").arg(deviceCount + 1);
+ QString connectionName = strtoqstr((*it)->getConnection());
+
+ m_recordTable->setText(deviceCount, RECORD_NAME_COL,
+ strtoqstr((*it)->getName()));
+
+ int currentConnectionIndex = m_recordConnections.size() - 1;
+ for (unsigned int i = 0; i < m_recordConnections.size(); ++i) {
+ if (m_recordConnections[i] == connectionName)
+ currentConnectionIndex = i;
+ }
+
+ QComboTableItem *item = new QComboTableItem(m_recordTable, m_recordConnections, false);
+ item->setCurrentItem(currentConnectionIndex);
+ m_recordTable->setItem(deviceCount, RECORD_CONNECTION_COL, item);
+
+ QCheckTableItem *check = new QCheckTableItem(m_recordTable, QString());
+ //check->setChecked((*it)->getId() == recordDevice);
+ //check->setText(((*it)->getId() == recordDevice) ?
+ // i18n("Yes") : i18n("No"));
+ check->setChecked((*it)->isRecording());
+ check->setText((*it)->isRecording() ? i18n("Yes") : i18n("No"));
+ m_recordTable->setItem(deviceCount, RECORD_CURRENT_COL, check);
+
+ m_recordTable->adjustRow(deviceCount);
+ ++deviceCount;
+ }
+
+ int minRecordColumnWidths[] = { 180, 70, 270 };
+ for (int i = 0; i < 3; ++i) {
+ m_recordTable->adjustColumn(i);
+ if (m_recordTable->columnWidth(i) < minRecordColumnWidths[i])
+ m_recordTable->setColumnWidth(i, minRecordColumnWidths[i]);
+ }
+
+ slotPlayDeviceSelected(m_playTable->currentRow(), m_playTable->currentColumn());
+ slotRecordDeviceSelected(m_recordTable->currentRow(), m_recordTable->currentColumn());
+ }
+
+ void
+ DeviceManagerDialog::makeConnectionList(unsigned int direction,
+ QStringList &list)
+ {
+ list.clear();
+
+ QByteArray data;
+ QByteArray replyData;
+ QCString replyType;
+ QDataStream arg(data, IO_WriteOnly);
+ arg << (int)Device::Midi;
+ arg << direction;
+
+ if (!rgapp->sequencerCall("getConnections(int, unsigned int)", replyType, replyData, data)) {
+ RG_DEBUG << "DeviceManagerDialog: can't call Sequencer" << endl;
+ list.append(m_noConnectionString);
+ return ;
+ }
+
+ QDataStream reply(replyData, IO_ReadOnly);
+ unsigned int connections = 0;
+ if (replyType == "unsigned int")
+ reply >> connections;
+
+ for (unsigned int i = 0; i < connections; ++i) {
+
+ QByteArray data;
+ QByteArray replyData;
+ QCString replyType;
+ QDataStream arg(data, IO_WriteOnly);
+ arg << (int)Device::Midi;
+ arg << direction;
+ arg << i;
+
+
+ if (!rgapp->sequencerCall("getConnection(int, unsigned int, unsigned int)",
+ replyType, replyData, data)) {
+ RG_DEBUG << "DeviceManagerDialog: can't call Sequencer" << endl;
+ list.append(i18n("No connection"));
+ return ;
+ }
+
+ QDataStream reply(replyData, IO_ReadOnly);
+ QString connection;
+ if (replyType == "QString") {
+ reply >> connection;
+ list.append(connection);
+ }
+ }
+
+ list.append(i18n("No connection"));
+ }
+
+ void
+ DeviceManagerDialog::closeEvent(QCloseEvent *e)
+ {
+ emit closing();
+ KMainWindow::closeEvent(e);
+ }
+
+ DeviceId
+ DeviceManagerDialog::getPlayDeviceIdAt(int row)
+ {
+ if (row < 0 || row > (int)m_playDevices.size())
+ return Device::NO_DEVICE;
+ return m_playDevices[row]->getId();
+ }
+
+ DeviceId
+ DeviceManagerDialog::getRecordDeviceIdAt(int row)
+ {
+ if (row < 0 || row > (int)m_recordDevices.size())
+ return Device::NO_DEVICE;
+ return m_recordDevices[row]->getId();
+ }
+
+ void
+ DeviceManagerDialog::slotAddPlayDevice()
+ {
+ QString connection = "";
+ if (m_playConnections.size() > 0)
+ connection = m_playConnections[m_playConnections.size() - 1];
+ CreateOrDeleteDeviceCommand *command = new CreateOrDeleteDeviceCommand
+ (m_studio,
+ qstrtostr(i18n("New Device")),
+ Device::Midi,
+ MidiDevice::Play,
+ qstrtostr(connection));
+ m_document->getCommandHistory()->addCommand(command);
+ }
+
+ void
+ DeviceManagerDialog::slotAddRecordDevice()
+ {
+ QString connection = "";
+ if (m_recordConnections.size() > 0)
+ connection = m_recordConnections[m_recordConnections.size() - 1];
+ CreateOrDeleteDeviceCommand *command = new CreateOrDeleteDeviceCommand
+ (m_studio,
+ qstrtostr(i18n("New Device")),
+ Device::Midi,
+ MidiDevice::Record,
+ qstrtostr(connection));
+ m_document->getCommandHistory()->addCommand(command);
+ }
+
+ void
+ DeviceManagerDialog::slotDeletePlayDevice()
+ {
+ // should really grey out if nothing current
+ DeviceId id = getPlayDeviceIdAt(m_playTable->currentRow());
+ if (id == Device::NO_DEVICE)
+ return ;
+ CreateOrDeleteDeviceCommand *command = new CreateOrDeleteDeviceCommand
+ (m_studio, id);
+ m_document->getCommandHistory()->addCommand(command);
+
+ QByteArray data;
+ QDataStream arg(data, IO_WriteOnly);
+ arg << (unsigned int)id;
+ rgapp->sequencerSend("removeDevice(unsigned int)", data);
+ }
+
+ void
+ DeviceManagerDialog::slotDeleteRecordDevice()
+ {
+ DeviceId id = getRecordDeviceIdAt(m_recordTable->currentRow());
+ if (id == Device::NO_DEVICE)
+ return ;
+ CreateOrDeleteDeviceCommand *command = new CreateOrDeleteDeviceCommand
+ (m_studio, id);
+ m_document->getCommandHistory()->addCommand(command);
+ }
+
+ void
+ DeviceManagerDialog::slotPlayValueChanged(int row, int col)
+ {
+ if (!m_document)
+ return ; // closing
+ DeviceId id = getPlayDeviceIdAt(row);
+ if (id == Device::NO_DEVICE)
+ return ;
+
+ Device *device = m_studio->getDevice(id);
+ if (!device) {
+ std::cerr << "WARNING: DeviceManagerDialog::slotPlayValueChanged(): device at row "
+ << row << " (id " << id << ") not found in studio"
+ << std::endl;
+ return ;
+ }
+
+ switch (col) {
+
+ case PLAY_NAME_COL: {
+ std::string name = qstrtostr(m_playTable->text(row, col));
+ if (device->getName() != name) {
+
+ m_document->getCommandHistory()->addCommand
+ (new RenameDeviceCommand(m_studio, id, name));
+ emit deviceNamesChanged();
+
+ QByteArray data;
+ QDataStream arg(data, IO_WriteOnly);
+
+ arg << (unsigned int)id;
+ arg << m_playTable->text(row, col);
+
+ rgapp->sequencerSend("renameDevice(unsigned int, QString)", data);
+ }
+ }
+ break;
+
+ case PLAY_CONNECTION_COL: {
+ std::string connection = qstrtostr(m_playTable->text(row, col));
+ if (connection == qstrtostr(m_noConnectionString))
+ connection = "";
+ if (device->getConnection() != connection) {
+ m_document->getCommandHistory()->addCommand
+ (new ReconnectDeviceCommand(m_studio, id, connection));
+ }
+ }
+ break;
+ }
+ }
+
+ void
+ DeviceManagerDialog::slotRecordValueChanged(int row, int col)
+ {
+ if (!m_document)
+ return ; // closing
+ DeviceId id = getRecordDeviceIdAt(row);
+ if (id == Device::NO_DEVICE)
+ return ;
+
+ Device *device = m_studio->getDevice(id);
+ if (!device) {
+ std::cerr << "WARNING: DeviceManagerDialog::slotRecordValueChanged(): device at row "
+ << row << " (id " << id << ") not found in studio"
+ << std::endl;
+ return ;
+ }
+
+ switch (col) {
+
+ case RECORD_NAME_COL: {
+ std::string name = qstrtostr(m_recordTable->text(row, col));
+ if (device->getName() != name) {
+
+ m_document->getCommandHistory()->addCommand
+ (new RenameDeviceCommand(m_studio, id, name));
+ emit deviceNamesChanged();
+
+ QByteArray data;
+ QDataStream arg(data, IO_WriteOnly);
+
+ arg << (unsigned int)id;
+ arg << m_recordTable->text(row, col);
+
+ rgapp->sequencerSend("renameDevice(unsigned int, QString)", data);
+ }
+ }
+ break;
+
+ case RECORD_CONNECTION_COL: {
+ std::string connection = qstrtostr(m_recordTable->text(row, col));
+ if (device->getConnection() != connection) {
+ m_document->getCommandHistory()->addCommand
+ (new ReconnectDeviceCommand(m_studio, id, connection));
+ }
+ }
+ break;
+
+ case RECORD_CURRENT_COL: {
+ m_recordTable->blockSignals(true);
+
+ QCheckTableItem *check =
+ dynamic_cast<QCheckTableItem *>(m_recordTable->item(row, col));
+ if (!check)
+ return ;
+
+ bool actionConnect = check->isChecked();
+
+ // The following lines are not strictly needed, but give the checkboxes
+ // a smoother behavior while waiting a confirmation from the sequencer.
+ //
+ check->setText(actionConnect ? i18n("Yes") : i18n("No"));
+ MidiDevice *device =
+ dynamic_cast<MidiDevice*>(m_studio->getDevice(id));
+ device->setRecording(actionConnect);
+
+ m_recordTable->setCurrentCell(row, 0);
+
+ m_document->getCommandHistory()->addCommand
+ (new ChangeRecordDeviceCommand(id, actionConnect));
+
+ m_recordTable->blockSignals(false);
+ }
+ break;
+ }
+ }
+
+ void
+ DeviceManagerDialog::slotPlayDeviceSelected(int row, int col)
+ {
+ RG_DEBUG << "slotPlayDeviceSelected(" << row << "," << col << ")" << endl;
+
+ bool enable = (row >= 0 && row < (int)m_playDevices.size());
+ m_deletePlayButton->setEnabled(enable);
+ m_importButton->setEnabled(enable);
+ m_exportButton->setEnabled(enable);
+ m_banksButton->setEnabled(enable);
+ m_controllersButton->setEnabled(enable);
+ }
+
+ void
+ DeviceManagerDialog::slotRecordDeviceSelected(int row, int col)
+ {
+ RG_DEBUG << "slotRecordDeviceSelected(" << row << "," << col << ")" << endl;
+
+ bool enable = (row >= 0 && row < (int)m_recordDevices.size());
+ m_deleteRecordButton->setEnabled(enable);
+ }
+
+ void
+ DeviceManagerDialog::slotImport()
+ {
+ DeviceId id = getPlayDeviceIdAt(m_playTable->currentRow());
+ if (id == Device::NO_DEVICE)
+ return ;
+
+ QString deviceDir = KGlobal::dirs()->findResource("appdata", "library/");
+ QDir dir(deviceDir);
+ if (!dir.exists()) {
+ deviceDir = ":ROSEGARDENDEVICE";
+ } else {
+ deviceDir = "file://" + deviceDir;
+ }
+
+ KURL url = KFileDialog::getOpenURL
+ (deviceDir,
+ "audio/x-rosegarden-device audio/x-rosegarden audio/x-soundfont",
+ this, i18n("Import from Device in File"));
+
+ if (url.isEmpty())
+ return ;
+
+ ImportDeviceDialog *dialog = new ImportDeviceDialog(this, url);
+ if (dialog->doImport() && dialog->exec() == QDialog::Accepted) {
+
+ ModifyDeviceCommand *command = 0;
+
+ BankList banks(dialog->getBanks());
+ ProgramList programs(dialog->getPrograms());
+ ControlList controls(dialog->getControllers());
+ KeyMappingList keyMappings(dialog->getKeyMappings());
+ MidiDevice::VariationType variation(dialog->getVariationType());
+ std::string librarianName(dialog->getLibrarianName());
+ std::string librarianEmail(dialog->getLibrarianEmail());
+
+ // don't record the librarian when
+ // merging banks -- it's misleading.
+ // (also don't use variation type)
+ if (!dialog->shouldOverwriteBanks()) {
+ librarianName = "";
+ librarianEmail = "";
+ }
+
+ command = new ModifyDeviceCommand(m_studio,
+ id,
+ dialog->getDeviceName(),
+ librarianName,
+ librarianEmail);
+
+ if (dialog->shouldOverwriteBanks()) {
+ command->setVariation(variation);
+ }
+ if (dialog->shouldImportBanks()) {
+ command->setBankList(banks);
+ command->setProgramList(programs);
+ }
+ if (dialog->shouldImportControllers()) {
+ command->setControlList(controls);
+ }
+ if (dialog->shouldImportKeyMappings()) {
+ command->setKeyMappingList(keyMappings);
+ }
+
+ command->setOverwrite(dialog->shouldOverwriteBanks());
+ command->setRename(dialog->shouldRename());
+
+ m_document->getCommandHistory()->addCommand(command);
+
+ if (dialog->shouldRename())
+ emit deviceNamesChanged();
+ }
+
+ delete dialog;
+ }
+
+ void
+ DeviceManagerDialog::slotExport()
+ {
+ QString extension = "rgd";
+
+ QString name =
+ KFileDialog::getSaveFileName(":ROSEGARDEN",
+ (extension.isEmpty() ? QString("*") : ("*." + extension)),
+ this,
+ i18n("Export Device as..."));
+
+ // Check for the existence of the name
+ if (name.isEmpty())
+ return ;
+
+ // Append extension if we don't have one
+ //
+ if (!extension.isEmpty()) {
+ if (!name.endsWith("." + extension)) {
+ name += "." + extension;
+ }
+ }
+
+ QFileInfo info(name);
+
+ if (info.isDir()) {
+ KMessageBox::sorry(this, i18n("You have specified a directory"));
+ return ;
+ }
+
+ if (info.exists()) {
+ int overwrite = KMessageBox::questionYesNo
+ (this, i18n("The specified file exists. Overwrite?"));
+
+ if (overwrite != KMessageBox::Yes)
+ return ;
+
+ }
+
+ std::vector<DeviceId> devices;
+ DeviceId id = getPlayDeviceIdAt(m_playTable->currentRow());
+ MidiDevice *md = 0;
+ if (id != Device::NO_DEVICE) {
+ md = dynamic_cast<MidiDevice *>(m_studio->getDevice(id));
+ }
+ if (md) {
+ ExportDeviceDialog ed(this, strtoqstr(md->getName()));
+ if (ed.exec() != QDialog::Accepted)
+ return ;
+ if (ed.getExportType() == ExportDeviceDialog::ExportOne) {
+ devices.push_back(id);
+ }
+ }
+
+ m_document->exportStudio(name, devices);
+ }
+
+ void
+ DeviceManagerDialog::slotSetBanks()
+ {
+ DeviceId id = getPlayDeviceIdAt(m_playTable->currentRow());
+ emit editBanks(id);
+ }
+
+ void
+ DeviceManagerDialog::slotSetControllers()
+ {
+ DeviceId id = getPlayDeviceIdAt(m_playTable->currentRow());
+ emit editControllers(id);
+ }
+
+ }
+#include "DeviceManagerDialog.moc"
diff --git a/src/gui/studio/DeviceManagerDialog.h b/src/gui/studio/DeviceManagerDialog.h
new file mode 100644
index 0000000..aebc54e
--- /dev/null
+++ b/src/gui/studio/DeviceManagerDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_DEVICEMANAGERDIALOG_H_
+#define _RG_DEVICEMANAGERDIALOG_H_
+
+#include "base/Device.h"
+#include <kmainwindow.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <vector>
+
+
+class QWidget;
+class QTable;
+class QPushButton;
+class QCloseEvent;
+
+
+namespace Rosegarden
+{
+
+class Studio;
+class RosegardenGUIDoc;
+class MidiDevice;
+
+
+class DeviceManagerDialog : public KMainWindow
+{
+ Q_OBJECT
+public:
+ DeviceManagerDialog(QWidget *parent, RosegardenGUIDoc *document);
+ ~DeviceManagerDialog();
+
+ void setModified(bool value);
+
+signals:
+ void deviceNamesChanged();
+
+ void editBanks(DeviceId);
+ void editControllers(DeviceId);
+
+ void closing();
+
+protected slots:
+ void slotClose();
+ void slotAddPlayDevice();
+ void slotAddRecordDevice();
+ void slotDeletePlayDevice();
+ void slotDeleteRecordDevice();
+ void slotPlayValueChanged(int row, int col);
+ void slotRecordValueChanged(int row, int col);
+ void slotPlayDeviceSelected(int row, int col);
+ void slotRecordDeviceSelected(int row, int col);
+
+ // for play devices only:
+ void slotImport();
+ void slotExport();
+ void slotSetBanks();
+ void slotSetControllers();
+
+ void slotDevicesResyncd();
+ void populate();
+
+protected:
+ virtual void closeEvent(QCloseEvent *);
+
+private:
+ RosegardenGUIDoc *m_document;
+ Studio *m_studio;
+
+ QPushButton *m_deletePlayButton;
+ QPushButton *m_deleteRecordButton;
+ QPushButton *m_importButton;
+ QPushButton *m_exportButton;
+ QPushButton *m_banksButton;
+ QPushButton *m_controllersButton;
+
+ QStringList m_playConnections;
+ QStringList m_recordConnections;
+ void makeConnectionList(unsigned int direction, QStringList &list);
+
+ QTable *m_playTable;
+ QTable *m_recordTable;
+
+ typedef std::vector<MidiDevice *> MidiDeviceList;
+ MidiDeviceList m_playDevices;
+ MidiDeviceList m_recordDevices;
+
+ DeviceId getPlayDeviceIdAt(int row); // NO_DEVICE = not found
+ DeviceId getRecordDeviceIdAt(int row); // NO_DEVICE = not found
+
+ QString m_noConnectionString;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/MidiBankListViewItem.cpp b/src/gui/studio/MidiBankListViewItem.cpp
new file mode 100644
index 0000000..2563ccb
--- /dev/null
+++ b/src/gui/studio/MidiBankListViewItem.cpp
@@ -0,0 +1,98 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "MidiBankListViewItem.h"
+
+#include <klocale.h>
+#include "base/Device.h"
+#include "MidiDeviceListViewItem.h"
+#include "MidiKeyMapListViewItem.h"
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+MidiBankListViewItem::MidiBankListViewItem(DeviceId deviceId,
+ int bankNb,
+ QListViewItem* parent,
+ QString name,
+ bool percussion, int msb, int lsb)
+ : MidiDeviceListViewItem(deviceId, parent, name, percussion, msb, lsb),
+ m_percussion(percussion),
+ m_bankNb(bankNb)
+{}
+
+void MidiBankListViewItem::setPercussion(bool percussion)
+{
+ m_percussion = percussion;
+ setText(1, QString(percussion ? i18n("Percussion Bank") : i18n("Bank")));
+}
+
+void MidiBankListViewItem::setMSB(int msb)
+{
+ setText(2, QString().setNum(msb));
+}
+
+void MidiBankListViewItem::setLSB(int lsb)
+{
+ setText(3, QString().setNum(lsb));
+}
+
+int MidiBankListViewItem::compare(QListViewItem *i, int col, bool ascending) const
+{
+ MidiBankListViewItem* bankItem = dynamic_cast<MidiBankListViewItem*>(i);
+
+ if (!bankItem) {
+ MidiKeyMapListViewItem *keyItem = dynamic_cast<MidiKeyMapListViewItem *>(i);
+ if (keyItem)
+ return -1; // banks before key maps
+ }
+
+ if (!bankItem || (col != 2 && col != 3)) {
+ return MidiDeviceListViewItem::compare(i, col, ascending);
+ }
+
+ int thisVal = text(col).toInt(),
+ otherVal = bankItem->text(col).toInt();
+
+ if (thisVal == otherVal) {
+ if (col == 2) { // if sorting on MSB, suborder with LSB
+ return compare(i, 3, ascending);
+ } else {
+ return 0;
+ }
+ }
+
+ // 'ascending' should be ignored according to Qt docs
+ //
+ return
+ thisVal > otherVal ? 1 :
+ thisVal == otherVal ? 0 :
+ -1;
+
+}
+
+}
diff --git a/src/gui/studio/MidiBankListViewItem.h b/src/gui/studio/MidiBankListViewItem.h
new file mode 100644
index 0000000..87f4b02
--- /dev/null
+++ b/src/gui/studio/MidiBankListViewItem.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_MIDIBANKLISTVIEWITEM_H_
+#define _RG_MIDIBANKLISTVIEWITEM_H_
+
+#include "base/Device.h"
+#include "MidiDeviceListViewItem.h"
+#include <qstring.h>
+
+
+class QListViewItem;
+
+
+namespace Rosegarden
+{
+
+
+
+class MidiBankListViewItem : public MidiDeviceListViewItem
+{
+public:
+ MidiBankListViewItem(DeviceId deviceId,
+ int bankNb,
+ QListViewItem* parent, QString name,
+ bool percussion,
+ int msb, int lsb);
+
+ int getBank() { return m_bankNb; }
+
+ void setPercussion(bool percussion);
+ bool isPercussion() const { return m_percussion; }
+ void setMSB(int msb);
+ void setLSB(int msb);
+
+ virtual int compare(QListViewItem *i, int col, bool ascending) const;
+
+protected:
+
+ //--------------- Data members ---------------------------------
+ bool m_percussion;
+ int m_bankNb;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/MidiDeviceListViewItem.cpp b/src/gui/studio/MidiDeviceListViewItem.cpp
new file mode 100644
index 0000000..30a339e
--- /dev/null
+++ b/src/gui/studio/MidiDeviceListViewItem.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "MidiDeviceListViewItem.h"
+
+#include <klocale.h>
+#include "base/Device.h"
+#include <qlistview.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+MidiDeviceListViewItem::MidiDeviceListViewItem(DeviceId deviceId,
+ QListView* parent, QString name)
+ : KListViewItem(parent, name),
+ m_deviceId(deviceId)
+{}
+
+MidiDeviceListViewItem::MidiDeviceListViewItem(DeviceId deviceId,
+ QListViewItem* parent, QString name,
+ bool percussion,
+ int msb, int lsb)
+ : KListViewItem(parent, name,
+ QString(percussion ? i18n("Percussion Bank") : i18n("Bank")),
+ QString().setNum(msb), QString().setNum(lsb)),
+ m_deviceId(deviceId)
+{}
+
+MidiDeviceListViewItem::MidiDeviceListViewItem(DeviceId deviceId,
+ QListViewItem* parent, QString name)
+: KListViewItem(parent, name, i18n("Key Mapping"), "", ""),
+m_deviceId(deviceId)
+{}
+
+int MidiDeviceListViewItem::compare(QListViewItem *i, int col, bool ascending) const
+{
+ MidiDeviceListViewItem* item = dynamic_cast<MidiDeviceListViewItem*>(i);
+ if (!item)
+ return QListViewItem::compare(i, col, ascending);
+ if (col == 0)
+ return
+ getDeviceId() > item->getDeviceId() ? 1 :
+ getDeviceId() == item->getDeviceId() ? 0 :
+ -1;
+
+ int thisVal = text(col).toInt(),
+ otherVal = item->text(col).toInt();
+
+ if (thisVal == otherVal) {
+ if (col == 2) { // if sorting on MSB, suborder with LSB
+ return compare(i, 3, ascending);
+ } else {
+ return 0;
+ }
+ }
+
+ // 'ascending' should be ignored according to Qt docs
+ //
+ return (thisVal > otherVal) ? 1 : -1;
+
+ //!!! how to use percussion here?
+}
+
+}
diff --git a/src/gui/studio/MidiDeviceListViewItem.h b/src/gui/studio/MidiDeviceListViewItem.h
new file mode 100644
index 0000000..53652c6
--- /dev/null
+++ b/src/gui/studio/MidiDeviceListViewItem.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_MIDIDEVICELISTVIEWITEM_H_
+#define _RG_MIDIDEVICELISTVIEWITEM_H_
+
+#include "base/Device.h"
+#include <klistview.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+
+
+class MidiDeviceListViewItem : public KListViewItem
+{
+public:
+ // Device
+ MidiDeviceListViewItem(DeviceId id,
+ QListView* parent, QString name);
+
+ // Bank
+ MidiDeviceListViewItem(DeviceId id,
+ QListViewItem* parent, QString name,
+ bool percussion,
+ int msb, int lsb);
+
+ // Key Mapping
+ MidiDeviceListViewItem(DeviceId id,
+ QListViewItem* parent, QString name);
+
+ DeviceId getDeviceId() const { return m_deviceId; }
+
+ virtual int compare(QListViewItem *i, int col, bool ascending) const;
+
+protected:
+
+ //--------------- Data members ---------------------------------
+ DeviceId m_deviceId;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/MidiKeyMapListViewItem.cpp b/src/gui/studio/MidiKeyMapListViewItem.cpp
new file mode 100644
index 0000000..c070e04
--- /dev/null
+++ b/src/gui/studio/MidiKeyMapListViewItem.cpp
@@ -0,0 +1,56 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "MidiKeyMapListViewItem.h"
+
+#include "MidiDeviceListViewItem.h"
+#include "MidiBankListViewItem.h"
+#include "base/Device.h"
+#include <klocale.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+MidiKeyMapListViewItem::MidiKeyMapListViewItem(DeviceId deviceId,
+ QListViewItem* parent,
+ QString name)
+ : MidiDeviceListViewItem(deviceId, parent, name),
+ m_name(name)
+{
+ setText(1, i18n("Key Mapping"));
+}
+
+int MidiKeyMapListViewItem::compare(QListViewItem *i, int col, bool ascending) const
+{
+ if (dynamic_cast<MidiBankListViewItem *>(i)) {
+ return 1; // banks before key maps
+ }
+
+ return MidiDeviceListViewItem::compare(i, col, ascending);
+}
+
+}
diff --git a/src/gui/studio/MidiKeyMapListViewItem.h b/src/gui/studio/MidiKeyMapListViewItem.h
new file mode 100644
index 0000000..df53de3
--- /dev/null
+++ b/src/gui/studio/MidiKeyMapListViewItem.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_MIDIKEYMAPLISTVIEWITEM_H_
+#define _RG_MIDIKEYMAPLISTVIEWITEM_H_
+
+#include "base/Device.h"
+#include "MidiDeviceListViewItem.h"
+#include <qstring.h>
+
+
+class QListViewItem;
+
+
+namespace Rosegarden
+{
+
+
+
+class MidiKeyMapListViewItem : public MidiDeviceListViewItem
+{
+public:
+ MidiKeyMapListViewItem(DeviceId deviceId,
+ QListViewItem* parent, QString name);
+
+ virtual int compare(QListViewItem *i, int col, bool ascending) const;
+
+ QString getName() const { return m_name; }
+
+protected:
+ QString m_name;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/MidiKeyMappingEditor.cpp b/src/gui/studio/MidiKeyMappingEditor.cpp
new file mode 100644
index 0000000..f31d0dc
--- /dev/null
+++ b/src/gui/studio/MidiKeyMappingEditor.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "MidiKeyMappingEditor.h"
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "BankEditorDialog.h"
+#include "base/MidiDevice.h"
+#include "base/MidiProgram.h"
+#include "base/NotationTypes.h"
+#include "MidiKeyMapListViewItem.h"
+#include "NameSetEditor.h"
+#include <kcompletion.h>
+#include <klineedit.h>
+#include <qframe.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qobject.h>
+#include <qobjectlist.h>
+#include <qpushbutton.h>
+#include <qstring.h>
+#include <qvgroupbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+MidiKeyMappingEditor::MidiKeyMappingEditor(BankEditorDialog* bankEditor,
+ QWidget* parent,
+ const char* name)
+ : NameSetEditor(bankEditor,
+ i18n("Key Mapping details"),
+ parent, name, i18n("Pitches"), false),
+ m_device(0)
+{
+ QWidget *additionalWidget = makeAdditionalWidget(m_mainFrame);
+ if (additionalWidget) {
+ m_mainLayout->addMultiCellWidget(additionalWidget, 0, 2, 0, 2);
+ }
+}
+
+QWidget *
+MidiKeyMappingEditor::makeAdditionalWidget(QWidget *parent)
+{
+ return 0;
+}
+
+void
+MidiKeyMappingEditor::clearAll()
+{
+ blockAllSignals(true);
+
+ for (unsigned int i = 0; i < m_names.size(); ++i)
+ m_names[i]->clear();
+
+ setTitle(i18n("Key Mapping details"));
+
+ setEnabled(false);
+
+ blockAllSignals(false);
+}
+
+void
+MidiKeyMappingEditor::populate(QListViewItem* item)
+{
+ RG_DEBUG << "MidiKeyMappingEditor::populate\n";
+
+ MidiKeyMapListViewItem *keyItem =
+ dynamic_cast<MidiKeyMapListViewItem *>(item);
+ if (!keyItem) {
+ RG_DEBUG << "MidiKeyMappingEditor::populate : not a key item - returning\n";
+ return ;
+ }
+
+ MidiDevice* device = m_bankEditor->getCurrentMidiDevice();
+ if (!device)
+ return ;
+
+ m_device = device;
+ m_mappingName = qstrtostr(keyItem->getName());
+
+ setEnabled(true);
+
+ reset();
+}
+
+void
+MidiKeyMappingEditor::reset()
+{
+ if (!m_device)
+ return ;
+
+ setTitle(strtoqstr(m_mappingName));
+
+ const MidiKeyMapping *m = m_device->getKeyMappingByName(m_mappingName);
+
+ if (!m) {
+ RG_DEBUG << "WARNING: MidiKeyMappingEditor::reset: No such mapping as " << m_mappingName << endl;
+ }
+
+ m_mapping = *m;
+
+ blockAllSignals(true);
+
+ // Librarian details
+ //
+ m_librarian->setText(strtoqstr(m_device->getLibrarianName()));
+ m_librarianEmail->setText(strtoqstr(m_device->getLibrarianEmail()));
+
+ for (MidiKeyMapping::KeyNameMap::const_iterator it =
+ m_mapping.getMap().begin();
+ it != m_mapping.getMap().end(); ++it) {
+
+ int i = it->first;
+ if (i < 0 || i > 127) {
+ RG_DEBUG << "WARNING: MidiKeyMappingEditor::reset: Key " << i
+ << " out of range in mapping " << m_mapping.getName()
+ << endl;
+ continue;
+ }
+
+ QString name = strtoqstr(it->second);
+ m_completion.addItem(name);
+ m_names[i]->setText(name);
+ m_names[i]->setCursorPosition(0);
+ }
+
+ blockAllSignals(false);
+}
+
+void
+MidiKeyMappingEditor::slotNameChanged(const QString& name)
+{
+ const KLineEdit* lineEdit = dynamic_cast<const KLineEdit*>(sender());
+ if (!lineEdit) {
+ RG_DEBUG << "MidiKeyMappingEditor::slotNameChanged() : %%% ERROR - signal sender is not a KLineEdit\n";
+ return ;
+ }
+
+ QString senderName = sender()->name();
+
+ // Adjust value back to zero rated
+ //
+ unsigned int pitch = senderName.toUInt() - 1;
+
+ RG_DEBUG << "MidiKeyMappingEditor::slotNameChanged("
+ << name << ") : pitch = " << pitch << endl;
+
+ if (qstrtostr(name) != m_mapping.getMap()[pitch]) {
+ m_mapping.getMap()[pitch] = qstrtostr(name);
+ m_bankEditor->setModified(true);
+ }
+}
+
+void
+MidiKeyMappingEditor::slotEntryButtonPressed()
+{}
+
+void MidiKeyMappingEditor::blockAllSignals(bool block)
+{
+ const QObjectList* allChildren = queryList("KLineEdit", "[0-9]+");
+ QObjectListIt it(*allChildren);
+ QObject *obj;
+
+ while ( (obj = it.current()) != 0 ) {
+ obj->blockSignals(block);
+ ++it;
+ }
+}
+
+}
+#include "MidiKeyMappingEditor.moc"
diff --git a/src/gui/studio/MidiKeyMappingEditor.h b/src/gui/studio/MidiKeyMappingEditor.h
new file mode 100644
index 0000000..86959b8
--- /dev/null
+++ b/src/gui/studio/MidiKeyMappingEditor.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_MIDIKEYMAPPINGEDITOR_H_
+#define _RG_MIDIKEYMAPPINGEDITOR_H_
+
+#include "base/MidiProgram.h"
+#include "NameSetEditor.h"
+#include <string>
+
+
+class QWidget;
+class QString;
+class QListViewItem;
+
+
+namespace Rosegarden
+{
+
+class MidiDevice;
+class BankEditorDialog;
+
+
+class MidiKeyMappingEditor : public NameSetEditor
+{
+ Q_OBJECT
+
+public:
+ MidiKeyMappingEditor(BankEditorDialog *bankEditor,
+ QWidget *parent,
+ const char *name = 0);
+
+ void clearAll();
+ void populate(QListViewItem *);
+ MidiKeyMapping &getMapping() { return m_mapping; }
+ void reset();
+
+public slots:
+ virtual void slotNameChanged(const QString &);
+ virtual void slotEntryButtonPressed();
+
+protected:
+ virtual QWidget *makeAdditionalWidget(QWidget *parent);
+ void blockAllSignals(bool block);
+
+ //--------------- Data members ---------------------------------
+
+ MidiDevice *m_device;
+ std::string m_mappingName;
+ MidiKeyMapping m_mapping;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/MidiMixerVUMeter.cpp b/src/gui/studio/MidiMixerVUMeter.cpp
new file mode 100644
index 0000000..2dd86cc
--- /dev/null
+++ b/src/gui/studio/MidiMixerVUMeter.cpp
@@ -0,0 +1,53 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "MidiMixerVUMeter.h"
+
+#include "gui/widgets/VUMeter.h"
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+MidiMixerVUMeter::MidiMixerVUMeter(QWidget *parent,
+ VUMeterType type,
+ int width,
+ int height,
+ const char *name):
+ VUMeter(parent, type, false, false, width, height, VUMeter::Vertical, name)
+{
+ setAlignment(AlignCenter);
+}
+
+void
+MidiMixerVUMeter::meterStart()
+{}
+
+void
+MidiMixerVUMeter::meterStop()
+{}
+
+}
diff --git a/src/gui/studio/MidiMixerVUMeter.h b/src/gui/studio/MidiMixerVUMeter.h
new file mode 100644
index 0000000..f67d922
--- /dev/null
+++ b/src/gui/studio/MidiMixerVUMeter.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_MIDIMIXERVUMETER_H_
+#define _RG_MIDIMIXERVUMETER_H_
+
+#include "gui/widgets/VUMeter.h"
+
+
+class QWidget;
+
+
+namespace Rosegarden
+{
+
+
+
+class MidiMixerVUMeter : public VUMeter
+{
+public:
+ MidiMixerVUMeter(QWidget *parent = 0,
+ VUMeterType type = Plain,
+ int width = 0,
+ int height = 0,
+ const char *name = 0);
+
+protected:
+ virtual void meterStart();
+ virtual void meterStop();
+
+private:
+ int m_textHeight;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/MidiMixerWindow.cpp b/src/gui/studio/MidiMixerWindow.cpp
new file mode 100644
index 0000000..127db7f
--- /dev/null
+++ b/src/gui/studio/MidiMixerWindow.cpp
@@ -0,0 +1,742 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "MidiMixerWindow.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/Device.h"
+#include "base/Instrument.h"
+#include "base/MidiDevice.h"
+#include "base/MidiProgram.h"
+#include "base/Studio.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/editors/notation/NotePixmapFactory.h"
+#include "gui/seqmanager/SequencerMapper.h"
+#include "gui/widgets/Fader.h"
+#include "gui/widgets/Rotary.h"
+#include "gui/widgets/VUMeter.h"
+#include "MidiMixerVUMeter.h"
+#include "MixerWindow.h"
+#include "sound/MappedEvent.h"
+#include "StudioControl.h"
+#include <kaction.h>
+#include <kmainwindow.h>
+#include <kstdaction.h>
+#include <qaccel.h>
+#include <qcolor.h>
+#include <qframe.h>
+#include <qiconset.h>
+#include <qlabel.h>
+#include <qobject.h>
+#include <qstring.h>
+#include <qtabwidget.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+MidiMixerWindow::MidiMixerWindow(QWidget *parent,
+ RosegardenGUIDoc *document):
+ MixerWindow(parent, document),
+ m_tabFrame(0)
+{
+ // Initial setup
+ //
+ setupTabs();
+
+ KStdAction::close(this,
+ SLOT(slotClose()),
+ actionCollection());
+
+ QIconSet 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-record")));
+ new KAction(i18n("&Record"), icon, 0, this,
+ SIGNAL(record()), actionCollection(),
+ "record");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-panic")));
+ new KAction(i18n("Panic"), icon, Key_P + CTRL + ALT, this,
+ SIGNAL(panic()), actionCollection(),
+ "panic");
+
+ createGUI("midimixer.rc");
+
+}
+
+void
+MidiMixerWindow::setupTabs()
+{
+ DeviceListConstIterator it;
+ MidiDevice *dev = 0;
+ InstrumentList instruments;
+ InstrumentList::const_iterator iIt;
+ int faderCount = 0, deviceCount = 1;
+
+ if (m_tabFrame)
+ delete m_tabFrame;
+
+ // Setup m_tabFrame
+ //
+ m_tabWidget = new QTabWidget(this);
+ setCentralWidget(m_tabWidget);
+ connect(m_tabWidget, SIGNAL(currentChanged(QWidget *)),
+ this, SLOT(slotCurrentTabChanged(QWidget *)));
+ m_tabWidget->setTabPosition(QTabWidget::Bottom);
+ setCaption(i18n("MIDI Mixer"));
+
+ for (it = m_studio->begin(); it != m_studio->end(); ++it) {
+ dev = dynamic_cast<MidiDevice*>(*it);
+
+ if (dev) {
+ // Get the control parameters that are on the IPB (and hence can
+ // be shown here too).
+ //
+ ControlList controls = dev->getIPBControlParameters();
+
+ instruments = dev->getPresentationInstruments();
+
+ // Don't add a frame for empty devices
+ //
+ if (!instruments.size())
+ continue;
+
+ m_tabFrame = new QFrame(m_tabWidget);
+ m_tabFrame->setFrameStyle(QFrame::TabWidgetPanel);
+ m_tabFrame->setMargin(10);
+
+ QGridLayout *mainLayout = new QGridLayout
+ (m_tabFrame, instruments.size() + 4, controls.size() + 4, 5);
+
+ // MIDI Mixer label
+ //
+ //QLabel *label = new QLabel(QString("%1 %2").
+ //arg(strtoqstr(dev->getName()))
+ //.arg(i18n("MIDI Mixer")), m_tabFrame);
+
+ QLabel *label = new QLabel("", m_tabFrame);
+ mainLayout->addMultiCellWidget(label, 0, 0, 0, 16, Qt::AlignCenter);
+
+ // control labels
+ for (unsigned int i = 0; i < controls.size(); ++i) {
+ label = new QLabel(strtoqstr(controls[i].getName()), m_tabFrame);
+ mainLayout->addWidget(label, i + 1, 0, Qt::AlignCenter);
+ }
+
+ // meter label
+ //
+ //label = new QLabel(i18n("Meter"), m_tabFrame);
+ //mainLayout->addWidget(label,
+ //controls.size() + 1, 0, Qt::AlignCenter);
+
+ // volume label
+ label = new QLabel(i18n("Volume"), m_tabFrame);
+ mainLayout->addWidget(label, controls.size() + 2, 0,
+ Qt::AlignCenter);
+
+ // instrument label
+ label = new QLabel(i18n("Instrument"), m_tabFrame);
+ mainLayout->addWidget(label, controls.size() + 3, 0,
+ Qt::AlignCenter);
+
+ int posCount = 1;
+ int firstInstrument = -1;
+
+ for (iIt = instruments.begin(); iIt != instruments.end(); ++iIt) {
+
+ // Add new fader struct
+ //
+ m_faders.push_back(new FaderStruct());
+
+ // Store the first ID
+ //
+ if (firstInstrument == -1)
+ firstInstrument = (*iIt)->getId();
+
+
+ // Add the controls
+ //
+ for (unsigned int i = 0; i < controls.size(); ++i) {
+ QColor knobColour = Qt::white;
+
+ if (controls[i].getColourIndex() > 0) {
+ Colour c =
+ m_document->getComposition().getGeneralColourMap().
+ getColourByIndex(controls[i].getColourIndex());
+
+ knobColour = QColor(c.getRed(),
+ c.getGreen(), c.getBlue());
+ }
+
+ Rotary *controller =
+ new Rotary(m_tabFrame,
+ controls[i].getMin(),
+ controls[i].getMax(),
+ 1.0,
+ 5.0,
+ controls[i].getDefault(),
+ 20,
+ Rotary::NoTicks,
+ false,
+ controls[i].getDefault() == 64); //!!! hacky
+
+ controller->setKnobColour(knobColour);
+
+ connect(controller, SIGNAL(valueChanged(float)),
+ this, SLOT(slotControllerChanged(float)));
+
+ mainLayout->addWidget(controller, i + 1, posCount,
+ Qt::AlignCenter);
+
+ // Store the rotary
+ //
+ m_faders[faderCount]->m_controllerRotaries.push_back(
+ std::pair<MidiByte, Rotary*>
+ (controls[i].getControllerValue(), controller));
+ }
+
+ // Pan rotary
+ //
+ MidiMixerVUMeter *meter =
+ new MidiMixerVUMeter(m_tabFrame,
+ VUMeter::FixedHeightVisiblePeakHold, 6, 30);
+ mainLayout->addWidget(meter, controls.size() + 1,
+ posCount, Qt::AlignCenter);
+ m_faders[faderCount]->m_vuMeter = meter;
+
+ // Volume fader
+ //
+ Fader *fader =
+ new Fader(0, 127, 100, 20, 80, m_tabFrame);
+ mainLayout->addWidget(fader, controls.size() + 2,
+ posCount, Qt::AlignCenter);
+ m_faders[faderCount]->m_volumeFader = fader;
+ //fader->setFader(float((*iIt)->getVolume()));
+
+ // Label
+ //
+ QLabel *idLabel = new QLabel(QString("%1").
+ arg((*iIt)->getId() - firstInstrument + 1),
+ m_tabFrame, "idLabel");
+
+ mainLayout->addWidget(idLabel, controls.size() + 3,
+ posCount, Qt::AlignCenter);
+
+ // store id in struct
+ m_faders[faderCount]->m_id = (*iIt)->getId();
+
+ // Connect them up
+ //
+ connect(fader, SIGNAL(faderChanged(float)),
+ this, SLOT(slotFaderLevelChanged(float)));
+
+ // Update all the faders and controllers
+ //
+ slotUpdateInstrument((*iIt)->getId());
+
+ // Increment counters
+ //
+ posCount++;
+ faderCount++;
+ }
+
+ QString name = QString("%1 (%2)").arg(strtoqstr(dev->getName()))
+ .arg(deviceCount++);
+
+ addTab(m_tabFrame, name);
+ }
+ }
+}
+
+void
+MidiMixerWindow::addTab(QWidget *tab, const QString &title)
+{
+ m_tabWidget->addTab(tab, title);
+}
+
+void
+MidiMixerWindow::slotFaderLevelChanged(float value)
+{
+ const QObject *s = sender();
+
+ for (FaderVector::const_iterator it = m_faders.begin();
+ it != m_faders.end(); ++it) {
+ if ((*it)->m_volumeFader == s) {
+ Instrument *instr = m_studio->
+ getInstrumentById((*it)->m_id);
+
+ if (instr) {
+
+ instr->setVolume(MidiByte(value));
+
+ MappedEvent mE((*it)->m_id,
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_VOLUME,
+ MidiByte(value));
+ StudioControl::sendMappedEvent(mE);
+
+ // send out to external controllers as well.
+ //!!! really want some notification of whether we have any!
+ int tabIndex = m_tabWidget->currentPageIndex();
+ if (tabIndex < 0)
+ tabIndex = 0;
+ int i = 0;
+ for (DeviceList::const_iterator dit = m_studio->begin();
+ dit != m_studio->end(); ++dit) {
+ RG_DEBUG << "slotFaderLevelChanged: i = " << i << ", tabIndex " << tabIndex << endl;
+ if (!dynamic_cast<MidiDevice*>(*dit))
+ continue;
+ if (i != tabIndex) {
+ ++i;
+ continue;
+ }
+ RG_DEBUG << "slotFaderLevelChanged: device id = " << instr->getDevice()->getId() << ", visible device id " << (*dit)->getId() << endl;
+ if (instr->getDevice()->getId() == (*dit)->getId()) {
+ RG_DEBUG << "slotFaderLevelChanged: sending control device mapped event for channel " << instr->getMidiChannel() << endl;
+ mE.setRecordedChannel(instr->getMidiChannel());
+ mE.setRecordedDevice(Device::CONTROL_DEVICE);
+ StudioControl::sendMappedEvent(mE);
+ }
+ break;
+ }
+ }
+
+ emit instrumentParametersChanged((*it)->m_id);
+ return ;
+ }
+ }
+}
+
+void
+MidiMixerWindow::slotControllerChanged(float value)
+{
+ const QObject *s = sender();
+ unsigned int i = 0, j = 0;
+
+ for (i = 0; i < m_faders.size(); ++i) {
+ for (j = 0; j < m_faders[i]->m_controllerRotaries.size(); ++j) {
+ if (m_faders[i]->m_controllerRotaries[j].second == s)
+ break;
+ }
+
+ // break out on match
+ if (j != m_faders[i]->m_controllerRotaries.size())
+ break;
+ }
+
+ // Don't do anything if we've not matched and got solid values
+ // for i and j
+ //
+ if (i == m_faders.size() || j == m_faders[i]->m_controllerRotaries.size())
+ return ;
+
+ //RG_DEBUG << "MidiMixerWindow::slotControllerChanged - found a controller"
+ //<< endl;
+
+ Instrument *instr = m_studio->getInstrumentById(
+ m_faders[i]->m_id);
+
+ if (instr) {
+
+ //RG_DEBUG << "MidiMixerWindow::slotControllerChanged - "
+ //<< "got instrument to change" << endl;
+
+ if (m_faders[i]->m_controllerRotaries[j].first ==
+ MIDI_CONTROLLER_PAN)
+ instr->setPan(MidiByte(value));
+ else {
+ instr->setControllerValue(m_faders[i]->
+ m_controllerRotaries[j].first,
+ MidiByte(value));
+ }
+
+ MappedEvent mE(m_faders[i]->m_id,
+ MappedEvent::MidiController,
+ m_faders[i]->m_controllerRotaries[j].first,
+ MidiByte(value));
+ StudioControl::sendMappedEvent(mE);
+
+ int tabIndex = m_tabWidget->currentPageIndex();
+ if (tabIndex < 0)
+ tabIndex = 0;
+ int i = 0;
+ for (DeviceList::const_iterator dit = m_studio->begin();
+ dit != m_studio->end(); ++dit) {
+ RG_DEBUG << "slotControllerChanged: i = " << i << ", tabIndex " << tabIndex << endl;
+ if (!dynamic_cast<MidiDevice*>(*dit))
+ continue;
+ if (i != tabIndex) {
+ ++i;
+ continue;
+ }
+ RG_DEBUG << "slotControllerChanged: device id = " << instr->getDevice()->getId() << ", visible device id " << (*dit)->getId() << endl;
+ if (instr->getDevice()->getId() == (*dit)->getId()) {
+ RG_DEBUG << "slotControllerChanged: sending control device mapped event for channel " << instr->getMidiChannel() << endl;
+ // send out to external controllers as well.
+ //!!! really want some notification of whether we have any!
+ mE.setRecordedChannel(instr->getMidiChannel());
+ mE.setRecordedDevice(Device::CONTROL_DEVICE);
+ StudioControl::sendMappedEvent(mE);
+ }
+ }
+
+ emit instrumentParametersChanged(m_faders[i]->m_id);
+ }
+}
+
+void
+MidiMixerWindow::slotUpdateInstrument(InstrumentId id)
+{
+ //RG_DEBUG << "MidiMixerWindow::slotUpdateInstrument - id = " << id << endl;
+
+ DeviceListConstIterator it;
+ MidiDevice *dev = 0;
+ InstrumentList instruments;
+ InstrumentList::const_iterator iIt;
+ int count = 0;
+
+ blockSignals(true);
+
+ for (it = m_studio->begin(); it != m_studio->end(); ++it) {
+ dev = dynamic_cast<MidiDevice*>(*it);
+
+ if (dev) {
+ instruments = dev->getPresentationInstruments();
+ ControlList controls = dev->getIPBControlParameters();
+
+ for (iIt = instruments.begin(); iIt != instruments.end(); ++iIt) {
+ // Match and set
+ //
+ if ((*iIt)->getId() == id) {
+ // Set Volume fader
+ //
+ m_faders[count]->m_volumeFader->blockSignals(true);
+ m_faders[count]->m_volumeFader->
+ setFader(float((*iIt)->getVolume()));
+ m_faders[count]->m_volumeFader->blockSignals(false);
+
+ /*
+ StaticControllers &staticControls =
+ (*iIt)->getStaticControllers();
+ RG_DEBUG << "STATIC CONTROLS SIZE = "
+ << staticControls.size() << endl;
+ */
+
+ // Set all controllers for this Instrument
+ //
+ for (unsigned int i = 0; i < controls.size(); ++i) {
+ float value = 0.0;
+
+ m_faders[count]->m_controllerRotaries[i].second->blockSignals(true);
+
+ if (controls[i].getControllerValue() ==
+ MIDI_CONTROLLER_PAN) {
+ m_faders[count]->m_controllerRotaries[i].
+ second->setPosition((*iIt)->getPan());
+ } else {
+ // The ControllerValues might not yet be set on
+ // the actual Instrument so don't always expect
+ // to find one. There might be a hole here for
+ // deleted Controllers to hang around on
+ // Instruments..
+ //
+ try {
+ value = float((*iIt)->getControllerValue
+ (controls[i].getControllerValue()));
+ } catch (std::string s) {
+ /*
+ RG_DEBUG <<
+ "MidiMixerWindow::slotUpdateInstrument - "
+ << "can't match controller "
+ << int(controls[i].
+ getControllerValue()) << " - \""
+ << s << "\"" << endl;
+ */
+ continue;
+ }
+
+ /*
+ RG_DEBUG << "MidiMixerWindow::slotUpdateInstrument"
+ << " - MATCHED "
+ << int(controls[i].getControllerValue())
+ << endl;
+ */
+
+ m_faders[count]->m_controllerRotaries[i].
+ second->setPosition(value);
+ }
+
+ m_faders[count]->m_controllerRotaries[i].second->blockSignals(false);
+ }
+ }
+ count++;
+ }
+ }
+ }
+
+ blockSignals(false);
+}
+
+void
+MidiMixerWindow::updateMeters(SequencerMapper *mapper)
+{
+ for (unsigned int i = 0; i != m_faders.size(); ++i) {
+ LevelInfo info;
+ if (!mapper->
+ getInstrumentLevelForMixer(m_faders[i]->m_id, info))
+ continue;
+ m_faders[i]->m_vuMeter->setLevel(double(info.level / 127.0));
+ RG_DEBUG << "MidiMixerWindow::updateMeters - level " << info.level << endl;
+ }
+}
+
+void
+MidiMixerWindow::updateMonitorMeter(SequencerMapper *)
+{
+ // none here
+}
+
+void
+MidiMixerWindow::slotControllerDeviceEventReceived(MappedEvent *e,
+ const void *preferredCustomer)
+{
+ if (preferredCustomer != this)
+ return ;
+ RG_DEBUG << "MidiMixerWindow::slotControllerDeviceEventReceived: this one's for me" << endl;
+ raise();
+
+ // get channel number n from event
+ // get nth instrument on current tab
+
+ if (e->getType() != MappedEvent::MidiController)
+ return ;
+ unsigned int channel = e->getRecordedChannel();
+ MidiByte controller = e->getData1();
+ MidiByte value = e->getData2();
+
+ int tabIndex = m_tabWidget->currentPageIndex();
+
+ int i = 0;
+
+ for (DeviceList::const_iterator it = m_studio->begin();
+ it != m_studio->end(); ++it) {
+
+ MidiDevice *dev =
+ dynamic_cast<MidiDevice*>(*it);
+
+ if (!dev)
+ continue;
+ if (i != tabIndex) {
+ ++i;
+ continue;
+ }
+
+ InstrumentList instruments = dev->getPresentationInstruments();
+
+ for (InstrumentList::const_iterator iIt =
+ instruments.begin(); iIt != instruments.end(); ++iIt) {
+
+ Instrument *instrument = *iIt;
+
+ if (instrument->getMidiChannel() != channel)
+ continue;
+
+ switch (controller) {
+
+ case MIDI_CONTROLLER_VOLUME:
+ RG_DEBUG << "Setting volume for instrument " << instrument->getId() << " to " << value << endl;
+ instrument->setVolume(value);
+ break;
+
+ case MIDI_CONTROLLER_PAN:
+ RG_DEBUG << "Setting pan for instrument " << instrument->getId() << " to " << value << endl;
+ instrument->setPan(value);
+ break;
+
+ default: {
+ ControlList cl = dev->getIPBControlParameters();
+ for (ControlList::const_iterator i = cl.begin();
+ i != cl.end(); ++i) {
+ if ((*i).getControllerValue() == controller) {
+ RG_DEBUG << "Setting controller " << controller << " for instrument " << instrument->getId() << " to " << value << endl;
+ instrument->setControllerValue(controller, value);
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ MappedEvent mE(instrument->getId(),
+ MappedEvent::MidiController,
+ MidiByte(controller),
+ MidiByte(value));
+ StudioControl::sendMappedEvent(mE);
+
+ slotUpdateInstrument(instrument->getId());
+ emit instrumentParametersChanged(instrument->getId());
+ }
+
+ break;
+ }
+}
+
+void
+MidiMixerWindow::slotCurrentTabChanged(QWidget *)
+{
+ sendControllerRefresh();
+}
+
+void
+MidiMixerWindow::sendControllerRefresh()
+{
+ //!!! need to know if we have a current external controller device,
+ // as this is expensive
+
+ int tabIndex = m_tabWidget->currentPageIndex();
+ RG_DEBUG << "MidiMixerWindow::slotCurrentTabChanged: current is " << tabIndex << endl;
+
+ if (tabIndex < 0)
+ return ;
+
+ int i = 0;
+
+ for (DeviceList::const_iterator dit = m_studio->begin();
+ dit != m_studio->end(); ++dit) {
+
+ MidiDevice *dev = dynamic_cast<MidiDevice*>(*dit);
+ RG_DEBUG << "device is " << (*dit)->getId() << ", dev " << dev << endl;
+
+ if (!dev)
+ continue;
+ if (i != tabIndex) {
+ ++i;
+ continue;
+ }
+
+ InstrumentList instruments = dev->getPresentationInstruments();
+ ControlList controls = dev->getIPBControlParameters();
+
+ RG_DEBUG << "device has " << instruments.size() << " presentation instruments, " << dev->getAllInstruments().size() << " instruments " << endl;
+
+ for (InstrumentList::const_iterator iIt =
+ instruments.begin(); iIt != instruments.end(); ++iIt) {
+
+ Instrument *instrument = *iIt;
+ int channel = instrument->getMidiChannel();
+
+ RG_DEBUG << "instrument is " << instrument->getId() << endl;
+
+ for (ControlList::const_iterator cIt =
+ controls.begin(); cIt != controls.end(); ++cIt) {
+
+ int controller = (*cIt).getControllerValue();
+ int value;
+ if (controller == MIDI_CONTROLLER_PAN) {
+ value = instrument->getPan();
+ } else {
+ try {
+ value = instrument->getControllerValue(controller);
+ } catch (std::string s) {
+ std::cerr << "Exception in MidiMixerWindow::currentChanged: " << s << " (controller " << controller << ", instrument " << instrument->getId() << ")" << std::endl;
+ value = 0;
+ }
+ }
+
+ MappedEvent mE(instrument->getId(),
+ MappedEvent::MidiController,
+ controller, value);
+ mE.setRecordedChannel(channel);
+ mE.setRecordedDevice(Device::CONTROL_DEVICE);
+ StudioControl::sendMappedEvent(mE);
+ }
+
+ MappedEvent mE(instrument->getId(),
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_VOLUME,
+ instrument->getVolume());
+ mE.setRecordedChannel(channel);
+ mE.setRecordedDevice(Device::CONTROL_DEVICE);
+ RG_DEBUG << "sending controller mapped event for channel " << channel << ", volume " << instrument->getVolume() << endl;
+ StudioControl::sendMappedEvent(mE);
+ }
+
+ break;
+ }
+}
+
+void
+MidiMixerWindow::slotSynchronise()
+{
+ RG_DEBUG << "MidiMixer::slotSynchronise" << endl;
+ //setupTabs();
+}
+
+}
+#include "MidiMixerWindow.moc"
diff --git a/src/gui/studio/MidiMixerWindow.h b/src/gui/studio/MidiMixerWindow.h
new file mode 100644
index 0000000..d90dc55
--- /dev/null
+++ b/src/gui/studio/MidiMixerWindow.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_MIDIMIXERWINDOW_H_
+#define _RG_MIDIMIXERWINDOW_H_
+
+#include "base/MidiProgram.h"
+#include "MixerWindow.h"
+#include <vector>
+
+
+class QWidget;
+class QTabWidget;
+class QString;
+class QFrame;
+
+
+namespace Rosegarden
+{
+
+class SequencerMapper;
+class Rotary;
+class RosegardenGUIDoc;
+class MidiMixerVUMeter;
+class MappedEvent;
+class Fader;
+
+
+class MidiMixerWindow : public MixerWindow
+{
+ Q_OBJECT
+
+public:
+ MidiMixerWindow(QWidget *parent, RosegardenGUIDoc *document);
+
+ /**
+ * Setup the tabs on the Mixer according to the Studio
+ */
+ void setupTabs();
+
+ /*
+ * Update the VU meters
+ */
+ void updateMeters(SequencerMapper *mapper);
+ void updateMonitorMeter(SequencerMapper *mapper);
+
+public slots:
+ void slotSynchronise(); // synchronise with updated studio
+
+ void slotControllerDeviceEventReceived(MappedEvent *,
+ const void *);
+
+ void slotCurrentTabChanged(QWidget *);
+
+signals:
+ void play();
+ void stop();
+ void fastForwardPlayback();
+ void rewindPlayback();
+ void fastForwardPlaybackToEnd();
+ void rewindPlaybackToBeginning();
+ void record();
+ void panic();
+
+ // to be redirected to the instrument parameter box if necessary
+ void instrumentParametersChanged(InstrumentId);
+
+protected slots:
+ void slotUpdateInstrument(InstrumentId);
+
+ //void slotPanChanged(float);
+ void slotFaderLevelChanged(float);
+ void slotControllerChanged(float);
+
+protected:
+ void addTab(QWidget *tab, const QString &title);
+
+ virtual void sendControllerRefresh();
+
+ QTabWidget *m_tabWidget;
+
+ struct FaderStruct {
+
+ FaderStruct():m_id(0), m_vuMeter(0), m_volumeFader(0) {}
+
+ InstrumentId m_id;
+ MidiMixerVUMeter *m_vuMeter;
+ Fader *m_volumeFader;
+ std::vector<std::pair<MidiByte, Rotary*> > m_controllerRotaries;
+
+ };
+
+ typedef std::vector<FaderStruct*> FaderVector;
+ FaderVector m_faders;
+
+ QFrame *m_tabFrame;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/MidiProgramsEditor.cpp b/src/gui/studio/MidiProgramsEditor.cpp
new file mode 100644
index 0000000..8f81a04
--- /dev/null
+++ b/src/gui/studio/MidiProgramsEditor.cpp
@@ -0,0 +1,631 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "MidiProgramsEditor.h"
+#include "MidiBankListViewItem.h"
+#include "NameSetEditor.h"
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "BankEditorDialog.h"
+#include "base/Device.h"
+#include "base/MidiDevice.h"
+#include "base/MidiProgram.h"
+#include "gui/widgets/RosegardenPopupMenu.h"
+#include <kcompletion.h>
+#include <kglobal.h>
+#include <klineedit.h>
+#include <klocale.h>
+#include <kstddirs.h>
+#include <qcheckbox.h>
+#include <qcursor.h>
+#include <qfile.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qobjectlist.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qpopupmenu.h>
+#include <qpushbutton.h>
+#include <qspinbox.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qvgroupbox.h>
+#include <qwidget.h>
+#include <algorithm>
+
+namespace Rosegarden
+{
+
+MidiProgramsEditor::MidiProgramsEditor(BankEditorDialog* bankEditor,
+ QWidget* parent,
+ const char* name)
+ : NameSetEditor(bankEditor,
+ i18n("Bank and Program details"),
+ parent, name, i18n("Programs"), true),
+ m_device(0),
+ m_bankList(bankEditor->getBankList()),
+ m_programList(bankEditor->getProgramList()),
+ m_oldBank(false, 0, 0)
+{
+ QWidget *additionalWidget = makeAdditionalWidget(m_mainFrame);
+ if (additionalWidget) {
+ m_mainLayout->addMultiCellWidget(additionalWidget, 0, 2, 0, 2);
+ }
+}
+
+QWidget *
+MidiProgramsEditor::makeAdditionalWidget(QWidget *parent)
+{
+ QFrame *frame = new QFrame(parent);
+
+ m_percussion = new QCheckBox(frame);
+ m_msb = new QSpinBox(frame);
+ m_lsb = new QSpinBox(frame);
+
+ QGridLayout *gridLayout = new QGridLayout(frame,
+ 3, // rows
+ 2, // cols
+ 2); // margin
+
+ gridLayout->addWidget(new QLabel(i18n("Percussion"), frame),
+ 0, 0, AlignLeft);
+ gridLayout->addWidget(m_percussion, 0, 1, AlignLeft);
+ connect(m_percussion, SIGNAL(clicked()),
+ this, SLOT(slotNewPercussion()));
+
+ gridLayout->addWidget(new QLabel(i18n("MSB Value"), frame),
+ 1, 0, AlignLeft);
+ m_msb->setMinValue(0);
+ m_msb->setMaxValue(127);
+ gridLayout->addWidget(m_msb, 1, 1, AlignLeft);
+
+ QToolTip::add
+ (m_msb,
+ i18n("Selects a MSB controller Bank number (MSB/LSB pairs are always unique for any Device)"));
+
+ QToolTip::add
+ (m_lsb,
+ i18n("Selects a LSB controller Bank number (MSB/LSB pairs are always unique for any Device)"));
+
+ connect(m_msb, SIGNAL(valueChanged(int)),
+ this, SLOT(slotNewMSB(int)));
+
+ gridLayout->addWidget(new QLabel(i18n("LSB Value"), frame),
+ 2, 0, AlignLeft);
+ m_lsb->setMinValue(0);
+ m_lsb->setMaxValue(127);
+ gridLayout->addWidget(m_lsb, 2, 1, AlignLeft);
+
+ connect(m_lsb, SIGNAL(valueChanged(int)),
+ this, SLOT(slotNewLSB(int)));
+
+ return frame;
+}
+
+ProgramList
+MidiProgramsEditor::getBankSubset(const MidiBank &bank)
+{
+ ProgramList program;
+ ProgramList::iterator it;
+
+ for (it = m_programList.begin(); it != m_programList.end(); it++) {
+ if (it->getBank() == bank)
+ program.push_back(*it);
+ }
+
+ return program;
+}
+
+MidiBank*
+MidiProgramsEditor::getCurrentBank()
+{
+ return m_currentBank;
+}
+
+void
+MidiProgramsEditor::modifyCurrentPrograms(const MidiBank &oldBank,
+ const MidiBank &newBank)
+{
+ ProgramList::iterator it;
+
+ for (it = m_programList.begin(); it != m_programList.end(); it++) {
+ if (it->getBank() == oldBank) {
+ *it = MidiProgram(newBank, it->getProgram(), it->getName());
+ }
+ }
+}
+
+void
+MidiProgramsEditor::clearAll()
+{
+ blockAllSignals(true);
+
+ for (unsigned int i = 0; i < m_names.size(); ++i)
+ m_names[i]->clear();
+
+ setTitle(i18n("Bank and Program details"));
+
+ m_percussion->setChecked(false);
+ m_msb->setValue(0);
+ m_lsb->setValue(0);
+ m_librarian->clear();
+ m_librarianEmail->clear();
+ m_currentBank = 0;
+ setEnabled(false);
+
+ blockAllSignals(false);
+}
+
+void
+MidiProgramsEditor::populate(QListViewItem* item)
+{
+ RG_DEBUG << "MidiProgramsEditor::populate\n";
+
+ MidiBankListViewItem* bankItem = dynamic_cast<MidiBankListViewItem*>(item);
+ if (!bankItem) {
+ RG_DEBUG << "MidiProgramsEditor::populate : not a bank item - returning\n";
+ return ;
+ }
+
+ DeviceId deviceId = bankItem->getDeviceId();
+ m_device = m_bankEditor->getMidiDevice(deviceId);
+ if (!m_device)
+ return ;
+
+ setEnabled(true);
+
+ setBankName(item->text(0));
+
+ RG_DEBUG << "MidiProgramsEditor::populate : bankItem->getBank = "
+ << bankItem->getBank() << endl;
+
+ m_currentBank = &(m_bankList[bankItem->getBank()]); // m_device->getBankByIndex(bankItem->getBank());
+
+ blockAllSignals(true);
+
+ // set the bank values
+ m_percussion->setChecked(m_currentBank->isPercussion());
+ m_msb->setValue(m_currentBank->getMSB());
+ m_lsb->setValue(m_currentBank->getLSB());
+
+ m_oldBank = *m_currentBank;
+
+ // Librarian details
+ //
+ m_librarian->setText(strtoqstr(m_device->getLibrarianName()));
+ m_librarianEmail->setText(strtoqstr(m_device->getLibrarianEmail()));
+
+ ProgramList programSubset = getBankSubset(*m_currentBank);
+ ProgramList::iterator it;
+
+ QPixmap noKeyPixmap, keyPixmap;
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QString file = pixmapDir + "/toolbar/key-white.png";
+ if (QFile(file).exists())
+ noKeyPixmap = QPixmap(file);
+ file = pixmapDir + "/toolbar/key-green.png";
+ if (QFile(file).exists())
+ keyPixmap = QPixmap(file);
+
+ bool haveKeyMappings = m_currentBank->isPercussion()
+ && (m_device->getKeyMappings().size() > 0);
+
+ for (unsigned int i = 0; i < m_names.size(); i++) {
+ m_names[i]->clear();
+ getEntryButton(i)->setEnabled(haveKeyMappings);
+ getEntryButton(i)->setPixmap(noKeyPixmap);
+ QToolTip::remove
+ ( getEntryButton(i) );
+
+ for (it = programSubset.begin(); it != programSubset.end(); it++) {
+ if (it->getProgram() == i) {
+
+ QString programName = strtoqstr(it->getName());
+ m_completion.addItem(programName);
+ m_names[i]->setText(programName);
+
+ if (m_device->getKeyMappingForProgram(*it)) {
+ getEntryButton(i)->setPixmap(keyPixmap);
+ QToolTip::add
+ (getEntryButton(i),
+ i18n("Key Mapping: %1").arg(
+ strtoqstr(m_device->getKeyMappingForProgram(*it)->getName())));
+ }
+
+ break;
+ }
+ }
+
+ // show start of label
+ m_names[i]->setCursorPosition(0);
+ }
+
+ blockAllSignals(false);
+}
+
+void
+MidiProgramsEditor::reset()
+{
+ m_percussion->blockSignals(true);
+ m_msb->blockSignals(true);
+ m_lsb->blockSignals(true);
+
+ m_percussion->setChecked(m_oldBank.isPercussion());
+ m_msb->setValue(m_oldBank.getMSB());
+ m_lsb->setValue(m_oldBank.getLSB());
+
+ if (m_currentBank) {
+ modifyCurrentPrograms(*m_currentBank, m_oldBank);
+ *m_currentBank = m_oldBank;
+ }
+
+ m_percussion->blockSignals(false);
+ m_msb->blockSignals(false);
+ m_lsb->blockSignals(false);
+}
+
+void
+MidiProgramsEditor::slotNewPercussion()
+{
+ RG_DEBUG << "MidiProgramsEditor::slotNewPercussion" << endl;
+ bool percussion = m_percussion->isChecked();
+ m_percussion->blockSignals(true);
+ if (banklistContains(MidiBank(percussion, m_msb->value(), m_lsb->value()))) {
+ RG_DEBUG << "MidiProgramsEditor::slotNewPercussion: calling setChecked(" << !percussion << ")" << endl;
+ m_percussion->setChecked(!percussion);
+ } else {
+ MidiBank newBank(percussion,
+ m_msb->value(),
+ m_lsb->value());
+ modifyCurrentPrograms(*getCurrentBank(), newBank);
+ *getCurrentBank() = newBank;
+ }
+ m_percussion->blockSignals(false);
+ m_bankEditor->setModified(true);
+}
+
+void
+MidiProgramsEditor::slotNewMSB(int value)
+{
+ RG_DEBUG << "MidiProgramsEditor::slotNewMSB(" << value << ")\n";
+
+ m_msb->blockSignals(true);
+
+ int msb;
+
+ try {
+ msb = ensureUniqueMSB(value, value > getCurrentBank()->getMSB());
+ } catch (bool) {
+ msb = getCurrentBank()->getMSB();
+ }
+
+ MidiBank newBank(m_percussion->isChecked(),
+ msb,
+ m_lsb->value());
+
+ modifyCurrentPrograms(*getCurrentBank(), newBank);
+
+ m_msb->setValue(msb);
+ *getCurrentBank() = newBank;
+
+ m_msb->blockSignals(false);
+
+ m_bankEditor->setModified(true);
+}
+
+void
+MidiProgramsEditor::slotNewLSB(int value)
+{
+ RG_DEBUG << "MidiProgramsEditor::slotNewLSB(" << value << ")\n";
+
+ m_lsb->blockSignals(true);
+
+ int lsb;
+
+ try {
+ lsb = ensureUniqueLSB(value, value > getCurrentBank()->getLSB());
+ } catch (bool) {
+ lsb = getCurrentBank()->getLSB();
+ }
+
+ MidiBank newBank(m_percussion->isChecked(),
+ m_msb->value(),
+ lsb);
+
+ modifyCurrentPrograms(*getCurrentBank(), newBank);
+
+ m_lsb->setValue(lsb);
+ *getCurrentBank() = newBank;
+
+ m_lsb->blockSignals(false);
+
+ m_bankEditor->setModified(true);
+}
+
+struct ProgramCmp
+{
+ bool operator()(const Rosegarden::MidiProgram &p1,
+ const Rosegarden::MidiProgram &p2)
+ {
+ if (p1.getProgram() == p2.getProgram()) {
+ const Rosegarden::MidiBank &b1(p1.getBank());
+ const Rosegarden::MidiBank &b2(p2.getBank());
+ if (b1.getMSB() == b2.getMSB())
+ if (b1.getLSB() == b2.getLSB())
+ return ((b1.isPercussion() ? 1 : 0) < (b2.isPercussion() ? 1 : 0));
+ else return (b1.getLSB() < b2.getLSB());
+ else return (b1.getMSB() < b2.getMSB());
+ } else return (p1.getProgram() < p2.getProgram());
+ }
+};
+
+void
+MidiProgramsEditor::slotNameChanged(const QString& programName)
+{
+ const KLineEdit* lineEdit = dynamic_cast<const KLineEdit*>(sender());
+ if (!lineEdit) {
+ RG_DEBUG << "MidiProgramsEditor::slotProgramChanged() : %%% ERROR - signal sender is not a KLineEdit\n";
+ return ;
+ }
+
+ QString senderName = sender()->name();
+
+ // Adjust value back to zero rated
+ //
+ unsigned int id = senderName.toUInt() - 1;
+
+ RG_DEBUG << "MidiProgramsEditor::slotNameChanged("
+ << programName << ") : id = " << id << endl;
+
+ MidiProgram *program = getProgram(*getCurrentBank(), id);
+
+ if (program == 0) {
+ // Do nothing if program name is empty
+ if (programName.isEmpty())
+ return ;
+
+ program = new MidiProgram(*getCurrentBank(), id);
+ m_programList.push_back(*program);
+
+ // Sort the program list by id
+ std::sort(m_programList.begin(), m_programList.end(), ProgramCmp());
+
+ // Now, get with the program
+ //
+ program = getProgram(*getCurrentBank(), id);
+ } else {
+ // If we've found a program and the label is now empty
+ // then remove it from the program list.
+ //
+ if (programName.isEmpty()) {
+ ProgramList::iterator it = m_programList.begin();
+ ProgramList tmpProg;
+
+ for (; it != m_programList.end(); it++) {
+ if (((unsigned int)it->getProgram()) == id) {
+ m_programList.erase(it);
+ m_bankEditor->setModified(true);
+ RG_DEBUG << "deleting empty program (" << id << ")" << endl;
+ return ;
+ }
+ }
+ }
+ }
+
+ if (qstrtostr(programName) != program->getName()) {
+ program->setName(qstrtostr(programName));
+ m_bankEditor->setModified(true);
+ }
+}
+
+void
+MidiProgramsEditor::slotEntryButtonPressed()
+{
+ QPushButton* button = dynamic_cast<QPushButton*>(const_cast<QObject *>(sender()));
+ if (!button) {
+ RG_DEBUG << "MidiProgramsEditor::slotEntryButtonPressed() : %%% ERROR - signal sender is not a QPushButton\n";
+ return ;
+ }
+
+ QString senderName = button->name();
+
+ if (!m_device)
+ return ;
+
+ const KeyMappingList &kml = m_device->getKeyMappings();
+ if (kml.empty())
+ return ;
+
+ // Adjust value back to zero rated
+ //
+ unsigned int id = senderName.toUInt() - 1;
+ MidiProgram *program = getProgram(*getCurrentBank(), id);
+ if (!program)
+ return ;
+ m_currentMenuProgram = id;
+
+ RosegardenPopupMenu *menu = new RosegardenPopupMenu(button);
+
+ const MidiKeyMapping *currentMapping =
+ m_device->getKeyMappingForProgram(*program);
+ int currentEntry = 0;
+
+ menu->insertItem(i18n("<no key mapping>"), this,
+ SLOT(slotEntryMenuItemSelected(int)), 0, 0);
+ menu->setItemParameter(0, 0);
+
+ for (int i = 0; i < kml.size(); ++i) {
+ menu->insertItem(strtoqstr(kml[i].getName()),
+ this, SLOT(slotEntryMenuItemSelected(int)),
+ 0, i + 1);
+ menu->setItemParameter(i + 1, i + 1);
+ if (currentMapping && (kml[i] == *currentMapping))
+ currentEntry = i + 1;
+ }
+
+ int itemHeight = menu->itemHeight(0) + 2;
+ QPoint pos = QCursor::pos();
+
+ pos.rx() -= 10;
+ pos.ry() -= (itemHeight / 2 + currentEntry * itemHeight);
+
+ menu->popup(pos);
+}
+
+void
+MidiProgramsEditor::slotEntryMenuItemSelected(int i)
+{
+ if (!m_device)
+ return ;
+
+ const KeyMappingList &kml = m_device->getKeyMappings();
+ if (kml.empty())
+ return ;
+
+ MidiProgram *program = getProgram(*getCurrentBank(), m_currentMenuProgram);
+ if (!program)
+ return ;
+
+ std::string newMapping;
+
+ if (i == 0) { // no key mapping
+ newMapping = "";
+ } else {
+ --i;
+ if (i < kml.size()) {
+ newMapping = kml[i].getName();
+ }
+ }
+
+ m_device->setKeyMappingForProgram(*program, newMapping);
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ bool haveKeyMappings = (m_device->getKeyMappings().size() > 0);
+ QPushButton *btn = getEntryButton(m_currentMenuProgram);
+
+ if (newMapping.empty()) {
+ QString file = pixmapDir + "/toolbar/key-white.png";
+ if (QFile(file).exists()) {
+ btn->setPixmap(QPixmap(file));
+ }
+ QToolTip::remove
+ (btn);
+ } else {
+ QString file = pixmapDir + "/toolbar/key-green.png";
+ if (QFile(file).exists()) {
+ btn->setPixmap(QPixmap(file));
+ }
+ QToolTip::add
+ (btn, i18n("Key Mapping: %1").arg(strtoqstr(newMapping)));
+ }
+ btn->setEnabled(haveKeyMappings);
+}
+
+int
+MidiProgramsEditor::ensureUniqueMSB(int msb, bool ascending)
+{
+ int newMSB = msb;
+ while (banklistContains(MidiBank(m_percussion->isChecked(),
+ newMSB, m_lsb->value()))
+ && newMSB < 128
+ && newMSB > -1)
+ if (ascending)
+ newMSB++;
+ else
+ newMSB--;
+
+ if (newMSB == -1 || newMSB == 128)
+ throw false;
+
+ return newMSB;
+}
+
+int
+MidiProgramsEditor::ensureUniqueLSB(int lsb, bool ascending)
+{
+ int newLSB = lsb;
+ while (banklistContains(MidiBank(m_percussion->isChecked(),
+ m_msb->value(), newLSB))
+ && newLSB < 128
+ && newLSB > -1)
+ if (ascending)
+ newLSB++;
+ else
+ newLSB--;
+
+ if (newLSB == -1 || newLSB == 128)
+ throw false;
+
+ return newLSB;
+}
+
+bool
+MidiProgramsEditor::banklistContains(const MidiBank &bank)
+{
+ BankList::iterator it;
+
+ for (it = m_bankList.begin(); it != m_bankList.end(); it++)
+ if (*it == bank)
+ return true;
+
+ return false;
+}
+
+MidiProgram*
+MidiProgramsEditor::getProgram(const MidiBank &bank, int programNo)
+{
+ ProgramList::iterator it = m_programList.begin();
+
+ for (; it != m_programList.end(); it++) {
+ if (it->getBank() == bank && it->getProgram() == programNo)
+ return &(*it);
+ }
+
+ return 0;
+}
+
+void
+MidiProgramsEditor::setBankName(const QString& s)
+{
+ setTitle(s);
+}
+
+void MidiProgramsEditor::blockAllSignals(bool block)
+{
+ const QObjectList* allChildren = queryList("KLineEdit", "[0-9]+");
+ QObjectListIt it(*allChildren);
+ QObject *obj;
+
+ while ( (obj = it.current()) != 0 ) {
+ obj->blockSignals(block);
+ ++it;
+ }
+
+ m_msb->blockSignals(block);
+ m_lsb->blockSignals(block);
+}
+
+}
+#include "MidiProgramsEditor.moc"
diff --git a/src/gui/studio/MidiProgramsEditor.h b/src/gui/studio/MidiProgramsEditor.h
new file mode 100644
index 0000000..d0ef565
--- /dev/null
+++ b/src/gui/studio/MidiProgramsEditor.h
@@ -0,0 +1,119 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_MIDIPROGRAMSEDITOR_H_
+#define _RG_MIDIPROGRAMSEDITOR_H_
+
+#include "base/MidiProgram.h"
+#include "NameSetEditor.h"
+
+
+class QWidget;
+class QString;
+class QSpinBox;
+class QListViewItem;
+class QCheckBox;
+class BankList;
+
+
+namespace Rosegarden
+{
+
+class MidiProgram;
+class MidiDevice;
+class BankEditorDialog;
+
+
+class MidiProgramsEditor : public NameSetEditor
+{
+ Q_OBJECT
+public:
+ MidiProgramsEditor(BankEditorDialog *bankEditor,
+ QWidget *parent,
+ const char *name = 0);
+
+ void clearAll();
+ void populate(QListViewItem*);
+ void reset();
+
+public slots:
+
+ // Check that any new MSB/LSB combination is unique for this device
+ //
+ void slotNewMSB(int value);
+ void slotNewLSB(int value);
+ void slotNewPercussion(); // gets value from checkbox
+
+ virtual void slotNameChanged(const QString &);
+ virtual void slotEntryButtonPressed();
+ void slotEntryMenuItemSelected(int);
+
+protected:
+
+ MidiBank* getCurrentBank();
+
+ int ensureUniqueMSB(int msb, bool ascending);
+ int ensureUniqueLSB(int lsb, bool ascending);
+
+ // Does the banklist contain this combination already?
+ //
+ bool banklistContains(const MidiBank &);
+
+ ProgramList getBankSubset(const MidiBank &);
+
+ /// Set the currently loaded programs to new MSB and LSB
+ void modifyCurrentPrograms(const MidiBank &oldBank,
+ const MidiBank &newBank);
+
+ // Get a program (pointer into program list) for modification
+ //
+ MidiProgram* getProgram(const MidiBank &bank, int program);
+
+ void setBankName(const QString& s);
+
+ virtual QWidget *makeAdditionalWidget(QWidget *parent);
+
+ void blockAllSignals(bool block);
+
+ //--------------- Data members ---------------------------------
+ QCheckBox *m_percussion;
+ QSpinBox *m_msb;
+ QSpinBox *m_lsb;
+
+ MidiDevice *m_device;
+
+ MidiBank *m_currentBank;
+ BankList &m_bankList;
+ ProgramList &m_programList;
+
+ MidiBank m_oldBank;
+
+ unsigned int m_currentMenuProgram;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/MixerWindow.cpp b/src/gui/studio/MixerWindow.cpp
new file mode 100644
index 0000000..2a65024
--- /dev/null
+++ b/src/gui/studio/MixerWindow.cpp
@@ -0,0 +1,75 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "MixerWindow.h"
+
+#include "misc/Debug.h"
+#include "base/MidiProgram.h"
+#include "base/Studio.h"
+#include "document/RosegardenGUIDoc.h"
+#include <kmainwindow.h>
+#include <qaccel.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+MixerWindow::MixerWindow(QWidget *parent,
+ RosegardenGUIDoc *document) :
+ KMainWindow(parent, "mixerwindow"),
+ m_document(document),
+ m_studio(&document->getStudio()),
+ m_currentId(0)
+{
+ m_accelerators = new QAccel(this);
+}
+
+void
+MixerWindow::closeEvent(QCloseEvent *e)
+{
+ RG_DEBUG << "MixerWindow::closeEvent()\n";
+ emit closing();
+ KMainWindow::closeEvent(e);
+}
+
+void
+MixerWindow::windowActivationChange(bool)
+{
+ if (isActiveWindow()) {
+ emit windowActivated();
+ sendControllerRefresh();
+ }
+}
+
+void
+MixerWindow::slotClose()
+{
+ RG_DEBUG << "MixerWindow::slotClose()\n";
+ close();
+}
+
+}
+#include "MixerWindow.moc"
diff --git a/src/gui/studio/MixerWindow.h b/src/gui/studio/MixerWindow.h
new file mode 100644
index 0000000..9e5f9cf
--- /dev/null
+++ b/src/gui/studio/MixerWindow.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_MIXERWINDOW_H_
+#define _RG_MIXERWINDOW_H_
+
+#include "base/MidiProgram.h"
+#include <kmainwindow.h>
+
+
+class QWidget;
+class QCloseEvent;
+class QAccel;
+
+
+namespace Rosegarden
+{
+
+class Studio;
+class RosegardenGUIDoc;
+
+
+class MixerWindow: public KMainWindow
+{
+ Q_OBJECT
+
+public:
+ MixerWindow(QWidget *parent, RosegardenGUIDoc *document);
+ QAccel* getAccelerators() { return m_accelerators; }
+
+signals:
+ void closing();
+ void windowActivated();
+
+protected slots:
+ void slotClose();
+
+protected:
+ virtual void closeEvent(QCloseEvent *);
+ virtual void windowActivationChange(bool);
+
+ virtual void sendControllerRefresh() = 0;
+
+ RosegardenGUIDoc *m_document;
+ Studio *m_studio;
+ InstrumentId m_currentId;
+
+ QAccel *m_accelerators;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/NameSetEditor.cpp b/src/gui/studio/NameSetEditor.cpp
new file mode 100644
index 0000000..8dadd0c
--- /dev/null
+++ b/src/gui/studio/NameSetEditor.cpp
@@ -0,0 +1,190 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "NameSetEditor.h"
+#include "BankEditorDialog.h"
+#include <kcompletion.h>
+#include <kglobalsettings.h>
+#include <klineedit.h>
+#include <klocale.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qstring.h>
+#include <qtabwidget.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qvgroupbox.h>
+#include <qwidget.h>
+#include <iostream>
+
+namespace Rosegarden
+{
+
+NameSetEditor::NameSetEditor(BankEditorDialog* bankEditor,
+ QString title,
+ QWidget* parent,
+ const char* name,
+ QString headingPrefix,
+ bool showEntryButtons)
+ : QVGroupBox(title, parent, name),
+ m_bankEditor(bankEditor),
+ m_mainFrame(new QFrame(this))
+{
+ m_mainLayout = new QGridLayout(m_mainFrame,
+ 4, // rows
+ 6, // cols
+ 2); // margin
+
+ // Librarian
+ //
+ QGroupBox *groupBox = new QGroupBox(2,
+ Qt::Horizontal,
+ i18n("Librarian"),
+ m_mainFrame);
+ m_mainLayout->addMultiCellWidget(groupBox, 0, 2, 3, 5);
+
+ new QLabel(i18n("Name"), groupBox);
+ m_librarian = new QLabel(groupBox);
+
+ new QLabel(i18n("Email"), groupBox);
+ m_librarianEmail = new QLabel(groupBox);
+
+ QToolTip::add
+ (groupBox,
+ i18n("The librarian maintains the Rosegarden device data for this device.\nIf you've made modifications to suit your own device, it might be worth\nliaising with the librarian in order to publish your information for the benefit\nof others."));
+
+ QTabWidget* tabw = new QTabWidget(this);
+
+ tabw->setMargin(10);
+
+ QHBox *h;
+ QVBox *v;
+ QHBox *numBox;
+
+ unsigned int tabs = 4;
+ unsigned int cols = 2;
+ unsigned int labelId = 0;
+
+ for (unsigned int tab = 0; tab < tabs; ++tab) {
+ h = new QHBox(tabw);
+
+ for (unsigned int col = 0; col < cols; ++col) {
+ v = new QVBox(h);
+
+ for (unsigned int row = 0; row < 128 / (tabs*cols); ++row) {
+ numBox = new QHBox(v);
+ QString numberText = QString("%1").arg(labelId + 1);
+
+ if (tab == 0 && col == 0 && row == 0) {
+ // Initial label; button to adjust whether labels start at 0 or 1
+ m_initialLabel = new QPushButton(numberText, numBox);
+ connect(m_initialLabel,
+ SIGNAL(clicked()),
+ this,
+ SLOT(slotToggleInitialLabel()));
+ } else {
+ QLabel *label = new QLabel(numberText, numBox);
+ label->setFixedWidth(40);
+ label->setAlignment(AlignCenter);
+ m_labels.push_back(label);
+ }
+
+
+
+ if (showEntryButtons) {
+ QPushButton *button = new QPushButton("", numBox, numberText);
+ button->setMaximumWidth(40);
+ button->setMaximumHeight(20);
+ button->setFlat(true);
+ connect(button, SIGNAL(clicked()),
+ this, SLOT(slotEntryButtonPressed()));
+ m_entryButtons.push_back(button);
+ }
+
+ KLineEdit* lineEdit = new KLineEdit(numBox, numberText);
+ lineEdit->setMinimumWidth(110);
+ lineEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
+ lineEdit->setCompletionObject(&m_completion);
+ m_names.push_back(lineEdit);
+
+ connect(m_names[labelId],
+ SIGNAL(textChanged(const QString&)),
+ this,
+ SLOT(slotNameChanged(const QString&)));
+
+ ++labelId;
+ }
+ }
+
+ tabw->addTab(h,
+ (tab == 0 ? headingPrefix + QString(" %1 - %2") :
+ QString("%1 - %2")).
+ arg(tab * (128 / tabs) + 1).
+ arg((tab + 1) * (128 / tabs)));
+ }
+
+ m_initialLabel->setMaximumSize(m_labels.front()->size());
+}
+
+void
+NameSetEditor::slotToggleInitialLabel()
+{
+ QString initial = m_initialLabel->text();
+
+ // strip some unrequested nice-ification.. urg!
+ if (initial.startsWith("&")) {
+ initial = initial.right(initial.length() - 1);
+ }
+
+ bool ok;
+ unsigned index = initial.toUInt(&ok);
+
+ if (!ok) {
+ std::cerr << "conversion of '"
+ << initial.ascii()
+ << "' to number failed"
+ << std::endl;
+ return ;
+ }
+
+ if (index == 0)
+ index = 1;
+ else
+ index = 0;
+
+ m_initialLabel->setText(QString("%1").arg(index++));
+ for (std::vector<QLabel*>::iterator it( m_labels.begin() );
+ it != m_labels.end();
+ ++it) {
+ (*it)->setText(QString("%1").arg(index++));
+ }
+}
+
+}
+#include "NameSetEditor.moc"
diff --git a/src/gui/studio/NameSetEditor.h b/src/gui/studio/NameSetEditor.h
new file mode 100644
index 0000000..e1e1476
--- /dev/null
+++ b/src/gui/studio/NameSetEditor.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_NAMESETEDITOR_H_
+#define _RG_NAMESETEDITOR_H_
+
+#include <kcompletion.h>
+#include <qstring.h>
+#include <qvgroupbox.h>
+#include <vector>
+
+
+class QWidget;
+class QPushButton;
+class QListViewItem;
+class QLabel;
+class QGridLayout;
+class QFrame;
+class KLineEdit;
+
+
+namespace Rosegarden
+{
+
+class BankEditorDialog;
+
+
+class NameSetEditor : public QVGroupBox
+{
+ Q_OBJECT
+public:
+ virtual void clearAll() = 0;
+
+ virtual void populate(QListViewItem *) = 0;
+ virtual void reset() = 0;
+
+public slots:
+ virtual void slotNameChanged(const QString&) = 0;
+ virtual void slotEntryButtonPressed() = 0;
+ void slotToggleInitialLabel();
+
+protected:
+ NameSetEditor(BankEditorDialog *bankEditor,
+ QString title,
+ QWidget *parent,
+ const char *name,
+ QString headingPrefix = "",
+ bool showEntryButtons = false);
+
+ QPushButton *getEntryButton(int n) { return m_entryButtons[n]; }
+ const QPushButton *getEntryButton(int n) const { return m_entryButtons[n]; }
+
+ QGridLayout *m_mainLayout;
+ BankEditorDialog* m_bankEditor;
+ KCompletion m_completion;
+ QPushButton *m_initialLabel;
+ std::vector<QLabel*> m_labels;
+ std::vector<KLineEdit*> m_names;
+ QFrame *m_mainFrame;
+ QLabel *m_librarian;
+ QLabel *m_librarianEmail;
+ std::vector<QPushButton *> m_entryButtons;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/OSCMessage.cpp b/src/gui/studio/OSCMessage.cpp
new file mode 100644
index 0000000..6496732
--- /dev/null
+++ b/src/gui/studio/OSCMessage.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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.
+*/
+
+
+#ifdef HAVE_LIBLO
+
+#include "OSCMessage.h"
+
+#include <cstdlib>
+#include <cstring>
+
+namespace Rosegarden
+{
+
+OSCMessage::~OSCMessage()
+{
+ clearArgs();
+}
+
+void
+OSCMessage::clearArgs()
+{
+ while (!m_args.empty()) {
+ free(m_args[0].second);
+ m_args.erase(m_args.begin());
+ }
+}
+
+void
+OSCMessage::addArg(char type, lo_arg *arg)
+{
+ lo_arg *newarg = 0;
+
+ if (type == 's') {
+
+ size_t sz = strlen((char *)arg) + 1;
+ if (sz < sizeof(lo_arg))
+ sz = sizeof(lo_arg);
+ newarg = (lo_arg *)malloc(sz);
+ strcpy((char *)newarg, (char *)arg);
+
+ } else {
+
+ newarg = (lo_arg *)malloc(sizeof(lo_arg));
+ memcpy((char *)newarg, (char *)arg, sizeof(lo_arg));
+ }
+
+ m_args.push_back(OSCArg(type, newarg));
+}
+
+size_t
+OSCMessage::getArgCount() const
+{
+ return m_args.size();
+}
+
+const lo_arg *
+OSCMessage::getArg(size_t i, char &type) const
+{
+ type = m_args[i].first;
+ return m_args[i].second;
+}
+
+}
+
+#endif // HAVE_LIBLO
diff --git a/src/gui/studio/OSCMessage.h b/src/gui/studio/OSCMessage.h
new file mode 100644
index 0000000..05bf63f
--- /dev/null
+++ b/src/gui/studio/OSCMessage.h
@@ -0,0 +1,75 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_OSCMESSAGE_H_
+#define _RG_OSCMESSAGE_H_
+
+#include <lo/lo.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace Rosegarden
+{
+
+
+
+class OSCMessage
+{
+public:
+ OSCMessage() { }
+ ~OSCMessage();
+
+ void setTarget(const int &target) { m_target = target; }
+ int getTarget() const { return m_target; }
+
+ void setTargetData(const int &targetData) { m_targetData = targetData; }
+ int getTargetData() const { return m_targetData; }
+
+ void setMethod(const std::string &method) { m_method = method; }
+ std::string getMethod() const { return m_method; }
+
+ void clearArgs();
+ void addArg(char type, lo_arg *arg);
+
+ size_t getArgCount() const;
+ const lo_arg *getArg(size_t i, char &type) const;
+
+private:
+ int m_target;
+ int m_targetData;
+ std::string m_method;
+ typedef std::pair<char, lo_arg *> OSCArg;
+ std::vector<OSCArg> m_args;
+};
+
+
+class TimerCallbackAssistant;
+
+
+}
+
+#endif
diff --git a/src/gui/studio/RemapInstrumentDialog.cpp b/src/gui/studio/RemapInstrumentDialog.cpp
new file mode 100644
index 0000000..dae43da
--- /dev/null
+++ b/src/gui/studio/RemapInstrumentDialog.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "RemapInstrumentDialog.h"
+
+#include "misc/Strings.h"
+#include "base/Device.h"
+#include "base/Instrument.h"
+#include "base/MidiDevice.h"
+#include "base/SoftSynthDevice.h"
+#include "base/Studio.h"
+#include "commands/studio/ModifyDeviceMappingCommand.h"
+#include "commands/studio/ModifyInstrumentMappingCommand.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/RosegardenGUIDoc.h"
+#include <kcombobox.h>
+#include <kcommand.h>
+#include <kdialogbase.h>
+#include <klocale.h>
+#include <qbuttongroup.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qradiobutton.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+RemapInstrumentDialog::RemapInstrumentDialog(QWidget *parent,
+ RosegardenGUIDoc *doc):
+ KDialogBase(parent, "", true, i18n("Remap Instrument assigments..."),
+ Ok | Apply | Cancel),
+ m_doc(doc)
+{
+ QVBox *vBox = makeVBoxMainWidget();
+
+ m_buttonGroup = new QButtonGroup(1, Qt::Horizontal,
+ i18n("Device or Instrument"),
+ vBox);
+
+ new QLabel(i18n("Remap Tracks by all Instruments on a Device or by single Instrument"), m_buttonGroup);
+ m_deviceButton = new QRadioButton(i18n("Device"), m_buttonGroup);
+ m_instrumentButton = new QRadioButton(i18n("Instrument"), m_buttonGroup);
+
+
+ connect(m_buttonGroup, SIGNAL(released(int)),
+ this, SLOT(slotRemapReleased(int)));
+
+ QGroupBox *groupBox = new QGroupBox(2, Qt::Horizontal,
+ i18n("Choose Source and Destination"),
+ vBox);
+
+ new QLabel(i18n("From"), groupBox);
+ new QLabel(i18n("To"), groupBox);
+ m_fromCombo = new KComboBox(groupBox);
+ m_toCombo = new KComboBox(groupBox);
+
+ m_buttonGroup->setButton(0);
+ populateCombo(0);
+}
+
+void
+RemapInstrumentDialog::populateCombo(int id)
+{
+ m_fromCombo->clear();
+ m_toCombo->clear();
+ Studio *studio = &m_doc->getStudio();
+
+ if (id == 0) {
+ DeviceList *devices = studio->getDevices();
+ DeviceListIterator it;
+ m_devices.clear();
+
+ for (it = devices->begin(); it != devices->end(); it++) {
+ MidiDevice *md =
+ dynamic_cast<MidiDevice *>(*it);
+
+ if (md) {
+ if (md->getDirection() == MidiDevice::Play) {
+ m_devices.push_back(*it);
+ m_fromCombo->insertItem(strtoqstr((*it)->getName()));
+ m_toCombo->insertItem(strtoqstr((*it)->getName()));
+ }
+ } else {
+ SoftSynthDevice *sd =
+ dynamic_cast<SoftSynthDevice *>(*it);
+ if (sd) {
+ m_devices.push_back(*it);
+ m_fromCombo->insertItem(strtoqstr((*it)->getName()));
+ m_toCombo->insertItem(strtoqstr((*it)->getName()));
+ }
+ }
+ }
+
+ if (m_devices.size() == 0) {
+ m_fromCombo->insertItem(i18n("<no devices>"));
+ m_toCombo->insertItem(i18n("<no devices>"));
+ }
+ } else {
+ m_instruments = studio->getPresentationInstruments();
+ InstrumentList::iterator it = m_instruments.begin();
+
+ for (; it != m_instruments.end(); it++) {
+ m_fromCombo->insertItem(strtoqstr((*it)->getPresentationName()));
+ m_toCombo->insertItem(strtoqstr((*it)->getPresentationName()));
+ }
+ }
+}
+
+void
+RemapInstrumentDialog::slotRemapReleased(int id)
+{
+ populateCombo(id);
+}
+
+void
+RemapInstrumentDialog::slotOk()
+{
+ slotApply();
+ accept();
+}
+
+void
+RemapInstrumentDialog::slotApply()
+{
+ if (m_buttonGroup->id(m_buttonGroup->selected()) == 0) // devices
+ {
+ ModifyDeviceMappingCommand *command =
+ new ModifyDeviceMappingCommand
+ (m_doc,
+ m_devices[m_fromCombo->currentItem()]->getId(),
+ m_devices[m_toCombo->currentItem()]->getId());
+ addCommandToHistory(command);
+ } else // instruments
+ {
+ ModifyInstrumentMappingCommand *command =
+ new ModifyInstrumentMappingCommand
+ (m_doc,
+ m_instruments[m_fromCombo->currentItem()]->getId(),
+ m_instruments[m_toCombo->currentItem()]->getId());
+ addCommandToHistory(command);
+ }
+
+ emit applyClicked();
+}
+
+void
+RemapInstrumentDialog::addCommandToHistory(KCommand *command)
+{
+ getCommandHistory()->addCommand(command);
+}
+
+MultiViewCommandHistory*
+RemapInstrumentDialog::getCommandHistory()
+{
+ return m_doc->getCommandHistory();
+}
+
+}
+#include "RemapInstrumentDialog.moc"
diff --git a/src/gui/studio/RemapInstrumentDialog.h b/src/gui/studio/RemapInstrumentDialog.h
new file mode 100644
index 0000000..669020e
--- /dev/null
+++ b/src/gui/studio/RemapInstrumentDialog.h
@@ -0,0 +1,84 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_REMAPINSTRUMENTDIALOG_H_
+#define _RG_REMAPINSTRUMENTDIALOG_H_
+
+#include "base/Studio.h"
+#include <kdialogbase.h>
+
+
+class QWidget;
+class QRadioButton;
+class QButtonGroup;
+class KCommand;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+
+class RosegardenGUIDoc;
+class MultiViewCommandHistory;
+
+
+class RemapInstrumentDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ RemapInstrumentDialog(QWidget *parent,
+ RosegardenGUIDoc *doc);
+
+ void populateCombo(int id);
+
+ void addCommandToHistory(KCommand *command);
+ MultiViewCommandHistory* getCommandHistory();
+
+public slots:
+ void slotRemapReleased(int id);
+
+ void slotOk();
+ void slotApply();
+
+protected:
+
+ RosegardenGUIDoc *m_doc;
+
+ QRadioButton *m_deviceButton;
+ QRadioButton *m_instrumentButton;
+
+ QButtonGroup *m_buttonGroup;
+ KComboBox *m_fromCombo;
+ KComboBox *m_toCombo;
+
+ DeviceList m_devices;
+ InstrumentList m_instruments;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/studio/StudioControl.cpp b/src/gui/studio/StudioControl.cpp
new file mode 100644
index 0000000..e94016a
--- /dev/null
+++ b/src/gui/studio/StudioControl.cpp
@@ -0,0 +1,582 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "StudioControl.h"
+
+#include "sound/Midi.h"
+#include "misc/Debug.h"
+#include "base/MidiProgram.h"
+#include "base/Profiler.h"
+#include "base/RealTime.h"
+#include "gui/application/RosegardenApplication.h"
+#include "sound/MappedCommon.h"
+#include "sound/MappedComposition.h"
+#include "sound/MappedEvent.h"
+#include "sound/MappedInstrument.h"
+#include "sound/MappedStudio.h"
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+MappedObjectId
+StudioControl::createStudioObject(MappedObject::MappedObjectType type)
+{
+Profiler profiler("StudioControl::createStudioObject", true);
+
+int value = -1;
+QByteArray data;
+QCString replyType;
+QByteArray replyData;
+QDataStream streamOut(data, IO_WriteOnly);
+
+streamOut << (int)type;
+
+if (!rgapp->sequencerCall("createMappedObject(int)",
+ replyType, replyData, data))
+{
+ SEQMAN_DEBUG << "createStudioObject - "
+ << "failed to contact Rosegarden sequencer"
+ << endl;
+} else
+{
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ streamIn >> value;
+}
+
+return value;
+}
+
+bool
+StudioControl::destroyStudioObject(MappedObjectId id)
+{
+ Profiler profiler("StudioControl::destroyStudioObject", true);
+
+ int value = 0;
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << int(id);
+
+ if (!rgapp->sequencerCall("destroyMappedObject(int)",
+ replyType, replyData, data)) {
+ SEQMAN_DEBUG << "destroyStudioObject - "
+ << "failed to contact Rosegarden sequencer"
+ << endl;
+ } else {
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ streamIn >> value;
+ }
+
+ if (value == 1)
+ return true;
+ else
+ return false;
+}
+
+MappedObjectPropertyList
+StudioControl::getStudioObjectProperty(MappedObjectId id,
+ const MappedObjectProperty &property)
+{
+ Profiler profiler("StudioControl::getStudioObjectProperty", true);
+
+ MappedObjectPropertyList list;
+
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)id;
+ streamOut << QString(property);
+
+ if (!rgapp->sequencerCall("getPropertyList(int, QString)",
+ replyType, replyData, data)) {
+ SEQMAN_DEBUG << "getStudioObjectProperty - "
+ << "failed to contact Rosegarden sequencer"
+ << endl;
+ } else {
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ streamIn >> list;
+ }
+
+ return list;
+}
+
+bool
+StudioControl::setStudioObjectProperty(MappedObjectId id,
+ const MappedObjectProperty &property,
+ MappedObjectValue value)
+{
+ Profiler profiler("StudioControl::setStudioObjectProperty(float)", true);
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)id;
+ streamOut << (QString)property;
+ streamOut << (float)value;
+
+ rgapp->sequencerSend("setMappedProperty(int, QString, float)", data);
+
+ return true;
+}
+
+bool
+StudioControl::setStudioObjectProperties(const MappedObjectIdList &ids,
+ const MappedObjectPropertyList &properties,
+ const MappedObjectValueList &values)
+{
+ Profiler profiler("StudioControl::setStudioObjectProperties(floats)", true);
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << ids;
+ streamOut << properties;
+ streamOut << values;
+
+ rgapp->sequencerSend("setMappedProperties(MappedObjectIdList, MappedObjectPropertyList, MappedObjectValueList)", data);
+
+ return true;
+}
+
+bool
+StudioControl::setStudioObjectProperty(MappedObjectId id,
+ const MappedObjectProperty &property,
+ const QString &value)
+{
+ Profiler profiler("StudioControl::setStudioObjectProperty(string)", true);
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)id;
+ streamOut << (QString)property;
+ streamOut << (QString)value;
+
+ rgapp->sequencerSend("setMappedProperty(int, QString, QString)", data);
+
+ return true;
+}
+
+bool
+StudioControl::setStudioObjectPropertyList(MappedObjectId id,
+ const MappedObjectProperty &property,
+ const MappedObjectPropertyList &values)
+{
+ Profiler profiler("StudioControl::setStudioObjectPropertyList", true);
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)id;
+ streamOut << (QString)property;
+ streamOut << values;
+
+ RG_DEBUG << "StudioControl::setStudioObjectPropertyList: " << values.size() << " values for property " << property << endl;
+
+ rgapp->sequencerSend("setMappedPropertyList(int, QString, MappedObjectPropertyList)",
+ data);
+
+ return true;
+}
+
+MappedObjectId
+StudioControl::getStudioObjectByType(MappedObject::MappedObjectType type)
+{
+ Profiler profiler("StudioControl::getStudioObjectByType", true);
+
+ int value = -1;
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)type;
+
+ if (!rgapp->sequencerCall("getMappedObjectId(int)",
+ replyType, replyData, data)) {
+ SEQMAN_DEBUG << "getStudioObjectByType - "
+ << "failed to contact Rosegarden sequencer"
+ << endl;
+ } else {
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ streamIn >> value;
+ }
+
+ return value;
+}
+
+void
+StudioControl::setStudioPluginPort(MappedObjectId pluginId,
+ unsigned long portId,
+ MappedObjectValue value)
+{
+ Profiler profiler("StudioControl::setStudioPluginPort", true);
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ // Use new MappedEvent interface
+ //
+ streamOut << (int)pluginId;
+ streamOut << (unsigned long)portId;
+ streamOut << (float)value;
+
+ rgapp->sequencerSend("setMappedPort(int, unsigned long int, float)", data);
+}
+
+MappedObjectValue
+StudioControl::getStudioPluginPort(MappedObjectId pluginId,
+ unsigned long portId)
+{
+ Profiler profiler("StudioControl::getStudioPluginPort", true);
+
+ float value = 0.0;
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)pluginId;
+ streamOut << (unsigned long)portId;
+
+ if (!rgapp->sequencerCall("getMappedPort(int, unsigned long int)",
+ replyType, replyData, data)) {
+ SEQMAN_DEBUG << "getStudioPluginPort - "
+ << "failed to contact Rosegarden sequencer"
+ << endl;
+ } else {
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ streamIn >> value;
+ }
+
+ return value;
+}
+
+MappedObjectPropertyList
+StudioControl::getPluginInformation()
+{
+ MappedObjectPropertyList list;
+
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+
+ if (!rgapp->sequencerCall("getPluginInformation()",
+ replyType, replyData, data)) {
+ SEQMAN_DEBUG << "getPluginInformation - "
+ << "failed to contact Rosegarden sequencer"
+ << endl;
+ } else {
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ streamIn >> list;
+ }
+
+ return list;
+}
+
+QString
+StudioControl::getPluginProgram(MappedObjectId id, int bank, int program)
+{
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)id;
+ streamOut << (int)bank;
+ streamOut << (int)program;
+
+ QString programName;
+
+ if (!rgapp->sequencerCall("getPluginProgram(int, int, int)",
+ replyType, replyData, data)) {
+ SEQMAN_DEBUG << "getPluginProgram - "
+ << "failed to contact Rosegarden sequencer"
+ << endl;
+ } else {
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ streamIn >> programName;
+ }
+
+ return programName;
+}
+
+unsigned long
+StudioControl::getPluginProgram(MappedObjectId id, QString name)
+{
+ QByteArray data;
+ QCString replyType;
+ QByteArray replyData;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)id;
+ streamOut << name;
+
+ unsigned long rv;
+
+ if (!rgapp->sequencerCall("getPluginProgram(int, QString)",
+ replyType, replyData, data)) {
+ SEQMAN_DEBUG << "getPluginProgram - "
+ << "failed to contact Rosegarden sequencer"
+ << endl;
+ } else {
+ QDataStream streamIn(replyData, IO_ReadOnly);
+ streamIn >> rv;
+ }
+
+ return rv;
+}
+
+void
+StudioControl::connectStudioObjects(MappedObjectId id1,
+ MappedObjectId id2)
+{
+ Profiler profiler("StudioControl::connectStudioObjects", true);
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)id1;
+ streamOut << (int)id2;
+
+ if (!rgapp->sequencerSend("connectMappedObjects(int, int)", data)) {
+ SEQMAN_DEBUG << "connectStudioObjects - "
+ << "failed to contact Rosegarden sequencer"
+ << endl;
+ }
+
+ return ;
+}
+
+void
+StudioControl::disconnectStudioObjects(MappedObjectId id1,
+ MappedObjectId id2)
+{
+ Profiler profiler("StudioControl::disconnectStudioObjects", true);
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)id1;
+ streamOut << (int)id2;
+
+ if (!rgapp->sequencerSend("disconnectMappedObjects(int, int)", data)) {
+ SEQMAN_DEBUG << "disconnectStudioObjects - "
+ << "failed to contact Rosegarden sequencer"
+ << endl;
+ }
+
+ return ;
+}
+
+void
+StudioControl::disconnectStudioObject(MappedObjectId id)
+{
+ Profiler profiler("StudioControl::disconnectStudioObject", true);
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)id;
+
+ if (!rgapp->sequencerSend("disconnectMappedObject(int)", data)) {
+ SEQMAN_DEBUG << "disconnectStudioObject - "
+ << "failed to contact Rosegarden sequencer"
+ << endl;
+ }
+
+ return ;
+}
+
+void
+StudioControl::sendMappedEvent(const MappedEvent &mE)
+{
+ Profiler profiler("StudioControl::sendMappedEvent", true);
+
+ static MappedEvent mEs;
+
+ mEs = mE; // just in case the passed mapped event has dubious
+ // origins and taking its address isn't safe
+
+ mEs.setPersistent(true); // to avoid that MappedComposition dtor try to free it
+
+ MappedComposition mC;
+ mC.insert(&mEs);
+ StudioControl::sendMappedComposition(mC);
+}
+
+void
+StudioControl::sendMappedComposition(const MappedComposition &mC)
+{
+ Profiler profiler("StudioControl::sendMappedComposition", true);
+
+ if (mC.size() == 0)
+ return ;
+
+ QCString replyType;
+ QByteArray replyData;
+
+ MappedComposition::const_iterator it = mC.begin();
+
+ for (; it != mC.end(); it++) {
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (*it);
+ rgapp->sequencerSend("processMappedEvent(MappedEvent)", data);
+ }
+}
+
+void
+StudioControl::sendMappedInstrument(const MappedInstrument &mI)
+{
+ Profiler profiler("StudioControl::sendMappedInstrument", true);
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (int)mI.getType();
+ streamOut << (unsigned char)mI.getChannel();
+ streamOut << (unsigned int)mI.getId();
+
+ rgapp->sequencerSend("setMappedInstrument(int, unsigned char, unsigned int)", data);
+}
+
+void
+StudioControl::sendQuarterNoteLength(const RealTime &length)
+{
+ Profiler profiler("StudioControl::sendQuarterNoteLength", true);
+
+ QByteArray data;
+ QDataStream streamOut(data, IO_WriteOnly);
+
+ streamOut << (long)length.sec;
+ streamOut << (long)length.nsec;
+
+ rgapp->sequencerSend("setQuarterNoteLength(long int, long int)", data);
+}
+
+void
+StudioControl::sendRPN(InstrumentId instrumentId,
+ MidiByte paramMSB,
+ MidiByte paramLSB,
+ MidiByte /* controller */,
+ MidiByte value)
+{
+ Profiler profiler("StudioControl::sendRPN", true);
+
+ MappedComposition mC;
+ MappedEvent *mE =
+ new MappedEvent(instrumentId,
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_RPN_2,
+ paramMSB);
+ mC.insert(mE);
+
+ mE = new MappedEvent(instrumentId,
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_RPN_1,
+ paramLSB);
+ mC.insert(mE);
+
+ mE = new MappedEvent(instrumentId,
+ MappedEvent::MidiController,
+ 6, // data value changed
+ value);
+ mC.insert(mE);
+
+
+ // Null the controller using - this is "best practice"
+ //
+ mE = new MappedEvent(instrumentId,
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_RPN_2,
+ MidiMaxValue); // null
+ mC.insert(mE);
+
+ mE = new MappedEvent(instrumentId,
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_RPN_1,
+ MidiMaxValue); // null
+ mC.insert(mE);
+
+
+ StudioControl::sendMappedComposition(mC);
+}
+
+void
+StudioControl::sendNRPN(InstrumentId instrumentId,
+ MidiByte paramMSB,
+ MidiByte paramLSB,
+ MidiByte /* controller */,
+ MidiByte value)
+{
+ Profiler profiler("StudioControl::sendNRPN", true);
+
+ MappedComposition mC;
+ MappedEvent *mE =
+ new MappedEvent(instrumentId,
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_NRPN_2,
+ paramMSB);
+ mC.insert(mE);
+
+ mE = new MappedEvent(instrumentId,
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_NRPN_1,
+ paramLSB);
+ mC.insert(mE);
+
+ mE = new MappedEvent(instrumentId,
+ MappedEvent::MidiController,
+ 6, // data value changed
+ value);
+ mC.insert(mE);
+
+
+ // Null the controller using - this is "best practice"
+ //
+ mE = new MappedEvent(instrumentId,
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_RPN_2,
+ MidiMaxValue); // null
+ mC.insert(mE);
+
+ mE = new MappedEvent(instrumentId,
+ MappedEvent::MidiController,
+ MIDI_CONTROLLER_RPN_1,
+ MidiMaxValue); // null
+ mC.insert(mE);
+}
+
+}
diff --git a/src/gui/studio/StudioControl.h b/src/gui/studio/StudioControl.h
new file mode 100644
index 0000000..cf05d44
--- /dev/null
+++ b/src/gui/studio/StudioControl.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_STUDIOCONTROL_H_
+#define _RG_STUDIOCONTROL_H_
+
+#include "base/MidiProgram.h"
+#include "sound/MappedCommon.h"
+#include "sound/MappedStudio.h"
+#include <qstring.h>
+
+
+class MappedObjectValueList;
+class MappedObjectIdList;
+
+
+namespace Rosegarden
+{
+
+class RealTime;
+class MappedInstrument;
+class MappedEvent;
+class MappedComposition;
+
+typedef std::pair<Rosegarden::MidiByte, Rosegarden::MidiByte> MidiControlPair;
+
+class StudioControl
+{
+public:
+
+ // Object management
+ //
+ static MappedObjectId
+ createStudioObject(MappedObject::MappedObjectType type);
+ static MappedObjectId
+ getStudioObjectByType(MappedObject::MappedObjectType type);
+ static bool destroyStudioObject(MappedObjectId id);
+
+ // Properties
+ //
+ static MappedObjectPropertyList
+ getStudioObjectProperty(MappedObjectId id,
+ const MappedObjectProperty &property);
+
+ // Set a value to a value
+ //
+ static bool setStudioObjectProperty(MappedObjectId id,
+ const MappedObjectProperty &property,
+ MappedObjectValue value);
+
+ // Set many values to values
+ //
+ static bool setStudioObjectProperties(const MappedObjectIdList &ids,
+ const MappedObjectPropertyList &properties,
+ const MappedObjectValueList &values);
+
+ // Set a value to a string
+ //
+ static bool setStudioObjectProperty(MappedObjectId id,
+ const MappedObjectProperty &property,
+ const QString &value);
+
+ // Set a value to a string list
+ //
+ static bool setStudioObjectPropertyList(MappedObjectId id,
+ const MappedObjectProperty &property,
+ const MappedObjectPropertyList &values);
+
+ static void setStudioPluginPort(MappedObjectId pluginId,
+ unsigned long portId,
+ MappedObjectValue value);
+
+ static MappedObjectValue getStudioPluginPort(MappedObjectId pluginId,
+ unsigned long portId);
+
+ // Get all plugin information
+ //
+ static MappedObjectPropertyList getPluginInformation();
+
+ // Get program name for a given program
+ //
+ static QString getPluginProgram(MappedObjectId, int bank, int program);
+
+ // Get program numbers for a given name (rv is bank << 16 + program)
+ // This is one of the nastiest hacks in the whole application
+ //
+ static unsigned long getPluginProgram(MappedObjectId, QString name);
+
+ // Connection
+ //
+ static void connectStudioObjects(MappedObjectId id1,
+ MappedObjectId id2);
+ static void disconnectStudioObjects(MappedObjectId id1,
+ MappedObjectId id2);
+ static void disconnectStudioObject(MappedObjectId id);
+
+ // Send controllers and other one off MIDI events using these
+ // interfaces.
+ //
+ static void sendMappedEvent(const MappedEvent& mE);
+ static void sendMappedComposition(const MappedComposition &mC);
+
+ // MappedInstrument
+ //
+ static void sendMappedInstrument(const MappedInstrument &mI);
+
+ // Send the Quarter Note Length has changed to the sequencer
+ //
+ static void sendQuarterNoteLength(const RealTime &length);
+
+ // Convenience wrappers for RPNs and NRPNs
+ //
+ static void sendRPN(InstrumentId instrumentId,
+ MidiByte paramMSB,
+ MidiByte paramLSB,
+ MidiByte controller,
+ MidiByte value);
+
+ static void sendNRPN(InstrumentId instrumentId,
+ MidiByte paramMSB,
+ MidiByte paramLSB,
+ MidiByte controller,
+ MidiByte value);
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/studio/SynthPluginManagerDialog.cpp b/src/gui/studio/SynthPluginManagerDialog.cpp
new file mode 100644
index 0000000..f9a54ea
--- /dev/null
+++ b/src/gui/studio/SynthPluginManagerDialog.cpp
@@ -0,0 +1,360 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "SynthPluginManagerDialog.h"
+#include <qlayout.h>
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "AudioPlugin.h"
+#include "AudioPluginManager.h"
+#include "AudioPluginOSCGUIManager.h"
+#include "base/AudioPluginInstance.h"
+#include "base/Instrument.h"
+#include "base/MidiProgram.h"
+#include "base/Studio.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include <kaction.h>
+#include <kcombobox.h>
+#include <kmainwindow.h>
+#include <kstdaction.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qobject.h>
+#include <qpushbutton.h>
+#include <qsizepolicy.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+SynthPluginManagerDialog::SynthPluginManagerDialog(QWidget *parent,
+ RosegardenGUIDoc *doc
+#ifdef HAVE_LIBLO
+ , AudioPluginOSCGUIManager *guiManager
+#endif
+ ) :
+ KMainWindow(parent, "synthpluginmanagerdialog"),
+ m_document(doc),
+ m_studio(&doc->getStudio()),
+ m_pluginManager(doc->getPluginManager())
+#ifdef HAVE_LIBLO
+ , m_guiManager(guiManager)
+#endif
+ {
+ setCaption(i18n("Manage Synth Plugins"));
+
+ QFrame *mainBox = new QFrame(this);
+ setCentralWidget(mainBox);
+
+ QVBoxLayout *mainLayout = new QVBoxLayout(mainBox, 10, 10);
+
+ QGroupBox *groupBox = new QGroupBox(1, Horizontal, i18n("Synth plugins"), mainBox);
+ mainLayout->addWidget(groupBox);
+
+ QFrame *pluginFrame = new QFrame(groupBox);
+ QGridLayout *pluginLayout = new QGridLayout(pluginFrame, 1, 4, 3, 3);
+
+ m_synthPlugins.clear();
+ m_synthPlugins.push_back( -1);
+
+ int count = 0;
+
+ for (PluginIterator itr = m_pluginManager->begin();
+ itr != m_pluginManager->end(); ++itr) {
+
+ if ((*itr)->isSynth()) {
+ m_synthPlugins.push_back(count);
+ }
+
+ ++count;
+ }
+
+ for (int i = 0; i < SoftSynthInstrumentCount; ++i) {
+
+ InstrumentId id = SoftSynthInstrumentBase + i;
+ Instrument *instrument = m_studio->getInstrumentById(id);
+ if (!instrument)
+ continue;
+
+ // pluginLayout->addWidget(new QLabel(instrument->getPresentationName().c_str(),
+ // pluginFrame), i, 0);
+ pluginLayout->addWidget(new QLabel(QString("%1").arg(i + 1),
+ pluginFrame), i, 0);
+
+ AudioPluginInstance *plugin = instrument->getPlugin
+ (Instrument::SYNTH_PLUGIN_POSITION);
+
+ std::string identifier;
+ if (plugin)
+ identifier = plugin->getIdentifier();
+
+ int currentItem = 0;
+
+ KComboBox *pluginCombo = new KComboBox(pluginFrame);
+ pluginCombo->insertItem(i18n("<none>"));
+
+ for (size_t j = 0; j < m_synthPlugins.size(); ++j) {
+
+ if (m_synthPlugins[j] == -1)
+ continue;
+
+ AudioPlugin *plugin =
+ m_pluginManager->getPlugin(m_synthPlugins[j]);
+
+ pluginCombo->insertItem(plugin->getName());
+
+ if (plugin->getIdentifier() == identifier.c_str()) {
+ pluginCombo->setCurrentItem(pluginCombo->count() - 1);
+ }
+ }
+
+ connect(pluginCombo, SIGNAL(activated(int)),
+ this, SLOT(slotPluginChanged(int)));
+
+ pluginLayout->addWidget(pluginCombo, i, 1);
+
+ m_synthCombos.push_back(pluginCombo);
+
+ QPushButton *controlsButton = new QPushButton(i18n("Controls"), pluginFrame);
+ pluginLayout->addWidget(controlsButton, i, 2);
+ connect(controlsButton, SIGNAL(clicked()), this, SLOT(slotControlsButtonClicked()));
+ m_controlsButtons.push_back(controlsButton);
+
+#ifdef HAVE_LIBLO
+
+ QPushButton *guiButton = new QPushButton(i18n("Editor >>"), pluginFrame);
+ pluginLayout->addWidget(guiButton, i, 3);
+ guiButton->setEnabled(m_guiManager->hasGUI
+ (id, Instrument::SYNTH_PLUGIN_POSITION));
+ connect(guiButton, SIGNAL(clicked()), this, SLOT(slotGUIButtonClicked()));
+ m_guiButtons.push_back(guiButton);
+#endif
+
+ }
+
+ QFrame* btnBox = new QFrame(mainBox);
+
+ btnBox->setSizePolicy(
+ QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed));
+
+ QPushButton *closeButton = new QPushButton(i18n("Close"), btnBox);
+
+ QHBoxLayout* layout = new QHBoxLayout(btnBox, 0, 10);
+ layout->addStretch(10);
+ layout->addWidget(closeButton);
+ layout->addSpacing(5);
+
+ KAction* close = KStdAction::close(this,
+ SLOT(slotClose()),
+ actionCollection());
+
+ closeButton->setText(close->text());
+ connect(closeButton, SIGNAL(clicked()), this, SLOT(slotClose()));
+
+ mainLayout->addWidget(btnBox);
+
+ createGUI("synthpluginmanager.rc");
+
+ setAutoSaveSettings(SynthPluginManagerConfigGroup, true);
+ }
+
+ SynthPluginManagerDialog::~SynthPluginManagerDialog()
+ {
+ RG_DEBUG << "\n*** SynthPluginManagerDialog::~SynthPluginManagerDialog()"
+ << endl;
+ }
+
+ void
+ SynthPluginManagerDialog::updatePlugin(InstrumentId id, int plugin)
+ {
+ if (id < SoftSynthInstrumentBase)
+ return ;
+ int row = id - SoftSynthInstrumentBase;
+ if (row >= m_synthCombos.size())
+ return ;
+
+ KComboBox *comboBox = m_synthCombos[row];
+
+ for (unsigned int i = 0; i < m_synthPlugins.size(); ++i) {
+ if (m_synthPlugins[i] == plugin) {
+ blockSignals(true);
+ comboBox->setCurrentItem(i);
+ blockSignals(false);
+ return ;
+ }
+ }
+
+ blockSignals(true);
+ comboBox->setCurrentItem(0);
+ blockSignals(false);
+ return ;
+ }
+
+ void
+ SynthPluginManagerDialog::slotClose()
+ {
+ close();
+ }
+
+ void
+ SynthPluginManagerDialog::closeEvent(QCloseEvent *e)
+ {
+ emit closing();
+ KMainWindow::closeEvent(e);
+ }
+
+ void
+ SynthPluginManagerDialog::slotGUIButtonClicked()
+ {
+ const QObject *s = sender();
+
+ int instrumentNo = -1;
+
+ for (unsigned int i = 0; i < m_guiButtons.size(); ++i) {
+ if (s == m_guiButtons[i])
+ instrumentNo = i;
+ }
+
+ if (instrumentNo == -1) {
+ RG_DEBUG << "WARNING: SynthPluginManagerDialog::slotGUIButtonClicked: unknown sender" << endl;
+ return ;
+ }
+
+ InstrumentId id = SoftSynthInstrumentBase + instrumentNo;
+
+ emit showPluginGUI(id, Instrument::SYNTH_PLUGIN_POSITION);
+ }
+
+ void
+ SynthPluginManagerDialog::slotControlsButtonClicked()
+ {
+ const QObject *s = sender();
+
+ int instrumentNo = -1;
+
+ for (unsigned int i = 0; i < m_controlsButtons.size(); ++i) {
+ if (s == m_controlsButtons[i])
+ instrumentNo = i;
+ }
+
+ if (instrumentNo == -1) {
+ RG_DEBUG << "WARNING: SynthPluginManagerDialog::slotControlsButtonClicked: unknown sender" << endl;
+ return ;
+ }
+
+ InstrumentId id = SoftSynthInstrumentBase + instrumentNo;
+
+ emit showPluginDialog(this, id, Instrument::SYNTH_PLUGIN_POSITION);
+ }
+
+ void
+ SynthPluginManagerDialog::slotPluginChanged(int index)
+ {
+ const QObject *s = sender();
+
+ RG_DEBUG << "SynthPluginManagerDialog::slotPluginChanged(" << index
+ << ")" << endl;
+
+ int instrumentNo = -1;
+
+ for (unsigned int i = 0; i < m_synthCombos.size(); ++i) {
+ if (s == m_synthCombos[i])
+ instrumentNo = i;
+ }
+
+ if (instrumentNo == -1) {
+ RG_DEBUG << "WARNING: SynthPluginManagerDialog::slotValueChanged: unknown sender" << endl;
+ return ;
+ }
+
+ InstrumentId id = SoftSynthInstrumentBase + instrumentNo;
+
+ if (index >= int(m_synthPlugins.size())) {
+ RG_DEBUG << "WARNING: SynthPluginManagerDialog::slotValueChanged: synth "
+ << index << " out of range" << endl;
+ return ;
+ }
+
+ // NB m_synthPlugins[0] is -1 to represent the <none> item
+
+ AudioPlugin *plugin = m_pluginManager->getPlugin(m_synthPlugins[index]);
+ Instrument *instrument = m_studio->getInstrumentById(id);
+
+ if (instrument) {
+
+ AudioPluginInstance *pluginInstance = instrument->getPlugin
+ (Instrument::SYNTH_PLUGIN_POSITION);
+
+ if (pluginInstance) {
+
+ if (plugin) {
+ RG_DEBUG << "plugin is " << plugin->getIdentifier() << endl;
+ pluginInstance->setIdentifier(plugin->getIdentifier().data());
+
+ // set ports to defaults
+
+ AudioPlugin::PortIterator it = plugin->begin();
+ int count = 0;
+
+ for (; it != plugin->end(); ++it) {
+
+ if (((*it)->getType() & PluginPort::Control) &&
+ ((*it)->getType() & PluginPort::Input)) {
+
+ if (pluginInstance->getPort(count) == 0) {
+ pluginInstance->addPort(count, (float)(*it)->getDefaultValue());
+ } else {
+ pluginInstance->getPort(count)->value = (*it)->getDefaultValue();
+ }
+ }
+
+ ++count;
+ }
+
+ } else {
+ pluginInstance->setIdentifier("");
+ }
+ }
+ }
+
+#ifdef HAVE_LIBLO
+ if (instrumentNo < m_guiButtons.size()) {
+ m_guiButtons[instrumentNo]->setEnabled
+ (m_guiManager->hasGUI
+ (id, Instrument::SYNTH_PLUGIN_POSITION));
+ }
+#endif
+
+ emit pluginSelected(id, Instrument::SYNTH_PLUGIN_POSITION,
+ m_synthPlugins[index]);
+ }
+
+ }
+#include "SynthPluginManagerDialog.moc"
diff --git a/src/gui/studio/SynthPluginManagerDialog.h b/src/gui/studio/SynthPluginManagerDialog.h
new file mode 100644
index 0000000..b66a338
--- /dev/null
+++ b/src/gui/studio/SynthPluginManagerDialog.h
@@ -0,0 +1,98 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_SYNTHPLUGINMANAGERDIALOG_H_
+#define _RG_SYNTHPLUGINMANAGERDIALOG_H_
+
+#include "base/MidiProgram.h"
+#include <kmainwindow.h>
+#include <vector>
+
+
+class QWidget;
+class QPushButton;
+class QCloseEvent;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class Studio;
+class RosegardenGUIDoc;
+class AudioPluginOSCGUIManager;
+class AudioPluginManager;
+
+
+class SynthPluginManagerDialog : public KMainWindow
+{
+ Q_OBJECT
+
+public:
+ SynthPluginManagerDialog(QWidget *parent,
+ RosegardenGUIDoc *doc
+#ifdef HAVE_LIBLO
+ , AudioPluginOSCGUIManager *guiManager
+#endif
+ );
+
+ virtual ~SynthPluginManagerDialog();
+
+ void updatePlugin(InstrumentId id, int plugin);
+
+signals:
+ void closing();
+ void pluginSelected(InstrumentId, int pluginIndex, int plugin);
+ void showPluginDialog(QWidget *, InstrumentId, int pluginIndex);
+ void showPluginGUI(InstrumentId, int pluginIndex);
+
+protected slots:
+ void slotClose();
+ void slotPluginChanged(int index);
+ void slotControlsButtonClicked();
+ void slotGUIButtonClicked();
+
+protected:
+ virtual void closeEvent(QCloseEvent *);
+
+protected:
+ RosegardenGUIDoc *m_document;
+ Studio *m_studio;
+ AudioPluginManager *m_pluginManager;
+ std::vector<int> m_synthPlugins;
+ std::vector<KComboBox *> m_synthCombos;
+ std::vector<QPushButton *> m_controlsButtons;
+ std::vector<QPushButton *> m_guiButtons;
+
+#ifdef HAVE_LIBLO
+ AudioPluginOSCGUIManager *m_guiManager;
+#endif
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/studio/TimerCallbackAssistant.cpp b/src/gui/studio/TimerCallbackAssistant.cpp
new file mode 100644
index 0000000..ec8518c
--- /dev/null
+++ b/src/gui/studio/TimerCallbackAssistant.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "TimerCallbackAssistant.h"
+
+#include <qobject.h>
+#include <qtimer.h>
+
+
+namespace Rosegarden
+{
+
+TimerCallbackAssistant::TimerCallbackAssistant(int ms, void (*callback)(void *data),
+ void *data) :
+ m_callback(callback),
+ m_data(data)
+{
+ QTimer *timer = new QTimer(this);
+ connect(timer, SIGNAL(timeout()), this, SLOT(slotCallback()));
+ timer->start(ms, FALSE);
+}
+
+TimerCallbackAssistant::~TimerCallbackAssistant()
+{
+ // nothing -- the QTimer is deleted automatically by its parent QObject (me)
+}
+
+void
+TimerCallbackAssistant::slotCallback()
+{
+ m_callback(m_data);
+}
+
+}
+#include "TimerCallbackAssistant.moc"
diff --git a/src/gui/studio/TimerCallbackAssistant.h b/src/gui/studio/TimerCallbackAssistant.h
new file mode 100644
index 0000000..2a8e353
--- /dev/null
+++ b/src/gui/studio/TimerCallbackAssistant.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_TIMERCALLBACKASSISTANT_H_
+#define _RG_TIMERCALLBACKASSISTANT_H_
+
+#include <qobject.h>
+
+
+
+
+namespace Rosegarden
+{
+
+
+/* This assistant class is here simply to work around the fact that
+ AudioPluginOSCGUI cannot be a QObject because it's only
+ conditionally compiled. */
+
+class TimerCallbackAssistant : public QObject
+{
+ Q_OBJECT
+
+public:
+ TimerCallbackAssistant(int ms, void (*callback)(void *data), void *data);
+ virtual ~TimerCallbackAssistant();
+
+protected slots:
+ void slotCallback();
+
+private:
+ void (*m_callback)(void *);
+ void *m_data;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/ui/RosegardenTransport.ui b/src/gui/ui/RosegardenTransport.ui
new file mode 100644
index 0000000..347b30b
--- /dev/null
+++ b/src/gui/ui/RosegardenTransport.ui
@@ -0,0 +1,4361 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>RosegardenTransport</class>
+<widget class="QFrame">
+ <property name="name">
+ <cstring>RosegardenTransport</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>417</width>
+ <height>134</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Rosegarden Transport</string>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>RecordingFrame</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>87</y>
+ <width>416</width>
+ <height>47</height>
+ </rect>
+ </property>
+ <property name="frameShape">
+ <enum>StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>PanelCloseButton</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>3</x>
+ <y>4</y>
+ <width>25</width>
+ <height>23</height>
+ </rect>
+ </property>
+ <property name="font">
+ <font>
+ <family>lucida</family>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="pixmap">
+ <pixmap>image0</pixmap>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Hide additional controls</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>PanicButton</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>174</x>
+ <y>4</y>
+ <width>42</width>
+ <height>38</height>
+ </rect>
+ </property>
+ <property name="font">
+ <font>
+ <family>lucidatypewriter</family>
+ <pointsize>8</pointsize>
+ </font>
+ </property>
+ <property name="cursor">
+ <cursor>0</cursor>
+ </property>
+ <property name="focusPolicy">
+ <enum>NoFocus</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="pixmap">
+ <pixmap>image1</pixmap>
+ </property>
+ <property name="toggleButton">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Panic Button</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Resets all MIDI devices if you've got stuck notes</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>MetronomeButton</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>217</x>
+ <y>4</y>
+ <width>42</width>
+ <height>38</height>
+ </rect>
+ </property>
+ <property name="font">
+ <font>
+ <family>lucidatypewriter</family>
+ <pointsize>8</pointsize>
+ </font>
+ </property>
+ <property name="cursor">
+ <cursor>0</cursor>
+ </property>
+ <property name="focusPolicy">
+ <enum>NoFocus</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="pixmap">
+ <pixmap>image2</pixmap>
+ </property>
+ <property name="toggleButton">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Metronome</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Provides a metronome click for you to play along with</string>
+ </property>
+ </widget>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>MidiEventFrame</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>31</x>
+ <y>7</y>
+ <width>137</width>
+ <height>34</height>
+ </rect>
+ </property>
+ <property name="palette">
+ <palette>
+ <active>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>223</red>
+ <green>223</green>
+ <blue>223</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </active>
+ <disabled>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </disabled>
+ <inactive>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </inactive>
+ </palette>
+ </property>
+ <property name="frameShape">
+ <enum>StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Shows MIDI activity in and out of Rosegarden</string>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>InLabel</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>18</x>
+ <y>1</y>
+ <width>25</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="palette">
+ <palette>
+ <active>
+ <color>
+ <red>192</red>
+ <green>216</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>223</red>
+ <green>223</green>
+ <blue>223</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </active>
+ <disabled>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </disabled>
+ <inactive>
+ <color>
+ <red>192</red>
+ <green>216</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </inactive>
+ </palette>
+ </property>
+ <property name="font">
+ <font>
+ <family>lucida</family>
+ <italic>1</italic>
+ </font>
+ </property>
+ <property name="text">
+ <string>IN</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>OutLabel</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>8</x>
+ <y>15</y>
+ <width>25</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="palette">
+ <palette>
+ <active>
+ <color>
+ <red>192</red>
+ <green>216</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>223</red>
+ <green>223</green>
+ <blue>223</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </active>
+ <disabled>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </disabled>
+ <inactive>
+ <color>
+ <red>192</red>
+ <green>216</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </inactive>
+ </palette>
+ </property>
+ <property name="font">
+ <font>
+ <family>lucida</family>
+ <italic>1</italic>
+ </font>
+ </property>
+ <property name="text">
+ <string>OUT</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>InDisplay</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>40</x>
+ <y>1</y>
+ <width>82</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="palette">
+ <palette>
+ <active>
+ <color>
+ <red>192</red>
+ <green>216</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>223</red>
+ <green>223</green>
+ <blue>223</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </active>
+ <disabled>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </disabled>
+ <inactive>
+ <color>
+ <red>192</red>
+ <green>216</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </inactive>
+ </palette>
+ </property>
+ <property name="font">
+ <font>
+ <family>lucida</family>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>NO EVENTS</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>OutDisplay</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>40</x>
+ <y>15</y>
+ <width>82</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="palette">
+ <palette>
+ <active>
+ <color>
+ <red>192</red>
+ <green>216</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>223</red>
+ <green>223</green>
+ <blue>223</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </active>
+ <disabled>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </disabled>
+ <inactive>
+ <color>
+ <red>192</red>
+ <green>216</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </inactive>
+ </palette>
+ </property>
+ <property name="font">
+ <font>
+ <family>lucida</family>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>NO EVENTS</string>
+ </property>
+ </widget>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>RecordButton</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>370</x>
+ <y>4</y>
+ <width>42</width>
+ <height>38</height>
+ </rect>
+ </property>
+ <property name="cursor">
+ <cursor>0</cursor>
+ </property>
+ <property name="focusPolicy">
+ <enum>NoFocus</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="pixmap">
+ <pixmap>image3</pixmap>
+ </property>
+ <property name="toggleButton">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Record</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Record either MIDI or audio</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>LoopButton</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>320</x>
+ <y>4</y>
+ <width>32</width>
+ <height>38</height>
+ </rect>
+ </property>
+ <property name="font">
+ <font>
+ <family>lucidatypewriter</family>
+ <pointsize>8</pointsize>
+ </font>
+ </property>
+ <property name="cursor">
+ <cursor>0</cursor>
+ </property>
+ <property name="focusPolicy">
+ <enum>NoFocus</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="pixmap">
+ <pixmap>image4</pixmap>
+ </property>
+ <property name="toggleButton">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Loop</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Turn on and off the loop markers (if set)</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>SoloButton</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>260</x>
+ <y>4</y>
+ <width>42</width>
+ <height>38</height>
+ </rect>
+ </property>
+ <property name="font">
+ <font>
+ <family>lucidatypewriter</family>
+ <pointsize>8</pointsize>
+ </font>
+ </property>
+ <property name="cursor">
+ <cursor>0</cursor>
+ </property>
+ <property name="focusPolicy">
+ <enum>NoFocus</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="pixmap">
+ <pixmap>image5</pixmap>
+ </property>
+ <property name="toggleButton">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Solo</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Mutes all but the currently selected track</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>SetStartLPButton</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>303</x>
+ <y>4</y>
+ <width>16</width>
+ <height>38</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="pixmap">
+ <pixmap>image6</pixmap>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Start loop or range here</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>SetStopLPButton</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>353</x>
+ <y>4</y>
+ <width>16</width>
+ <height>38</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="pixmap">
+ <pixmap>image7</pixmap>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>End loop or range here</string>
+ </property>
+ </widget>
+ </widget>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>MainFrame</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>416</width>
+ <height>87</height>
+ </rect>
+ </property>
+ <property name="frameShape">
+ <enum>StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>LCDBoxFrame</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>31</x>
+ <y>7</y>
+ <width>225</width>
+ <height>71</height>
+ </rect>
+ </property>
+ <property name="palette">
+ <palette>
+ <active>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>223</red>
+ <green>223</green>
+ <blue>223</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </active>
+ <disabled>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </disabled>
+ <inactive>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </inactive>
+ </palette>
+ </property>
+ <property name="frameShape">
+ <enum>StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <widget class="Rosegarden::Label">
+ <property name="name">
+ <cstring>UnitHoursPixmap</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>39</x>
+ <y>6</y>
+ <width>20</width>
+ <height>34</height>
+ </rect>
+ </property>
+ <property name="pixmap">
+ <pixmap>image8</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring></cstring>
+ </property>
+ </widget>
+ <widget class="Rosegarden::Label">
+ <property name="name">
+ <cstring>HourColonPixmap</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>59</x>
+ <y>6</y>
+ <width>5</width>
+ <height>34</height>
+ </rect>
+ </property>
+ <property name="pixmap">
+ <pixmap>image9</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring></cstring>
+ </property>
+ </widget>
+ <widget class="Rosegarden::Label">
+ <property name="name">
+ <cstring>TenMinutesPixmap</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>64</x>
+ <y>6</y>
+ <width>20</width>
+ <height>34</height>
+ </rect>
+ </property>
+ <property name="pixmap">
+ <pixmap>image8</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring></cstring>
+ </property>
+ </widget>
+ <widget class="Rosegarden::Label">
+ <property name="name">
+ <cstring>UnitMinutesPixmap</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>84</x>
+ <y>6</y>
+ <width>20</width>
+ <height>34</height>
+ </rect>
+ </property>
+ <property name="pixmap">
+ <pixmap>image8</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring></cstring>
+ </property>
+ </widget>
+ <widget class="Rosegarden::Label">
+ <property name="name">
+ <cstring>TenSecondsPixmap</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>109</x>
+ <y>6</y>
+ <width>20</width>
+ <height>34</height>
+ </rect>
+ </property>
+ <property name="pixmap">
+ <pixmap>image8</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring></cstring>
+ </property>
+ </widget>
+ <widget class="Rosegarden::Label">
+ <property name="name">
+ <cstring>UnitSecondsPixmap</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>129</x>
+ <y>6</y>
+ <width>20</width>
+ <height>34</height>
+ </rect>
+ </property>
+ <property name="pixmap">
+ <pixmap>image8</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring></cstring>
+ </property>
+ </widget>
+ <widget class="Rosegarden::Label">
+ <property name="name">
+ <cstring>HundredthsPixmap</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>169</x>
+ <y>16</y>
+ <width>14</width>
+ <height>24</height>
+ </rect>
+ </property>
+ <property name="pixmap">
+ <pixmap>image8</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring></cstring>
+ </property>
+ </widget>
+ <widget class="Rosegarden::Label">
+ <property name="name">
+ <cstring>NegativePixmap</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>2</x>
+ <y>6</y>
+ <width>20</width>
+ <height>34</height>
+ </rect>
+ </property>
+ <property name="pixmap">
+ <pixmap>image10</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring></cstring>
+ </property>
+ </widget>
+ <widget class="Rosegarden::Label">
+ <property name="name">
+ <cstring>TenHoursPixmap</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>19</x>
+ <y>6</y>
+ <width>20</width>
+ <height>34</height>
+ </rect>
+ </property>
+ <property name="pixmap">
+ <pixmap>image8</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring></cstring>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TimeSigLabel</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>6</x>
+ <y>51</y>
+ <width>28</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="palette">
+ <palette>
+ <active>
+ <color>
+ <red>192</red>
+ <green>216</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>223</red>
+ <green>223</green>
+ <blue>223</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </active>
+ <disabled>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </disabled>
+ <inactive>
+ <color>
+ <red>192</red>
+ <green>216</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </inactive>
+ </palette>
+ </property>
+ <property name="font">
+ <font>
+ <family>lucida</family>
+ <italic>1</italic>
+ </font>
+ </property>
+ <property name="text">
+ <string>SIG</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>DivisionLabel</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>66</x>
+ <y>51</y>
+ <width>24</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="palette">
+ <palette>
+ <active>
+ <color>
+ <red>192</red>
+ <green>216</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>223</red>
+ <green>223</green>
+ <blue>223</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </active>
+ <disabled>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </disabled>
+ <inactive>
+ <color>
+ <red>192</red>
+ <green>216</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </inactive>
+ </palette>
+ </property>
+ <property name="font">
+ <font>
+ <family>lucida</family>
+ <italic>1</italic>
+ </font>
+ </property>
+ <property name="text">
+ <string>DIV</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>DivisionDisplay</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>86</x>
+ <y>51</y>
+ <width>27</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="palette">
+ <palette>
+ <active>
+ <color>
+ <red>192</red>
+ <green>216</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>223</red>
+ <green>223</green>
+ <blue>223</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </active>
+ <disabled>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </disabled>
+ <inactive>
+ <color>
+ <red>192</red>
+ <green>216</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </inactive>
+ </palette>
+ </property>
+ <property name="font">
+ <font>
+ <family>lucida</family>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>/16</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="hAlign" stdset="0">
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TempoLabel</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>124</x>
+ <y>51</y>
+ <width>43</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="palette">
+ <palette>
+ <active>
+ <color>
+ <red>192</red>
+ <green>216</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>223</red>
+ <green>223</green>
+ <blue>223</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </active>
+ <disabled>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </disabled>
+ <inactive>
+ <color>
+ <red>192</red>
+ <green>216</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </inactive>
+ </palette>
+ </property>
+ <property name="font">
+ <font>
+ <family>lucida</family>
+ <italic>1</italic>
+ </font>
+ </property>
+ <property name="text">
+ <string>TEMPO</string>
+ </property>
+ </widget>
+ <widget class="Rosegarden::Label">
+ <property name="name">
+ <cstring>TimeSigDisplay</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>23</x>
+ <y>51</y>
+ <width>33</width>
+ <height>16</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>ToEndLabel</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>192</x>
+ <y>1</y>
+ <width>28</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="palette">
+ <palette>
+ <active>
+ <color>
+ <red>192</red>
+ <green>216</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>223</red>
+ <green>223</green>
+ <blue>223</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </active>
+ <disabled>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </disabled>
+ <inactive>
+ <color>
+ <red>192</red>
+ <green>216</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </inactive>
+ </palette>
+ </property>
+ <property name="font">
+ <font>
+ <family>lucida</family>
+ <italic>1</italic>
+ </font>
+ </property>
+ <property name="text">
+ <string>END</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="hAlign" stdset="0">
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TimeDisplayLabel</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>157</x>
+ <y>1</y>
+ <width>35</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="palette">
+ <palette>
+ <active>
+ <color>
+ <red>192</red>
+ <green>216</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>223</red>
+ <green>223</green>
+ <blue>223</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </active>
+ <disabled>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </disabled>
+ <inactive>
+ <color>
+ <red>192</red>
+ <green>216</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>192</red>
+ <green>192</green>
+ <blue>192</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>220</red>
+ <green>220</green>
+ <blue>220</blue>
+ </color>
+ <color>
+ <red>96</red>
+ <green>96</green>
+ <blue>96</blue>
+ </color>
+ <color>
+ <red>128</red>
+ <green>128</green>
+ <blue>128</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </inactive>
+ </palette>
+ </property>
+ <property name="font">
+ <font>
+ <family>lucida</family>
+ <italic>1</italic>
+ </font>
+ </property>
+ <property name="text">
+ <string>BAR</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="hAlign" stdset="0">
+ </property>
+ </widget>
+ <widget class="Rosegarden::Label">
+ <property name="name">
+ <cstring>TenThousandthsPixmap</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>201</x>
+ <y>16</y>
+ <width>14</width>
+ <height>24</height>
+ </rect>
+ </property>
+ <property name="pixmap">
+ <pixmap>image8</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring></cstring>
+ </property>
+ </widget>
+ <widget class="Rosegarden::Label">
+ <property name="name">
+ <cstring>MinuteColonPixmap</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>104</x>
+ <y>6</y>
+ <width>5</width>
+ <height>34</height>
+ </rect>
+ </property>
+ <property name="pixmap">
+ <pixmap>image9</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring></cstring>
+ </property>
+ </widget>
+ <widget class="Rosegarden::Label">
+ <property name="name">
+ <cstring>SecondColonPixmap</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>183</x>
+ <y>14</y>
+ <width>4</width>
+ <height>27</height>
+ </rect>
+ </property>
+ <property name="pixmap">
+ <pixmap>image9</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring></cstring>
+ </property>
+ </widget>
+ <widget class="Rosegarden::Label">
+ <property name="name">
+ <cstring>HundredthColonPixmap</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>151</x>
+ <y>14</y>
+ <width>4</width>
+ <height>27</height>
+ </rect>
+ </property>
+ <property name="pixmap">
+ <pixmap>image9</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring></cstring>
+ </property>
+ </widget>
+ <widget class="Rosegarden::Label">
+ <property name="name">
+ <cstring>TenthsPixmap</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>155</x>
+ <y>16</y>
+ <width>14</width>
+ <height>24</height>
+ </rect>
+ </property>
+ <property name="pixmap">
+ <pixmap>image8</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring></cstring>
+ </property>
+ </widget>
+ <widget class="Rosegarden::Label">
+ <property name="name">
+ <cstring>ThousandthsPixmap</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>187</x>
+ <y>16</y>
+ <width>14</width>
+ <height>24</height>
+ </rect>
+ </property>
+ <property name="pixmap">
+ <pixmap>image8</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring></cstring>
+ </property>
+ </widget>
+ <widget class="Rosegarden::Label">
+ <property name="name">
+ <cstring>TempoDisplay</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>159</x>
+ <y>51</y>
+ <width>55</width>
+ <height>16</height>
+ </rect>
+ </property>
+ </widget>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>TimeDisplayButton</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>3</x>
+ <y>4</y>
+ <width>25</width>
+ <height>25</height>
+ </rect>
+ </property>
+ <property name="font">
+ <font>
+ <family>lucida</family>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="pixmap">
+ <pixmap>image11</pixmap>
+ </property>
+ <property name="toggleButton">
+ <bool>false</bool>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Switch between real time, musical time, and frame count</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>PanelOpenButton</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>3</x>
+ <y>56</y>
+ <width>25</width>
+ <height>25</height>
+ </rect>
+ </property>
+ <property name="font">
+ <font>
+ <family>lucida</family>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="pixmap">
+ <pixmap>image12</pixmap>
+ </property>
+ <property name="toggleButton">
+ <bool>true</bool>
+ </property>
+ <property name="on">
+ <bool>true</bool>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Show additional controls</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>RewindButton</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>260</x>
+ <y>4</y>
+ <width>42</width>
+ <height>38</height>
+ </rect>
+ </property>
+ <property name="cursor">
+ <cursor>0</cursor>
+ </property>
+ <property name="focusPolicy">
+ <enum>NoFocus</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="pixmap">
+ <pixmap>image13</pixmap>
+ </property>
+ <property name="autoRepeat">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Rewind</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Moves the current pointer position back one bar.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>RewindEndButton</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>260</x>
+ <y>43</y>
+ <width>42</width>
+ <height>38</height>
+ </rect>
+ </property>
+ <property name="cursor">
+ <cursor>0</cursor>
+ </property>
+ <property name="focusPolicy">
+ <enum>NoFocus</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="pixmap">
+ <pixmap>image14</pixmap>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Rewind to beginning</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Moves the pointer position to the start of the composition. (This may mean going forwards if the pointer is currently before the start.)</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>PlayButton</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>303</x>
+ <y>4</y>
+ <width>66</width>
+ <height>38</height>
+ </rect>
+ </property>
+ <property name="cursor">
+ <cursor>0</cursor>
+ </property>
+ <property name="focusPolicy">
+ <enum>NoFocus</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="pixmap">
+ <pixmap>image15</pixmap>
+ </property>
+ <property name="toggleButton">
+ <bool>true</bool>
+ </property>
+ <property name="default">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Play/Pause</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Plays from the current pointer position, or pauses playback if already in progress.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>StopButton</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>303</x>
+ <y>43</y>
+ <width>66</width>
+ <height>38</height>
+ </rect>
+ </property>
+ <property name="cursor">
+ <cursor>0</cursor>
+ </property>
+ <property name="focusPolicy">
+ <enum>NoFocus</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="pixmap">
+ <pixmap>image16</pixmap>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Stop</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Stops playback or recording.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>FfwdButton</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>370</x>
+ <y>4</y>
+ <width>42</width>
+ <height>38</height>
+ </rect>
+ </property>
+ <property name="cursor">
+ <cursor>0</cursor>
+ </property>
+ <property name="focusPolicy">
+ <enum>NoFocus</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="pixmap">
+ <pixmap>image17</pixmap>
+ </property>
+ <property name="autoRepeat">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Fast forward</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Moves the current pointer position forwards one bar.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>FfwdEndButton</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>370</x>
+ <y>43</y>
+ <width>42</width>
+ <height>38</height>
+ </rect>
+ </property>
+ <property name="cursor">
+ <cursor>0</cursor>
+ </property>
+ <property name="focusPolicy">
+ <enum>NoFocus</enum>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="pixmap">
+ <pixmap>image18</pixmap>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Fast forward to end</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Moves the pointer position to the end of the composition. (This may mean going backwards if the pointer is already beyond the end.)</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>ToEndButton</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>3</x>
+ <y>28</y>
+ <width>25</width>
+ <height>25</height>
+ </rect>
+ </property>
+ <property name="font">
+ <font>
+ <family>lucida</family>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="pixmap">
+ <pixmap>image19</pixmap>
+ </property>
+ <property name="toggleButton">
+ <bool>true</bool>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Display time to end</string>
+ </property>
+ </widget>
+ </widget>
+</widget>
+<customwidgets>
+ <customwidget>
+ <class>Rosegarden::Label</class>
+ <header location="local">gui/widgets/Label.h</header>
+ <sizehint>
+ <width>-1</width>
+ <height>-1</height>
+ </sizehint>
+ <container>0</container>
+ <sizepolicy>
+ <hordata>5</hordata>
+ <verdata>5</verdata>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ <pixmap>image20</pixmap>
+ </customwidget>
+</customwidgets>
+<images>
+ <image name="image0">
+ <data format="PNG" length="117">89504e470d0a1a0a0000000d494844520000000b0000000b0806000000a9ac77260000003c4944415418956360a02f6061f84facd2ff0c0c0cde509a28850c8434202b8401ac1ab029c4d0c088a49810606444e2e03299818181612b118651000006f40d0bd43b3a0f0000000049454e44ae426082</data>
+ </image>
+ <image name="image1">
+ <data format="PNG" length="233">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000000b049444154388de5d44d0a41511887f11fcac4c4c8166cc0166cc67a0c6dc0c0c0dc0694890c311029260694e2d635a0e496ee474794a74ea77f9df7e9edf49ec38f500a292ba3871362ecd109258f13eb98a59b22544289e789bc09255ea6e4c2e2454a2e2c5e7f4a3cce2bce4a0d91e7b835438961f69046a8a61dce33c793c7bec525a4b8effeac07396abe4703235cb1423b94b8ebf513daa51564bde32891cff9fa7a4f1d431c30452b94f80fb8017e53288b176e48270000000049454e44ae426082</data>
+ </image>
+ <image name="image2">
+ <data format="PNG" length="678">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000026d49444154388dd594b18b954714c58feb351e7090237cc8ac04f30a4510ed421a0b0b0b5d6c74154c6cd244c93f10b0b6b188ff41c0264520e8be52510bc146110b51c1a86d40631267e1538eebf83e9bb7fabd64dd7d65729b19ee9cf9dd33979901fe6fb16e2d811a1d25b8e06a30080070f57c2965387515256d93749289b300c0e026355a1c0c061dc9874cbccfc44e8d5e2190a6834a3b99f8b7a42e37f905c9ad924e48ea98b848322110249f2ba953a3a3abf1663ecc027300b620f053694b43721f80e3a80080a1ed16151581a1abe1d627a6031b97197c05e3d438b31581393502830bcb3206872401608ec18dd3b523690713af93ec063b06ef247508bc60f0b39e6c03c932f87cd0493ab8b66300a52d4f097eaba445b79e6110a22eb97aa9277b0be06a2905000e4f051ec37f77f559db300c046efe5343f25710b07d0c81f553810180899541cc6a16a8f81e31a973ebeb0c2e29699b92be5c89b162b598891feba87ed1be6ee191b7a794fef06bdf595eafa3ea607c555d7779e49775a95e5bd3b19206e3ab0600570802c639256d9fd051972581e0a195ccfd0bccc4af49ceb8fa37b73e62f811029b0d5f50d29e65ddb33f9f2d187eabacbdfdfc27c1087c534a0183175dfd86c1d300c0e00157df53d2770060fbb95bdf766b8038b292eb7e1bf632b123f98ee4ee71a17539e72ee7fc8ba42708dcf8a06ff403c98e89b75607373a9b9bdc31f1ee447bc8f392460c7624cff7f2bb73933b499564fe34587a2ca9937466029c7a9b0213cf58490fc67b4ef7f313ff71ceb9736b20f080e45fab1eaf17a594fd0c0e4b5be63fd6ef85ed7904166cef713508c298620c02819fa735f2df8cf7e809f1988a2b677a0000000049454e44ae426082</data>
+ </image>
+ <image name="image3">
+ <data format="PNG" length="400">89504e470d0a1a0a0000000d4948445200000024000000160806000000a8b23e3e00000157494441544889edd6b14a1c411807f0df49f00a899ca056575da1410842848065ea60ef9b58f8009601df207d0211f20a36819026a085852914e1e2e55039f492dc976247104960c6bb0b08f7876966e7637fecce7eb34cf2c8521ba63898c53c66d2541f9d1aedff0a4a9097788d352ce0092ef0097bf83c0cac04d30cb682c3e05710f7c6efe028d80996c78d5908b683ef7f81dc1f57c16ed01a17a61e6c06df3230b7a39b9ee6ec3840ad602f18148022f812d53ecbca548169092f947f082dac06f5518356b15888a16a09cf64beb61250a370fddd7b344a16e7a68b41318748b55929011da053cca1874355d31c29e86b4295e61807356e460d3ac17b9c17d45ce3238e0a6af2938e8ddda097d17f7e06ef82e763c1dc412d076f82937f34c9417016bc0dd673fbcf6d1e7ada37f10a1b58c1d374e952b5813f601fa7b97b67285042d555bf1d73984ed37dfc40bb1432496efe00bc33c2225c6892300000000049454e44ae426082</data>
+ </image>
+ <image name="image4">
+ <data format="PNG" length="1360">89504e470d0a1a0a0000000d494844520000001e000000160806000000d7632ccf00000517494441544889c595df4f135914c7bf7377663ad3625b185a4263429cb2ac8b499ba5b8188d4e23613711b2215af561f7c997f5c1c87fb2cf3c9b6563021add0775d7043a86170544419b9520f127050af6c7303fee4c3bdd972d01a4467cd94f729f6e723fe7dc7bceb9c0ff04536f435114b6502834542a95e8a54b97a61f3d7a8477efde616d6d0df97c1ea6690200445184244908854288442248241218191939ee38ce0b5114b5999919e7b3c48aa2b09aa6f900444f9d3a3573fffe7dacadadc1b66d54ab5594cb65504ae1ba2e18860121043ccf8365591042c0711cc2e13092c9242627278f3b8ef322140a9554552dd715777676f23e9fefeb0b172e3cbb76edda0e618d6ab5bab500806198adb51d9ee7110a8570f1e2455cbf7efd3b5dd7ff79fdfab5f591585114d6308c6f8e1c39f2eceeddbba094826118088280a6a62644a3514422110c0f0feff93c972f5fae66b3592c2e2ee2c3870fb02c0baeeb82e338f4f6f6626161a19b523a9fc9646c0020db226c3a7dfaf4b37bf7ee41d334b02c8b68348aab57afa2adaded478ee30ed59302c0f0f030c3b2ec215996fb868686d0dede0e9ee7a1eb3ac6c7c7914c26a7fd7ebf544b960180aeae2ecee7f39d58595999c8e5726059168aa2c0308c41afd73b75e3c68de57ac2bd387bf66c0ba5b4471084dbaaaac2b66d84c361b4b6b6feb0b1b1a16632199b00403018f4cbb23c512a9550ad56118bc56018c6a0cfe74bef570a00376fde5c154571dc308cc1582c068661a0691a6459fe5b96653f00104551588661da676767a1eb3a00c034cddfbd5eefd3b1b1b1e27ea535c6c6c636bd5eef53cbb24600c0300ccccdcda152a9749c3f7f9e2700044ae9f7ebebeb5b05518f542ac57f4910aeebc2344dacafafc3719c2e003c711c87715d97d45ac4711cf03cff8b6118f1542a15d82e354df3dbcf95a552a906c330e23ccfff5c2e97b75a9010c2504a1996e338d375dd694992502c16615916e6e7e7914c266fe9ba3e78eedcb9298661d601042ccbea9565f9c9d2d252ddea4ea552bcebba8da669f688a278ebe1c387a094421004844221701c37ebf178285155b5cc71dc622c1683d7eb0500148b45a4d369e472b95bddddddef0dc3485a96251b8611dbdcdc44381caed6131b8671ece8d1a32babababb7d3e9348ac522188641434303e2f138388e5b181d1db55900a8542ac5376fdefc140c06ffdcdcdc84655928140a78fefc39debe7d8be6e6e6bfdadada400881a669b06d1b92245537363676675ebd73e70e1e3f7e8c62b1b8355a6b432897cbf5572a9522f0df005155d52284cc9d3973067ebf1f8490ad82c8e57278f9f2255455c5f4f4342ccb42a552413e9f8724491f65dedada8a5c2e07d334e1ba2e0821686c6cc4c0c0003c1ecf7c4f4f8f03ec1c9982e3388983070f4ea6d369944a25d8b6fdc92a2784a0a5a505d96cb6764e351008a0542a816118783c1e040201f4f5f5a150281c17046166747474e7c85455d5a294ce2e2f2f9fb872e50a3a3a3ad0dcdc0c51144108d953bc173ccfc3e7f3211c0ee3f0e1c3181a1a423e9f3f2608c2939a7447c635128904278a629010d2198fc7d30f1e3cc0caca0a4cd304a574ebb7aa5de1ae77ae46a3510483419c3c79120b0b0b0a2124a3695ae193dfe2ee009a9a9a1a1dc7e9ecefef9f989a9a422693c1e2e2221cc7413018dc2ddd175fd5dbc866b3eed2d2922e49d2fb57af5efd66dbf61fe57299d5753d218a625de9c0c080f7c08103c866b3f58b633f241289402291f8351289d4ede1fdf0f955031894d289e5e5e52fbedeedfc0bff3e8d2e6a3a39850000000049454e44ae426082</data>
+ </image>
+ <image name="image5">
+ <data format="PNG" length="364">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000013349444154388dedd4bf4a034110c7f18f2141fc53092a884d6a3b5b0b7b5f208d4fa0d88a0fa058586a69616161ed1b08828501452cc43f85858546100de21f142dee8473d9e42e0a56fe60e06698f9eeeccedef2af02aa62058768e20d77b8c2054e718d06368a4267f0828f36f68e7bd48b42a77280597b6805294762f3815fc7266e3182d1f4fb0c5b45bb2539cf6c57d54e8abf548ac42a813f8ba19fc043ed8a0f6a1f4b988c2c5e4813788cc0b3d6c436a6c5e7d45255ac4aee69decdd8c740a7dd97d21d2ce3b80d7cbd5370a82ae67012801bbf056717c8829fc384d875cb535932b4ac2ef28afa308673df07f72af97d2fd3eec2335ec803d724af58acb895eda03b047505fe41daf58de475ebc7307a83bcc174076b58c4531eb886f1483c9b7f8523ecc580ed54414f27057faa4f01ea7bd4f97814ab0000000049454e44ae426082</data>
+ </image>
+ <image name="image6">
+ <data format="PNG" length="130">89504e470d0a1a0a0000000d494844520000000e00000010080600000026944e3a0000004949444154289163601868f01f8a090226120c245923561710d208d3c4488a469c9ab0091215300c0c0c8cc4060e0660c165220301a7e2b311a681ac50c5a999183f92ec54829ae90b007d450a172e17e6880000000049454e44ae426082</data>
+ </image>
+ <image name="image7">
+ <data format="PNG" length="130">89504e470d0a1a0a0000000d494844520000000e00000010080600000026944e3a00000049494441542891636018ace03f14c301130e450401368d4469c6a6919118cdb86c24a89928d3b1a8c769234500390a888a0e0624458c38e4f1c6234e4df86cc4ab099746829a06060000c15a0d0ef2df0b870000000049454e44ae426082</data>
+ </image>
+ <image name="image8">
+ <data format="XPM.GZ" length="923">789c9d924d0a83301085f739c5e0eca454ad1184d22314ba2c942e261169176e5abb28a577afd1980cfe54705cbd2fc973f2325108e7d311c2483c6baaef1af48d1e1016afaa7a5fae878f087631a4122424c146045bd080715b46a29132913a4f8d5446e6695ee8cc483252670595657b76a21639122b858e2b4efd7e6c3e5faae7481c2329c7492d72232c6ffeea8d8c58c747f6db757c64ef38bb2e2acf790acd02fecf1f87fb7b4e7c01c9f3619b1d1fb7b98a4fd8afe213f696b35bcd713b4a5d9f1c778367df8b4fa7cb9353b211cee6ffdd8b1f2c0ac43d</data>
+ </image>
+ <image name="image9">
+ <data format="XPM.GZ" length="412">789cd3d7528808f055d0d2e72a2e492cc94c5648ce482c52d04a29cdcdad8c8eb5ade652325530365130513054d2e152d253485650360003105719c435313449b630067193405c0b638b946453103711c44d364d494c4b03eb05017a3194131393c08ca4c444652a1808320fcc009947175fd45a730100351049ee</data>
+ </image>
+ <image name="image10">
+ <data format="XPM.GZ" length="923">789cd3d7528808f055d0d2e72a2e492cc94c5648ce482c52d04a29cdcdad8c8eb5ade65232325030365130513054d2e152d253485650360003105719c435313449b630067113415c0b638b946453103709c44d364d494c4b03ebc50246c5e92daeac9c0802ca68e2497080ae1ea21c5dfd80b97f8488d75a7301005e68a747</data>
+ </image>
+ <image name="image11">
+ <data format="PNG" length="83">89504e470d0a1a0a0000000d494844520000000b0000000b0806000000a9ac77260000001a49444154189563602013fc87629c624ca49836aa986cc5240100b319040db65cea050000000049454e44ae426082</data>
+ </image>
+ <image name="image12">
+ <data format="PNG" length="113">89504e470d0a1a0a0000000d494844520000000b0000000b0806000000a9ac7726000000384944415418956360a0156084d2ff8951cb82c4f1c1a3700b32c71b8fe9ffa1f228009b06ac0ab169c0ab10ae818385832885c836d011000071460bb64f8d4c3f0000000049454e44ae426082</data>
+ </image>
+ <image name="image13">
+ <data format="PNG" length="248">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000000bf49444154388dedd3b16a42311840e18fe2e050e82c38f7110a6eb6601ddcfa767d81ce5dba74ea26143a17bab9585727471d222236f9d3e8e2e0814b86737312fecbe5c239708ba7235c353ac773a3731544ef3045afd1814e107dc74da3db91bbf1c9d15cf821d818b93fec8f628c577433eff5f15670551658179e55e05e72b1fd514cf05b38f43370d7b5f0178685c03270590e3fde0f06dbf590c855c330c33dbe1b5d354cfa5507d26c5b5c354c9aeb233e1addbfe9627484bb90d80042ac3405a2aa17210000000049454e44ae426082</data>
+ </image>
+ <image name="image14">
+ <data format="PNG" length="226">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000000a949444154388dedd3210a02411406e00f316c50bc8547106c2aa8c1e6dd148cdec06c320b66c166d164f0046b70c3b2e830bb2a88f8c3f0c29bf9e63130fcf3ed69635ae6c00c695643e8098b32709a5b8fd2c125eb3f84eb656ecba16bb4429b6a9f40cbc2835894f8a718638524768ad8899701b4f90a3cc1f949aff10abc432f805786e1806e56df0ac3117decdf0d73ffc65d6cabc0f3422de68a113615068b4a82e1a7f01fc90dab2a1b4817a92dca0000000049454e44ae426082</data>
+ </image>
+ <image name="image15">
+ <data format="PNG" length="240">89504e470d0a1a0a0000000d4948445200000024000000140806000000e57a9f35000000b7494441544889edd4b109c2401886e107c546b4b372033b47b0710d67710d4b577009b1b2b6138434e20236b1300195e44c882629f2c20fc71d1fbcf71d1c1d61fa81b309a264bd47fc7f9d30d34422c601b36675de8562dcb1c6a040f6351705f62a09a573c4bc4d4245daaa5d289d13966d124a6783719b84625cb0a82ad40b085d4b5e608851c94c698ab6b3f36c342bf7b3272b2274c3ea4bae36a1cf561a13ca6ba511a1adfc566a153acbfe006b27ebd3eb6894072e56ba3745befe0b0000000049454e44ae426082</data>
+ </image>
+ <image name="image16">
+ <data format="PNG" length="105">89504e470d0a1a0a0000000d4948445200000024000000140806000000e57a9f3500000030494441544889edceb10d000008024175ff9d75041209dd5f4ff82ac0d3c676131ff3088922482148214821482108483b655f0118dca7db050000000049454e44ae426082</data>
+ </image>
+ <image name="image17">
+ <data format="PNG" length="249">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000000c049444154388dedd33d6a42411440e18f68914210ecdc804b10d249305aa473776e203bb0b1b20b08a9035980a6b1b0b21253f843c03777de08761e1806e63087cbc0f0e0de3412e713ecb1297459a658a157e82e3c05ae8b4ff40b5d360c1dcc1381c865c3d00e02495727fc3ff05ae8ae98e250b17698056e9c0b7f242e9f0329f77b0ea49ea295385f6319b8f75c387571806de0be4ac33f7839edb55cb346f41b43c7df56dbe5265e9ea6a98a462e0c2f3052fda6910b79c3f30deec1357f84474343c1d303120000000049454e44ae426082</data>
+ </image>
+ <image name="image18">
+ <data format="PNG" length="243">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000000ba49444154388dedd3316a02411487f11f89458a848087f008829d48d4c2cebb2958e61069acac7381c01e402b0b0f209b62151665dcb7d94db71f0c036f1edffb33ccd0f1df3c27ea4b9c716c7be0067b0c1ef4ac905ff65ae25c917898e8c94beb8ea78a017d6c1fc8935489e1fd2ff288b82c9fb42dbecabf306f227e4bd45ff0d944fc9aa81fb088887b91a692748c2cd21cbde30ca3a89458e21f7c287e6298aac4df8aa4b5a455e21d663825ced7377b88a9e2697534e717602c1d33e8c59cfe0000000049454e44ae426082</data>
+ </image>
+ <image name="image19">
+ <data format="PNG" length="155">89504e470d0a1a0a0000000d494844520000000b0000000b0806000000a9ac772600000062494441541895a5d0cb0980400c45d1a3b8746593b6623dd6611f8268093a6e14469951c407817c6e42127e2a24e200e50b78510c9f6091838b1bd862c58605fd7d48886cc41cef19fbd5d171163a0ca8d13ced1f3065f2c9c35385ecebb29ff8ac1d37bc1e2a162452d80000000049454e44ae426082</data>
+ </image>
+ <image name="image20">
+ <data format="PNG" length="324">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000010b49444154388d9d95311284200c453f4c0acbad2d3d0e67dd9a2358ec21283dc316ccb0858b136302a24d66243e7ffe4771ebbaa2756d14ca37031301bc2e88aef51c8dc0784d3994898039eb2ff04fa0bc6e144a42284df028f4348180530fba203ae47f13ab5a7fcaa154ef0fc5a301cd393a4df9c98a843d88bbd03ab606df685ff3c098d22aa2050700f7febcca2854eb979e7bcba3112870f59ab431f8d5da8260fd72bdabd882ca2f4eaefb96626d7c99bea958a65ad36e4135f845b17613d883d1c6b7fe15b24fddc7550987574fad2f4edae9ad660e9741f5e007d84a5f0634b25b1c3f41ee0476b79efec75a604febe504a99e8e4064ff9ca373bdc3d4b2473beb360aa5deff0164ca87a1385f32640000000049454e44ae426082</data>
+ </image>
+</images>
+<tabstops>
+ <tabstop>PlayButton</tabstop>
+ <tabstop>StopButton</tabstop>
+ <tabstop>MetronomeButton</tabstop>
+ <tabstop>RecordButton</tabstop>
+ <tabstop>RewindEndButton</tabstop>
+ <tabstop>RewindButton</tabstop>
+ <tabstop>FfwdButton</tabstop>
+ <tabstop>FfwdEndButton</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/src/gui/ui/audiomanager.rc b/src/gui/ui/audiomanager.rc
new file mode 100644
index 0000000..65188d2
--- /dev/null
+++ b/src/gui/ui/audiomanager.rc
@@ -0,0 +1,67 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="Rosegarden::AudioManagerDialog" version="1230">
+
+<MenuBar>
+
+<Menu name="file">
+ <Action name="add_audio" append="new_merge"/>
+ <Action name="export_audio" append="new_merge"/>
+</Menu>
+
+<Menu name="edit">
+ <Action name="remove_audio"/>
+ <Action name="remove_all_audio"/>
+ <Action name="remove_all_unused_audio"/>
+ <Separator/>
+ <Action name="delete_unused_audio"/>
+</Menu>
+
+<Menu name="action">
+<Text>&amp;Action</Text>
+ <Action name="preview_audio"/>
+ <Action name="insert_audio"/>
+ <Action name="distribute_audio"/>
+</Menu>
+</MenuBar>
+
+<ToolBar name="General Toolbar">
+ <text>General Toolbar</text>
+
+ <Action name="add_audio" />
+ <Action name="export_audio" />
+ <Action name="remove_audio" />
+ <Action name="preview_audio" />
+ <Action name="insert_audio" />
+</ToolBar>
+
+<State name="have_audio_files">
+ <enable>
+ <Action name="remove_all_audio"/>
+ <Action name="remove_all_unused_audio"/>
+ <Action name="delete_unused_audio"/>
+ </enable>
+</State>
+
+<State name="have_audio_selected">
+ <enable>
+ <Action name="export_audio"/>
+ <Action name="remove_audio"/>
+ <Action name="rename_audio"/>
+ <Action name="distribute_audio"/>
+ </enable>
+</State>
+
+<State name="have_audible_preview">
+ <enable>
+ <Action name="preview_audio"/>
+ </enable>
+</State>
+
+<State name="have_audio_insertable">
+ <enable>
+ <Action name="insert_audio"/>
+ <enable>
+</State>
+
+</kpartgui>
diff --git a/src/gui/ui/bankeditor.rc b/src/gui/ui/bankeditor.rc
new file mode 100644
index 0000000..9d85e52
--- /dev/null
+++ b/src/gui/ui/bankeditor.rc
@@ -0,0 +1,22 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="BankEditor" version="1230">
+
+<MenuBar>
+ <Menu name="file">
+ <Action name="file_close_discard"/>
+ </Menu>
+</MenuBar>
+
+<ToolBar name="mainToolBar">
+ <Action name="file_close"/>
+</ToolBar>
+
+<State name="on_bank_item">
+ <enable>
+ <Action name="edit_copy"/>
+ <Action name="edit_paste"/>
+ </enable>
+</State>
+
+</kpartgui>
diff --git a/src/gui/ui/clefinserter.rc b/src/gui/ui/clefinserter.rc
new file mode 100644
index 0000000..ade8806
--- /dev/null
+++ b/src/gui/ui/clefinserter.rc
@@ -0,0 +1,11 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="Rosegarden::NotationTool" version="1230">
+
+<Menu noMerge="1" name="ClefInserter">
+ <Action name="select" />
+ <Action name="notes" />
+ <Action name="erase" />
+</Menu>
+
+</kpartgui>
diff --git a/src/gui/ui/controleditor.rc b/src/gui/ui/controleditor.rc
new file mode 100644
index 0000000..37b8aa6
--- /dev/null
+++ b/src/gui/ui/controleditor.rc
@@ -0,0 +1,5 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="ControlEditor" version="1230">
+
+</kpartgui>
diff --git a/src/gui/ui/devicemanager.rc b/src/gui/ui/devicemanager.rc
new file mode 100644
index 0000000..27fc121
--- /dev/null
+++ b/src/gui/ui/devicemanager.rc
@@ -0,0 +1,5 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="DeviceManager" version="1230">
+
+</kpartgui>
diff --git a/src/gui/ui/eventlist.rc b/src/gui/ui/eventlist.rc
new file mode 100644
index 0000000..96f8af0
--- /dev/null
+++ b/src/gui/ui/eventlist.rc
@@ -0,0 +1,105 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="Rosegarden::EventList" version="1230">
+
+<MenuBar>
+
+<Menu name="file" append="new_merge">
+</Menu>
+
+<Menu name="edit" append="new_merge">
+ <Action name="insert"/>
+ <Action name="delete"/>
+ <Action name="edit_simple"/>
+ <Action name="edit_advanced"/>
+ <Separator/>
+ <Action name="select_all"/>
+ <Action name="clear_selection"/>
+ <Action name="filter_selection"/>
+ <Separator/>
+ <Action name="set_segment_start"/>
+ <Action name="set_segment_duration"/>
+</Menu>
+
+<Menu name="view"><text>&amp;View</text>
+ <Action name="time_musical"/>
+ <Action name="time_real"/>
+ <Action name="time_raw"/>
+</Menu>
+
+<Menu name="segment">
+ <text>Se&amp;gment</text>
+ <Menu name="open-with">
+ <text>Edit &amp;With</text>
+ <Action name="open_in_matrix"/>
+ <Action name="open_in_notation"/>
+ </Menu>
+</Menu>
+</MenuBar>
+
+<ToolBar name="Actions Toolbar">
+ <text>Actions Toolbar</text>
+ <Action name="insert"/>
+ <Action name="delete"/>
+ <Action name="edit_simple"/>
+ <Action name="edit_advanced"/>
+ <Action name="filter_selection"/>
+</ToolBar>
+
+<ToolBar name="Time Toolbar">
+ <text>Time Toolbar</text>
+ <Action name="time_musical"/>
+ <Action name="time_real"/>
+ <Action name="time_raw"/>
+</ToolBar>
+
+</kpartgui>
+
+<ToolBar name="Transport Toolbar">
+ <text>Transport Toolbar</text>
+
+ <Action name="playback_pointer_start" />
+ <Action name="playback_pointer_back_bar" />
+ <Action name="play" />
+ <Action name="playback_pointer_forward_bar" />
+ <Action name="playback_pointer_end" />
+ <Action name="stop" />
+ <Action name="playback_pointer_to_cursor" />
+ <Action name="cursor_to_playback_pointer" />
+ <Action name="toggle_solo"/>
+</ToolBar>
+
+<ToolBar name="Actions Toolbar">
+ <text>Actions Toolbar</text>
+</ToolBar>
+
+<ToolBar name="Zoom Toolbar">
+ <text>Zoom Toolbar</text>
+</ToolBar>
+
+<State name="have_selection">
+ <enable>
+ <Action name="edit_cut"/>
+ <Action name="edit_copy"/>
+ <Action name="delete"/>
+ <Action name="edit_simple"/>
+ <Action name="edit_advanced"/>
+ <Action name="clear_selection"/>
+ <Action name="filter_selection"/>
+ </enable>
+</State>
+
+<State name="have_clipboard_single_segment">
+ <enable>
+ <Action name="edit_paste"/>
+ </enable>
+</State>
+
+<State name="have_control_ruler">
+ <enable>
+ <Action name="show_control_rulers"/>
+ </enable>
+</State>
+
+</kpartgui>
+
diff --git a/src/gui/ui/markereditor.rc b/src/gui/ui/markereditor.rc
new file mode 100644
index 0000000..fd5d157
--- /dev/null
+++ b/src/gui/ui/markereditor.rc
@@ -0,0 +1,37 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="MarkerEditor" version="1230">
+
+<MenuBar>
+
+ <Menu name="file">
+ <Action name="file_close_discard"/>
+ </Menu>
+
+ <Menu name="view"><text>&amp;View</text>
+ <Action name="time_musical"/>
+ <Action name="time_real"/>
+ <Action name="time_raw"/>
+ </Menu>
+
+</MenuBar>
+
+<ToolBar name="mainToolBar">
+ <Action name="file_close"/>
+</ToolBar>
+
+<ToolBar name="Time Toolbar">
+ <text>Time Toolbar</text>
+ <Action name="time_musical"/>
+ <Action name="time_real"/>
+ <Action name="time_raw"/>
+</ToolBar>
+
+<State name="on_control_item">
+ <enable>
+ <Action name="edit_insert"/>
+ <Action name="edit_delete"/>
+ </enable>
+</State>
+
+</kpartgui>
diff --git a/src/gui/ui/markerruler.rc b/src/gui/ui/markerruler.rc
new file mode 100644
index 0000000..6a2b9a5
--- /dev/null
+++ b/src/gui/ui/markerruler.rc
@@ -0,0 +1,14 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="Rosegarden::MarkerRuler" version="1230">
+
+<Menu name="marker_ruler_menu">
+ <Action name="insert_marker_here" />
+ <Action name="insert_marker_at_pointer" />
+ <Separator/>
+ <Action name="delete_marker" />
+ <Separator/>
+ <Action name="edit_marker" />
+</Menu>
+
+</kpartgui>
diff --git a/src/gui/ui/matrix.rc b/src/gui/ui/matrix.rc
new file mode 100644
index 0000000..24b6159
--- /dev/null
+++ b/src/gui/ui/matrix.rc
@@ -0,0 +1,301 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="Rosegarden::Matrix" version="1500">
+
+<MenuBar>
+
+<Menu name="file" append="new_merge">
+</Menu>
+
+<Menu name="edit">
+ <Action name="delete"/>
+ <Action name="select_all"/>
+ <Action name="clear_selection"/>
+ <Action name="filter_selection"/>
+</Menu>
+
+
+<Menu name="View">
+ <text>&amp;View</text>
+ <Menu name="Grid">
+ <text>&amp;Grid</text>
+ <Action name="snap_none"/>
+ <Action name="snap_unit"/>
+ <Action name="snap_64"/>
+ <Action name="snap_48"/>
+ <Action name="snap_32"/>
+ <Action name="snap_24"/>
+ <Action name="snap_16"/>
+ <Action name="snap_12"/>
+ <Action name="snap_8"/>
+ <Action name="snap_6"/>
+ <Action name="snap_4"/>
+ <Action name="snap_2"/>
+ <Action name="snap_beat"/>
+ <Action name="snap_bar"/>
+ </Menu>
+ <Separator/>
+ <Action name="show_velocity_control_ruler"/>
+ <Action name="show_controller_events_ruler"/>
+<!-- <Action name="add_control_ruler"/> -->
+ <Menu name="add_control_ruler"><text>Add Event Ruler</text></Menu>
+</Menu>
+
+<Menu name="document">
+ <text>&amp;Composition</text>
+ <Action name="add_tempo"/>
+ <Action name="add_time_signature"/>
+</Menu>
+<Menu name="segments">
+ <text>&amp;Segment</text>
+ <Menu name="open-with">
+ <text>Edit &amp;With</text>
+ <Action name="open_in_event_list"/>
+ <Action name="open_in_notation"/>
+ </Menu>
+ <Separator/>
+ <Action name="set_segment_start"/>
+ <Action name="set_segment_duration"/>
+</Menu>
+<Menu name="Transforms">
+ <text>Ad&amp;just</text>
+ <Menu name="quantize_notes">
+ <text>&amp;Quantize</text>
+ <Action name="quantize"/>
+ <Action name="repeat_quantize"/>
+ <Action name="legatoize"/>
+ </Menu>
+ <Action name="collapse_notes"/>
+ <Separator/>
+ <Action name="jog_left"/>
+ <Action name="jog_right"/>
+ <Separator/>
+ <Action name="velocity_up"/>
+ <Action name="velocity_down"/>
+ <Action name="set_to_current_velocity"/>
+ <Action name="set_velocities"/>
+ <Separator/>
+ <Menu name="Rescale">
+ <text>Rescale</text>
+ <Action name="halve_durations"/>
+ <Action name="double_durations"/>
+ <Separator/>
+ <Action name="rescale"/>
+ </Menu>
+ <Menu name="Transpose">
+ <text>Trans&amp;pose</text>
+ <Action name="transpose_up"/>
+ <Action name="transpose_down"/>
+ <Separator/>
+ <Action name="transpose_up_octave"/>
+ <Action name="transpose_down_octave"/>
+ <Separator/>
+ <Action name="general_transpose"/>
+ <Action name="general_diatonic_transpose"/>
+ </Menu>
+ <Menu name="Convert">
+ <text>Convert</text>
+ <Action name="invert"/>
+ <Action name="retrograde"/>
+ <Action name="retrograde_invert"/>
+ </Menu>
+ <Separator/>
+ <Action name="trigger_segment"/>
+ <Action name="remove_trigger"/>
+</Menu>
+
+
+<Menu name="Tools">
+ <text>&amp;Tools</text>
+ <Action name="select" />
+ <Action name="draw" />
+ <Action name="erase" />
+ <Action name="move" />
+ <Action name="resize" />
+ <Separator/>
+ <Menu name="Move">
+ <text>Local &amp;Cursor</text>
+ <Action name="cursor_back"/>
+ <Action name="cursor_forward"/>
+ <Action name="cursor_back_bar"/>
+ <Action name="cursor_forward_bar"/>
+ <Action name="cursor_start"/>
+ <Action name="cursor_end"/>
+ <Separator/>
+ <Action name="extend_selection_backward"/>
+ <Action name="extend_selection_forward"/>
+ <Action name="preview_selection"/>
+ <Action name="clear_loop"/>
+ <Separator/>
+ <Action name="cursor_to_playback_pointer"/>
+ <Action name="playback_pointer_to_cursor"/>
+ </Menu>
+
+ <Menu name="Transport">
+ <text>T&amp;ransport</text>
+ <Action name="play"/>
+ <Action name="stop"/>
+ <Action name="playback_pointer_back_bar"/>
+ <Action name="playback_pointer_forward_bar"/>
+ <Action name="playback_pointer_start"/>
+ <Action name="playback_pointer_end"/>
+ <Action name="playback_pointer_to_cursor" />
+ <Action name="cursor_to_playback_pointer" />
+ <Action name="toggle_solo"/>
+ <Action name="toggle_tracking"/>
+ <Action name="panic"/>
+ </Menu>
+
+ <Separator/>
+ <Action name="insert_note_actionmenu"/>
+ <Action name="chord_mode"/>
+
+ <Separator/>
+ <Action name="toggle_step_by_step"/>
+</Menu>
+
+<Menu name="settings" append="show_merge"><text>&amp;Settings</text>
+ <Menu name="toolbars" append="show_merge"><text>&amp;Toolbars</text>
+ <Action name="options_show_toolbar" append="show_merge" />
+ <Action name="show_tools_toolbar" append="show_merge" />
+ <Action name="options_show_statusbar" append="show_merge" />
+ </Menu>
+ <Menu name="rulers" append="show_merge"><text>&amp;Rulers</text>
+ <Action name="show_chords_ruler" append="show_merge" />
+ <Action name="show_tempo_ruler" append="show_merge" />
+ </Menu>
+ <Separator append="show_merge" />
+ <Action name="show_inst_parameters" append="show_merge" />
+</Menu>
+
+
+</MenuBar>
+
+<ToolBar name="Tools Toolbar">
+ <text>Tools Toolbar</text>
+
+ <Action name="select" />
+ <Action name="draw" />
+ <Action name="erase" />
+ <Action name="move" />
+ <Action name="resize" />
+ <Separator/>
+ <Action name="toggle_step_by_step"/>
+ <Action name="filter_selection"/>
+ <Action name="quantize"/>
+</ToolBar>
+
+<ToolBar name="Transport Toolbar">
+ <text>Transport Toolbar</text>
+
+ <Action name="playback_pointer_start" />
+ <Action name="playback_pointer_back_bar" />
+ <Action name="play" />
+ <Action name="playback_pointer_forward_bar" />
+ <Action name="playback_pointer_end" />
+ <Action name="stop" />
+ <Action name="playback_pointer_to_cursor" />
+ <Action name="cursor_to_playback_pointer" />
+ <Action name="toggle_solo"/>
+ <Action name="toggle_tracking"/>
+ <Action name="panic"/>
+</ToolBar>
+
+<ToolBar name="Actions Toolbar">
+ <text>Actions Toolbar</text>
+</ToolBar>
+
+<ToolBar name="Zoom Toolbar">
+ <text>Zoom Toolbar</text>
+</ToolBar>
+
+<Menu noMerge="1" name="controller_events_ruler_menu">
+ <Text>Control Ruler actions</Text>
+ <Action name="insert_control_ruler_item"/>
+ <Action name="erase_control_ruler_item"/>
+ <Action name="clear_control_ruler_item"/>
+ <Separator/>
+ <Action name="start_control_line_item"/>
+ <Separator/>
+ <Action name="flip_control_events_forward"/>
+ <Action name="flip_control_events_back"/>
+</Menu>
+
+<Menu noMerge="1" name="property_ruler_menu">
+ <Text>Property Ruler actions</Text>
+ <Action name="draw_property_line"/>
+ <Action name="select_all_properties"/>
+ <Separator/>
+ <Action name="flip_control_events_forward"/>
+ <Action name="flip_control_events_back"/>
+</Menu>
+
+
+<State name="have_selection">
+ <enable>
+ <Action name="edit_cut"/>
+ <Action name="edit_copy"/>
+ <Action name="quantize"/>
+ <Action name="collapse_notes"/>
+ <Action name="legatoize"/>
+ <Action name="rescale"/>
+ <Action name="double_durations"/>
+ <Action name="halve_durations"/>
+ <Action name="velocity_up"/>
+ <Action name="velocity_down"/>
+ <Action name="jog_left"/>
+ <Action name="jog_right"/>
+ <Action name="set_to_current_velocity"/>
+ <Action name="set_velocities"/>
+ <Action name="trigger_segment"/>
+ <Action name="remove_trigger"/>
+ <Action name="transpose_up"/>
+ <Action name="transpose_down"/>
+ <Action name="transpose_up_octave"/>
+ <Action name="transpose_down_octave"/>
+ <Action name="general_transpose"/>
+ <Action name="general_diatonic_transpose"/>
+ <Action name="invert"/>
+ <Action name="retrograde"/>
+ <Action name="retrograde_invert"/>
+ <Action name="preview_selection"/>
+ <Action name="clear_selection"/>
+ <Action name="filter_selection"/>
+ </enable>
+</State>
+
+<State name="have_clipboard_single_segment">
+ <enable>
+ <Action name="edit_paste"/>
+ </enable>
+</State>
+
+<State name="have_note_events_in_segment">
+ <enable>
+ <Action name="draw_property_line"/>
+ </enable>
+</State>
+
+<State name="have_control_ruler">
+ <enable>
+ <Action name="show_control_rulers"/>
+ </enable>
+</State>
+
+<State name="have_controller_item_selected">
+ <enable>
+ <Action name="erase_control_ruler_item"/>
+ </enable>
+ <disable>
+ <Action name="insert_control_ruler_item"/>
+ </disable>
+</State>
+
+<State name="parametersbox_closed">
+ <enable>
+ <Action name="show_inst_parameters" />
+ </enable>
+</State>
+
+</kpartgui>
+
diff --git a/src/gui/ui/matrixeraser.rc b/src/gui/ui/matrixeraser.rc
new file mode 100644
index 0000000..20694d4
--- /dev/null
+++ b/src/gui/ui/matrixeraser.rc
@@ -0,0 +1,15 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="Rosegarden::MatrixTool" version="1230">
+
+<Menu name="MatrixEraser">
+ <Action name="select" />
+ <Action name="move"/>
+ <Action name="draw"/>
+ <Action name="resize"/>
+<!-- <Separator/> -->
+<!-- Defunct: <Action name="halve_durations"/> -->
+<!-- Defunct: <Action name="double_durations"/> -->
+</Menu>
+
+</kpartgui>
diff --git a/src/gui/ui/matrixmover.rc b/src/gui/ui/matrixmover.rc
new file mode 100644
index 0000000..2d0d555
--- /dev/null
+++ b/src/gui/ui/matrixmover.rc
@@ -0,0 +1,15 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="Rosegarden::MatrixTool" version="1230">
+
+<Menu name="MatrixMover">
+ <Action name="select"/>
+ <Action name="draw" />
+ <Action name="erase"/>
+ <Action name="resize"/>
+<!-- <Separator/> -->
+<!-- Defunct: <Action name="halve_durations"/> -->
+<!-- Defunct: <Action name="double_durations"/> -->
+</Menu>
+
+</kpartgui>
diff --git a/src/gui/ui/matrixpainter.rc b/src/gui/ui/matrixpainter.rc
new file mode 100644
index 0000000..9b4cbce
--- /dev/null
+++ b/src/gui/ui/matrixpainter.rc
@@ -0,0 +1,22 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="Rosegarden::MatrixTool" version="1230">
+
+<Menu name="MatrixPainter">
+ <Action name="select" />
+ <Action name="move"/>
+ <Action name="erase"/>
+ <Action name="resize"/>
+<!-- <Separator/> -->
+<!-- Defunct: <Action name="halve_durations"/> -->
+<!-- Defunct: <Action name="double_durations"/> -->
+</Menu>
+
+<!-- Defunct: <State name="have_selection"> -->
+<!-- <enable> -->
+<!-- <Action name="halve_durations"/> -->
+<!-- <Action name="double_durations"/> -->
+<!-- </enable> -->
+<!--</State> -->
+
+</kpartgui>
diff --git a/src/gui/ui/matrixresizer.rc b/src/gui/ui/matrixresizer.rc
new file mode 100644
index 0000000..8d85634
--- /dev/null
+++ b/src/gui/ui/matrixresizer.rc
@@ -0,0 +1,15 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="Rosegarden::MatrixTool" version="1230">
+
+<Menu name="MatrixResizer">
+ <Action name="select" />
+ <Action name="move"/>
+ <Action name="draw"/>
+ <Action name="erase"/>
+<!-- <Separator/> -->
+<!-- Defunct: <Action name="halve_durations"/> -->
+<!-- Defunct: <Action name="double_durations"/> -->
+</Menu>
+
+</kpartgui>
diff --git a/src/gui/ui/matrixselector.rc b/src/gui/ui/matrixselector.rc
new file mode 100644
index 0000000..cb8ec41
--- /dev/null
+++ b/src/gui/ui/matrixselector.rc
@@ -0,0 +1,15 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="Rosegarden::MatrixTool" version="1230">
+
+<Menu name="MatrixSelector">
+ <Action name="draw" />
+ <Action name="move"/>
+ <Action name="erase"/>
+ <Action name="resize"/>
+<!-- <Separator/> -->
+<!-- Defunct: <Action name="halve_durations"/> -->
+<!-- Defunct: <Action name="double_durations"/> -->
+</Menu>
+
+</kpartgui>
diff --git a/src/gui/ui/midimixer.rc b/src/gui/ui/midimixer.rc
new file mode 100644
index 0000000..6ac7bb4
--- /dev/null
+++ b/src/gui/ui/midimixer.rc
@@ -0,0 +1,34 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="Rosegarden::MidiMixer" version="1230">
+
+<MenuBar>
+
+ <Menu name="Transport">
+ <text>T&amp;ransport</text>
+ <Action name="play" />
+ <Action name="stop" />
+ <Action name="playback_pointer_back_bar" />
+ <Action name="playback_pointer_forward_bar" />
+ <Action name="playback_pointer_start" />
+ <Action name="playback_pointer_end" />
+ <Action name="record" />
+ <Action name="panic" />
+ </Menu>
+
+</MenuBar>
+
+<ToolBar name="Transport Toolbar">
+ <text>Transport Toolbar</text>
+ <Action name="playback_pointer_start" />
+ <Action name="playback_pointer_back_bar" />
+ <Action name="play" />
+ <Action name="playback_pointer_forward_bar" />
+ <Action name="playback_pointer_end" />
+ <Action name="stop" />
+ <Action name="record" />
+ <Action name="panic" />
+</ToolBar>
+
+</kpartgui>
+
diff --git a/src/gui/ui/mixer.rc b/src/gui/ui/mixer.rc
new file mode 100644
index 0000000..6dcef9f
--- /dev/null
+++ b/src/gui/ui/mixer.rc
@@ -0,0 +1,65 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="Rosegarden::Mixer" version="1230">
+
+<MenuBar>
+
+ <Menu name="Transport">
+ <text>T&amp;ransport</text>
+ <Action name="play" />
+ <Action name="stop" />
+ <Action name="playback_pointer_back_bar" />
+ <Action name="playback_pointer_forward_bar" />
+ <Action name="playback_pointer_start" />
+ <Action name="playback_pointer_end" />
+ <Action name="record" />
+ <Action name="panic" />
+ </Menu>
+
+<Menu name="settings"><text>&amp;Settings</text>
+
+ <Action name="show_midi" append="show_merge" />
+ <Action name="show_audio_faders" append="show_merge" />
+ <Action name="show_synth_faders" append="show_merge" />
+ <Action name="show_audio_submasters" append="show_merge" />
+ <Action name="show_plugin_buttons" append="show_merge" />
+
+ <Separator append="show_merge"/>
+
+ <Action name="show_unassigned_faders" append="show_merge" />
+
+ <Menu name="Inputs">
+ <text>Number of Stereo &amp;Inputs</text>
+ <Action name="inputs_1"/>
+ <Action name="inputs_2"/>
+ <Action name="inputs_4"/>
+ <Action name="inputs_8"/>
+ <Action name="inputs_16"/>
+ </Menu>
+
+ <Menu name="Submasters">
+ <text>&amp;Number of Submasters</text>
+ <Action name="submasters_0"/>
+ <Action name="submasters_2"/>
+ <Action name="submasters_4"/>
+ <Action name="submasters_8"/>
+ </Menu>
+
+</Menu>
+
+</MenuBar>
+
+<ToolBar name="Transport Toolbar">
+ <text>Transport Toolbar</text>
+ <Action name="playback_pointer_start" />
+ <Action name="playback_pointer_back_bar" />
+ <Action name="play" />
+ <Action name="playback_pointer_forward_bar" />
+ <Action name="playback_pointer_end" />
+ <Action name="stop" />
+ <Action name="record" />
+ <Action name="panic" />
+</ToolBar>
+
+</kpartgui>
+
diff --git a/src/gui/ui/notation.rc b/src/gui/ui/notation.rc
new file mode 100644
index 0000000..be6f23b
--- /dev/null
+++ b/src/gui/ui/notation.rc
@@ -0,0 +1,853 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="Rosegarden::Notation" version="1500">
+
+<MenuBar>
+
+<Menu name="file" append="new_merge">
+<Action name="file_print_lilypond" append="print_merge" />
+<Action name="file_preview_lilypond" append="print_merge" />
+</Menu>
+
+<Menu name="edit">
+ <Action name="cut_and_close"/>
+ <Action name="general_paste"/>
+ <Action name="delete"/>
+ <Separator/>
+ <Action name="move_events_up_staff"/>
+ <Action name="move_events_down_staff"/>
+ <Separator/>
+ <Action name="select_from_start"/>
+ <Action name="select_to_end"/>
+ <Action name="select_whole_staff"/>
+ <Action name="clear_selection"/>
+ <Action name="filter_selection"/>
+</Menu>
+
+<Menu name="View">
+ <text>&amp;View</text>
+<!--
+<Action name="debug_dump"/>
+<Separator/>
+-->
+ <Action name="note_font_actionmenu"/>
+ <Action name="note_font_size_actionmenu"/>
+ <Action name="stretch_actionmenu"/>
+ <Action name="proportion_actionmenu"/>
+<Separator/>
+ <Menu name="layout"><text>&amp;Layout Mode</text>
+ <Action name="linear_mode"/>
+ <Action name="continuous_page_mode"/>
+ <Action name="multi_page_mode"/>
+ </Menu>
+<Separator/>
+ <Action name="lyric_editor"/>
+ <Separator/>
+ <Action name="show_velocity_control_ruler"/>
+ <Action name="show_controller_events_ruler"/>
+<!-- <Action name="add_control_ruler"/> -->
+ <Menu name="add_control_ruler"><text>Add Event Ruler</text></Menu>
+ <Separator/>
+ <Action name="show_track_headers"/>
+</Menu>
+
+<Menu name="document">
+ <text>&amp;Composition</text>
+ <Action name="add_tempo"/>
+ <Action name="add_time_signature"/>
+</Menu>
+
+<Menu name="segment">
+ <text>Se&amp;gment</text>
+ <Menu name="open-with">
+ <text>Edit &amp;With</text>
+ <Action name="open_in_event_list"/>
+ <Action name="open_in_matrix"/>
+ <Action name="open_in_percussion_matrix"/>
+ </Menu>
+ <Separator/>
+ <Action name="add_clef"/>
+ <Action name="add_key_signature"/>
+ <Separator/>
+ <Action name="add_sustain_down"/>
+ <Action name="add_sustain_up"/>
+ <Separator/>
+ <Action name="set_segment_start"/>
+ <Action name="set_segment_duration"/>
+ <Separator/>
+ <Action name="transpose_segment"/>
+ <Action name="switch_preset"/>
+</Menu>
+
+
+<Menu name="Notes">
+ <text>N&amp;ote</text>
+ <Separator/>
+ <Menu name="Marks">
+ <text>Mar&amp;ks</text>
+ <Action name="add_accent"/>
+ <Action name="add_tenuto"/>
+ <Action name="add_staccato"/>
+ <Action name="add_staccatissimo"/>
+ <Action name="add_marcato"/>
+ <Action name="add_text_sf"/>
+ <Action name="add_text_rf"/>
+ <Separator/>
+ <Action name="add_trill"/>
+ <Action name="add_long-trill"/>
+ <Action name="add_trill-line"/>
+ <Action name="add_turn"/>
+ <Action name="add_mordent"/>
+ <Action name="add_mordent-inverted"/>
+ <Action name="add_mordent-long"/>
+ <Action name="add_mordent-long-inverted"/>
+ <Separator/>
+ <Action name="add_up-bow"/>
+ <Action name="add_down-bow"/>
+ <Action name="add_pause"/>
+ <Separator/>
+ <Action name="add_text_mark"/>
+ <Separator/>
+ <Action name="remove_marks"/>
+ </Menu>
+ <Menu name="ornaments">
+ <text>&amp;Ornaments</text>
+ <Action name="use_ornament"/>
+ <Action name="remove_ornament"/>
+ <Action name="make_ornament"/>
+ <Action name="ornament_actionmenu"/>
+ </Menu>
+ <Separator/>
+ <Menu name="Fingering">
+ <text>&amp;Fingerings</text>
+ <Action name="remove_fingering_marks"/>
+ <Separator/>
+ <Action name="add_fingering_0"/>
+ <Action name="add_fingering_1"/>
+ <Action name="add_fingering_2"/>
+ <Action name="add_fingering_3"/>
+ <Action name="add_fingering_4"/>
+ <Action name="add_fingering_5"/>
+ <Action name="add_fingering_plus"/>
+ <Action name="add_fingering_mark"/>
+ </Menu>
+ <Menu name="Slashes">
+ <text>S&amp;lashes</text>
+ <Action name="slashes_0"/>
+ <Action name="slashes_1"/>
+ <Action name="slashes_2"/>
+ <Action name="slashes_3"/>
+ <Action name="slashes_4"/>
+ <Action name="slashes_5"/>
+ </Menu>
+ <Action name="note_style_actionmenu"/>
+ <Separator/>
+ <Menu name="Respell">
+ <text>&amp;Accidentals</text>
+ <Action name="respell_restore"/>
+ <Separator/>
+ <Action name="respell_doubleflat"/>
+ <Action name="respell_flat"/>
+ <Action name="respell_natural"/>
+ <Action name="respell_sharp"/>
+ <Action name="respell_doublesharp"/>
+ <Separator/>
+ <Action name="show_cautionary"/>
+ <Action name="cancel_cautionary"/>
+ </Menu>
+ <Separator/>
+ <Action name="stems_up"/>
+ <Action name="stems_down"/>
+ <Action name="restore_stems"/>
+</Menu>
+
+<Menu name="Phrase">
+ <text>&amp;Phrase</text>
+ <Action name="make_chord"/>
+ <Separator/>
+ <Action name="beam"/>
+ <Action name="auto_beam"/>
+ <Action name="break_group"/>
+ <Action name="remove_indications"/>
+ <Separator/>
+ <Action name="tuplet"/>
+ <Action name="simple_tuplet"/>
+ <Action name="break_tuplets"/>
+ <Separator/>
+ <Action name="grace"/>
+ <Separator/>
+ <Action name="slur"/>
+ <Action name="phrasing_slur"/>
+ <!-- <Action name="glissando"/> -->
+ <Menu name="Slurs">
+ <text>Slur &amp;Position</text>
+ <Action name="restore_slurs"/>
+ <Separator/>
+ <Action name="slurs_above"/>
+ <Action name="slurs_below"/>
+ </Menu>
+ <Separator/>
+ <Action name="tie_notes"/>
+ <Action name="untie_notes"/>
+ <Menu name="Ties">
+ <text>Tie &amp;Position</text>
+ <Action name="restore_ties"/>
+ <Separator/>
+ <Action name="ties_above"/>
+ <Action name="ties_below"/>
+ </Menu>
+ <Separator/>
+ <Action name="crescendo"/>
+ <Action name="decrescendo"/>
+ <Separator/>
+ <Menu name="octaves">
+ <text>&amp;Octaves</text>
+ <Action name="octave_2up"/>
+ <Action name="octave_up"/>
+ <Action name="octave_down"/>
+ <Action name="octave_2down"/>
+ </Menu>
+</Menu>
+
+<Menu name="Adjust">
+ <text>Ad&amp;just</text>
+ <Menu name="rests">
+ <text>R&amp;ests</text>
+ <Action name="normalize_rests"/>
+ <Action name="collapse_rests_aggressively"/>
+ </Menu>
+ <Menu name="transform_notes">
+ <text>&amp;Notes</text>
+ <Action name="collapse_notes"/>
+ <Action name="make_notes_viable"/>
+ <Action name="de_counterpoint"/>
+ </Menu>
+ <Separator/>
+ <Menu name="quantize_notes">
+ <text>&amp;Quantize</text>
+ <Action name="quantize"/>
+ <Action name="fix_quantization"/>
+ <Action name="remove_quantization"/>
+ </Menu>
+ <Action name="interpret"/>
+ <Separator/>
+ <Menu name="Rescale">
+ <text>Rescale</text>
+ <Action name="halve_durations"/>
+ <Action name="double_durations"/>
+ <Separator/>
+ <Action name="rescale"/>
+ </Menu>
+ <Menu name="Transpose">
+ <text>Trans&amp;pose</text>
+ <Action name="transpose_up"/>
+ <Action name="transpose_down"/>
+ <Separator/>
+ <Action name="transpose_up_octave"/>
+ <Action name="transpose_down_octave"/>
+ <Separator/>
+ <Action name="general_transpose"/>
+ <Action name="general_diatonic_transpose"/>
+ </Menu>
+ <Menu name="Convert">
+ <text>Convert</text>
+ <Action name="invert"/>
+ <Action name="retrograde"/>
+ <Action name="retrograde_invert"/>
+ </Menu>
+ <Separator/>
+ <Menu name="fine_positioning">
+ <text>&amp;Fine Positioning</text>
+ <Action name="fine_position_restore"/>
+ <Separator/>
+ <Action name="fine_position_left"/>
+ <Action name="fine_position_right"/>
+ <Action name="fine_position_up"/>
+ <Action name="fine_position_down"/>
+ </Menu>
+ <Menu name="fine_timing">
+ <text>Fine Ti&amp;ming</text>
+ <Action name="jog_left"/>
+ <Action name="jog_right"/>
+ </Menu>
+ <Menu name="visibility">
+ <text>&amp;Visibility</text>
+ <Action name="make_invisible"/>
+ <Action name="make_visible"/>
+ </Menu>
+</Menu>
+
+<Menu name="Tools">
+ <text>&amp;Tools</text>
+ <Action name="select"/>
+ <Action name="erase"/>
+ <Menu noMerge="1" name="Notes">
+ <text>&amp;Notes</text>
+ <!-- Reference names, but with _ instead of - -->
+ <Action name="breve" />
+ <Action name="semibreve" />
+ <Action name="minim" />
+ <Action name="crotchet" />
+ <Action name="quaver" />
+ <Action name="semiquaver" />
+ <Action name="demisemi" />
+ <Action name="hemidemisemi" />
+ <Separator/>
+ <Action name="dotted_semibreve" />
+ <Action name="dotted_minim" />
+ <Action name="dotted_crotchet" />
+ <Action name="dotted_quaver" />
+ <Action name="dotted_semiquaver" />
+ <Action name="dotted_demisemi" />
+ <Separator/>
+ <Action name="switch_from_rest_to_note" />
+ </Menu>
+
+ <Menu noMerge="1" name="Rests">
+ <text>&amp;Rests</text>
+ <!-- Reference names, but with _ instead of - -->
+ <Action name="rest_breve" />
+ <Action name="rest_semibreve" />
+ <Action name="rest_minim" />
+ <Action name="rest_crotchet" />
+ <Action name="rest_quaver" />
+ <Action name="rest_semiquaver" />
+ <Action name="rest_demisemi" />
+ <Action name="rest_hemidemisemi" />
+ <Separator/>
+ <Action name="dotted_rest_semibreve" />
+ <Action name="dotted_rest_minim" />
+ <Action name="dotted_rest_crotchet" />
+ <Action name="dotted_rest_quaver" />
+ <Action name="dotted_rest_semiquaver" />
+ <Action name="dotted_rest_demisemi" />
+ <Separator/>
+ <Action name="switch_from_note_to_rest" />
+ </Menu>
+
+ <Menu noMerge="1" name="Accidentals">
+ <text>&amp;Accidentals</text>
+ <Action name="no_accidental" />
+ <Action name="follow_accidental" />
+ <Action name="sharp_accidental" />
+ <Action name="flat_accidental" />
+ <Action name="natural_accidental" />
+ <Action name="double_sharp_accidental" />
+ <Action name="double_flat_accidental" />
+ </Menu>
+
+ <Menu noMerge="1" name="Clefs">
+ <text>&amp;Clefs</text>
+ <Action name="treble_clef" />
+ <Action name="alto_clef" />
+ <Action name="tenor_clef" />
+ <Action name="bass_clef" />
+ </Menu>
+ <Separator/>
+ <Action name="text" />
+ <Action name="guitarchord" />
+ <Separator/>
+ <Menu name="Move">
+ <text>Local &amp;Cursor</text>
+ <Action name="cursor_back"/>
+ <Action name="cursor_forward"/>
+ <Action name="cursor_back_bar"/>
+ <Action name="cursor_forward_bar"/>
+ <Action name="cursor_start"/>
+ <Action name="cursor_end"/>
+ <Separator/>
+ <Action name="extend_selection_backward"/>
+ <Action name="extend_selection_forward"/>
+ <Action name="preview_selection"/>
+ <Action name="clear_loop"/>
+ <!-- not implemented yet
+ <Separator/>
+ <Action name="move_selection_left"/>
+ <Action name="move_selection_right"/>
+ <Action name="copy_selection_left"/>
+ <Action name="copy_selection_right"/>
+ -->
+ <Separator/>
+ <Action name="cursor_to_playback_pointer"/>
+ <Action name="playback_pointer_to_cursor"/>
+ <Separator/>
+ <Action name="cursor_up_staff"/>
+ <Action name="cursor_down_staff"/>
+ <Action name="cursor_prior_segment"/>
+ <Action name="cursor_next_segment"/>
+ </Menu>
+
+ <Menu name="Transport">
+ <text>T&amp;ransport</text>
+ <Action name="play"/>
+ <Action name="stop"/>
+ <Action name="playback_pointer_back_bar"/>
+ <Action name="playback_pointer_forward_bar"/>
+ <Action name="playback_pointer_start"/>
+ <Action name="playback_pointer_end"/>
+ <Action name="playback_pointer_to_cursor" />
+ <Action name="cursor_to_playback_pointer" />
+ <Action name="toggle_solo"/>
+ <Action name="toggle_tracking"/>
+ <Action name="panic"/>
+ </Menu>
+
+ <Separator/>
+
+ <Action name="insert_note_actionmenu"/>
+ <Action name="insert_rest"/>
+
+ <!--
+ <Action name="insert_melody_mode"/>
+ <Action name="insert_chord_mode"/>
+ -->
+
+ <Action name="chord_mode"/>
+ <Action name="triplet_mode"/>
+ <Action name="grace_mode"/>
+
+ <Separator/>
+ <Action name="toggle_step_by_step"/>
+
+</Menu>
+
+<Menu name="settings">
+ <text>&amp;Settings</text>
+ <Menu name="toolbars" append="show_merge" >
+ <text>&amp;Toolbars</text>
+ <Action name="options_show_toolbar" append="show_merge" />
+ <Action name="show_tools_toolbar" append="show_merge" />
+ <Action name="show_notes_toolbar" append="show_merge" />
+ <Action name="show_rests_toolbar" append="show_merge" />
+ <Action name="show_accidentals_toolbar" append="show_merge" />
+ <Action name="show_clefs_toolbar" append="show_merge" />
+ <Action name="show_marks_toolbar" append="show_merge" />
+ <Action name="show_group_toolbar" append="show_merge" />
+ <Action name="show_transport_toolbar" append="show_merge" />
+ <Action name="show_layout_toolbar" append="show_merge" />
+ <Action name="show_meta_toolbar" append="show_merge" />
+ <Action name="options_show_statusbar" append="show_merge" />
+ </Menu>
+ <Menu name="rulers" append="show_merge" >
+ <text>&amp;Rulers</text>
+ <Action name="show_chords_ruler" append="show_merge" />
+ <Action name="show_raw_note_ruler" append="show_merge" />
+ <Action name="show_tempo_ruler" append="show_merge" />
+ </Menu>
+ <Separator append="show_merge" />
+ <Action name="show_annotations" append="show_merge" />
+ <Action name="show_lilypond_directives" append="show_merge" />
+</Menu>
+
+</MenuBar>
+
+<ToolBar name="Tools Toolbar">
+ <text>Tools Toolbar</text>
+ <Action name="select" />
+ <Action name="erase" />
+ <Action name="text" />
+ <Action name="guitarchord" />
+ <Separator/>
+ <Action name="toggle_step_by_step"/>
+ <Action name="filter_selection"/>
+ <Action name="quantize"/>
+</ToolBar>
+
+<ToolBar name="Transport Toolbar">
+ <text>Transport Toolbar</text>
+ <Action name="playback_pointer_start" />
+ <Action name="playback_pointer_back_bar" />
+ <Action name="play" />
+ <Action name="playback_pointer_forward_bar" />
+ <Action name="playback_pointer_end" />
+ <Action name="stop" />
+ <Action name="playback_pointer_to_cursor" />
+ <Action name="cursor_to_playback_pointer" />
+ <Action name="toggle_solo"/>
+ <Action name="toggle_tracking"/>
+ <Action name="panic"/>
+</ToolBar>
+
+<ToolBar name="Notes Toolbar" newline="true" hidden="false" position="left">
+ <text>Notes Toolbar</text>
+
+ <!-- Reference names, but with _ instead of - -->
+ <Action name="breve" />
+ <Action name="semibreve" />
+ <Action name="minim" />
+ <Action name="crotchet" />
+ <Action name="quaver" />
+ <Action name="semiquaver" />
+ <Action name="demisemi" />
+ <Action name="hemidemisemi" />
+ <Separator/>
+ <Action name="dotted_semibreve" />
+ <Action name="dotted_minim" />
+ <Action name="dotted_crotchet" />
+ <Action name="dotted_quaver" />
+ <Action name="dotted_semiquaver" />
+ <Action name="dotted_demisemi" />
+
+ <Separator/>
+
+ <Action name="chord_mode"/>
+ <Action name="triplet_mode" />
+ <Action name="grace_mode" />
+</ToolBar>
+
+<ToolBar name="Rests Toolbar" hidden="false" position="left">
+ <text>Rests Toolbar</text>
+
+ <!-- Reference names, but with _ instead of - -->
+ <Action name="rest_breve" />
+ <Action name="rest_semibreve" />
+ <Action name="rest_minim" />
+ <Action name="rest_crotchet" />
+ <Action name="rest_quaver" />
+ <Action name="rest_semiquaver" />
+ <Action name="rest_demisemi" />
+ <Action name="rest_hemidemisemi" />
+ <Separator/>
+ <Action name="dotted_rest_semibreve" />
+ <Action name="dotted_rest_minim" />
+ <Action name="dotted_rest_crotchet" />
+ <Action name="dotted_rest_quaver" />
+ <Action name="dotted_rest_semiquaver" />
+ <Action name="dotted_rest_demisemi" />
+
+</ToolBar>
+
+<ToolBar name="Clefs Toolbar" hidden="false" position="left" newline="true">
+ <text>Clefs Toolbar</text>
+
+ <Action name="treble_clef" />
+ <Action name="alto_clef" />
+ <Action name="tenor_clef" />
+ <Action name="bass_clef" />
+</ToolBar>
+
+<ToolBar name="Accidentals Toolbar" hidden="false" position="left">
+ <text>Accidentals Toolbar</text>
+
+ <Action name="no_accidental" />
+ <Action name="follow_accidental" />
+ <Action name="sharp_accidental" />
+ <Action name="flat_accidental" />
+ <Action name="natural_accidental" />
+ <Action name="double_sharp_accidental" />
+ <Action name="double_flat_accidental" />
+</ToolBar>
+
+<ToolBar name="Group Toolbar" hidden="false" position="right">
+ <text>Group Toolbar</text>
+
+ <Action name="make_chord"/>
+ <Action name="beam"/>
+ <Action name="break_group"/>
+ <Separator/>
+ <Action name="tuplet"/>
+ <Action name="simple_tuplet"/>
+<!-- <Action name="grace"/> -->
+ <Separator/>
+ <Action name="slur"/>
+<!-- <Action name="glissando"/> -->
+ <Action name="tie_notes"/>
+ <Action name="de_counterpoint"/>
+ <Separator/>
+ <Action name="crescendo"/>
+ <Action name="decrescendo"/>
+ <Action name="octave_up"/>
+</ToolBar>
+
+<ToolBar name="Marks Toolbar" newline="true" hidden="false" position="right">
+ <text>Marks Toolbar</text>
+
+ <Action name="add_accent"/>
+ <Action name="add_tenuto"/>
+ <Action name="add_staccato"/>
+ <Action name="add_staccatissimo"/>
+ <Action name="add_marcato"/>
+ <Action name="add_text_sf"/>
+ <Action name="add_text_rf"/>
+ <Action name="add_trill"/>
+ <Action name="add_long-trill"/>
+ <Action name="add_turn"/>
+ <Action name="add_pause"/>
+ <Action name="add_up-bow"/>
+ <Action name="add_down-bow"/>
+ <Action name="add_mordent"/>
+ <Action name="add_mordent-inverted"/>
+ <Action name="add_mordent-long"/>
+ <Action name="add_mordent-long-inverted"/>
+ <Action name="add_text_mark"/>
+</ToolBar>
+
+<ToolBar name="Meta Toolbar" newline="true" hidden="true">
+ <text>Meta Toolbar</text>
+
+ <Action name="show_notes_toolbar" />
+ <Action name="show_rests_toolbar" />
+ <Action name="show_accidentals_toolbar" />
+ <Action name="show_clefs_toolbar" />
+ <Action name="show_marks_toolbar" />
+ <Action name="show_group_toolbar" />
+</ToolBar>
+
+<ToolBar name="Layout Toolbar" hidden="true">
+ <text>Layout Toolbar</text>
+ <Action name="linear_mode" />
+ <Action name="continuous_page_mode" />
+ <Action name="multi_page_mode" />
+<!-- the remainder filled at runtime -->
+</ToolBar>
+
+<Menu noMerge="1" name="controller_events_ruler_menu">
+ <Text>Control Ruler actions</Text>
+ <Action name="insert_control_ruler_item"/>
+ <Action name="erase_control_ruler_item"/>
+ <Action name="clear_control_ruler_item"/>
+ <Separator/>
+ <Action name="start_control_line_item"/>
+ <Separator/>
+ <Action name="flip_control_events_forward"/>
+ <Action name="flip_control_events_back"/>
+</Menu>
+
+<Menu noMerge="1" name="property_ruler_menu">
+ <Text>Property Ruler actions</Text>
+ <Action name="draw_property_line"/>
+ <Action name="select_all_properties"/>
+ <Separator/>
+ <Action name="flip_control_events_forward"/>
+ <Action name="flip_control_events_back"/>
+</Menu>
+
+
+
+<State name="note_insert_tool_current">
+ <enable>
+ <Action name="insert_rest"/>
+ <Action name="insert_note_actionmenu"/>
+ <Action name="switch_from_note_to_rest"/>
+ </enable>
+</State>
+
+<State name="rest_insert_tool_current">
+ <enable>
+ <Action name="insert_rest"/>
+ <Action name="switch_from_rest_to_note"/>
+ </enable>
+</State>
+
+<State name="have_selection">
+ <enable>
+ <Action name="edit_cut"/>
+ <Action name="edit_copy"/>
+ <Action name="delete"/>
+ <Action name="cut_and_close"/>
+ <Action name="debug_dump"/>
+ <Action name="preview_selection"/>
+ <Action name="normalize_rests"/>
+ <Action name="clear_selection"/>
+ <Action name="filter_selection"/>
+ <Action name="jog_left"/>
+ <Action name="jog_right"/>
+ <Action name="slurs_above"/>
+ <Action name="slurs_below"/>
+ <Action name="restore_slurs"/>
+ <Action name="ties_above"/>
+ <Action name="ties_below"/>
+ <Action name="restore_ties"/>
+ <Action name="fine_position_left"/>
+ <Action name="fine_position_right"/>
+ <Action name="fine_position_up"/>
+ <Action name="fine_position_down"/>
+ <Action name="fine_position_restore"/>
+ <Action name="tuplet"/>
+ <Action name="simple_tuplet"/>
+ <Action name="break_tuplets"/>
+ <Action name="make_invisible"/>
+ <Action name="make_visible"/>
+ <Action name="move_events_up_staff"/>
+ <Action name="move_events_down_staff"/>
+ </enable>
+</State>
+
+<State name="have_rests_in_selection">
+ <enable>
+ <Action name="note_style_actionmenu"/>
+ <Action name="remove_marks"/>
+ <Action name="add_pause"/>
+ <Action name="add_text_mark"/>
+<!-- <Action name="normalize_rests"/> -->
+ <Action name="collapse_rests_aggressively"/>
+ <Menu name="Adjust"/>
+ </enable>
+</State>
+
+<State name="have_notes_in_selection">
+ <enable>
+ <Action name="respell_doubleflat"/>
+ <Action name="respell_flat"/>
+ <Action name="respell_natural"/>
+ <Action name="respell_sharp"/>
+ <Action name="respell_doublesharp"/>
+ <Action name="respell_restore"/>
+ <Action name="show_cautionary"/>
+ <Action name="cancel_cautionary"/>
+ <Action name="make_chord"/>
+ <Action name="beam"/>
+ <Action name="auto_beam"/>
+ <Action name="break_group"/>
+<!-- <Action name="break_tuplets"/> -->
+<!-- <Action name="grace"/> -->
+<!-- <Action name="ungrace"/> -->
+ <Action name="slur"/>
+ <Action name="phrasing_slur"/>
+ <Action name="crescendo"/>
+ <Action name="decrescendo"/>
+<!-- <Action name="glissando"/> -->
+ <Action name="octave_2up"/>
+ <Action name="octave_up"/>
+ <Action name="octave_down"/>
+ <Action name="octave_2down"/>
+<!-- <Action name="remove_indications"/> -->
+ <Action name="stems_up"/>
+ <Action name="stems_down"/>
+ <Action name="restore_stems"/>
+ <Action name="slashes_0"/>
+ <Action name="slashes_1"/>
+ <Action name="slashes_2"/>
+ <Action name="slashes_3"/>
+ <Action name="slashes_4"/>
+ <Action name="slashes_5"/>
+ <Action name="note_style_actionmenu"/>
+ <Action name="remove_marks"/>
+ <Action name="remove_fingering_marks"/>
+ <Action name="add_fingering_0"/>
+ <Action name="add_fingering_1"/>
+ <Action name="add_fingering_2"/>
+ <Action name="add_fingering_3"/>
+ <Action name="add_fingering_4"/>
+ <Action name="add_fingering_5"/>
+ <Action name="add_fingering_plus"/>
+ <Action name="add_fingering_mark"/>
+ <Action name="add_accent"/>
+ <Action name="add_tenuto"/>
+ <Action name="add_staccato"/>
+ <Action name="add_staccatissimo"/>
+ <Action name="add_marcato"/>
+ <Action name="add_text_sf"/>
+ <Action name="add_text_rf"/>
+ <Action name="add_trill"/>
+ <Action name="add_long-trill"/>
+ <Action name="add_trill-line"/>
+ <Action name="add_turn"/>
+ <Action name="add_pause"/>
+ <Action name="add_up-bow"/>
+ <Action name="add_down-bow"/>
+ <Action name="add_mordent"/>
+ <Action name="add_mordent-inverted"/>
+ <Action name="add_mordent-long"/>
+ <Action name="add_mordent-long-inverted"/>
+ <Action name="add_text_mark"/>
+ <Action name="make_ornament"/>
+ <Action name="remove_ornament"/>
+ <Action name="use_ornament"/>
+ <Action name="collapse_notes"/>
+ <Action name="tie_notes"/>
+ <Action name="untie_notes"/>
+ <Action name="make_notes_viable"/>
+ <Action name="de_counterpoint"/>
+ <Action name="quantize"/>
+ <Action name="interpret"/>
+ <Action name="fix_quantization"/>
+ <Action name="remove_quantization"/>
+ <Action name="transpose_up"/>
+ <Action name="transpose_down"/>
+ <Action name="transpose_up_octave"/>
+ <Action name="transpose_down_octave"/>
+ <Action name="general_transpose"/>
+ <Action name="general_diatonic_transpose"/>
+ <Action name="invert"/>
+ <Action name="retrograde"/>
+ <Action name="retrograde_invert"/>
+ <Action name="halve_durations"/>
+ <Action name="double_durations"/>
+ <Action name="rescale"/>
+ <Menu name="Stems"/>
+ <Menu name="Slashes"/>
+ <Menu name="Transpose"/>
+ <Menu name="Convert"/>
+<Menu name="Notes"/>
+<Menu name="beams"/>
+<Menu name="octaves"/>
+<Menu name="indications"/>
+<Menu name="Slurs"/>
+<Menu name="Ties"/>
+<Menu name="Marks"/>
+<Menu name="ornaments"/>
+<Menu name="Fingering"/>
+<Menu name="Slashes"/>
+<Menu name="Adjust"/>
+<Menu name="Respell"/>
+<Menu name="triplets"/>
+<Menu name="Stems"/>
+<Menu name="rests"/>
+<Menu name="transform_notes"/>
+<Menu name="quantize_notes"/>
+ </enable>
+</State>
+
+<State name="have_clipboard_single_segment">
+ <enable>
+ <Action name="edit_paste"/>
+ <Action name="general_paste"/>
+ </enable>
+</State>
+
+<State name="have_multiple_staffs">
+ <enable>
+ <Action name="cursor_up_staff"/>
+ <Action name="cursor_down_staff"/>
+ <Action name="cursor_prior_segment"/>
+ <Action name="cursor_next_segment"/>
+ <Action name="move_events_up_staff"/>
+ <Action name="move_events_down_staff"/>
+ </enable>
+</State>
+
+<State name="linear_mode">
+ <enable>
+ <Action name="show_velocity_control_ruler"/>
+ <Action name="show_controller_events_ruler"/>
+ <Menu name="add_control_ruler"/>
+ <Action name="add_control_ruler"/>
+ <Action name="show_chords_ruler"/>
+ <Action name="show_raw_note_ruler"/>
+ <Action name="show_tempo_ruler"/>
+ </enable>
+</State>
+
+<State name="have_note_events_in_segment">
+ <enable>
+ <Action name="draw_property_line"/>
+ </enable>
+</State>
+
+<State name="have_control_ruler">
+ <enable>
+ <Action name="show_control_rulers"/>
+ </enable>
+</State>
+
+<State name="have_controller_item_selected">
+ <enable>
+ <Action name="erase_control_ruler_item"/>
+ </enable>
+ <disable>
+ <Action name="insert_control_ruler_item"/>
+ </disable>
+</State>
+
+</kpartgui>
diff --git a/src/gui/ui/notationeraser.rc b/src/gui/ui/notationeraser.rc
new file mode 100644
index 0000000..e3e3289
--- /dev/null
+++ b/src/gui/ui/notationeraser.rc
@@ -0,0 +1,12 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="Rosegarden::NotationTool" version="1230">
+
+<Menu name="NotationEraser">
+ <Action name="select" />
+ <Action name="insert" />
+ <Separator/>
+ <Action name="toggle_rest_collapse" />
+</Menu>
+
+</kpartgui>
diff --git a/src/gui/ui/notationselector.rc b/src/gui/ui/notationselector.rc
new file mode 100644
index 0000000..4893b8f
--- /dev/null
+++ b/src/gui/ui/notationselector.rc
@@ -0,0 +1,26 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="Rosegarden::NotationTool" version="1230">
+
+<Menu noMerge="1" name="NotationSelector">
+ <Action name="insert" />
+ <Action name="erase" />
+ <Separator />
+<!-- <Action name="collapse_rests" /> -->
+ <Action name="collapse_rests_aggressively" />
+ <Separator />
+ <Action name="respell_flat" />
+ <Action name="respell_sharp" />
+ <Action name="respell_natural" />
+ <Separator />
+ <Action name="collapse_notes" />
+ <Action name="interpret" />
+ <Separator />
+ <Action name="move_events_up_staff" />
+ <Action name="move_events_down_staff" />
+ <Separator />
+ <Action name="make_invisible" />
+ <Action name="make_visible" />
+</Menu>
+
+</kpartgui>
diff --git a/src/gui/ui/noteinserter.rc b/src/gui/ui/noteinserter.rc
new file mode 100644
index 0000000..507857f
--- /dev/null
+++ b/src/gui/ui/noteinserter.rc
@@ -0,0 +1,23 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="Rosegarden::NotationTool" version="1230">
+
+<Menu noMerge="1" name="NoteInserter">
+ <Action name="select" />
+ <Action name="erase" />
+ <Action name="rests" />
+ <Separator/>
+ <Action name="toggle_auto_beam" />
+ <Separator/>
+ <Action name="toggle_dot" />
+ <Separator/>
+ <Action name="no_accidental" />
+ <Action name="follow_accidental" />
+ <Action name="sharp_accidental" />
+ <Action name="flat_accidental" />
+ <Action name="natural_accidental" />
+ <Action name="double_sharp_accidental" />
+ <Action name="double_flat_accidental" />
+</Menu>
+
+</kpartgui>
diff --git a/src/gui/ui/restinserter.rc b/src/gui/ui/restinserter.rc
new file mode 100644
index 0000000..37636c8
--- /dev/null
+++ b/src/gui/ui/restinserter.rc
@@ -0,0 +1,13 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="Rosegarden::NotationTool" version="1230">
+
+<Menu noMerge="1" name="RestInserter">
+ <Action name="notes" />
+ <Action name="erase" />
+ <Action name="select" />
+ <Separator/>
+ <Action name="toggle_dot" />
+</Menu>
+
+</kpartgui>
diff --git a/src/gui/ui/rosegardenui.rc b/src/gui/ui/rosegardenui.rc
new file mode 100644
index 0000000..ba6e735
--- /dev/null
+++ b/src/gui/ui/rosegardenui.rc
@@ -0,0 +1,440 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="Rosegarden" version="1230">
+
+<MenuBar>
+ <Menu name="file">
+ <!-- these two would like to be open_merge, but that sticks them at
+ the very bottom in older KDE3 versions: -->
+ <Menu name="import" append="new_merge"><text>&amp;Import</text>
+ <Action name="file_import_project" />
+ <Separator/>
+ <Action name="file_import_midi" />
+ <Action name="file_import_rg21" />
+ <Action name="file_import_hydrogen" />
+ </Menu>
+ <Menu name="merge" append="new_merge"><text>&amp;Merge</text>
+ <Action name="file_merge" />
+ <Action name="file_merge_midi" />
+ <Action name="file_merge_rg21" />
+ <Action name="file_merge_hydrogen" />
+ </Menu>
+ <!-- this one would like to be save_merge, but that sticks it at
+ the very bottom in older KDE3 versions: -->
+ <Menu name="export" append="new_merge"><text>&amp;Export</text>
+ <Action name="file_export_project" />
+ <Separator/>
+ <Action name="file_export_lilypond" />
+ <Separator/>
+ <Action name="file_export_midi" />
+ <Separator/>
+ <Action name="file_export_csound" />
+ <Action name="file_export_mup" />
+ <Action name="file_export_musicxml" />
+ </Menu>
+ <Action name="file_print_lilypond" append="print_merge" />
+ <Action name="file_preview_lilypond" append="print_merge" />
+ <Separator/>
+ <Action name="audio_manager"/>
+ <Separator/>
+ <Action name="file_show_playlist" />
+ </Menu>
+ <Menu name="edit">
+ <Action name="delete"/>
+ <Separator/>
+ <Action name="cut_range"/>
+ <Action name="copy_range"/>
+ <Action name="paste_range"/>
+ <Action name="insert_range"/>
+ <Separator/>
+ <Action name="select_all"/>
+ </Menu>
+ <Menu name="document">
+ <text>&amp;Composition</text>
+ <Action name="add_tempo"/>
+ <Action name="add_time_signature"/>
+ <Action name="edit_tempos"/>
+ <Separator/>
+ <Action name="set_tempo_to_segment_length"/>
+ <Action name="groove_quantize"/>
+ <Separator/>
+ <Action name="change_composition_length"/>
+ <Separator/>
+ <Action name="edit_markers"/>
+ <Action name="edit_doc_properties"/>
+ <Separator/>
+ <Action name="set_quick_marker"/>
+ <Action name="jump_to_quick_marker"/>
+ </Menu>
+ <Menu name="Studio">
+ <text>&amp;Studio</text>
+ <Action name="audio_mixer"/>
+ <Action name="midi_mixer"/>
+ <Separator/>
+ <Action name="manage_devices"/>
+ <Action name="manage_synths"/>
+ <Action name="manage_metronome"/>
+ <Separator/>
+ <Action name="modify_midi_filters"/>
+ <Action name="enable_midi_routing"/>
+ <Separator/>
+ <Action name="reset_midi_network"/>
+ <Separator/>
+ <Action name="load_studio"/>
+ <Action name="load_default_studio"/>
+ <Action name="save_default_studio"/>
+ </Menu>
+ <Menu name="Segments">
+ <text>Se&amp;gments</text>
+ <Menu name="edit_with">
+ <text>Edit &amp;With</text>
+ <Action name="edit_default"/>
+ <Separator/>
+ <Action name="edit_matrix"/>
+ <Action name="edit_percussion_matrix"/>
+ <Action name="edit_notation"/>
+ <Action name="edit_event_list"/>
+ </Menu>
+ <Separator/>
+ <Action name="relabel_segment"/>
+ <Action name="quantize_selection"/>
+ <Action name="repeat_quantize"/>
+ <Action name="transpose"/>
+ <Separator/>
+ <Action name="rescale"/>
+ <Action name="set_segment_start"/>
+ <Action name="set_segment_duration"/>
+ <Separator/>
+ <Action name="jog_left"/>
+ <Action name="jog_right"/>
+ <Separator/>
+ <Action name="join_segments"/>
+ <Menu name="split">
+ <text>&amp;Split</text>
+ <Action name="auto_split"/>
+ <Action name="split_by_pitch"/>
+ <Action name="split_by_recording"/>
+ <Action name="split_at_time"/>
+ </Menu>
+ <Action name="repeats_to_real_copies"/>
+ <Separator/>
+ <Action name="manage_trigger_segments"/>
+ </Menu>
+ <Menu name="Tracks">
+ <text>Trac&amp;ks</text>
+ <Action name="add_track"/>
+ <Action name="add_tracks"/>
+ <Action name="delete_track"/>
+ <Separator/>
+ <Action name="move_track_down"/>
+ <Action name="move_track_up"/>
+ <Separator/>
+ <Action name="select_next_track"/>
+ <Action name="select_previous_track"/>
+ <Separator/>
+ <Action name="toggle_mute_track"/>
+ <Action name="toggle_arm_track"/>
+ <Separator/>
+ <Action name="unmute_all_tracks"/>
+ <Action name="mute_all_tracks"/>
+ <Separator/>
+ <Menu name="set_track_instrument"><text>Set &amp;Instrument</text></Menu>
+ <Action name="remap_instruments"/>
+ </Menu>
+ <Menu name="Tools">
+ <text>&amp;Tools</text>
+ <Action name="select"/>
+ <Action name="draw"/>
+ <Action name="erase"/>
+ <Action name="move"/>
+ <Action name="resize"/>
+ <Action name="split"/>
+ <Separator/>
+ <Menu name="Transport">
+ <text>T&amp;ransport</text>
+ <Action name="play"/>
+ <Action name="stop"/>
+ <Action name="fast_forward"/>
+ <Action name="rewind"/>
+ <Action name="record"/>
+ <Action name="recordtoggle"/>
+ <Action name="toggle_tracking"/>
+ <Action name="panic"/>
+ </Menu>
+ </Menu>
+ <Menu name="settings">
+ <Menu name="toolbars" append="show_merge"><text>&amp;Toolbars</text>
+ <Action name="show_stock_toolbar" append="show_merge" />
+ <Action name="show_tools_toolbar" append="show_merge" />
+ <Action name="show_tracks_toolbar" append="show_merge" />
+ <Action name="show_editors_toolbar" append="show_merge" />
+ <Action name="show_transport_toolbar" append="show_merge" />
+ <Action name="show_zoom_toolbar" append="show_merge" />
+ <Action name="show_status_bar" append="show_merge" />
+ </Menu>
+ <Menu name="rulers" append="show_merge"><text>&amp;Rulers</text>
+ <Action name="show_rulers" append="show_merge" />
+ <Action name="show_chord_name_ruler" append="show_merge" />
+ <Action name="show_tempo_ruler" append="show_merge" />
+ </Menu>
+ <Separator append="show_merge" />
+ <Action name="show_transport" append="show_merge" />
+ <Action name="show_inst_segment_parameters" append="show_merge" />
+ <Separator append="show_merge" />
+ <Action name="show_previews" append="show_merge" />
+ <Action name="show_segment_labels" append="show_merge" />
+ <Action name="show_tracklabels" append="show_merge" />
+ <!-- Action name="toggle_all" append="show_merge" /> -->
+ </Menu>
+ <Menu name="help" append="about_merge"><text>&amp;Help</text>
+ <Action name="tutorial"/>
+ <Action name="guidelines"/>
+ </Menu>
+
+</MenuBar>
+
+<ToolBar name="Tools Toolbar">
+ <text>Tools Toolbar</text>
+ <Action name="select"/>
+ <Action name="draw"/>
+ <Action name="erase"/>
+ <Action name="move"/>
+ <Action name="resize"/>
+ <Action name="split"/>
+ <!-- <Action name="join"/> -->
+</ToolBar>
+
+<ToolBar name="Tracks Toolbar">
+ <text>Tracks Toolbar</text>
+ <Action name="add_track"/>
+ <Action name="delete_track"/>
+ <Action name="move_track_up"/>
+ <Action name="move_track_down"/>
+ <Separator/>
+ <Action name="mute_all_tracks"/>
+ <Action name="unmute_all_tracks"/>
+<!-- <Separator/>
+ <Action name="show_previews"/>
+ <Action name="show_segment_labels"/>
+ <Action name="show_tracklabels"/> -->
+</ToolBar>
+
+<ToolBar name="Editors Toolbar">
+ <text>Editors Toolbar</text>
+ <Action name="edit_matrix"/>
+ <Action name="edit_percussion_matrix"/>
+ <Action name="edit_notation"/>
+ <Action name="edit_event_list"/>
+ <Action name="audio_manager"/>
+ <Separator/>
+ <Action name="quantize_selection"/>
+ <Separator/>
+ <Action name="manage_devices"/>
+ <Action name="manage_synths"/>
+ <Action name="midi_mixer"/>
+ <Action name="audio_mixer"/>
+</ToolBar>
+
+<ToolBar name="Transport Toolbar">
+ <text>Transport Toolbar</text>
+ <Action name="rewindtobeginning" />
+ <Action name="rewind" />
+ <Action name="play" />
+ <Action name="fast_forward" />
+ <Action name="fastforwardtoend" />
+ <Action name="stop" />
+ <Action name="record" />
+ <Action name="toggle_tracking"/>
+ <Action name="panic"/>
+</ToolBar>
+
+<ToolBar name="Zoom Toolbar">
+ <text>Zoom Toolbar</text>
+</ToolBar>
+
+<Menu noMerge="1" name="segment_tool_menu">
+ <Action name="edit_default"/>
+ <Separator/>
+ <Action name="edit_matrix"/>
+ <Action name="edit_percussion_matrix"/>
+ <Action name="edit_notation"/>
+ <Action name="edit_event_list"/>
+ <Separator/>
+ <Action name="edit_undo"/>
+ <Action name="edit_redo"/>
+ <Separator/>
+ <Action name="edit_cut"/>
+ <Action name="edit_copy"/>
+ <Action name="edit_paste"/>
+ <Separator/>
+ <Action name="delete"/>
+ <Action name="join_segments"/>
+ <Separator/>
+ <Action name="quantize_selection"/>
+ <Action name="repeat_quantize"/>
+ <Action name="relabel_segment"/>
+ <Action name="transpose"/>
+ <Separator/>
+ <Action name="select"/>
+ <Action name="move"/>
+ <Action name="draw"/>
+ <Action name="erase"/>
+ <Action name="resize"/>
+ <Action name="split"/>
+ <!-- <Action name="join"/> -->
+</Menu>
+
+<State name="new_file">
+ <disable>
+ <Action name="file_revert"/>
+ </disable>
+ <enable>
+ <Action name="file_close"/>
+ <Action name="file_print"/>
+ <Action name="file_print_preview"/>
+ </enable>
+</State>
+
+<State name="new_file_modified">
+ <enable>
+ </enable>
+</State>
+
+<State name="saved_file_modified">
+ <enable>
+ <Action name="file_revert"/>
+ </enable>
+</State>
+
+<State name="have_project_packager">
+ <enable>
+ <Action name="file_import_project"/>
+ <Action name="file_export_project"/>
+ </enable>
+</State>
+
+<State name="have_lilypondview">
+ <enable>
+ <Action name="file_preview_lilypond"/>
+ </enable>
+</State>
+
+<State name="have_segments">
+ <enable>
+ <Action name="move"/>
+ <Action name="erase"/>
+ <Action name="resize"/>
+ <Action name="split"/>
+ <!-- <Action name="join"/> -->
+ <Action name="file_print"/>
+ <Action name="file_print_preview"/>
+ </enable>
+</State>
+
+<State name="have_selection">
+ <enable>
+ <Action name="edit_cut"/>
+ <Action name="edit_copy"/>
+ <Action name="delete"/>
+ <Action name="edit_default"/>
+ <Action name="edit_matrix"/>
+ <Action name="edit_percussion_matrix"/>
+ <Action name="edit_notation"/>
+ <Action name="edit_event_list"/>
+ <Action name="quantize_selection"/>
+ <Action name="repeat_quantize"/>
+<!-- <Action name="harmonize_selection"/> -->
+ <Action name="rescale"/>
+ <Action name="jog_left"/>
+ <Action name="jog_right"/>
+ <Action name="auto_split"/>
+ <Action name="split_by_pitch"/>
+ <Action name="split_by_recording"/>
+ <Action name="split_at_time"/>
+ <Action name="join_segments"/>
+ <Action name="relabel_segment"/>
+ <Action name="transpose"/>
+ <Action name="set_segment_start"/>
+ <Action name="set_segment_duration"/>
+ <Action name="collapse"/>
+ <Action name="set_tempo_to_segment_length"/>
+ <Action name="repeats_to_real_copies"/>
+ <Action name="groove_quantize"/>
+ </enable>
+</State>
+
+<State name="have_clipboard">
+ <enable>
+ <Action name="edit_paste"/>
+ <Action name="paste_range"/>
+ </enable>
+</State>
+
+<State name="audio_segment_selected">
+ <enable>
+ <Action name="set_tempo_to_segment_length"/>
+ </enable>
+ <disable>
+ <Action name="edit_notation"/>
+ <Action name="edit_matrix"/>
+ <Action name="edit_percussion_matrix"/>
+ <Action name="edit_event_list"/>
+ <Action name="quantize_selection"/>
+ <Action name="repeat_quantize"/>
+<!-- <Action name="rescale"/> -->
+ <Action name="split_by_pitch"/>
+ <Action name="split_by_recording"/>
+ <Action name="join_segments"/>
+ <Action name="collapse"/>
+ <Action name="repeats_to_real_copies"/>
+ <Action name="groove_quantize"/>
+ </disable>
+</State>
+
+<State name="got_midi_devices">
+ <enable>
+ <Action name="modify_banks"/>
+ <Action name="remap_instruments"/>
+ <Action name="midi_mixer"/>
+ </enable>
+</State>
+
+<State name="got_audio">
+ <enable>
+ <Action name="audio_manager"/>
+ <Action name="audio_mixer"/>
+ </enable>
+</State>
+
+<State name="sequencer_running">
+ <enable>
+ <Action name="rewindtobeginning" />
+ <Action name="rewind" />
+ <Action name="play" />
+ <Action name="fast_forward" />
+ <Action name="fastforwardtoend" />
+ <Action name="stop" />
+ <Action name="record" />
+ <Action name="recordtoggle" />
+ </enable>
+</State>
+
+<State name="not_playing">
+ <enable>
+ </enable>
+</State>
+
+<State name="parametersbox_closed">
+ <enable>
+ <Action name="show_inst_segment_parameters" />
+ </enable>
+</State>
+
+<State name="have_range">
+ <enable>
+ <Action name="cut_range"/>
+ <Action name="copy_range"/>
+ </enable>
+</State>
+
+</kpartgui>
diff --git a/src/gui/ui/temporuler.rc b/src/gui/ui/temporuler.rc
new file mode 100644
index 0000000..1db6b62
--- /dev/null
+++ b/src/gui/ui/temporuler.rc
@@ -0,0 +1,19 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="Rosegarden::TempoRuler" version="1230">
+
+<Menu name="tempo_ruler_menu">
+ <Action name="insert_tempo_here" />
+ <Action name="insert_tempo_at_pointer" />
+ <Separator/>
+ <Action name="delete_tempo" />
+ <Separator/>
+ <Action name="ramp_to_next" />
+ <Action name="unramp" />
+ <Separator/>
+ <Action name="edit_tempo" />
+ <Action name="edit_time_signature" />
+ <Action name="edit_tempos" />
+</Menu>
+
+</kpartgui>
diff --git a/src/gui/ui/tempoview.rc b/src/gui/ui/tempoview.rc
new file mode 100644
index 0000000..b48443f
--- /dev/null
+++ b/src/gui/ui/tempoview.rc
@@ -0,0 +1,96 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="Rosegarden::TempoView" version="1500">
+
+<MenuBar>
+
+<Menu name="edit" append="new_merge">
+ <Action name="insert_tempo"/>
+ <Action name="insert_timesig"/>
+ <Action name="delete"/>
+ <Action name="edit"/>
+ <Separator/>
+ <Action name="select_all"/>
+ <Action name="clear_selection"/>
+</Menu>
+
+<Menu name="view"><text>&amp;View</text>
+ <Action name="time_musical"/>
+ <Action name="time_real"/>
+ <Action name="time_raw"/>
+</Menu>
+
+</MenuBar>
+
+<ToolBar name="Actions Toolbar">
+ <text>Actions Toolbar</text>
+ <Action name="insert_tempo"/>
+ <Action name="insert_timesig"/>
+ <Action name="delete"/>
+ <Action name="edit"/>
+</ToolBar>
+
+<ToolBar name="Time Toolbar">
+ <text>Time Toolbar</text>
+ <Action name="time_musical"/>
+ <Action name="time_real"/>
+ <Action name="time_raw"/>
+</ToolBar>
+
+</kpartgui>
+
+<ToolBar name="Transport Toolbar">
+ <text>Transport Toolbar</text>
+
+ <Action name="playback_pointer_start" />
+ <Action name="playback_pointer_back_bar" />
+ <Action name="play" />
+ <Action name="playback_pointer_forward_bar" />
+ <Action name="playback_pointer_end" />
+ <Action name="stop" />
+ <Action name="playback_pointer_to_cursor" />
+ <Action name="cursor_to_playback_pointer" />
+ <Action name="toggle_solo"/>
+ <Action name="panic"/>
+</ToolBar>
+
+<ToolBar name="Actions Toolbar">
+ <text>Actions Toolbar</text>
+</ToolBar>
+
+<ToolBar name="Zoom Toolbar">
+ <text>Zoom Toolbar</text>
+</ToolBar>
+
+<State name="have_selection">
+ <enable>
+ <Action name="edit_cut"/>
+ <Action name="edit_copy"/>
+ <Action name="edit"/>
+ <Action name="quantize"/>
+ <Action name="velocity_up"/>
+ <Action name="velocity_down"/>
+ <Action name="set_velocities"/>
+ <Action name="transpose_up"/>
+ <Action name="transpose_down"/>
+ <Action name="transpose_up_octave"/>
+ <Action name="transpose_down_octave"/>
+ <Action name="general_transpose"/>
+ <Action name="general_diatonic_transpose"/>
+ <Action name="invert"/>
+ <Action name="retrograde"/>
+ <Action name="retrograde_invert"/>
+ <Action name="preview_selection"/>
+ <Action name="clear_selection"/>
+ <Action name="filter_selection"/>
+ </enable>
+</State>
+
+<State name="have_control_ruler">
+ <enable>
+ <Action name="show_control_rulers"/>
+ </enable>
+</State>
+
+</kpartgui>
+
diff --git a/src/gui/ui/textinserter.rc b/src/gui/ui/textinserter.rc
new file mode 100644
index 0000000..2d445a6
--- /dev/null
+++ b/src/gui/ui/textinserter.rc
@@ -0,0 +1,11 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="Rosegarden::NotationTool" version="1230">
+
+<Menu noMerge="1" name="TextInserter">
+ <Action name="select" />
+ <Action name="notes" />
+ <Action name="erase" />
+</Menu>
+
+</kpartgui>
diff --git a/src/gui/ui/triggermanager.rc b/src/gui/ui/triggermanager.rc
new file mode 100644
index 0000000..56efa52
--- /dev/null
+++ b/src/gui/ui/triggermanager.rc
@@ -0,0 +1,40 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+
+<kpartgui name="TriggerManager" version="1230">
+
+<MenuBar>
+
+ <Menu name="file">
+ <Action name="file_close_discard"/>
+ </Menu>
+
+ <Menu name="edit">
+ <Action name="paste_to_trigger_segment"/>
+ </Menu>
+
+ <Menu name="view"><text>&amp;View</text>
+ <Action name="time_musical"/>
+ <Action name="time_real"/>
+ <Action name="time_raw"/>
+ /Menu>
+
+</MenuBar>
+
+<ToolBar name="mainToolBar">
+ <Action name="file_close"/>
+</ToolBar>
+
+<ToolBar name="Time Toolbar">
+ <text>Time Toolbar</text>
+ <Action name="time_musical"/>
+ <Action name="time_real"/>
+ <Action name="time_raw"/>
+</ToolBar>
+
+<State name="have_clipboard_single_segment">
+ <enable>
+ <Action name="paste_to_trigger_segment"/>
+ </enable>
+</State>
+
+</kpartgui>
diff --git a/src/gui/widgets/AudioFaderBox.cpp b/src/gui/widgets/AudioFaderBox.cpp
new file mode 100644
index 0000000..05789ff
--- /dev/null
+++ b/src/gui/widgets/AudioFaderBox.cpp
@@ -0,0 +1,294 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "AudioFaderBox.h"
+#include <qlayout.h>
+
+#include <klocale.h>
+#include <kstddirs.h>
+#include "misc/Debug.h"
+#include "AudioRouteMenu.h"
+#include "AudioVUMeter.h"
+#include "base/AudioLevel.h"
+#include "base/Instrument.h"
+#include "base/Studio.h"
+#include "Fader.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/application/RosegardenGUIApp.h"
+#include "gui/studio/AudioPluginOSCGUIManager.h"
+#include "Rotary.h"
+#include <kglobal.h>
+#include <qframe.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qobject.h>
+#include <qpixmap.h>
+#include <qpushbutton.h>
+#include <qsignalmapper.h>
+#include <qstring.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include "VUMeter.h"
+
+
+namespace Rosegarden
+{
+
+AudioFaderBox::AudioFaderBox(QWidget *parent,
+ QString id,
+ bool haveInOut,
+ const char *name):
+ QFrame(parent, name),
+ m_signalMapper(new QSignalMapper(this)),
+ m_id(id),
+ m_isStereo(false)
+{
+ // Plugin box
+ //
+ QPushButton *plugin;
+ QVBox *pluginVbox = 0;
+
+ pluginVbox = new QVBox(this);
+ pluginVbox->setSpacing(2);
+
+ for (int i = 0; i < 5; i++) {
+ plugin = new QPushButton(pluginVbox);
+ plugin->setText(i18n("<no plugin>"));
+
+ QToolTip::add
+ (plugin, i18n("Audio plugin button"));
+
+ m_plugins.push_back(plugin);
+ m_signalMapper->setMapping(plugin, i);
+ connect(plugin, SIGNAL(clicked()),
+ m_signalMapper, SLOT(map()));
+ }
+
+ m_synthButton = new QPushButton(this);
+ m_synthButton->setText(i18n("<no synth>"));
+ QToolTip::add
+ (m_synthButton, i18n("Synth plugin button"));
+
+ // VU meter and fader
+ //
+ QHBox *faderHbox = new QHBox(this);
+
+ m_vuMeter = new AudioVUMeter(faderHbox, VUMeter::AudioPeakHoldShort,
+ true, true);
+
+ m_recordFader = new Fader(AudioLevel::ShortFader,
+ 20, m_vuMeter->height(), faderHbox);
+
+ m_recordFader->setOutlineColour(GUIPalette::getColour(GUIPalette::RecordFaderOutline));
+
+ delete m_vuMeter; // only used the first one to establish height,
+ // actually want it after the record fader in
+ // hbox
+ m_vuMeter = new AudioVUMeter(faderHbox, VUMeter::AudioPeakHoldShort,
+ true, true);
+
+ m_fader = new Fader(AudioLevel::ShortFader,
+ 20, m_vuMeter->height(), faderHbox);
+
+ m_fader->setOutlineColour(GUIPalette::getColour(GUIPalette::PlaybackFaderOutline));
+
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ m_monoPixmap.load(QString("%1/misc/mono.xpm").arg(pixmapDir));
+ m_stereoPixmap.load(QString("%1/misc/stereo.xpm").arg(pixmapDir));
+
+ m_pan = new Rotary(this, -100.0, 100.0, 1.0, 5.0, 0.0, 22,
+ Rotary::NoTicks, false, true);
+
+ // same as the knob colour on the MIDI pan
+ m_pan->setKnobColour(GUIPalette::getColour(GUIPalette::RotaryPastelGreen));
+
+ m_stereoButton = new QPushButton(this);
+ m_stereoButton->setPixmap(m_monoPixmap); // default is mono
+ m_stereoButton->setFixedSize(24, 24);
+
+ connect(m_stereoButton, SIGNAL(clicked()),
+ this, SLOT(slotChannelStateChanged()));
+
+ m_synthGUIButton = new QPushButton(this);
+ m_synthGUIButton->setText(i18n("Editor"));
+
+ if (haveInOut) {
+ m_audioInput = new AudioRouteMenu(this,
+ AudioRouteMenu::In,
+ AudioRouteMenu::Regular);
+ m_audioOutput = new AudioRouteMenu(this,
+ AudioRouteMenu::Out,
+ AudioRouteMenu::Regular);
+ } else {
+ m_pan->setKnobColour(GUIPalette::getColour(GUIPalette::RotaryPastelOrange));
+
+ m_audioInput = 0;
+ m_audioOutput = 0;
+ }
+
+ QToolTip::add
+ (m_pan, i18n("Set the audio pan position in the stereo field"));
+ QToolTip::add
+ (m_synthGUIButton, i18n("Open synth plugin's native editor"));
+ QToolTip::add
+ (m_stereoButton, i18n("Mono or Stereo Instrument"));
+ QToolTip::add
+ (m_recordFader, i18n("Record level"));
+ QToolTip::add
+ (m_fader, i18n("Playback level"));
+ QToolTip::add
+ (m_vuMeter, i18n("Audio level"));
+
+ QGridLayout *grid = new QGridLayout(this, 3, 6, 4, 4);
+
+ grid->addMultiCellWidget(m_synthButton, 0, 0, 0, 2);
+
+ if (haveInOut) {
+ m_inputLabel = new QLabel(i18n("In:"), this);
+ grid->addWidget(m_inputLabel, 0, 0, AlignRight);
+ grid->addMultiCellWidget(m_audioInput->getWidget(), 0, 0, 1, 2);
+ m_outputLabel = new QLabel(i18n("Out:"), this);
+ grid->addWidget(m_outputLabel, 0, 3, AlignRight);
+ grid->addMultiCellWidget(m_audioOutput->getWidget(), 0, 0, 4, 5);
+ }
+
+ grid->addMultiCellWidget(pluginVbox, 2, 2, 0, 2);
+ grid->addMultiCellWidget(faderHbox, 1, 2, 3, 5);
+
+ grid->addWidget(m_synthGUIButton, 1, 0);
+ grid->addWidget(m_pan, 1, 2);
+ grid->addWidget(m_stereoButton, 1, 1);
+
+ for (int i = 0; i < 5; ++i) {
+ // Force width
+ m_plugins[i]->setFixedWidth(m_plugins[i]->width());
+ }
+ m_synthButton->setFixedWidth(m_plugins[0]->width());
+
+ m_synthButton->hide();
+ m_synthGUIButton->hide();
+}
+
+void
+AudioFaderBox::setIsSynth(bool isSynth)
+{
+ if (isSynth) {
+ m_inputLabel->hide();
+ m_synthButton->show();
+ m_synthGUIButton->show();
+ m_audioInput->getWidget()->hide();
+ m_recordFader->hide();
+ } else {
+ m_synthButton->hide();
+ m_synthGUIButton->hide();
+ m_inputLabel->show();
+ m_audioInput->getWidget()->show();
+ m_recordFader->show();
+ }
+}
+
+void
+AudioFaderBox::slotSetInstrument(Studio *studio,
+ Instrument *instrument)
+{
+ if (m_audioInput)
+ m_audioInput->slotSetInstrument(studio, instrument);
+ if (m_audioOutput)
+ m_audioOutput->slotSetInstrument(studio, instrument);
+ if (instrument)
+ setAudioChannels(instrument->getAudioChannels());
+ if (instrument) {
+
+ RG_DEBUG << "AudioFaderBox::slotSetInstrument(" << instrument->getId() << ")" << endl;
+
+ setIsSynth(instrument->getType() == Instrument::SoftSynth);
+ if (instrument->getType() == Instrument::SoftSynth) {
+ bool gui = false;
+ RG_DEBUG << "AudioFaderBox::slotSetInstrument(" << instrument->getId() << "): is soft synth" << endl;
+#ifdef HAVE_LIBLO
+
+ gui = RosegardenGUIApp::self()->getPluginGUIManager()->hasGUI
+ (instrument->getId(), Instrument::SYNTH_PLUGIN_POSITION);
+ RG_DEBUG << "AudioFaderBox::slotSetInstrument(" << instrument->getId() << "): has gui = " << gui << endl;
+#endif
+
+ m_synthGUIButton->setEnabled(gui);
+ }
+ }
+}
+
+bool
+AudioFaderBox::owns(const QObject *object)
+{
+ return (object &&
+ ((object->parent() == this) ||
+ (object->parent() && (object->parent()->parent() == this))));
+}
+
+void
+AudioFaderBox::setAudioChannels(int channels)
+{
+ m_isStereo = (channels > 1);
+
+ switch (channels) {
+ case 1:
+ if (m_stereoButton)
+ m_stereoButton->setPixmap(m_monoPixmap);
+ m_isStereo = false;
+ break;
+
+ case 2:
+ if (m_stereoButton)
+ m_stereoButton->setPixmap(m_stereoPixmap);
+ m_isStereo = true;
+ break;
+ default:
+ RG_DEBUG << "AudioFaderBox::setAudioChannels - "
+ << "unsupported channel numbers (" << channels
+ << ")" << endl;
+ return ;
+ }
+
+ if (m_audioInput)
+ m_audioInput->slotRepopulate();
+ if (m_audioOutput)
+ m_audioOutput->slotRepopulate();
+}
+
+void
+AudioFaderBox::slotChannelStateChanged()
+{
+ if (m_isStereo) {
+ setAudioChannels(1);
+ emit audioChannelsChanged(1);
+ } else {
+ setAudioChannels(2);
+ emit audioChannelsChanged(2);
+ }
+}
+
+}
+#include "AudioFaderBox.moc"
diff --git a/src/gui/widgets/AudioFaderBox.h b/src/gui/widgets/AudioFaderBox.h
new file mode 100644
index 0000000..060fc9c
--- /dev/null
+++ b/src/gui/widgets/AudioFaderBox.h
@@ -0,0 +1,114 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_AUDIOFADERBOX_H_
+#define _RG_AUDIOFADERBOX_H_
+
+#include <qframe.h>
+#include <qpixmap.h>
+#include <qstring.h>
+#include <vector>
+
+
+class QWidget;
+class QSignalMapper;
+class QPushButton;
+class QObject;
+class QLabel;
+
+
+namespace Rosegarden
+{
+
+class Studio;
+class Rotary;
+class Instrument;
+class Fader;
+class AudioVUMeter;
+class AudioRouteMenu;
+
+
+class AudioFaderBox : public QFrame
+{
+ Q_OBJECT
+
+public:
+ AudioFaderBox(QWidget *parent,
+ QString id = "",
+ bool haveInOut = true,
+ const char *name = 0);
+
+ void setAudioChannels(int);
+
+ void setIsSynth(bool);
+
+ bool owns(const QObject *object);
+
+ QPushButton *m_synthButton;
+ std::vector<QPushButton*> m_plugins;
+
+ AudioVUMeter *m_vuMeter;
+
+ Fader *m_fader;
+ Fader *m_recordFader;
+ Rotary *m_pan;
+
+ QPixmap m_monoPixmap;
+ QPixmap m_stereoPixmap;
+
+ QSignalMapper *m_signalMapper;
+
+ QLabel *m_inputLabel;
+ QLabel *m_outputLabel;
+
+ AudioRouteMenu *m_audioInput;
+ AudioRouteMenu *m_audioOutput;
+
+ QPushButton *m_synthGUIButton;
+
+ QString m_id;
+
+ bool isStereo() const { return m_isStereo; }
+
+signals:
+ void audioChannelsChanged(int);
+
+public slots:
+ void slotSetInstrument(Studio *studio,
+ Instrument *instrument);
+
+protected slots:
+ void slotChannelStateChanged();
+
+protected:
+ QPushButton *m_stereoButton;
+ bool m_isStereo;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/AudioListItem.h b/src/gui/widgets/AudioListItem.h
new file mode 100644
index 0000000..bbd8fc2
--- /dev/null
+++ b/src/gui/widgets/AudioListItem.h
@@ -0,0 +1,97 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_AUDIOLISTITEM_H_
+#define _RG_AUDIOLISTITEM_H_
+
+#include <sound/AudioFile.h>
+#include <klistview.h>
+
+namespace Rosegarden
+{
+
+class Segment;
+
+// Add an Id to a QListViewItem
+//
+class AudioListItem : public KListViewItem
+{
+
+public:
+
+ AudioListItem(KListView *parent):KListViewItem(parent),
+ m_segment(0) {;}
+
+ AudioListItem(KListViewItem *parent):KListViewItem(parent),
+ m_segment(0) {;}
+
+ AudioListItem(KListView *parent,
+ QString label,
+ AudioFileId id):
+ KListViewItem(parent,
+ label,
+ "", "", "", "", "", "", ""),
+ m_id(id),
+ m_segment(0) {;}
+
+ AudioListItem(KListViewItem *parent,
+ QString label,
+ AudioFileId id):
+ KListViewItem(parent,
+ label,
+ "", "", "", "", "", "", ""),
+ m_id(id),
+ m_segment(0) {;}
+
+
+ AudioFileId getId() { return m_id; }
+
+ void setStartTime(const RealTime &time)
+ { m_startTime = time; }
+ RealTime getStartTime() { return m_startTime; }
+
+ void setDuration(const RealTime &time)
+ { m_duration = time; }
+ RealTime getDuration() { return m_duration; }
+
+ void setSegment(Segment *segment)
+ { m_segment = segment; }
+ Segment *getSegment() { return m_segment; }
+
+protected:
+ AudioFileId m_id;
+
+ // for audio segments
+ RealTime m_startTime;
+ RealTime m_duration;
+
+ // pointer to a segment
+ Segment *m_segment;
+
+};
+
+}
+
+
+#endif /*AUDIOLISTITEM_H_*/
diff --git a/src/gui/widgets/AudioListView.cpp b/src/gui/widgets/AudioListView.cpp
new file mode 100644
index 0000000..45193a6
--- /dev/null
+++ b/src/gui/widgets/AudioListView.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "AudioListView.h"
+
+#include "misc/Debug.h"
+#include "gui/widgets/AudioListItem.h"
+#include "qdragobject.h"
+
+namespace Rosegarden {
+
+AudioListView::AudioListView(QWidget *parent, const char *name)
+ : KListView(parent, name)
+{
+ setDragEnabled(true);
+ setAcceptDrops(true);
+ setDropVisualizer(false);
+}
+
+bool AudioListView::acceptDrag(QDropEvent* e) const
+{
+ return QUriDrag::canDecode(e) || KListView::acceptDrag(e);
+}
+
+QDragObject* AudioListView::dragObject()
+{
+ AudioListItem* item = dynamic_cast<AudioListItem*>(currentItem());
+
+ QString audioData;
+ QTextOStream ts(&audioData);
+ ts << "AudioFileManager\n"
+ << item->getId() << '\n'
+ << item->getStartTime().sec << '\n'
+ << item->getStartTime().nsec << '\n'
+ << item->getDuration().sec << '\n'
+ << item->getDuration().nsec << '\n';
+
+ RG_DEBUG << "AudioListView::dragObject - "
+ << "file id = " << item->getId()
+ << ", start time = " << item->getStartTime() << endl;
+
+ return new QTextDrag(audioData, this);
+}
+
+}
diff --git a/src/gui/widgets/AudioListView.h b/src/gui/widgets/AudioListView.h
new file mode 100644
index 0000000..477054a
--- /dev/null
+++ b/src/gui/widgets/AudioListView.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_AUDIOLISTVIEW_H_
+#define _RG_AUDIOLISTVIEW_H_
+
+#include <klistview.h>
+
+namespace Rosegarden {
+
+class AudioListView : public KListView
+{
+public:
+ AudioListView(QWidget *parent = 0, const char *name = 0);
+
+protected:
+ bool acceptDrag(QDropEvent* e) const;
+ virtual QDragObject* dragObject();
+};
+
+}
+
+#endif /*AUDIOLISTVIEW_H_*/
diff --git a/src/gui/widgets/AudioRouteMenu.cpp b/src/gui/widgets/AudioRouteMenu.cpp
new file mode 100644
index 0000000..6f4c93f
--- /dev/null
+++ b/src/gui/widgets/AudioRouteMenu.cpp
@@ -0,0 +1,381 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "AudioRouteMenu.h"
+#include "WheelyButton.h"
+
+#include "base/Instrument.h"
+#include "base/Studio.h"
+#include "gui/studio/StudioControl.h"
+#include "gui/widgets/RosegardenPopupMenu.h"
+#include "sound/MappedCommon.h"
+#include "sound/MappedStudio.h"
+#include <kcombobox.h>
+#include <klocale.h>
+#include <qcursor.h>
+#include <qobject.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+AudioRouteMenu::AudioRouteMenu(QWidget *par,
+ Direction direction,
+ Format format,
+ Studio *studio,
+ Instrument *instrument) :
+ QObject(par),
+ m_studio(studio),
+ m_instrument(instrument),
+ m_direction(direction),
+ m_format(format)
+{
+ switch (format) {
+
+ case Compact: {
+ m_combo = 0;
+ m_button = new WheelyButton(par);
+ connect(m_button, SIGNAL(wheel(bool)), this, SLOT(slotWheel(bool)));
+ connect(m_button, SIGNAL(clicked()), this, SLOT(slotShowMenu()));
+ break;
+ }
+
+ case Regular: {
+ m_button = 0;
+ m_combo = new KComboBox(par);
+ connect(m_combo, SIGNAL(activated(int)), this, SLOT(slotEntrySelected(int)));
+ break;
+ }
+
+ }
+
+ slotRepopulate();
+}
+
+QWidget *
+AudioRouteMenu::getWidget()
+{
+ if (m_button)
+ return m_button;
+ else
+ return m_combo;
+}
+
+void
+AudioRouteMenu::slotRepopulate()
+{
+ switch (m_format) {
+
+ case Compact:
+ m_button->setText(getEntryText(getCurrentEntry()));
+ break;
+
+ case Regular:
+ m_combo->clear();
+ for (int i = 0; i < getNumEntries(); ++i) {
+ m_combo->insertItem(getEntryText(i));
+ }
+ m_combo->setCurrentItem(getCurrentEntry());
+ break;
+ }
+}
+
+void
+AudioRouteMenu::slotSetInstrument(Studio *studio,
+ Instrument *instrument)
+{
+ m_studio = studio;
+ m_instrument = instrument;
+ slotRepopulate();
+}
+
+void
+AudioRouteMenu::slotWheel(bool up)
+{
+ int current = getCurrentEntry();
+ if (up) { // actually moves down the list
+ if (current > 0)
+ slotEntrySelected(current - 1);
+ } else {
+ if (current < getNumEntries() - 1)
+ slotEntrySelected(current + 1);
+ }
+}
+
+void
+AudioRouteMenu::slotShowMenu()
+{
+ if (getNumEntries() == 0)
+ return ;
+
+ RosegardenPopupMenu *menu = new RosegardenPopupMenu((QWidget *)parent());
+
+ for (int i = 0; i < getNumEntries(); ++i) {
+
+ menu->insertItem(getEntryText(i), this, SLOT(slotEntrySelected(int)),
+ 0, i);
+ menu->setItemParameter(i, i);
+ }
+
+ int itemHeight = menu->itemHeight(0) + 2;
+ QPoint pos = QCursor::pos();
+
+ pos.rx() -= 10;
+ pos.ry() -= (itemHeight / 2 + getCurrentEntry() * itemHeight);
+
+ menu->popup(pos);
+}
+
+int
+AudioRouteMenu::getNumEntries()
+{
+ if (!m_instrument)
+ return 0;
+
+ switch (m_direction) {
+
+ case In: {
+ int stereoIns =
+ m_studio->getRecordIns().size() +
+ m_studio->getBusses().size();
+
+ if (m_instrument->getAudioChannels() > 1) {
+ return stereoIns;
+ } else {
+ return stereoIns * 2;
+ }
+
+ break;
+ }
+
+ case Out:
+ return m_studio->getBusses().size();
+ }
+
+ return 0;
+}
+
+int
+AudioRouteMenu::getCurrentEntry()
+{
+ if (!m_instrument)
+ return 0;
+
+ switch (m_direction) {
+
+ case In: {
+ bool stereo = (m_instrument->getAudioChannels() > 1);
+
+ bool isBuss;
+ int channel;
+ int input = m_instrument->getAudioInput(isBuss, channel);
+
+ if (isBuss) {
+ int recordIns = m_studio->getRecordIns().size();
+ if (stereo) {
+ return recordIns + input;
+ } else {
+ return recordIns * 2 + input * 2 + channel;
+ }
+ } else {
+ if (stereo) {
+ return input;
+ } else {
+ return input * 2 + channel;
+ }
+ }
+
+ break;
+ }
+
+ case Out:
+ return m_instrument->getAudioOutput();
+ }
+
+ return 0;
+}
+
+QString
+AudioRouteMenu::getEntryText(int entry)
+{
+ switch (m_direction) {
+
+ case In: {
+ bool stereo = (m_instrument->getAudioChannels() > 1);
+ int recordIns = m_studio->getRecordIns().size();
+
+ if (stereo) {
+ if (entry < recordIns) {
+ return i18n("In %1").arg(entry + 1);
+ } else if (entry == recordIns) {
+ return i18n("Master");
+ } else {
+ return i18n("Sub %1").arg(entry - recordIns);
+ }
+ } else {
+ int channel = entry % 2;
+ entry /= 2;
+ if (entry < recordIns) {
+ return (channel ? i18n("In %1 R") :
+ i18n("In %1 L")).arg(entry + 1);
+ } else if (entry == recordIns) {
+ return (channel ? i18n("Master R") :
+ i18n("Master L"));
+ } else {
+ return (channel ? i18n("Sub %1 R") :
+ i18n("Sub %1 L")).arg(entry - recordIns);
+ }
+ }
+ break;
+ }
+
+ case Out:
+ if (entry == 0)
+ return i18n("Master");
+ else
+ return i18n("Sub %1").arg(entry);
+ }
+
+ return QString();
+}
+
+void
+AudioRouteMenu::slotEntrySelected(int i)
+{
+ switch (m_direction) {
+
+ case In: {
+ bool stereo = (m_instrument->getAudioChannels() > 1);
+
+ bool oldIsBuss;
+ int oldChannel;
+ int oldInput = m_instrument->getAudioInput(oldIsBuss, oldChannel);
+
+ bool newIsBuss;
+ int newChannel = 0;
+ int newInput;
+
+ int recordIns = m_studio->getRecordIns().size();
+
+ if (stereo) {
+ newIsBuss = (i >= recordIns);
+ if (newIsBuss) {
+ newInput = i - recordIns;
+ } else {
+ newInput = i;
+ }
+ } else {
+ newIsBuss = (i >= recordIns * 2);
+ newChannel = i % 2;
+ if (newIsBuss) {
+ newInput = i / 2 - recordIns;
+ } else {
+ newInput = i / 2;
+ }
+ }
+
+ MappedObjectId oldMappedId = 0, newMappedId = 0;
+
+ if (oldIsBuss) {
+ Buss *buss = m_studio->getBussById(oldInput);
+ if (buss)
+ oldMappedId = buss->getMappedId();
+ } else {
+ RecordIn *in = m_studio->getRecordIn(oldInput);
+ if (in)
+ oldMappedId = in->getMappedId();
+ }
+
+ if (newIsBuss) {
+ Buss *buss = m_studio->getBussById(newInput);
+ if (!buss)
+ return ;
+ newMappedId = buss->getMappedId();
+ } else {
+ RecordIn *in = m_studio->getRecordIn(newInput);
+ if (!in)
+ return ;
+ newMappedId = in->getMappedId();
+ }
+
+ if (oldMappedId != 0) {
+ StudioControl::disconnectStudioObjects
+ (oldMappedId, m_instrument->getMappedId());
+ } else {
+ StudioControl::disconnectStudioObject
+ (m_instrument->getMappedId());
+ }
+
+ StudioControl::setStudioObjectProperty
+ (m_instrument->getMappedId(),
+ MappedAudioFader::InputChannel,
+ MappedObjectValue(newChannel));
+
+ if (newMappedId != 0) {
+ StudioControl::connectStudioObjects
+ (newMappedId, m_instrument->getMappedId());
+ }
+
+ if (newIsBuss) {
+ m_instrument->setAudioInputToBuss(newInput, newChannel);
+ } else {
+ m_instrument->setAudioInputToRecord(newInput, newChannel);
+ }
+
+ break;
+ }
+
+ case Out: {
+ BussId bussId = m_instrument->getAudioOutput();
+ Buss *oldBuss = m_studio->getBussById(bussId);
+ Buss *newBuss = m_studio->getBussById(i);
+ if (!newBuss)
+ return ;
+
+ if (oldBuss) {
+ StudioControl::disconnectStudioObjects
+ (m_instrument->getMappedId(), oldBuss->getMappedId());
+ } else {
+ StudioControl::disconnectStudioObject
+ (m_instrument->getMappedId());
+ }
+
+ StudioControl::connectStudioObjects
+ (m_instrument->getMappedId(), newBuss->getMappedId());
+
+ m_instrument->setAudioOutput(i);
+ break;
+ }
+ }
+
+ slotRepopulate();
+ emit changed();
+}
+
+}
+#include "AudioRouteMenu.moc"
diff --git a/src/gui/widgets/AudioRouteMenu.h b/src/gui/widgets/AudioRouteMenu.h
new file mode 100644
index 0000000..259da68
--- /dev/null
+++ b/src/gui/widgets/AudioRouteMenu.h
@@ -0,0 +1,94 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_AUDIOROUTEMENU_H_
+#define _RG_AUDIOROUTEMENU_H_
+
+#include <qobject.h>
+#include <qstring.h>
+
+
+class QWidget;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class WheelyButton;
+class Studio;
+class Instrument;
+
+
+class AudioRouteMenu : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum Direction { In, Out };
+ enum Format { Compact, Regular };
+
+ AudioRouteMenu(QWidget *parent,
+ Direction direction,
+ Format format,
+ Studio *studio = 0,
+ Instrument *instrument = 0);
+
+ QWidget *getWidget();
+
+public slots:
+ void slotRepopulate();
+ void slotSetInstrument(Studio *, Instrument *);
+
+protected slots:
+ void slotWheel(bool up);
+ void slotShowMenu();
+ void slotEntrySelected(int);
+
+signals:
+ // The menu writes changes directly to the instrument, but it
+ // also emits this to let you know something has changed
+ void changed();
+
+private:
+ Studio *m_studio;
+ Instrument *m_instrument;
+ Direction m_direction;
+ Format m_format;
+
+ WheelyButton *m_button;
+ KComboBox *m_combo;
+
+ int getNumEntries();
+ int getCurrentEntry(); // for instrument
+ QString getEntryText(int n);
+};
+
+
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/AudioVUMeter.cpp b/src/gui/widgets/AudioVUMeter.cpp
new file mode 100644
index 0000000..4c15a07
--- /dev/null
+++ b/src/gui/widgets/AudioVUMeter.cpp
@@ -0,0 +1,103 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "AudioVUMeter.h"
+
+#include "gui/rulers/VelocityColour.h"
+#include <qcolor.h>
+#include <qlabel.h>
+#include <qpainter.h>
+#include <qtimer.h>
+#include <qwidget.h>
+#include "VUMeter.h"
+
+
+namespace Rosegarden
+{
+
+AudioVUMeter::AudioVUMeter(QWidget *parent,
+ VUMeter::VUMeterType type,
+ bool stereo,
+ bool hasRecord,
+ int width,
+ int height,
+ const char *name) :
+ QWidget(parent, name),
+ m_stereo(stereo)
+{
+ setBackgroundMode(Qt::NoBackground);
+ setFixedSize(width, height);
+
+ // This offset is intended to match that for the height of the
+ // button pixmap in Fader (in studiowidgets.cpp, which
+ // is probably where this class should be too)
+
+ m_yoff = height / 7;
+ m_yoff /= 10;
+ ++m_yoff;
+ m_yoff *= 10;
+ ++m_yoff;
+
+ // This one is _not_ intended to match that for the button width
+
+ m_xoff = width / 4;
+ if (m_xoff % 2 == 1)
+ ++m_xoff;
+
+ m_meter = new AudioVUMeterImpl(this, type, stereo, hasRecord,
+ width - m_xoff, height - m_yoff, name);
+
+ m_meter->move(m_xoff / 2, m_yoff / 2);
+}
+
+void
+AudioVUMeter::paintEvent(QPaintEvent *e)
+{
+ QPainter paint(this);
+ paint.setPen(colorGroup().mid());
+ paint.drawRect(0, 0, width(), height());
+
+ paint.setPen(colorGroup().background());
+ paint.setBrush(colorGroup().background());
+ paint.drawRect(1, 1, width() - 2, m_yoff / 2 - 1);
+ paint.drawRect(1, 1, m_xoff / 2 - 1, height() - 2);
+ paint.drawRect(width() - m_xoff / 2 - 1, 1, m_xoff / 2, height() - 2);
+ paint.drawRect(1, height() - m_yoff / 2 - 1, width() - 2, m_yoff / 2);
+ paint.end();
+
+ m_meter->paintEvent(e);
+}
+
+AudioVUMeter::AudioVUMeterImpl::AudioVUMeterImpl(QWidget *parent,
+ VUMeterType type,
+ bool stereo,
+ bool hasRecord,
+ int width,
+ int height,
+ const char *name) :
+ VUMeter(parent, type, stereo, hasRecord, width, height, VUMeter::Vertical, name)
+{}
+
+}
diff --git a/src/gui/widgets/AudioVUMeter.h b/src/gui/widgets/AudioVUMeter.h
new file mode 100644
index 0000000..cff7c27
--- /dev/null
+++ b/src/gui/widgets/AudioVUMeter.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_AUDIOVUMETER_H_
+#define _RG_AUDIOVUMETER_H_
+
+#include <qwidget.h>
+#include "VUMeter.h"
+
+
+class QPaintEvent;
+
+
+namespace Rosegarden
+{
+
+
+
+class AudioVUMeter : public QWidget
+{
+public:
+ AudioVUMeter(QWidget *parent = 0,
+ VUMeter::VUMeterType type = VUMeter::AudioPeakHoldShort,
+ bool stereo = true,
+ bool hasRecord = false,
+ int width = 12,
+ int height = 140,
+ const char *name = 0);
+
+ void setLevel(double dB) {
+ m_meter->setLevel(dB);
+ }
+ void setLevel(double dBleft, double dBright) {
+ m_meter->setLevel(dBleft, dBright);
+ }
+
+ void setRecordLevel(double dB) {
+ m_meter->setRecordLevel(dB);
+ }
+ void setRecordLevel(double dBleft, double dBright) {
+ m_meter->setRecordLevel(dBleft, dBright);
+ }
+
+ virtual void paintEvent(QPaintEvent*);
+
+protected:
+ class AudioVUMeterImpl : public VUMeter
+ {
+ public:
+ AudioVUMeterImpl(QWidget *parent,
+ VUMeterType type,
+ bool stereo,
+ bool hasRecord,
+ int width,
+ int height,
+ const char *name);
+ protected:
+ virtual void meterStart() { }
+ virtual void meterStop() { }
+ };
+
+ AudioVUMeterImpl *m_meter;
+ bool m_stereo;
+ int m_yoff;
+ int m_xoff;
+};
+
+
+// A push button that emits wheel events. Used by AudioRouteMenu.
+//
+
+}
+
+#endif
diff --git a/src/gui/widgets/BigArrowButton.h b/src/gui/widgets/BigArrowButton.h
new file mode 100644
index 0000000..505e374
--- /dev/null
+++ b/src/gui/widgets/BigArrowButton.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_BIGARROWBUTTON_H_
+#define _RG_BIGARROWBUTTON_H_
+
+#include <karrowbutton.h>
+
+namespace Rosegarden {
+
+class BigArrowButton : public KArrowButton
+{
+public:
+ BigArrowButton(QWidget *parent = 0, Qt::ArrowType arrow = Qt::UpArrow,
+ const char *name = 0) :
+ KArrowButton(parent, arrow, name) { }
+ virtual ~BigArrowButton() { }
+
+ virtual QSize sizeHint() const {
+ return QSize(20, 20);
+ }
+};
+
+}
+
+#endif /*BIGARROWBUTTON_H_*/
diff --git a/src/gui/widgets/CollapsingFrame.cpp b/src/gui/widgets/CollapsingFrame.cpp
new file mode 100644
index 0000000..1f71ebf
--- /dev/null
+++ b/src/gui/widgets/CollapsingFrame.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "CollapsingFrame.h"
+#include <kapplication.h>
+#include <kstddirs.h>
+#include <kconfig.h>
+#include <kglobal.h>
+#include <qfont.h>
+#include <qframe.h>
+#include <qlayout.h>
+#include <qpixmap.h>
+#include <qstring.h>
+#include <qtoolbutton.h>
+#include <qwidget.h>
+#include <cassert>
+
+
+namespace Rosegarden
+{
+
+CollapsingFrame::CollapsingFrame(QString label, QWidget *parent, const char *n) :
+ QFrame(parent, n),
+ m_widget(0),
+ m_fill(false),
+ m_collapsed(false)
+{
+ m_layout = new QGridLayout(this, 3, 3, 0, 0);
+
+ m_toggleButton = new QToolButton(this);
+ m_toggleButton->setTextLabel(label);
+ m_toggleButton->setUsesTextLabel(true);
+ m_toggleButton->setUsesBigPixmap(false);
+ m_toggleButton->setTextPosition(QToolButton::BesideIcon);
+ m_toggleButton->setAutoRaise(true);
+
+ QFont font(m_toggleButton->font());
+ font.setBold(true);
+ m_toggleButton->setFont(font);
+
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QPixmap pixmap(pixmapDir + "/misc/arrow-expanded.png");
+ m_toggleButton->setIconSet(pixmap);
+
+ connect(m_toggleButton, SIGNAL(clicked()), this, SLOT(toggle()));
+
+ m_layout->addMultiCellWidget(m_toggleButton, 0, 0, 0, 2);
+}
+
+CollapsingFrame::~CollapsingFrame()
+{}
+
+void
+CollapsingFrame::setWidgetFill(bool fill)
+{
+ m_fill = fill;
+}
+
+QFont
+CollapsingFrame::font() const
+{
+ return m_toggleButton->font();
+}
+
+void
+CollapsingFrame::setFont(QFont font)
+{
+ m_toggleButton->setFont(font);
+}
+
+void
+CollapsingFrame::setWidget(QWidget *widget)
+{
+ assert(!m_widget);
+ m_widget = widget;
+ if (m_fill) {
+ m_layout->addMultiCellWidget(widget, 1, 1, 0, 2);
+ } else {
+ m_layout->addWidget(widget, 1, 1);
+ }
+
+ bool expanded = true;
+ if (name(0)) {
+ KConfig *config = kapp->config();
+ QString groupTemp = config->group();
+ config->setGroup("CollapsingFrame");
+ expanded = config->readBoolEntry(name(), true);
+ config->setGroup(groupTemp);
+ }
+ if (expanded != !m_collapsed)
+ toggle();
+}
+
+void
+CollapsingFrame::toggle()
+{
+ int h = m_toggleButton->height();
+
+ m_collapsed = !m_collapsed;
+
+ m_widget->setShown(!m_collapsed);
+
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QPixmap pixmap;
+
+ if (m_collapsed) {
+ pixmap = QPixmap(pixmapDir + "/misc/arrow-contracted.png");
+ } else {
+ pixmap = QPixmap(pixmapDir + "/misc/arrow-expanded.png");
+ }
+
+ if (name(0)) {
+ KConfig *config = kapp->config();
+ QString groupTemp = config->group();
+ config->setGroup("CollapsingFrame");
+ config->writeEntry(name(), !m_collapsed);
+ config->setGroup(groupTemp);
+ }
+
+ m_toggleButton->setIconSet(pixmap);
+
+ m_toggleButton->setMaximumHeight(h);
+}
+
+}
+#include "CollapsingFrame.moc"
diff --git a/src/gui/widgets/CollapsingFrame.h b/src/gui/widgets/CollapsingFrame.h
new file mode 100644
index 0000000..780a8b1
--- /dev/null
+++ b/src/gui/widgets/CollapsingFrame.h
@@ -0,0 +1,75 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_COLLAPSINGFRAME_H_
+#define _RG_COLLAPSINGFRAME_H_
+
+#include <qframe.h>
+#include <qstring.h>
+
+
+class QWidget;
+class QToolButton;
+class QGridLayout;
+
+
+namespace Rosegarden
+{
+
+
+
+class CollapsingFrame : public QFrame
+{
+ Q_OBJECT
+
+public:
+ CollapsingFrame(QString label, QWidget *parent = 0, const char *name = 0);
+ virtual ~CollapsingFrame();
+
+ QFont font() const;
+ void setFont(QFont font);
+
+ /// If true, the widget fills the available space. Call before setWidget
+ void setWidgetFill(bool fill);
+
+ /// This frame contains a single other widget. Set it here.
+ void setWidget(QWidget *w);
+
+public slots:
+ void toggle();
+
+protected:
+ QGridLayout *m_layout;
+ QToolButton *m_toggleButton;
+ QWidget *m_widget;
+ bool m_fill;
+ bool m_collapsed;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/ColourTable.cpp b/src/gui/widgets/ColourTable.cpp
new file mode 100644
index 0000000..c5fcfc6
--- /dev/null
+++ b/src/gui/widgets/ColourTable.cpp
@@ -0,0 +1,131 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ColourTable.h"
+
+#include <klocale.h>
+#include "misc/Strings.h"
+#include "base/ColourMap.h"
+#include "ColourTableItem.h"
+#include "gui/general/GUIPalette.h"
+#include <kcolordialog.h>
+#include <klineeditdlg.h>
+#include <qcolor.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include <qtable.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+ColourTable::ColourTable
+(QWidget *parent, ColourMap &input, ColourList &list)
+ : QTable(1, 2, parent, "RColourTable")
+{
+ setSorting(FALSE);
+ setSelectionMode(QTable::SingleRow);
+ horizontalHeader()->setLabel(0, i18n("Name"));
+ horizontalHeader()->setLabel(1, i18n("Color"));
+ populate_table(input, list);
+ connect(this, SIGNAL(doubleClicked(int, int, int, const QPoint&)),
+ SLOT(slotEditEntry(int, int)));
+
+}
+
+void
+ColourTable::slotEditEntry(int row, int col)
+{
+
+ switch (col) {
+ case 0: {
+ if (row == 0)
+ return ;
+ bool ok = false;
+ QString newName = KLineEditDlg::getText(i18n("Modify Color Name"), i18n("Enter new name"),
+ item(row, col)->text(), &ok);
+
+ if ((ok == true) && (!newName.isEmpty())) {
+ emit entryTextChanged(row, newName);
+ return ;
+ }
+ }
+ break;
+ case 1: {
+ QColor temp = m_colours[row];
+ KColorDialog box(this, "", true);
+
+ int result = box.getColor(temp);
+
+ if (result == KColorDialog::Accepted) {
+ emit entryColourChanged(row, temp);
+ return ;
+ }
+ }
+ break;
+ default: // Should never happen
+ break;
+ }
+
+}
+
+void
+ColourTable::populate_table(ColourMap &input, ColourList &list)
+{
+ m_colours.reserve(input.size());
+ setNumRows(input.size());
+
+ QString name;
+
+ unsigned int i = 0;
+
+ for (RCMap::const_iterator it = input.begin(); it != input.end(); ++it) {
+ if (it->second.second == std::string(""))
+ name = i18n("Default Color");
+ else
+ name = strtoqstr(it->second.second);
+
+ QTableItem *text = new QTableItem(
+ dynamic_cast<QTable*>(this),
+ QTableItem::Never, name);
+
+ setItem(i, 0, text);
+
+ list[i] = it->first;
+ m_colours[i] = GUIPalette::convertColour(it->second.first);
+
+ ColourTableItem *temp = new ColourTableItem(this, m_colours[i]);
+ setItem(i, 1, temp);
+
+ verticalHeader()->setLabel(i, QString::number(it->first));
+
+ ++i;
+ }
+
+}
+
+}
+#include "ColourTable.moc"
diff --git a/src/gui/widgets/ColourTable.h b/src/gui/widgets/ColourTable.h
new file mode 100644
index 0000000..48e2309
--- /dev/null
+++ b/src/gui/widgets/ColourTable.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ Portions of this file Copyright 2003
+ Mark Hymers <markh@linuxfromscratch.org>
+
+ 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_ROSEGARDENCOLOURTABLE_H_
+#define _RG_ROSEGARDENCOLOURTABLE_H_
+
+#include <map>
+#include <qtable.h>
+#include <vector>
+
+
+class QWidget;
+class ColourList;
+
+
+namespace Rosegarden
+{
+
+class ColourMap;
+
+
+class ColourTable : public QTable
+{
+ Q_OBJECT
+
+public:
+ typedef std::map<unsigned int, unsigned int, std::less<unsigned int> > ColourList;
+ ColourTable(QWidget *parent, ColourMap &input, ColourList &list);
+ void populate_table(ColourMap &input, ColourList &list);
+
+
+signals:
+ void entryTextChanged(unsigned int, QString);
+ void entryColourChanged(unsigned int, QColor);
+
+public slots:
+ void slotEditEntry (int, int);
+
+protected:
+ std::vector<QColor> m_colours;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/ColourTableItem.cpp b/src/gui/widgets/ColourTableItem.cpp
new file mode 100644
index 0000000..3dfbd87
--- /dev/null
+++ b/src/gui/widgets/ColourTableItem.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ColourTableItem.h"
+
+#include <qcolor.h>
+#include <qpainter.h>
+#include <qrect.h>
+#include <qtable.h>
+
+
+namespace Rosegarden
+{
+
+void
+ColourTableItem::setColor(QColor &input)
+{
+ currentColour = input;
+}
+
+void
+ColourTableItem::paint(QPainter *p, const QColorGroup &cg, const QRect &cr, bool selected)
+{
+ QColorGroup g(cg);
+ g.setColor(QColorGroup::Base, currentColour);
+ selected = false;
+ QTableItem::paint(p, g, cr, selected);
+}
+
+}
diff --git a/src/gui/widgets/ColourTableItem.h b/src/gui/widgets/ColourTableItem.h
new file mode 100644
index 0000000..a8c906e
--- /dev/null
+++ b/src/gui/widgets/ColourTableItem.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ROSEGARDENCOLOURTABLEITEM_H_
+#define _RG_ROSEGARDENCOLOURTABLEITEM_H_
+
+#include <qcolor.h>
+#include <qtable.h>
+
+class QTable;
+class QRect;
+class QPainter;
+class QColorGroup;
+
+
+namespace Rosegarden
+{
+
+
+
+class ColourTableItem : public QTableItem
+{
+public:
+ ColourTableItem(QTable *t, const QColor &input)
+ : QTableItem(t, QTableItem::Never, ""),
+ currentColour(input) {}
+ void setColor(QColor &input);
+ void paint(QPainter *p, const QColorGroup &cg, const QRect &cr, bool selected);
+
+protected:
+ QColor currentColour;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/CurrentProgressDialog.cpp b/src/gui/widgets/CurrentProgressDialog.cpp
new file mode 100644
index 0000000..2e3735f
--- /dev/null
+++ b/src/gui/widgets/CurrentProgressDialog.cpp
@@ -0,0 +1,84 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "CurrentProgressDialog.h"
+
+#include "ProgressDialog.h"
+#include <qobject.h>
+
+
+namespace Rosegarden
+{
+
+CurrentProgressDialog* CurrentProgressDialog::getInstance()
+{
+ if (!m_instance)
+ m_instance = new CurrentProgressDialog(0);
+
+ return m_instance;
+}
+
+
+ProgressDialog*
+CurrentProgressDialog::get()
+{
+ return m_currentProgressDialog;
+}
+
+void
+CurrentProgressDialog::set(ProgressDialog* d)
+{
+ if (m_currentProgressDialog)
+ m_currentProgressDialog->disconnect(getInstance());
+
+ m_currentProgressDialog = d;
+
+ // this lets the progress dialog deregister itself when it's deleted
+ connect(d, SIGNAL(destroyed()),
+ getInstance(), SLOT(slotCurrentProgressDialogDestroyed()));
+}
+
+void CurrentProgressDialog::freeze()
+{
+ if (m_currentProgressDialog)
+ m_currentProgressDialog->slotFreeze();
+}
+
+void CurrentProgressDialog::thaw()
+{
+ if (m_currentProgressDialog)
+ m_currentProgressDialog->slotThaw();
+}
+
+void CurrentProgressDialog::slotCurrentProgressDialogDestroyed()
+{
+ m_currentProgressDialog = 0;
+}
+
+CurrentProgressDialog* CurrentProgressDialog::m_instance = 0;
+ProgressDialog* CurrentProgressDialog::m_currentProgressDialog = 0;
+
+}
+#include "CurrentProgressDialog.moc"
diff --git a/src/gui/widgets/CurrentProgressDialog.h b/src/gui/widgets/CurrentProgressDialog.h
new file mode 100644
index 0000000..d0eea2e
--- /dev/null
+++ b/src/gui/widgets/CurrentProgressDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_CURRENTPROGRESSDIALOG_H_
+#define _RG_CURRENTPROGRESSDIALOG_H_
+
+#include <qobject.h>
+
+
+
+
+namespace Rosegarden
+{
+
+class ProgressDialog;
+
+
+class CurrentProgressDialog : public QObject
+{
+ Q_OBJECT
+public:
+ static CurrentProgressDialog* getInstance();
+
+ static ProgressDialog* get();
+ static void set(ProgressDialog*);
+
+ /**
+ * Block the current progress so that it won't appear
+ * regardless of passing time and occurring events.
+ * This is useful when you want to show another dialog
+ * and you want to make sure the progress dialog is out of the way
+ */
+ static void freeze();
+
+ /**
+ * Restores the progress dialog to its normal state atfer a freeze()
+ */
+ static void thaw();
+
+public slots:
+ /// Called then the current progress dialog is being destroyed
+ void slotCurrentProgressDialogDestroyed();
+
+protected:
+ CurrentProgressDialog(QObject* parent, const char* name = 0)
+ : QObject(parent, name) {}
+
+ //--------------- Data members ---------------------------------
+ static CurrentProgressDialog* m_instance;
+ static ProgressDialog* m_currentProgressDialog;
+};
+
+
+// A Text popup - a tooltip we can control.
+//
+
+}
+
+#endif
diff --git a/src/gui/widgets/DiatonicPitchChooser.cpp b/src/gui/widgets/DiatonicPitchChooser.cpp
new file mode 100644
index 0000000..95b8b3a
--- /dev/null
+++ b/src/gui/widgets/DiatonicPitchChooser.cpp
@@ -0,0 +1,244 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "DiatonicPitchChooser.h"
+
+#include <iostream>
+#include <klocale.h>
+#include "base/NotationRules.h"
+#include "base/NotationTypes.h"
+#include "gui/general/MidiPitchLabel.h"
+#include "PitchDragLabel.h"
+#include <kcombobox.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qspinbox.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+namespace Rosegarden
+{
+
+DiatonicPitchChooser::DiatonicPitchChooser(QString title,
+ QWidget *parent,
+ int defaultNote,
+ int defaultPitch,
+ int defaultOctave) :
+ QGroupBox(1, Horizontal, title, parent),
+ m_defaultPitch(defaultPitch)
+{
+ m_pitchDragLabel = new PitchDragLabel(this, defaultPitch);
+
+ QHBox *hbox = new QHBox(this);
+ hbox->setSpacing(6);
+
+ m_step = new KComboBox( hbox );
+ m_step->setSizeLimit( 7 );
+ m_step->insertItem(i18n("C"));
+ m_step->insertItem(i18n("D"));
+ m_step->insertItem(i18n("E"));
+ m_step->insertItem(i18n("F"));
+ m_step->insertItem(i18n("G"));
+ m_step->insertItem(i18n("A"));
+ m_step->insertItem(i18n("B"));
+ m_step->setCurrentItem(defaultNote);
+
+ m_octave = new KComboBox( hbox );
+ m_octave->insertItem(i18n("-2"));
+ m_octave->insertItem(i18n("-1"));
+ m_octave->insertItem(i18n("0"));
+ m_octave->insertItem(i18n("1"));
+ m_octave->insertItem(i18n("2"));
+ m_octave->insertItem(i18n("3"));
+ m_octave->insertItem(i18n("4"));
+ m_octave->insertItem(i18n("5"));
+ m_octave->insertItem(i18n("6"));
+ m_octave->insertItem(i18n("7"));
+ m_octave->setCurrentItem(defaultOctave);
+
+ m_accidental = new KComboBox( hbox );
+ m_accidental->insertItem(i18n("double flat"));
+ m_accidental->insertItem(i18n("flat"));
+ m_accidental->insertItem(i18n("natural"));
+ m_accidental->insertItem(i18n("sharp"));
+ m_accidental->insertItem(i18n("double sharp"));
+ m_accidental->setCurrentItem(2); // default: natural
+
+ m_pitchLabel = new QLabel(QString("%1").arg(getPitch()), hbox);
+
+ m_pitchLabel->setMinimumWidth(40);
+
+ connect(m_accidental, SIGNAL(activated(int)),
+ this, SLOT(slotSetAccidental(int)));
+
+ connect(m_octave, SIGNAL(activated(int)),
+ this, SLOT(slotSetOctave(int)));
+
+ connect(m_step, SIGNAL(activated(int)),
+ this, SLOT(slotSetStep(int)));
+
+ //connect(m_pitch, SIGNAL(valueChanged(int)),
+ // this, SIGNAL(pitchChanged(int)));
+
+ //connect(m_pitch, SIGNAL(valueChanged(int)),
+ // this, SIGNAL(preview(int)));
+
+ connect(m_pitchDragLabel, SIGNAL(pitchDragged(int,int,int)),
+ this, SLOT(slotSetNote(int,int,int)));
+
+ //connect(m_pitchDragLabel, SIGNAL(pitchChanged(int)),
+ // this, SLOT(slotSetPitch(int)));
+
+ connect(m_pitchDragLabel, SIGNAL(pitchChanged(int,int,int)),
+ this, SLOT(slotSetNote(int,int,int)));
+
+ //connect(m_pitchDragLabel, SIGNAL(pitchChanged(int)),
+ // this, SIGNAL(pitchChanged(int)));
+
+ connect(m_pitchDragLabel, SIGNAL(pitchDragged(int,int,int)),
+ this, SIGNAL(noteChanged(int,int,int)));
+
+ connect(m_pitchDragLabel, SIGNAL(pitchChanged(int,int,int)),
+ this, SIGNAL(noteChanged(int,int,int)));
+
+ connect(m_pitchDragLabel, SIGNAL(preview(int)),
+ this, SIGNAL(preview(int)));
+
+}
+
+int
+DiatonicPitchChooser::getPitch() const
+{
+ return 12 * m_octave->currentItem() + scale_Cmajor[m_step->currentItem()] +
+ (m_accidental->currentItem() - 2);
+}
+
+int
+DiatonicPitchChooser::getAccidental()
+{
+ return m_accidental->currentItem() - 2;
+}
+
+void
+DiatonicPitchChooser::slotSetPitch(int pitch)
+{
+ if (m_pitchDragLabel->getPitch() != pitch)
+ m_pitchDragLabel->slotSetPitch(pitch);
+
+ m_octave->setCurrentItem((int)(((long) pitch) / 12));
+
+ int step = steps_Cmajor[pitch % 12];
+ m_step->setCurrentItem(step);
+
+ int pitchChange = (pitch % 12) - scale_Cmajor[step];
+
+ m_accidental->setCurrentItem(pitchChange + 2);
+
+ m_pitchLabel->setText(QString("%1").arg(pitch));
+
+ update();
+}
+
+void
+DiatonicPitchChooser::slotSetStep(int step)
+{
+ if (m_step->currentItem() != step)
+ m_step->setCurrentItem(step);
+ std::cout << "slot_step called" << std::endl;
+ setLabelsIfNeeded();
+ update();
+}
+
+void
+DiatonicPitchChooser::slotSetOctave(int octave)
+{
+ if (m_octave->currentItem() != octave)
+ m_octave->setCurrentItem(octave);
+ setLabelsIfNeeded();
+ update();
+}
+
+/** input 0..4: doubleflat .. doublesharp */
+void
+DiatonicPitchChooser::slotSetAccidental(int accidental)
+{
+ if (m_accidental->currentItem() != accidental)
+ m_accidental->setCurrentItem(accidental);
+ setLabelsIfNeeded();
+ update();
+}
+
+/** sets the m_pitchDragLabel and m_pitchLabel if needed */
+void
+DiatonicPitchChooser::setLabelsIfNeeded()
+{
+ //if (m_pitchDragLabel->)
+ //{
+ m_pitchDragLabel->slotSetPitch(getPitch(), m_octave->currentItem(), m_step->currentItem());
+ //}
+ m_pitchLabel->setText(QString("%1").arg(getPitch()));
+}
+
+void
+DiatonicPitchChooser::slotSetNote(int pitch, int octave, int step)
+{
+ //if (m_pitch->value() != p)
+ // m_pitch->setValue(p);
+ if (m_pitchDragLabel->getPitch() != pitch)
+ m_pitchDragLabel->slotSetPitch(pitch, octave, step);
+
+ m_octave->setCurrentItem(octave);
+ m_step->setCurrentItem(step);
+
+ int pitchOffset = pitch - (octave * 12 + scale_Cmajor[step]);
+ m_accidental->setCurrentItem(pitchOffset + 2);
+
+ //MidiPitchLabel pl(p);
+ m_pitchLabel->setText(QString("%1").arg(pitch));
+ update();
+}
+
+void
+DiatonicPitchChooser::slotResetToDefault()
+{
+ slotSetPitch(m_defaultPitch);
+}
+
+int
+DiatonicPitchChooser::getOctave() const
+{
+ return m_octave->currentItem();
+}
+
+
+int
+DiatonicPitchChooser::getStep() const
+{
+ return m_step->currentItem();
+}
+
+}
+#include "DiatonicPitchChooser.moc"
diff --git a/src/gui/widgets/DiatonicPitchChooser.h b/src/gui/widgets/DiatonicPitchChooser.h
new file mode 100644
index 0000000..8e5b20d
--- /dev/null
+++ b/src/gui/widgets/DiatonicPitchChooser.h
@@ -0,0 +1,103 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ROSEGARDENDIATONICPITCHCHOOSER_H_
+#define _RG_ROSEGARDENDIATONICPITCHCHOOSER_H_
+
+#include <qgroupbox.h>
+#include <qstring.h>
+
+
+class QWidget;
+class QSpinBox;
+class QComboBox;
+class QLabel;
+
+
+namespace Rosegarden
+{
+
+class PitchDragLabel;
+
+
+class DiatonicPitchChooser : public QGroupBox
+{
+ Q_OBJECT
+public:
+ DiatonicPitchChooser(QString title,
+ QWidget *parent,
+ int defaultNote = 0,
+ int defaultPitch = 60,
+ int defaultOctave = 5);
+
+ // C0=0, D0=1, C1=12, etc.
+ int getPitch() const;
+
+ // C=0, D=1, E=2, F=3, etc.
+ int getStep() const;
+
+ // pitch 0 is the first C of octave 0.
+ int getOctave() const;
+
+ // 0 = none,
+ // -x = x flats
+ // x = x sharps
+ int getAccidental();
+
+signals:
+ void pitchChanged(int);
+ //pitch, octave, step
+ void noteChanged(int,int,int);
+ void preview(int);
+
+public slots:
+ void slotSetPitch(int);
+ //pitch, octave, step
+ void slotSetNote(int,int,int);
+ void slotSetStep(int);
+ void slotSetOctave(int);
+ void slotSetAccidental(int);
+ void slotResetToDefault();
+
+protected:
+ int m_defaultPitch;
+
+ PitchDragLabel *m_pitchDragLabel;
+
+ QComboBox *m_step;
+ QComboBox *m_accidental;
+ QComboBox *m_octave;
+
+ QLabel *m_pitchLabel;
+
+private:
+ void setLabelsIfNeeded();
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/Fader.cpp b/src/gui/widgets/Fader.cpp
new file mode 100644
index 0000000..2413323
--- /dev/null
+++ b/src/gui/widgets/Fader.cpp
@@ -0,0 +1,567 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "Fader.h"
+#include "TextFloat.h"
+
+#include "misc/Debug.h"
+#include "base/AudioLevel.h"
+#include <qcolor.h>
+#include <qevent.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include <qtimer.h>
+#include <qwidget.h>
+#include <cmath>
+
+namespace Rosegarden
+{
+
+Fader::PixmapCache Fader::m_pixmapCache;
+
+
+Fader::Fader(AudioLevel::FaderType type,
+ int w, int h, QWidget *parent) :
+ QWidget(parent),
+ m_integral(false),
+ m_vertical(h > w),
+ m_min(0),
+ m_max(0),
+ m_type(type),
+ m_clickMousePos( -1),
+ m_float(new TextFloat(this)),
+ m_floatTimer(new QTimer())
+{
+ setBackgroundMode(Qt::NoBackground);
+ setFixedSize(w, h); // provisional
+ calculateButtonPixmap();
+ // if (m_vertical) {
+ // setFixedSize(w, h + m_buttonPixmap->height() + 4);
+ // } else {
+ // setFixedSize(w + m_buttonPixmap->width() + 4, h);
+ // }
+
+ if (m_vertical) {
+ m_sliderMin = buttonPixmap()->height() / 2 + 2;
+ m_sliderMax = height() - m_sliderMin;
+ } else {
+ m_sliderMin = buttonPixmap()->width() / 2 + 2;
+ m_sliderMax = width() - m_sliderMin;
+ }
+
+ m_outlineColour = colorGroup().mid();
+
+ calculateGroovePixmap();
+ setFader(0.0);
+
+ connect(m_floatTimer, SIGNAL(timeout()), this, SLOT(slotFloatTimeout()));
+ m_float->hide();
+}
+
+Fader::Fader(int min, int max, int deflt,
+ int w, int h, QWidget *parent) :
+ QWidget(parent),
+ m_integral(true),
+ m_vertical(h > w),
+ m_min(min),
+ m_max(max),
+ m_clickMousePos( -1),
+ m_float(new TextFloat(this)),
+ m_floatTimer(new QTimer())
+{
+ setBackgroundMode(Qt::NoBackground);
+ setFixedSize(w, h); // provisional
+ calculateButtonPixmap();
+ // if (m_vertical) {
+ // setFixedSize(w, h + m_buttonPixmap->height() + 4);
+ // } else {
+ // setFixedSize(w + m_buttonPixmap->width() + 4, h);
+ // }
+
+ if (m_vertical) {
+ m_sliderMin = buttonPixmap()->height() / 2 + 2;
+ m_sliderMax = height() - m_sliderMin;
+ } else {
+ m_sliderMin = buttonPixmap()->width() / 2 + 2;
+ m_sliderMax = width() - m_sliderMin;
+ }
+
+ m_outlineColour = colorGroup().mid();
+
+ calculateGroovePixmap();
+ setFader(deflt);
+
+ connect(m_floatTimer, SIGNAL(timeout()), this, SLOT(slotFloatTimeout()));
+ m_float->hide();
+}
+
+Fader::Fader(int min, int max, int deflt,
+ bool vertical, QWidget *parent) :
+ QWidget(parent),
+ m_integral(true),
+ m_vertical(vertical),
+ m_min(min),
+ m_max(max),
+ m_clickMousePos( -1),
+ m_float(new TextFloat(this)),
+ m_floatTimer(new QTimer())
+{
+ setBackgroundMode(Qt::NoBackground);
+ calculateButtonPixmap();
+
+ if (m_vertical) {
+ m_sliderMin = buttonPixmap()->height() / 2 + 2;
+ m_sliderMax = height() - m_sliderMin;
+ } else {
+ m_sliderMin = buttonPixmap()->width() / 2 + 2;
+ m_sliderMax = width() - m_sliderMin;
+ }
+
+ m_outlineColour = colorGroup().mid();
+
+ calculateGroovePixmap();
+ setFader(deflt);
+
+ connect(m_floatTimer, SIGNAL(timeout()), this, SLOT(slotFloatTimeout()));
+ m_float->hide();
+}
+
+Fader::~Fader()
+{}
+
+void
+Fader::setOutlineColour(QColor c)
+{
+ m_outlineColour = c;
+ calculateGroovePixmap();
+}
+
+QPixmap *
+Fader::groovePixmap()
+{
+ PixmapCache::iterator i = m_pixmapCache.find(SizeRec(width(), height()));
+ if (i != m_pixmapCache.end()) {
+ ColourPixmapRec::iterator j = i->second.first.find(m_outlineColour.pixel());
+ if (j != i->second.first.end()) {
+ return j->second;
+ }
+ }
+ return 0;
+}
+
+QPixmap *
+Fader::buttonPixmap()
+{
+ PixmapCache::iterator i = m_pixmapCache.find(SizeRec(width(), height()));
+ if (i != m_pixmapCache.end()) {
+ return i->second.second;
+ } else
+ return 0;
+}
+
+float
+Fader::getFaderLevel() const
+{
+ return m_value;
+}
+
+void
+Fader::setFader(float value)
+{
+ m_value = value;
+ emit faderChanged(value);
+ paintEvent(0);
+}
+
+float
+Fader::position_to_value(int position)
+{
+ float value;
+
+ if (m_integral) {
+ float sliderLength = float(m_sliderMax) - float(m_sliderMin);
+ value = float(position)
+ * float(m_max - m_min) / sliderLength - float(m_min);
+ if (value > m_max)
+ value = float(m_max);
+ if (value < m_min)
+ value = float(m_min);
+ } else {
+ value = AudioLevel::fader_to_dB
+ (position, m_sliderMax - m_sliderMin, m_type);
+ }
+ /*
+ RG_DEBUG << "Fader::position_to_value - position = " << position
+ << ", new value = " << value << endl;
+
+ if (m_min != m_max) // works for integral case
+ {
+ if (value > m_max) value = float(m_max);
+ if (value < m_min) value = float(m_min);
+ }
+
+ RG_DEBUG << "Fader::position_to_value - limited value = " << value << endl;
+ */
+ return value;
+}
+
+int
+Fader::value_to_position(float value)
+{
+ int position;
+
+ if (m_integral) {
+ float sliderLength = float(m_sliderMax) - float(m_sliderMin);
+ position =
+ int(sliderLength * (value - float(m_min)) / float(m_max - m_min) + 0.1);
+ } else {
+ position =
+ AudioLevel::dB_to_fader
+ (value, m_sliderMax - m_sliderMin, m_type);
+ }
+
+ return position;
+}
+
+void
+Fader::paintEvent(QPaintEvent *)
+{
+ QPainter paint(this);
+ int position = value_to_position(m_value);
+
+ if (m_vertical) {
+
+ int aboveButton = height() - position - m_sliderMin - buttonPixmap()->height() / 2;
+ int belowButton = position + m_sliderMin - buttonPixmap()->height() / 2;
+
+ if (aboveButton > 0) {
+ paint.drawPixmap(0, 0,
+ *groovePixmap(),
+ 0, 0,
+ groovePixmap()->width(), aboveButton);
+ }
+
+ if (belowButton > 0) {
+ paint.drawPixmap(0, aboveButton + buttonPixmap()->height(),
+ *groovePixmap(),
+ 0, aboveButton + buttonPixmap()->height(),
+ groovePixmap()->width(), belowButton);
+ }
+
+ int buttonMargin = (width() - buttonPixmap()->width()) / 2;
+
+ paint.drawPixmap(buttonMargin, aboveButton, *buttonPixmap());
+
+ paint.drawPixmap(0, aboveButton,
+ *groovePixmap(),
+ 0, aboveButton,
+ buttonMargin, buttonPixmap()->height());
+
+ paint.drawPixmap(buttonMargin + buttonPixmap()->width(), aboveButton,
+ *groovePixmap(),
+ buttonMargin + buttonPixmap()->width(), aboveButton,
+ width() - buttonMargin - buttonPixmap()->width(),
+ buttonPixmap()->height());
+
+ } else {
+ //... update
+ int leftOfButton =
+ (m_sliderMax - m_sliderMin) - position - buttonPixmap()->width() / 2;
+
+ int rightOfButton =
+ position - buttonPixmap()->width() / 2;
+
+ if (leftOfButton > 0) {
+ paint.drawPixmap(0, 0,
+ *groovePixmap(),
+ 0, 0,
+ leftOfButton, groovePixmap()->height());
+ }
+
+ if (rightOfButton > 0) {
+ paint.drawPixmap(rightOfButton + buttonPixmap()->width(), 0,
+ *groovePixmap(),
+ groovePixmap()->width() - rightOfButton, 0,
+ rightOfButton, groovePixmap()->height());
+ }
+
+ paint.drawPixmap(leftOfButton, 0, *buttonPixmap());
+ }
+
+ paint.end();
+}
+
+void
+Fader::mousePressEvent(QMouseEvent *e)
+{
+ m_clickMousePos = -1;
+
+ if (e->button() == LeftButton) {
+
+ if (e->type() == QEvent::MouseButtonDblClick) {
+ setFader(0);
+ return ;
+ }
+
+ if (m_vertical) {
+ int buttonPosition = value_to_position(m_value);
+ int clickPosition = height() - e->y() - m_sliderMin;
+
+ if (clickPosition < buttonPosition + buttonPixmap()->height() / 2 &&
+ clickPosition > buttonPosition - buttonPixmap()->height() / 2) {
+ m_clickMousePos = clickPosition;
+ m_clickButtonPos = value_to_position(m_value);
+ showFloatText();
+ }
+ }
+ }
+}
+
+void
+Fader::mouseReleaseEvent(QMouseEvent *e)
+{
+ mouseMoveEvent(e);
+ m_clickMousePos = -1;
+}
+
+void
+Fader::mouseMoveEvent(QMouseEvent *e)
+{
+ if (m_clickMousePos >= 0) {
+ if (m_vertical) {
+ int mousePosition = height() - e->y() - m_sliderMin;
+ int delta = mousePosition - m_clickMousePos;
+ int buttonPosition = m_clickButtonPos + delta;
+ if (buttonPosition < 0)
+ buttonPosition = 0;
+ if (buttonPosition > m_sliderMax - m_sliderMin) {
+ buttonPosition = m_sliderMax - m_sliderMin;
+ }
+ setFader(position_to_value(buttonPosition));
+ showFloatText();
+ }
+ }
+}
+
+void
+Fader::wheelEvent(QWheelEvent *e)
+{
+ int buttonPosition = value_to_position(m_value);
+ if (e->state() & ShiftButton) {
+ if (e->delta() > 0)
+ buttonPosition += 10;
+ else
+ buttonPosition -= 10;
+ } else {
+ if (e->delta() > 0)
+ buttonPosition += 1;
+ else
+ buttonPosition -= 1;
+ }
+ RG_DEBUG << "Fader::wheelEvent - button position = " << buttonPosition << endl;
+ setFader(position_to_value(buttonPosition));
+ RG_DEBUG << "Fader::wheelEvent - value = " << m_value << endl;
+
+ showFloatText();
+}
+
+void
+Fader::showFloatText()
+{
+ // draw on the float text
+
+ QString text;
+
+ if (m_integral) {
+ text = QString("%1").arg(int(m_value));
+ } else if (m_value == AudioLevel::DB_FLOOR) {
+ text = "Off";
+ } else {
+ float v = fabs(m_value);
+ text = QString("%1%2.%3%4%5 dB")
+ .arg(m_value < 0 ? '-' : '+')
+ .arg(int(v))
+ .arg(int(v * 10) % 10)
+ .arg(int(v * 100) % 10)
+ .arg(int(v * 1000) % 10);
+ }
+
+ m_float->setText(text);
+
+ // Reposition - we need to sum the relative positions up to the
+ // topLevel or dialog to please move().
+ //
+ QWidget *par = parentWidget();
+ QPoint totalPos = this->pos();
+
+ while (par->parentWidget() && !par->isTopLevel() && !par->isDialog()) {
+ totalPos += par->pos();
+ par = par->parentWidget();
+ }
+
+ // Move just top/right
+ //
+ m_float->move(totalPos + QPoint(width() + 2, 0));
+
+ // Show
+ m_float->show();
+
+ // one shot, 500ms
+ m_floatTimer->start(500, true);
+}
+
+void
+Fader::slotFloatTimeout()
+{
+ m_float->hide();
+}
+
+void
+Fader::calculateGroovePixmap()
+{
+ QPixmap *& map = m_pixmapCache[SizeRec(width(), height())].first[m_outlineColour.pixel()];
+
+ delete map;
+ map = new QPixmap(width(), height());
+ map->fill(colorGroup().background());
+ QPainter paint(map);
+ paint.setBrush(colorGroup().background());
+
+ if (m_vertical) {
+
+ paint.setPen(m_outlineColour);
+ paint.drawRect(0, 0, width(), height());
+
+ if (m_integral) {
+ //...
+ } else {
+ for (int dB = -70; dB <= 10; ) {
+ int position = value_to_position(float(dB));
+ if (position >= 0 &&
+ position < m_sliderMax - m_sliderMin) {
+ if (dB == 0)
+ paint.setPen(colorGroup().dark());
+ else
+ paint.setPen(colorGroup().midlight());
+ paint.drawLine(1, (m_sliderMax - position),
+ width() - 2, (m_sliderMax - position));
+ }
+ if (dB < -10)
+ dB += 10;
+ else
+ dB += 2;
+ }
+ }
+
+ paint.setPen(colorGroup().dark());
+ paint.setBrush(colorGroup().mid());
+ paint.drawRect(width() / 2 - 3, height() - m_sliderMax,
+ 6, m_sliderMax - m_sliderMin);
+ paint.end();
+ } else {
+ //...
+ }
+}
+
+void
+Fader::calculateButtonPixmap()
+{
+ PixmapCache::iterator i = m_pixmapCache.find(SizeRec(width(), height()));
+ if (i != m_pixmapCache.end() && i->second.second)
+ return ;
+
+ QPixmap *& map = m_pixmapCache[SizeRec(width(), height())].second;
+
+ if (m_vertical) {
+
+ int buttonHeight = height() / 7;
+ buttonHeight /= 10;
+ ++buttonHeight;
+ buttonHeight *= 10;
+ ++buttonHeight;
+ int buttonWidth = width() * 2 / 3;
+ buttonWidth /= 5;
+ ++buttonWidth;
+ buttonWidth *= 5;
+ if (buttonWidth > width() - 2)
+ buttonWidth = width() - 2;
+
+ map = new QPixmap(buttonWidth, buttonHeight);
+ map->fill(colorGroup().background());
+
+ int x = 0;
+ int y = 0;
+
+ QPainter paint(map);
+
+ paint.setPen(colorGroup().light());
+ paint.drawLine(x + 1, y, x + buttonWidth - 2, y);
+ paint.drawLine(x, y + 1, x, y + buttonHeight - 2);
+
+ paint.setPen(colorGroup().midlight());
+ paint.drawLine(x + 1, y + 1, x + buttonWidth - 2, y + 1);
+ paint.drawLine(x + 1, y + 1, x + 1, y + buttonHeight - 2);
+
+ paint.setPen(colorGroup().mid());
+ paint.drawLine(x + 2, y + buttonHeight - 2, x + buttonWidth - 2,
+ y + buttonHeight - 2);
+ paint.drawLine(x + buttonWidth - 2, y + 2, x + buttonWidth - 2,
+ y + buttonHeight - 2);
+
+ paint.setPen(colorGroup().dark());
+ paint.drawLine(x + 1, y + buttonHeight - 1, x + buttonWidth - 2,
+ y + buttonHeight - 1);
+ paint.drawLine(x + buttonWidth - 1, y + 1, x + buttonWidth - 1,
+ y + buttonHeight - 2);
+
+ paint.setPen(colorGroup().shadow());
+ paint.drawLine(x + 1, y + buttonHeight / 2, x + buttonWidth - 2,
+ y + buttonHeight / 2);
+
+ paint.setPen(colorGroup().mid());
+ paint.drawLine(x + 1, y + buttonHeight / 2 - 1, x + buttonWidth - 2,
+ y + buttonHeight / 2 - 1);
+ paint.drawPoint(x, y + buttonHeight / 2);
+
+ paint.setPen(colorGroup().light());
+ paint.drawLine(x + 1, y + buttonHeight / 2 + 1, x + buttonWidth - 2,
+ y + buttonHeight / 2 + 1);
+
+ paint.setPen(colorGroup().button());
+ paint.setBrush(colorGroup().button());
+ paint.drawRect(x + 2, y + 2, buttonWidth - 4, buttonHeight / 2 - 4);
+ paint.drawRect(x + 2, y + buttonHeight / 2 + 2,
+ buttonWidth - 4, buttonHeight / 2 - 4);
+
+ paint.end();
+ } else {
+ //...
+ }
+}
+
+}
+#include "Fader.moc"
diff --git a/src/gui/widgets/Fader.h b/src/gui/widgets/Fader.h
new file mode 100644
index 0000000..f4afb24
--- /dev/null
+++ b/src/gui/widgets/Fader.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ROSEGARDENFADER_H_
+#define _RG_ROSEGARDENFADER_H_
+
+#include "base/AudioLevel.h"
+#include <map>
+#include <qcolor.h>
+#include <qwidget.h>
+#include <utility>
+
+
+class QWheelEvent;
+class QTimer;
+class QPixmap;
+class QPaintEvent;
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class TextFloat;
+
+
+class Fader : public QWidget
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Construct a dB fader. The fader calculates its orientation
+ * based on the given dimensions.
+ */
+ Fader(AudioLevel::FaderType,
+ int width, int height, QWidget *parent);
+
+ /**
+ * Construct a fader on an integral scale. The fader calculates
+ * its orientation based on the given dimensions.
+ */
+ Fader(int min, int max, int deflt,
+ int width, int height, QWidget *parent);
+
+ /**
+ * Construct a fader on an integral scale, with a 1:1 ratio of
+ * pixel positions and values.
+ */
+ Fader(int min, int max, int deflt,
+ bool vertical, QWidget *parent);
+
+ virtual ~Fader();
+
+ void setOutlineColour(QColor);
+
+ float getFaderLevel() const;
+
+public slots:
+ void setFader(float value);
+ void slotFloatTimeout();
+
+signals:
+ void faderChanged(float);
+
+protected:
+ virtual void paintEvent(QPaintEvent *);
+ virtual void mousePressEvent(QMouseEvent *);
+ virtual void mouseReleaseEvent(QMouseEvent *);
+ virtual void mouseMoveEvent(QMouseEvent *);
+ virtual void wheelEvent(QWheelEvent *);
+
+ float position_to_value(int);
+ int value_to_position(float);
+
+ void calculateGroovePixmap();
+ void calculateButtonPixmap();
+ void showFloatText();
+
+ bool m_integral;
+ bool m_vertical;
+
+ int m_sliderMin;
+ int m_sliderMax;
+ float m_value;
+
+ int m_min;
+ int m_max;
+ AudioLevel::FaderType m_type;
+
+ int m_clickMousePos;
+ int m_clickButtonPos;
+
+ TextFloat *m_float;
+ QTimer *m_floatTimer;
+
+ QPixmap *groovePixmap();
+ QPixmap *buttonPixmap();
+
+ QColor m_outlineColour;
+
+ typedef std::pair<int, int> SizeRec;
+ typedef std::map<unsigned int, QPixmap *> ColourPixmapRec; // key is QColor::pixel()
+ typedef std::pair<ColourPixmapRec, QPixmap *> PixmapRec;
+ typedef std::map<SizeRec, PixmapRec> PixmapCache;
+ static PixmapCache m_pixmapCache;
+};
+
+
+// AudioVUMeter - a vertical audio meter. Default is stereo.
+//
+
+}
+
+#endif
diff --git a/src/gui/widgets/HSpinBox.cpp b/src/gui/widgets/HSpinBox.cpp
new file mode 100644
index 0000000..efdb9d1
--- /dev/null
+++ b/src/gui/widgets/HSpinBox.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "HSpinBox.h"
+
+#include <qstring.h>
+#include <cmath>
+#include <algorithm>
+
+namespace Rosegarden
+{
+
+QString HSpinBox::mapValueToText(int j)
+{
+ QString str;
+ str.sprintf(m_format, float(j) / m_scaleFactor);
+ return str;
+}
+
+int HSpinBox::mapTextToValue( bool* ok )
+{
+ *ok = true;
+ float f = atof(text());
+ return int(f * m_scaleFactor);
+}
+
+HSpinBox::HSpinBox( int minV, int maxV, int step, QWidget* parent,
+ double bottom, double top, int decimals, float initialValue)
+ : QSpinBox(minV,maxV,step,parent)
+{
+ setValidator(new QDoubleValidator(bottom,top,decimals,this));
+ initialize(decimals);
+ setValuef(initialValue);
+}
+
+ //constructor with default settings
+HSpinBox::HSpinBox( QWidget* parent, float initialValue, int step,
+ double bottom, double top, int decimals,
+ const QObject* recv, const char* mem)
+ : QSpinBox((int)(bottom*pow(10.0, decimals)),
+ (int)(top*pow(10.0, decimals)), step, parent)
+{
+ setValidator(new QDoubleValidator(bottom,top,decimals,this));
+ initialize(decimals);
+ setValuef(initialValue);
+ if (recv != NULL && mem != NULL)
+ QObject::connect(this, SIGNAL(valueChanged(int)), recv, mem);
+}
+
+float HSpinBox::valuef() { return float(value()) / m_scaleFactor; }
+void HSpinBox::setValuef(float v) { setValue(static_cast<int>(v * m_scaleFactor)); }
+
+void HSpinBox::initialize(int digits) {
+ m_scaleFactor = pow(10.0, digits);
+ sprintf(m_format, "%c%1i.%1if", '%', digits+3, digits);
+}
+
+
+}
diff --git a/src/gui/widgets/HSpinBox.h b/src/gui/widgets/HSpinBox.h
new file mode 100644
index 0000000..aa60b65
--- /dev/null
+++ b/src/gui/widgets/HSpinBox.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_HSPINBOX_H_
+#define _RG_HSPINBOX_H_
+
+#include <qobject.h>
+#include <qspinbox.h>
+#include <qstring.h>
+#include <qvalidator.h>
+
+
+namespace Rosegarden
+{
+
+class HSpinBox : public QSpinBox
+{
+ QString mapValueToText(int j);
+
+ int mapTextToValue( bool* ok );
+
+public:
+ HSpinBox( int minV, int maxV, int step, QWidget* parent,
+ double bottom, double top, int decimals, float initialValue);
+
+ //constructor with default settings
+ HSpinBox( QWidget* parent, float initialValue = 0.2, int step=1,
+ double bottom=-25.0, double top=25.0, int decimals=3,
+ const QObject* recv=NULL, const char* mem=NULL);
+
+ float valuef();
+ void setValuef(float v);
+ void initialize(int digits);
+
+private:
+
+ float m_scaleFactor; //scale of the value
+ char m_format[3]; //text format
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/Label.cpp b/src/gui/widgets/Label.cpp
new file mode 100644
index 0000000..69f504b
--- /dev/null
+++ b/src/gui/widgets/Label.cpp
@@ -0,0 +1,2 @@
+#include "Label.h"
+#include "Label.moc"
diff --git a/src/gui/widgets/Label.h b/src/gui/widgets/Label.h
new file mode 100644
index 0000000..e704c35
--- /dev/null
+++ b/src/gui/widgets/Label.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ROSEGARDENLABEL_H_
+#define _RG_ROSEGARDENLABEL_H_
+
+#include <qlabel.h>
+
+
+class QWidget;
+class QWheelEvent;
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class Label : public QLabel
+{
+ Q_OBJECT
+public:
+ Label(QWidget *parent = 0, const char *name=0):
+ QLabel(parent, name) {;}
+
+protected:
+ virtual void mouseDoubleClickEvent(QMouseEvent * /*e*/)
+ { emit doubleClicked(); }
+
+ virtual void wheelEvent(QWheelEvent * e)
+ { emit scrollWheel(e->delta()); }
+
+signals:
+ void doubleClicked();
+ void scrollWheel(int);
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/MidiFaderWidget.cpp b/src/gui/widgets/MidiFaderWidget.cpp
new file mode 100644
index 0000000..a2ef7fc
--- /dev/null
+++ b/src/gui/widgets/MidiFaderWidget.cpp
@@ -0,0 +1,41 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "MidiFaderWidget.h"
+
+#include "AudioVUMeter.h"
+#include "Fader.h"
+#include "Rotary.h"
+#include <kcombobox.h>
+#include <qframe.h>
+#include <qpushbutton.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+}
+#include "MidiFaderWidget.moc"
diff --git a/src/gui/widgets/MidiFaderWidget.h b/src/gui/widgets/MidiFaderWidget.h
new file mode 100644
index 0000000..7bdf520
--- /dev/null
+++ b/src/gui/widgets/MidiFaderWidget.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_MIDIFADERWIDGET_H_
+#define _RG_MIDIFADERWIDGET_H_
+
+#include <qframe.h>
+#include <qstring.h>
+
+
+class QWidget;
+class QPushButton;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class Rotary;
+class Fader;
+class AudioVUMeter;
+
+
+class MidiFaderWidget : public QFrame
+{
+ Q_OBJECT
+
+public:
+ MidiFaderWidget(QWidget *parent,
+ QString id = "");
+
+ AudioVUMeter *m_vuMeter;
+
+ Fader *m_fader;
+
+ QPushButton *m_muteButton;
+ QPushButton *m_soloButton;
+ QPushButton *m_recordButton;
+ Rotary *m_pan;
+
+ KComboBox *m_output;
+
+ QString m_id;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/PitchChooser.cpp b/src/gui/widgets/PitchChooser.cpp
new file mode 100644
index 0000000..e20647d
--- /dev/null
+++ b/src/gui/widgets/PitchChooser.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "PitchChooser.h"
+
+#include <klocale.h>
+#include "gui/general/MidiPitchLabel.h"
+#include "PitchDragLabel.h"
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qspinbox.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+PitchChooser::PitchChooser(QString title,
+ QWidget *parent,
+ int defaultPitch) :
+ QGroupBox(1, Horizontal, title, parent),
+ m_defaultPitch(defaultPitch)
+{
+ m_pitchDragLabel = new PitchDragLabel(this, defaultPitch);
+
+ QHBox *hbox = new QHBox(this);
+ hbox->setSpacing(6);
+
+ new QLabel(i18n("Pitch:"), hbox);
+
+ m_pitch = new QSpinBox(hbox);
+ m_pitch->setMinValue(0);
+ m_pitch->setMaxValue(127);
+ m_pitch->setValue(defaultPitch);
+
+ MidiPitchLabel pl(defaultPitch);
+ m_pitchLabel = new QLabel(pl.getQString(), hbox);
+ m_pitchLabel->setMinimumWidth(40);
+
+ connect(m_pitch, SIGNAL(valueChanged(int)),
+ this, SLOT(slotSetPitch(int)));
+
+ connect(m_pitch, SIGNAL(valueChanged(int)),
+ this, SIGNAL(pitchChanged(int)));
+
+ connect(m_pitch, SIGNAL(valueChanged(int)),
+ this, SIGNAL(preview(int)));
+
+ connect(m_pitchDragLabel, SIGNAL(pitchDragged(int)),
+ this, SLOT(slotSetPitch(int)));
+
+ connect(m_pitchDragLabel, SIGNAL(pitchChanged(int)),
+ this, SLOT(slotSetPitch(int)));
+
+ connect(m_pitchDragLabel, SIGNAL(pitchChanged(int)),
+ this, SIGNAL(pitchChanged(int)));
+
+ connect(m_pitchDragLabel, SIGNAL(preview(int)),
+ this, SIGNAL(preview(int)));
+
+}
+
+int
+PitchChooser::getPitch() const
+{
+ return m_pitch->value();
+}
+
+void
+PitchChooser::slotSetPitch(int p)
+{
+ if (m_pitch->value() != p)
+ m_pitch->setValue(p);
+ if (m_pitchDragLabel->getPitch() != p)
+ m_pitchDragLabel->slotSetPitch(p);
+
+ MidiPitchLabel pl(p);
+ m_pitchLabel->setText(pl.getQString());
+ update();
+}
+
+void
+PitchChooser::slotResetToDefault()
+{
+ slotSetPitch(m_defaultPitch);
+}
+
+}
+#include "PitchChooser.moc"
diff --git a/src/gui/widgets/PitchChooser.h b/src/gui/widgets/PitchChooser.h
new file mode 100644
index 0000000..df3b8ef
--- /dev/null
+++ b/src/gui/widgets/PitchChooser.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ROSEGARDENPITCHCHOOSER_H_
+#define _RG_ROSEGARDENPITCHCHOOSER_H_
+
+#include <qgroupbox.h>
+#include <qstring.h>
+
+
+class QWidget;
+class QSpinBox;
+class QLabel;
+
+
+namespace Rosegarden
+{
+
+class PitchDragLabel;
+
+
+class PitchChooser : public QGroupBox
+{
+ Q_OBJECT
+public:
+ PitchChooser(QString title,
+ QWidget *parent,
+ int defaultPitch = 60);
+
+ int getPitch() const;
+
+signals:
+ void pitchChanged(int);
+ void preview(int);
+
+public slots:
+ void slotSetPitch(int);
+ void slotResetToDefault();
+
+protected:
+ int m_defaultPitch;
+ PitchDragLabel *m_pitchDragLabel;
+ QSpinBox *m_pitch;
+ QLabel *m_pitchLabel;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/PitchDragLabel.cpp b/src/gui/widgets/PitchDragLabel.cpp
new file mode 100644
index 0000000..1d22a77
--- /dev/null
+++ b/src/gui/widgets/PitchDragLabel.cpp
@@ -0,0 +1,269 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "PitchDragLabel.h"
+
+#include "base/NotationRules.h"
+#include "base/NotationTypes.h"
+#include "gui/editors/notation/NotePixmapFactory.h"
+#include <qcanvas.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qsize.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+PitchDragLabel::PitchDragLabel(QWidget *parent,
+ int defaultPitch,
+ bool defaultSharps) :
+ QWidget(parent),
+ m_pitch(defaultPitch),
+ m_usingSharps(defaultSharps),
+ m_clickedY(0),
+ m_clicked(false),
+ m_npf(new NotePixmapFactory())
+{
+ calculatePixmap();
+}
+
+PitchDragLabel::~PitchDragLabel()
+{
+ delete m_npf;
+}
+
+void
+PitchDragLabel::slotSetPitch(int p)
+{
+ bool up = (p > m_pitch);
+ m_usingSharps = up;
+ if (m_pitch == p)
+ return ;
+ m_pitch = p;
+ calculatePixmap();
+ emitPitchChange();
+ paintEvent(0);
+}
+
+void
+PitchDragLabel::slotSetPitch(int pitch, int octave, int step)
+{
+ if (m_pitch == pitch)
+ return ;
+ m_pitch = pitch;
+ calculatePixmap(pitch, octave, step);
+ emit pitchChanged(pitch);
+ emit pitchChanged(pitch, octave, step);
+ paintEvent(0);
+}
+
+void
+PitchDragLabel::mousePressEvent(QMouseEvent *e)
+{
+ if (e->button() == LeftButton) {
+ m_clickedY = e->y();
+ m_clickedPitch = m_pitch;
+ m_clicked = true;
+ emit preview(m_pitch);
+ }
+}
+
+void
+PitchDragLabel::mouseMoveEvent(QMouseEvent *e)
+{
+ if (m_clicked) {
+
+ int y = e->y();
+ int diff = y - m_clickedY;
+ int pitchDiff = diff * 4 / m_npf->getLineSpacing();
+
+ int newPitch = m_clickedPitch - pitchDiff;
+ if (newPitch < 0)
+ newPitch = 0;
+ if (newPitch > 127)
+ newPitch = 127;
+
+ if (m_pitch != newPitch) {
+ bool up = (newPitch > m_pitch);
+ m_pitch = newPitch;
+ m_usingSharps = up;
+ calculatePixmap();
+ emit pitchDragged(m_pitch);
+ if (up)
+ {
+ // use sharps
+ emit pitchDragged(m_pitch, (int)(((long)m_pitch) / 12),
+ steps_Cmajor_with_sharps[m_pitch % 12]);
+ }
+ else
+ {
+ // use flats
+ emit pitchDragged(m_pitch, (int)(((long)m_pitch) / 12),
+ steps_Cmajor_with_flats[m_pitch % 12]);
+ }
+ emit preview(m_pitch);
+ paintEvent(0);
+ }
+ }
+}
+
+void
+PitchDragLabel::mouseReleaseEvent(QMouseEvent *e)
+{
+ mouseMoveEvent(e);
+ emitPitchChange();
+ m_clicked = false;
+}
+
+void
+PitchDragLabel::emitPitchChange()
+{
+ emit pitchChanged(m_pitch);
+
+ Pitch newPitch(m_pitch);
+
+ if (m_usingSharps)
+ {
+ Rosegarden::Key key = Rosegarden::Key("C major");
+ emit pitchDragged(m_pitch, newPitch.getOctave(0), newPitch.getNoteInScale(key));
+ }
+ else
+ {
+ Rosegarden::Key key = Rosegarden::Key("A minor");
+ emit pitchDragged(m_pitch, newPitch.getOctave(0), (newPitch.getNoteInScale(key) + 5) % 7);
+ }
+}
+
+void
+PitchDragLabel::wheelEvent(QWheelEvent *e)
+{
+ if (e->delta() > 0) {
+ if (m_pitch < 127) {
+ ++m_pitch;
+ m_usingSharps = true;
+ calculatePixmap();
+ emitPitchChange();
+ emit preview(m_pitch);
+ paintEvent(0);
+ }
+ } else {
+ if (m_pitch > 0) {
+ --m_pitch;
+ m_usingSharps = false;
+ calculatePixmap();
+ emitPitchChange();
+ emit preview(m_pitch);
+ paintEvent(0);
+ }
+ }
+}
+
+void
+PitchDragLabel::paintEvent(QPaintEvent *)
+{
+ QPainter paint(this);
+ paint.fillRect(0, 0, width(), height(), paint.backgroundColor());
+
+ int x = width() / 2 - m_pixmap.width() / 2;
+ if (x < 0)
+ x = 0;
+
+ int y = height() / 2 - m_pixmap.height() / 2;
+ if (y < 0)
+ y = 0;
+
+ paint.drawPixmap(x, y, m_pixmap);
+
+
+}
+
+QSize
+PitchDragLabel::sizeHint() const
+{
+ return QSize(150, 135);
+}
+
+void
+PitchDragLabel::calculatePixmap(int pitch, int octave, int step) const
+{
+ std::string clefType = Clef::Treble;
+ int octaveOffset = 0;
+
+ if (m_pitch > 94) {
+ octaveOffset = 2;
+ } else if (m_pitch > 82) {
+ octaveOffset = 1;
+ } else if (m_pitch < 60) {
+ clefType = Clef::Bass;
+ if (m_pitch < 24) {
+ octaveOffset = -2;
+ } else if (m_pitch < 36) {
+ octaveOffset = -1;
+ }
+ }
+
+ QCanvasPixmap *pmap = m_npf->makePitchDisplayPixmap
+ (m_pitch,
+ Clef(clefType, octaveOffset),
+ octave, step);
+
+ m_pixmap = *pmap;
+
+ delete pmap;
+}
+
+void
+PitchDragLabel::calculatePixmap() const
+{
+ std::string clefType = Clef::Treble;
+ int octaveOffset = 0;
+
+ if (m_pitch > 94) {
+ octaveOffset = 2;
+ } else if (m_pitch > 82) {
+ octaveOffset = 1;
+ } else if (m_pitch < 60) {
+ clefType = Clef::Bass;
+ if (m_pitch < 24) {
+ octaveOffset = -2;
+ } else if (m_pitch < 36) {
+ octaveOffset = -1;
+ }
+ }
+
+ QCanvasPixmap *pmap = m_npf->makePitchDisplayPixmap
+ (m_pitch,
+ Clef(clefType, octaveOffset),
+ m_usingSharps);
+
+ m_pixmap = *pmap;
+
+ delete pmap;
+}
+
+}
+#include "PitchDragLabel.moc"
diff --git a/src/gui/widgets/PitchDragLabel.h b/src/gui/widgets/PitchDragLabel.h
new file mode 100644
index 0000000..7114611
--- /dev/null
+++ b/src/gui/widgets/PitchDragLabel.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ROSEGARDENPITCHDRAGLABEL_H_
+#define _RG_ROSEGARDENPITCHDRAGLABEL_H_
+
+#include <qpixmap.h>
+#include <qsize.h>
+#include <qwidget.h>
+
+
+class QWheelEvent;
+class QPaintEvent;
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class NotePixmapFactory;
+
+
+class PitchDragLabel : public QWidget
+{
+ Q_OBJECT
+public:
+ PitchDragLabel(QWidget *parent,
+ int defaultPitch = 60, bool defaultSharps = true);
+ ~PitchDragLabel();
+
+ int getPitch() const { return m_pitch; }
+
+ virtual QSize sizeHint() const;
+
+signals:
+ void pitchDragged(int);
+ // pitch, octave, step
+ void pitchDragged(int,int,int);
+ void pitchChanged(int); // mouse release
+ // pitch, octave, step
+ void pitchChanged(int,int,int); // mouse release
+ void preview(int);
+
+public slots:
+ void slotSetPitch(int);
+ void slotSetPitch(int,int,int);
+
+protected:
+ virtual void paintEvent(QPaintEvent *);
+ virtual void mousePressEvent(QMouseEvent *e);
+ virtual void mouseReleaseEvent(QMouseEvent *e);
+ virtual void mouseMoveEvent(QMouseEvent *e);
+ virtual void wheelEvent(QWheelEvent *e);
+
+ void calculatePixmap() const;
+ void calculatePixmap(int pitch, int octave, int step) const;
+
+ /** emits 'pitchChanged' events, both diatonic and chromatic */
+ void emitPitchChange();
+
+ mutable QPixmap m_pixmap;
+
+ int m_pitch;
+ int m_clickedY;
+ int m_clickedPitch;
+ bool m_clicked;
+
+ bool m_usingSharps;
+
+ NotePixmapFactory *m_npf;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/PluginControl.cpp b/src/gui/widgets/PluginControl.cpp
new file mode 100644
index 0000000..acf33ea
--- /dev/null
+++ b/src/gui/widgets/PluginControl.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "PluginControl.h"
+#include "Rotary.h"
+#include "misc/Strings.h"
+#include "base/AudioPluginInstance.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/studio/AudioPluginManager.h"
+#include "gui/widgets/Rotary.h"
+#include <qfont.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qobject.h>
+#include <qstring.h>
+#include <qwidget.h>
+#include <cmath>
+
+
+namespace Rosegarden
+{
+
+PluginControl::PluginControl(QWidget *parent,
+ QGridLayout *layout,
+ ControlType type,
+ PluginPort *port,
+ AudioPluginManager *aPM,
+ int index,
+ float initialValue,
+ bool showBounds,
+ bool hidden):
+ QObject(parent),
+ m_layout(layout),
+ m_type(type),
+ m_port(port),
+ m_pluginManager(aPM),
+ m_index(index)
+{
+ QFont plainFont;
+ plainFont.setPointSize((plainFont.pointSize() * 9 ) / 10);
+
+ QLabel *controlTitle =
+ new QLabel(QString("%1 ").arg(strtoqstr(port->getName())), parent);
+ controlTitle->setFont(plainFont);
+
+ if (type == Rotary) {
+ float lowerBound = port->getLowerBound();
+ float upperBound = port->getUpperBound();
+ // Default value was already handled when calling this constructor
+
+ if (lowerBound > upperBound) {
+ float swap = upperBound;
+ upperBound = lowerBound;
+ lowerBound = swap;
+ }
+
+ float step = (upperBound - lowerBound) / 100.0;
+ float pageStep = step * 10.f;
+ Rotary::TickMode ticks = Rotary::PageStepTicks;
+ bool snapToTicks = false;
+
+ if (port->getDisplayHint() & PluginPort::Integer) {
+ step = 1.0;
+ ticks = Rotary::StepTicks;
+ if (upperBound - lowerBound > 30.0)
+ pageStep = 10.0;
+ snapToTicks = true;
+ }
+ if (port->getDisplayHint() & PluginPort::Toggled) {
+ lowerBound = -0.0001;
+ upperBound = 1.0001;
+ step = 1.0;
+ pageStep = 1.0;
+ ticks = Rotary::StepTicks;
+ snapToTicks = true;
+ }
+
+ float displayLower = lowerBound, displayUpper = upperBound;
+
+ bool logarithmic = (port->getDisplayHint() & PluginPort::Logarithmic);
+
+ if (logarithmic) {
+ float logthresh = -10;
+ float thresh = powf(10, logthresh);
+ if (lowerBound > thresh) lowerBound = log10f(lowerBound);
+ else {
+ if (upperBound > 1) lowerBound = 0;
+ else lowerBound = logthresh;
+ }
+ if (upperBound > thresh) upperBound = log10f(upperBound);
+ else upperBound = logthresh;
+
+ step = (upperBound - lowerBound) / 100.0;
+ pageStep = step * 10.f;
+ initialValue = log10f(initialValue);
+ }
+
+ QLabel *low;
+ if (port->getDisplayHint() &
+ (PluginPort::Integer | PluginPort::Toggled)) {
+ low = new QLabel(QString("%1").arg(int(displayLower)), parent);
+ } else {
+ low = new QLabel(QString("%1").arg(displayLower), parent);
+ }
+ low->setFont(plainFont);
+
+// std::cerr << "port " << port->getName() << ": lower bound "
+// << displayLower << ", upper bound " << displayUpper
+// << ", logarithmic " << logarithmic << ", default "
+// << initialValue << ", actual lower " << lowerBound
+// << ", actual upper " << upperBound << ", step "
+// << step << std::endl;
+
+ m_dial = new ::Rosegarden::Rotary(parent,
+ lowerBound, // min
+ upperBound, // max
+ step, // step
+ pageStep, // page step
+ initialValue, // initial
+ 30, // size
+ ticks,
+ snapToTicks,
+ false, // centred
+ logarithmic);
+
+ m_dial->setKnobColour(GUIPalette::getColour(GUIPalette::RotaryPlugin));
+
+ connect(m_dial, SIGNAL(valueChanged(float)),
+ this, SLOT(slotValueChanged(float)));
+
+ QLabel *upp;
+ if (port->getDisplayHint() &
+ (PluginPort::Integer | PluginPort::Toggled)) {
+ upp = new QLabel(QString("%1").arg(int(displayUpper)), parent);
+ } else {
+ upp = new QLabel(QString("%1").arg(displayUpper), parent);
+ }
+ upp->setFont(plainFont);
+
+ QWidgetItem *item;
+
+ if (!hidden) {
+ controlTitle->show();
+ item = new QWidgetItem(controlTitle);
+ item->setAlignment(Qt::AlignRight | Qt::AlignBottom);
+ m_layout->addItem(item);
+ } else {
+ controlTitle->hide();
+ }
+
+ if (showBounds && !hidden) {
+ low->show();
+ item = new QWidgetItem(low);
+ item->setAlignment(Qt::AlignRight | Qt::AlignBottom);
+ m_layout->addItem(item);
+ } else {
+ low->hide();
+ }
+
+ if (!hidden) {
+ m_dial->show();
+ item = new QWidgetItem(m_dial);
+ item->setAlignment(Qt::AlignCenter);
+ m_layout->addItem(item);
+ } else {
+ m_dial->hide();
+ }
+
+ if (showBounds && !hidden) {
+ upp->show();
+ item = new QWidgetItem(upp);
+ item->setAlignment(Qt::AlignLeft | Qt::AlignBottom);
+ m_layout->addItem(item);
+ } else {
+ upp->hide();
+ }
+ }
+}
+
+void
+PluginControl::setValue(float value, bool emitSignals)
+{
+ if (!emitSignals)
+ m_dial->blockSignals(true);
+ m_dial->setPosition(value);
+ if (!emitSignals)
+ m_dial->blockSignals(false);
+ else
+ emit valueChanged(value);
+}
+
+float
+PluginControl::getValue() const
+{
+ return m_dial == 0 ? 0 : m_dial->getPosition();
+}
+
+void
+PluginControl::slotValueChanged(float value)
+{
+ emit valueChanged(value);
+}
+
+}
+#include "PluginControl.moc"
diff --git a/src/gui/widgets/PluginControl.h b/src/gui/widgets/PluginControl.h
new file mode 100644
index 0000000..83d2d33
--- /dev/null
+++ b/src/gui/widgets/PluginControl.h
@@ -0,0 +1,104 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_PLUGINCONTROL_H_
+#define _RG_PLUGINCONTROL_H_
+
+#include <qobject.h>
+#include <vector>
+
+
+class QWidget;
+class QHBox;
+class QGridLayout;
+
+
+namespace Rosegarden
+{
+
+class Rotary;
+class PluginPort;
+class AudioPluginManager;
+class Studio;
+
+class PluginControl : public QObject
+{
+ Q_OBJECT
+public:
+
+ typedef enum
+ {
+ Rotary,
+ Slider,
+ NumericSlider
+ } ControlType;
+
+ PluginControl(QWidget *parent,
+ QGridLayout *layout,
+ ControlType type,
+ PluginPort *port,
+ AudioPluginManager *pluginManager,
+ int index,
+ float initialValue,
+ bool showBounds,
+ bool hidden);
+
+ void setValue(float value, bool emitSignals = true);
+ float getValue() const;
+
+ int getIndex() const { return m_index; }
+
+ void show();
+ void hide();
+
+public slots:
+ void slotValueChanged(float value);
+
+signals:
+ void valueChanged(float value);
+
+protected:
+
+ //--------------- Data members ---------------------------------
+
+ QGridLayout *m_layout;
+
+ ControlType m_type;
+ PluginPort *m_port;
+
+ ::Rosegarden::Rotary *m_dial; // we have to specify the namespace here otherwise gcc 4.1 thinks it's the enum value above
+ AudioPluginManager *m_pluginManager;
+
+ int m_index;
+
+};
+
+typedef std::vector<PluginControl*>::iterator ControlIterator;
+typedef std::vector<QHBox*>::iterator ControlLineIterator;
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/ProgressBar.cpp b/src/gui/widgets/ProgressBar.cpp
new file mode 100644
index 0000000..c4cb88e
--- /dev/null
+++ b/src/gui/widgets/ProgressBar.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ProgressBar.h"
+
+#include <kprogress.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+ProgressBar::ProgressBar(int totalSteps,
+ bool /*useDelay*/,
+ QWidget *creator,
+ const char *name,
+ WFlags f) :
+ KProgress(totalSteps, creator, name, f)
+{}
+
+}
+#include "ProgressBar.moc"
diff --git a/src/gui/widgets/ProgressBar.h b/src/gui/widgets/ProgressBar.h
new file mode 100644
index 0000000..3ce93e1
--- /dev/null
+++ b/src/gui/widgets/ProgressBar.h
@@ -0,0 +1,56 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ROSEGARDENPROGRESSBAR_H_
+#define _RG_ROSEGARDENPROGRESSBAR_H_
+
+#include <kprogress.h>
+
+
+class QWidget;
+
+
+namespace Rosegarden
+{
+
+
+
+class ProgressBar : public KProgress
+{
+ Q_OBJECT
+
+public:
+ ProgressBar(int totalSteps,
+ bool useDelay,
+ QWidget *creator = 0,
+ const char *name = 0,
+ WFlags f = 0);
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/ProgressDialog.cpp b/src/gui/widgets/ProgressDialog.cpp
new file mode 100644
index 0000000..1f6811f
--- /dev/null
+++ b/src/gui/widgets/ProgressDialog.cpp
@@ -0,0 +1,209 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ProgressDialog.h"
+#include "CurrentProgressDialog.h"
+#include "misc/Debug.h"
+#include "gui/application/RosegardenApplication.h"
+#include <klocale.h>
+#include <qcursor.h>
+#include <qprogressdialog.h>
+#include <qstring.h>
+#include <qtimer.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+bool ProgressDialog::m_modalVisible = false;
+
+
+ProgressDialog::ProgressDialog(QWidget *creator,
+ const char *name,
+ bool modal):
+ KProgressDialog(creator, name,
+ i18n("Processing..."), QString::null, modal),
+ m_wasVisible(false),
+ m_frozen(false),
+ m_modal(modal)
+{
+ setCaption(i18n("Processing..."));
+ RG_DEBUG << "ProgressDialog::ProgressDialog type 1 - "
+ << labelText() << " - modal : " << modal << endl;
+
+ connect(progressBar(), SIGNAL(percentageChanged (int)),
+ this, SLOT(slotCheckShow(int)));
+
+ m_chrono.start();
+
+ CurrentProgressDialog::set
+ (this);
+
+ setMinimumDuration(500); // set a default value for this
+}
+
+ProgressDialog::ProgressDialog(
+ const QString &labelText,
+ int totalSteps,
+ QWidget *creator,
+ const char *name,
+ bool modal) :
+ KProgressDialog(creator,
+ name,
+ i18n("Processing..."),
+ labelText,
+ modal),
+ m_wasVisible(false),
+ m_frozen(false),
+ m_modal(modal)
+{
+ progressBar()->setTotalSteps(totalSteps);
+
+ RG_DEBUG << "ProgressDialog::ProgressDialog type 2 - "
+ << labelText << " - modal : " << modal << endl;
+
+ connect(progressBar(), SIGNAL(percentageChanged (int)),
+ this, SLOT(slotCheckShow(int)));
+
+ m_chrono.start();
+
+ CurrentProgressDialog::set
+ (this);
+
+ setMinimumDuration(500); // set a default value for this
+}
+
+ProgressDialog::~ProgressDialog()
+{
+ m_modalVisible = false;
+}
+
+void
+ProgressDialog::polish()
+{
+ KProgressDialog::polish();
+
+ if (allowCancel())
+ setCursor(Qt::ArrowCursor);
+ else
+ QApplication::setOverrideCursor(QCursor(Qt::waitCursor));
+}
+
+void ProgressDialog::hideEvent(QHideEvent* e)
+{
+ if (!allowCancel())
+ QApplication::restoreOverrideCursor();
+
+ KProgressDialog::hideEvent(e);
+ m_modalVisible = false;
+}
+
+void
+ProgressDialog::slotSetOperationName(QString name)
+{
+ // RG_DEBUG << "ProgressDialog::slotSetOperationName("
+ // << name << ") visible : " << isVisible() << endl;
+
+ setLabel(name);
+ // Little trick stolen from QProgressDialog
+ // increase resize only, never shrink
+ int w = QMAX( isVisible() ? width() : 0, sizeHint().width() );
+ int h = QMAX( isVisible() ? height() : 0, sizeHint().height() );
+ resize( w, h );
+}
+
+void ProgressDialog::slotCancel()
+{
+ RG_DEBUG << "ProgressDialog::slotCancel()\n";
+ KProgressDialog::slotCancel();
+ slotFreeze();
+}
+
+void ProgressDialog::slotCheckShow(int)
+{
+ // RG_DEBUG << "ProgressDialog::slotCheckShow() : "
+ // << m_chrono.elapsed() << " - " << minimumDuration()
+ // << endl;
+
+ if (!isVisible() &&
+ !m_frozen &&
+ m_chrono.elapsed() > minimumDuration()) {
+ RG_DEBUG << "ProgressDialog::slotCheckShow() : showing dialog\n";
+ show();
+ if (m_modal)
+ m_modalVisible = true;
+ processEvents();
+ }
+}
+
+void ProgressDialog::slotFreeze()
+{
+ RG_DEBUG << "ProgressDialog::slotFreeze()\n";
+
+ m_wasVisible = isVisible();
+ if (isVisible()) {
+ m_modalVisible = false;
+ hide();
+ }
+
+ // This is also a convenient place to ensure the wait cursor (if
+ // currently shown) returns to the original cursor to ensure that
+ // the user can respond to whatever's freezing the progress dialog
+ QApplication::restoreOverrideCursor();
+
+ mShowTimer->stop();
+ m_frozen = true;
+}
+
+void ProgressDialog::slotThaw()
+{
+ RG_DEBUG << "ProgressDialog::slotThaw()\n";
+
+ if (m_wasVisible) {
+ if (m_modal)
+ m_modalVisible = true;
+ show();
+ }
+
+ // Restart timer
+ mShowTimer->start(minimumDuration());
+ m_frozen = false;
+ m_chrono.restart();
+}
+
+void ProgressDialog::processEvents()
+{
+ // RG_DEBUG << "ProgressDialog::processEvents: modalVisible is "
+ // << m_modalVisible << endl;
+ if (m_modalVisible) {
+ kapp->processEvents(50);
+ } else {
+ rgapp->refreshGUI(50);
+ }
+}
+
+}
+#include "ProgressDialog.moc"
diff --git a/src/gui/widgets/ProgressDialog.h b/src/gui/widgets/ProgressDialog.h
new file mode 100644
index 0000000..b753493
--- /dev/null
+++ b/src/gui/widgets/ProgressDialog.h
@@ -0,0 +1,98 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ROSEGARDENPROGRESSDIALOG_H_
+#define _RG_ROSEGARDENPROGRESSDIALOG_H_
+
+#define private protected // fugly
+#include <kprogress.h>
+#undef private
+#include <qdatetime.h>
+
+class QWidget;
+class QString;
+class QHideEvent;
+
+
+namespace Rosegarden
+{
+
+
+
+class ProgressDialog : public KProgressDialog
+{
+ Q_OBJECT
+public:
+ ProgressDialog(QWidget * creator = 0,
+ const char * name = 0,
+ bool modal = true);
+
+ ProgressDialog(const QString &labelText,
+ int totalSteps,
+ QWidget *creator = 0,
+ const char *name = 0,
+ bool modal = true);
+
+ ~ProgressDialog();
+
+ /**
+ * A "safe" way to process events without worrying about user
+ * input during the process. If there is a modal progress dialog
+ * visible, then this will permit user input so as to allow the
+ * user to hit Cancel; otherwise it will prevent all user input
+ */
+ static void processEvents();
+
+ virtual void polish();
+
+public slots:
+ void slotSetOperationName(QString);
+ void slotCancel();
+
+ /// Stop and hide (if it's shown) the progress dialog
+ void slotFreeze();
+
+ /// Restore the dialog to its normal state
+ void slotThaw();
+
+protected slots:
+ void slotCheckShow(int);
+
+protected:
+ virtual void hideEvent(QHideEvent*);
+
+ //--------------- Data members ---------------------------------
+
+ QTime m_chrono;
+ bool m_wasVisible;
+ bool m_frozen;
+ bool m_modal;
+ static bool m_modalVisible;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/QDeferScrollView.cpp b/src/gui/widgets/QDeferScrollView.cpp
new file mode 100644
index 0000000..01864a3
--- /dev/null
+++ b/src/gui/widgets/QDeferScrollView.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "QDeferScrollView.h"
+
+#include <qscrollview.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+QDeferScrollView::QDeferScrollView(QWidget* parent, const char *name, WFlags f)
+ : QScrollView(parent, name, f)
+{
+ setFocusPolicy(QWidget::WheelFocus);
+}
+
+void QDeferScrollView::setBottomMargin(int m)
+{
+ setMargins(leftMargin(), topMargin(), rightMargin(), m);
+}
+
+void QDeferScrollView::contentsWheelEvent(QWheelEvent* e)
+{
+ emit gotWheelEvent(e);
+}
+
+}
+#include "QDeferScrollView.moc"
diff --git a/src/gui/widgets/QDeferScrollView.h b/src/gui/widgets/QDeferScrollView.h
new file mode 100644
index 0000000..e4b2e3d
--- /dev/null
+++ b/src/gui/widgets/QDeferScrollView.h
@@ -0,0 +1,75 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_QDEFERSCROLLVIEW_H_
+#define _RG_QDEFERSCROLLVIEW_H_
+
+#include <qscrollview.h>
+
+
+class QWidget;
+class QWheelEvent;
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * A QScrollView which defers vertical scrolling (through mouse wheel)
+ * elsewhere, typically another QScrollView, so that both can be kept
+ * in sync. The master scrollview will connect its vertical scrollbar
+ * to the slave view so the scrollbar will act on both views.
+ *
+ * The slave scrollview will defer its scrolling to the master by
+ * having the gotWheelEvent() signal connected to a slot in the master
+ * scrollview, which will simply process the wheel event as if it had
+ * received it itself.
+ *
+ * @see TrackEditor
+ * @see SegmentCanvas
+ * @see TrackEditor::m_trackButtonScroll
+ */
+class QDeferScrollView : public QScrollView
+{
+ Q_OBJECT
+public:
+ QDeferScrollView(QWidget* parent=0, const char *name=0, WFlags f=0);
+
+ void setBottomMargin(int);
+
+signals:
+ void gotWheelEvent(QWheelEvent*);
+
+protected:
+ virtual void contentsWheelEvent(QWheelEvent*);
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/QuantizeParameters.cpp b/src/gui/widgets/QuantizeParameters.cpp
new file mode 100644
index 0000000..19ba96a
--- /dev/null
+++ b/src/gui/widgets/QuantizeParameters.cpp
@@ -0,0 +1,497 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "QuantizeParameters.h"
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include <klocale.h>
+#include "base/NotationTypes.h"
+#include "base/Quantizer.h"
+#include "base/BasicQuantizer.h"
+#include "base/LegatoQuantizer.h"
+#include "base/NotationQuantizer.h"
+#include "gui/editors/notation/NotationStrings.h"
+#include "gui/editors/notation/NotePixmapFactory.h"
+#include <kcombobox.h>
+#include <kconfig.h>
+#include <qcheckbox.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qobject.h>
+#include <qpixmap.h>
+#include <qpushbutton.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+QuantizeParameters::QuantizeParameters(QWidget *parent,
+ QuantizerType defaultQuantizer,
+ bool showNotationOption,
+ bool showAdvancedButton,
+ QString configCategory,
+ QString preamble) :
+ QFrame(parent),
+ m_configCategory(configCategory),
+ m_standardQuantizations
+ (BasicQuantizer::getStandardQuantizations())
+{
+ m_mainLayout = new QGridLayout(this,
+ preamble ? 3 : 4, 2,
+ preamble ? 10 : 0,
+ preamble ? 5 : 4);
+
+ int zero = 0;
+ if (preamble) {
+ QLabel *label = new QLabel(preamble, this);
+ label->setAlignment(Qt::WordBreak);
+ m_mainLayout->addMultiCellWidget(label, 0, 0, 0, 1);
+ zero = 1;
+ }
+
+ QGroupBox *quantizerBox = new QGroupBox
+ (1, Horizontal, i18n("Quantizer"), this);
+
+ m_mainLayout->addWidget(quantizerBox, zero, 0);
+ QFrame *typeFrame = new QFrame(quantizerBox);
+
+ QGridLayout *layout = new QGridLayout(typeFrame, 2, 2, 5, 3);
+ layout->addWidget(new QLabel(i18n("Quantizer type:"), typeFrame), 0, 0);
+ m_typeCombo = new KComboBox(typeFrame);
+ m_typeCombo->insertItem(i18n("Grid quantizer"));
+ m_typeCombo->insertItem(i18n("Legato quantizer"));
+ m_typeCombo->insertItem(i18n("Heuristic notation quantizer"));
+ layout->addWidget(m_typeCombo, 0, 1);
+
+ m_notationTarget = new QCheckBox
+ (i18n("Quantize for notation only (leave performance unchanged)"),
+ typeFrame);
+ layout->addMultiCellWidget(m_notationTarget, 1, 1, 0, 1);
+ if (!showNotationOption)
+ m_notationTarget->hide();
+
+ QHBox *parameterBox = new QHBox(this);
+ m_mainLayout->addWidget(parameterBox, zero + 1, 0);
+
+ m_notationBox = new QGroupBox
+ (1, Horizontal, i18n("Notation parameters"), parameterBox);
+ QFrame *notationFrame = new QFrame(m_notationBox);
+
+ layout = new QGridLayout(notationFrame, 4, 2, 5, 3);
+
+ layout->addWidget(new QLabel(i18n("Base grid unit:"), notationFrame),
+ 1, 0);
+ m_notationUnitCombo = new KComboBox(notationFrame);
+ layout->addWidget(m_notationUnitCombo, 1, 1);
+
+ layout->addWidget(new QLabel(i18n("Complexity:"),
+ notationFrame), 0, 0);
+
+ m_simplicityCombo = new KComboBox(notationFrame);
+ m_simplicityCombo->insertItem(i18n("Very high"));
+ m_simplicityCombo->insertItem(i18n("High"));
+ m_simplicityCombo->insertItem(i18n("Normal"));
+ m_simplicityCombo->insertItem(i18n("Low"));
+ m_simplicityCombo->insertItem(i18n("Very low"));
+ layout->addWidget(m_simplicityCombo, 0, 1);
+
+ layout->addWidget(new QLabel(i18n("Tuplet level:"),
+ notationFrame), 2, 0);
+ m_maxTuplet = new KComboBox(notationFrame);
+ m_maxTuplet->insertItem(i18n("None"));
+ m_maxTuplet->insertItem(i18n("2-in-the-time-of-3"));
+ m_maxTuplet->insertItem(i18n("Triplet"));
+ /*
+ m_maxTuplet->insertItem(i18n("4-Tuplet"));
+ m_maxTuplet->insertItem(i18n("5-Tuplet"));
+ m_maxTuplet->insertItem(i18n("6-Tuplet"));
+ m_maxTuplet->insertItem(i18n("7-Tuplet"));
+ m_maxTuplet->insertItem(i18n("8-Tuplet"));
+ */
+ m_maxTuplet->insertItem(i18n("Any"));
+ layout->addWidget(m_maxTuplet, 2, 1);
+
+ m_counterpoint = new QCheckBox(i18n("Permit counterpoint"), notationFrame);
+ layout->addMultiCellWidget(m_counterpoint, 3, 3, 0, 1);
+
+ m_gridBox = new QGroupBox
+ (1, Horizontal, i18n("Grid parameters"), parameterBox);
+ QFrame *gridFrame = new QFrame(m_gridBox);
+
+ layout = new QGridLayout(gridFrame, 4, 2, 5, 3);
+
+ layout->addWidget(new QLabel(i18n("Base grid unit:"), gridFrame), 0, 0);
+ m_gridUnitCombo = new KComboBox(gridFrame);
+ layout->addWidget(m_gridUnitCombo, 0, 1);
+
+ m_swingLabel = new QLabel(i18n("Swing:"), gridFrame);
+ layout->addWidget(m_swingLabel, 1, 0);
+ m_swingCombo = new KComboBox(gridFrame);
+ layout->addWidget(m_swingCombo, 1, 1);
+
+ m_iterativeLabel = new QLabel(i18n("Iterative amount:"), gridFrame);
+ layout->addWidget(m_iterativeLabel, 2, 0);
+ m_iterativeCombo = new KComboBox(gridFrame);
+ layout->addWidget(m_iterativeCombo, 2, 1);
+
+ m_durationCheckBox = new QCheckBox
+ (i18n("Quantize durations as well as start times"), gridFrame);
+ layout->addMultiCellWidget(m_durationCheckBox, 3, 3, 0, 1);
+
+ m_postProcessingBox = new QGroupBox
+ (1, Horizontal, i18n("After quantization"), this);
+
+ if (preamble) {
+ m_mainLayout->addMultiCellWidget(m_postProcessingBox,
+ zero, zero + 1,
+ 1, 1);
+ } else {
+ m_mainLayout->addWidget(m_postProcessingBox, zero + 3, 0);
+ }
+
+ bool advanced = true;
+ m_advancedButton = 0;
+ if (showAdvancedButton) {
+ m_advancedButton =
+ new QPushButton(i18n("Show advanced options"), this);
+ m_mainLayout->addWidget(m_advancedButton, zero + 2, 0, Qt::AlignLeft);
+ QObject::connect(m_advancedButton, SIGNAL(clicked()),
+ this, SLOT(slotAdvancedChanged()));
+ }
+
+ QFrame *postFrame = new QFrame(m_postProcessingBox);
+
+ layout = new QGridLayout(postFrame, 4, 1, 5, 3);
+ m_rebeam = new QCheckBox(i18n("Re-beam"), postFrame);
+ m_articulate = new QCheckBox
+ (i18n("Add articulations (staccato, tenuto, slurs)"), postFrame);
+ m_makeViable = new QCheckBox(i18n("Tie notes at barlines etc"), postFrame);
+ m_deCounterpoint = new QCheckBox(i18n("Split-and-tie overlapping chords"), postFrame);
+
+ layout->addWidget(m_rebeam, 0, 0);
+ layout->addWidget(m_articulate, 1, 0);
+ layout->addWidget(m_makeViable, 2, 0);
+ layout->addWidget(m_deCounterpoint, 3, 0);
+
+ QPixmap noMap = NotePixmapFactory::toQPixmap
+ (NotePixmapFactory::makeToolbarPixmap("menu-no-note"));
+
+ int defaultType = 0;
+ timeT defaultUnit =
+ Note(Note::Demisemiquaver).getDuration();
+
+ if (!m_configCategory) {
+ if (defaultQuantizer == Notation)
+ m_configCategory = "Quantize Dialog Notation";
+ else
+ m_configCategory = "Quantize Dialog Grid";
+ }
+
+ int defaultSwing = 0;
+ int defaultIterate = 100;
+
+ if (m_configCategory) {
+ KConfig *config = kapp->config();
+ config->setGroup(m_configCategory);
+ defaultType =
+ config->readNumEntry("quantizetype",
+ (defaultQuantizer == Notation) ? 2 :
+ (defaultQuantizer == Legato) ? 1 :
+ 0);
+ defaultUnit =
+ config->readNumEntry("quantizeunit", defaultUnit);
+ defaultSwing =
+ config->readNumEntry("quantizeswing", defaultSwing);
+ defaultIterate =
+ config->readNumEntry("quantizeiterate", defaultIterate);
+ m_notationTarget->setChecked
+ (config->readBoolEntry("quantizenotationonly",
+ defaultQuantizer == Notation));
+ m_durationCheckBox->setChecked
+ (config->readBoolEntry("quantizedurations", false));
+ m_simplicityCombo->setCurrentItem
+ (config->readNumEntry("quantizesimplicity", 13) - 11);
+ m_maxTuplet->setCurrentItem
+ (config->readNumEntry("quantizemaxtuplet", 3) - 1);
+ m_counterpoint->setChecked
+ (config->readBoolEntry("quantizecounterpoint", false));
+ m_rebeam->setChecked
+ (config->readBoolEntry("quantizerebeam", true));
+ m_makeViable->setChecked
+ (config->readBoolEntry("quantizemakeviable", false));
+ m_deCounterpoint->setChecked
+ (config->readBoolEntry("quantizedecounterpoint", false));
+ m_articulate->setChecked
+ (config->readBoolEntry("quantizearticulate", true));
+ advanced = config->readBoolEntry("quantizeshowadvanced", false);
+ } else {
+ defaultType =
+ (defaultQuantizer == Notation) ? 2 :
+ (defaultQuantizer == Legato) ? 1 : 0;
+ m_notationTarget->setChecked(defaultQuantizer == Notation);
+ m_durationCheckBox->setChecked(false);
+ m_simplicityCombo->setCurrentItem(2);
+ m_maxTuplet->setCurrentItem(2);
+ m_counterpoint->setChecked(false);
+ m_rebeam->setChecked(true);
+ m_makeViable->setChecked(defaultQuantizer == Notation);
+ m_deCounterpoint->setChecked(defaultQuantizer == Notation);
+ m_articulate->setChecked(true);
+ advanced = false;
+ }
+
+ if (preamble || advanced) {
+ m_postProcessingBox->show();
+ } else {
+ m_postProcessingBox->hide();
+ }
+
+ for (unsigned int i = 0; i < m_standardQuantizations.size(); ++i) {
+
+ timeT time = m_standardQuantizations[i];
+ timeT error = 0;
+
+ QPixmap pmap = NotePixmapFactory::toQPixmap
+ (NotePixmapFactory::makeNoteMenuPixmap(time, error));
+ QString label = NotationStrings::makeNoteMenuLabel(time, false, error);
+
+ if (error == 0) {
+ m_gridUnitCombo->insertItem(pmap, label);
+ m_notationUnitCombo->insertItem(pmap, label);
+ } else {
+ m_gridUnitCombo->insertItem(noMap, QString("%1").arg(time));
+ m_notationUnitCombo->insertItem(noMap, QString("%1").arg(time));
+ }
+
+ if (m_standardQuantizations[i] == defaultUnit) {
+ m_gridUnitCombo->setCurrentItem(m_gridUnitCombo->count() - 1);
+ m_notationUnitCombo->setCurrentItem
+ (m_notationUnitCombo->count() - 1);
+ }
+ }
+
+ for (int i = -100; i <= 200; i += 10) {
+ m_swingCombo->insertItem(i == 0 ? i18n("None") : QString("%1%").arg(i));
+ if (i == defaultSwing)
+ m_swingCombo->setCurrentItem(m_swingCombo->count() - 1);
+ }
+
+ for (int i = 10; i <= 100; i += 10) {
+ m_iterativeCombo->insertItem(i == 100 ? i18n("Full quantize") :
+ QString("%1%").arg(i));
+ if (i == defaultIterate)
+ m_iterativeCombo->setCurrentItem(m_iterativeCombo->count() - 1);
+ }
+
+ switch (defaultType) {
+ case 0: // grid
+ m_gridBox->show();
+ m_swingLabel->show();
+ m_swingCombo->show();
+ m_iterativeLabel->show();
+ m_iterativeCombo->show();
+ m_notationBox->hide();
+ m_durationCheckBox->show();
+ m_typeCombo->setCurrentItem(0);
+ break;
+ case 1: // legato
+ m_gridBox->show();
+ m_swingLabel->hide();
+ m_swingCombo->hide();
+ m_iterativeLabel->hide();
+ m_iterativeCombo->hide();
+ m_notationBox->hide();
+ m_durationCheckBox->hide();
+ m_typeCombo->setCurrentItem(1);
+ case 2: // notation
+ m_gridBox->hide();
+ m_notationBox->show();
+ m_typeCombo->setCurrentItem(2);
+ break;
+ }
+
+ connect(m_typeCombo, SIGNAL(activated(int)), SLOT(slotTypeChanged(int)));
+}
+
+Quantizer *
+QuantizeParameters::getQuantizer() const
+{
+ //!!! Excessive duplication with
+ // EventQuantizeCommand::makeQuantizer in editcommands.cpp
+
+ int type = m_typeCombo->currentItem();
+ timeT unit = 0;
+
+ if (type == 0 || type == 1) {
+ unit = m_standardQuantizations[m_gridUnitCombo->currentItem()];
+ } else {
+ unit = m_standardQuantizations[m_notationUnitCombo->currentItem()];
+ }
+
+ Quantizer *quantizer = 0;
+
+ int swing = m_swingCombo->currentItem();
+ swing *= 10;
+ swing -= 100;
+
+ int iterate = m_iterativeCombo->currentItem();
+ iterate *= 10;
+ iterate += 10;
+
+ if (type == 0) {
+
+ if (m_notationTarget->isChecked()) {
+ quantizer = new BasicQuantizer
+ (Quantizer::RawEventData,
+ Quantizer::NotationPrefix,
+ unit, m_durationCheckBox->isChecked(),
+ swing, iterate);
+ } else {
+ quantizer = new BasicQuantizer
+ (Quantizer::RawEventData,
+ Quantizer::RawEventData,
+ unit, m_durationCheckBox->isChecked(),
+ swing, iterate);
+ }
+ } else if (type == 1) {
+ if (m_notationTarget->isChecked()) {
+ quantizer = new LegatoQuantizer
+ (Quantizer::RawEventData,
+ Quantizer::NotationPrefix, unit);
+ } else {
+ quantizer = new LegatoQuantizer
+ (Quantizer::RawEventData,
+ Quantizer::RawEventData,
+ unit);
+ }
+ } else {
+
+ NotationQuantizer *nq;
+
+ if (m_notationTarget->isChecked()) {
+ nq = new NotationQuantizer();
+ } else {
+ nq = new NotationQuantizer
+ (Quantizer::RawEventData,
+ Quantizer::RawEventData);
+ }
+
+ nq->setUnit(unit);
+ nq->setSimplicityFactor(m_simplicityCombo->currentItem() + 11);
+ nq->setMaxTuplet(m_maxTuplet->currentItem() + 1);
+ nq->setContrapuntal(m_counterpoint->isChecked());
+ nq->setArticulate(m_articulate->isChecked());
+
+ quantizer = nq;
+ }
+
+ if (m_configCategory) {
+ KConfig *config = kapp->config();
+ config->setGroup(m_configCategory);
+ config->writeEntry("quantizetype", type);
+ config->writeEntry("quantizeunit", unit);
+ config->writeEntry("quantizeswing", swing);
+ config->writeEntry("quantizeiterate", iterate);
+ config->writeEntry("quantizenotationonly",
+ m_notationTarget->isChecked());
+ if (type == 0) {
+ config->writeEntry("quantizedurations",
+ m_durationCheckBox->isChecked());
+ } else {
+ config->writeEntry("quantizesimplicity",
+ m_simplicityCombo->currentItem() + 11);
+ config->writeEntry("quantizemaxtuplet",
+ m_maxTuplet->currentItem() + 1);
+ config->writeEntry("quantizecounterpoint",
+ m_counterpoint->isChecked());
+ config->writeEntry("quantizearticulate",
+ m_articulate->isChecked());
+ }
+ config->writeEntry("quantizerebeam", m_rebeam->isChecked());
+ config->writeEntry("quantizemakeviable", m_makeViable->isChecked());
+ config->writeEntry("quantizedecounterpoint", m_deCounterpoint->isChecked());
+ }
+
+ return quantizer;
+}
+
+void
+QuantizeParameters::slotAdvancedChanged()
+{
+ if (m_postProcessingBox->isVisible()) {
+ if (m_advancedButton)
+ m_advancedButton->setText(i18n("Show Advanced Options"));
+ m_postProcessingBox->hide();
+ } else {
+ if (m_advancedButton)
+ m_advancedButton->setText(i18n("Hide Advanced Options"));
+ m_postProcessingBox->show();
+ }
+ adjustSize();
+}
+
+void
+QuantizeParameters::showAdvanced(bool show)
+{
+ if (show) {
+ m_postProcessingBox->show();
+ } else {
+ m_postProcessingBox->hide();
+ }
+ adjustSize();
+}
+
+void
+QuantizeParameters::slotTypeChanged(int index)
+{
+ if (index == 0) {
+ m_gridBox->show();
+ m_swingLabel->show();
+ m_swingCombo->show();
+ m_iterativeLabel->show();
+ m_iterativeCombo->show();
+ m_durationCheckBox->show();
+ m_notationBox->hide();
+ } else if (index == 1) {
+ m_gridBox->show();
+ m_swingLabel->hide();
+ m_swingCombo->hide();
+ m_iterativeLabel->hide();
+ m_iterativeCombo->hide();
+ m_durationCheckBox->hide();
+ m_notationBox->hide();
+ } else {
+ m_gridBox->hide();
+ m_notationBox->show();
+ }
+}
+
+}
+#include "QuantizeParameters.moc"
diff --git a/src/gui/widgets/QuantizeParameters.h b/src/gui/widgets/QuantizeParameters.h
new file mode 100644
index 0000000..8eee7ff
--- /dev/null
+++ b/src/gui/widgets/QuantizeParameters.h
@@ -0,0 +1,117 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ROSEGARDENQUANTIZEPARAMETERS_H_
+#define _RG_ROSEGARDENQUANTIZEPARAMETERS_H_
+
+#include <qframe.h>
+#include <qstring.h>
+#include <vector>
+#include "base/Event.h"
+#include <qgroupbox.h>
+
+
+class QWidget;
+class QPushButton;
+class QLabel;
+class QGridLayout;
+class QCheckBox;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class Quantizer;
+
+
+class QuantizeParameters : public QFrame
+{
+ Q_OBJECT
+public:
+ enum QuantizerType { Grid, Legato, Notation };
+
+ QuantizeParameters(QWidget *parent,
+ QuantizerType defaultQuantizer,
+ bool showNotationOption,
+ bool showAdvancedButton,
+ QString configCategory,
+ QString preamble = 0);
+
+ /**
+ * Returned quantizer object is on heap -- caller must delete.
+ * Also writes values to KConfig if so requested in constructor.
+ */
+ Quantizer *getQuantizer() const;
+
+ QWidget *getAdvancedWidget() { return m_postProcessingBox; }
+
+ bool shouldRebeam() const { return m_rebeam; }
+ bool shouldDeCounterpoint() const { return m_deCounterpoint; }
+ bool shouldMakeViable() const { return m_makeViable; }
+
+ void showAdvanced(bool show);
+
+public slots:
+ void slotTypeChanged(int);
+ void slotAdvancedChanged();
+
+protected:
+ QString m_configCategory;
+
+ std::vector<timeT> m_standardQuantizations;
+
+ QGridLayout *m_mainLayout;
+
+ KComboBox *m_typeCombo;
+
+ QGroupBox *m_gridBox;
+ QCheckBox *m_durationCheckBox;
+ KComboBox *m_gridUnitCombo;
+ QLabel *m_swingLabel;
+ KComboBox *m_swingCombo;
+ QLabel *m_iterativeLabel;
+ KComboBox *m_iterativeCombo;
+
+ QGroupBox *m_notationBox;
+ QCheckBox *m_notationTarget;
+ KComboBox *m_notationUnitCombo;
+ KComboBox *m_simplicityCombo;
+ KComboBox *m_maxTuplet;
+ QCheckBox *m_counterpoint;
+
+ QPushButton *m_advancedButton;
+ QGroupBox *m_postProcessingBox;
+ QCheckBox *m_articulate;
+ QCheckBox *m_makeViable;
+ QCheckBox *m_deCounterpoint;
+ QCheckBox *m_rebeam;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/RosegardenPopupMenu.h b/src/gui/widgets/RosegardenPopupMenu.h
new file mode 100644
index 0000000..aca82a9
--- /dev/null
+++ b/src/gui/widgets/RosegardenPopupMenu.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ROSEGARDENPOPUPMENU_H_
+#define _RG_ROSEGARDENPOPUPMENU_H_
+
+#include <qpopupmenu.h>
+
+namespace Rosegarden {
+
+class RosegardenPopupMenu : public QPopupMenu
+{
+ // just to make itemHeight public
+public:
+ RosegardenPopupMenu(QWidget *parent) : QPopupMenu(parent) { }
+ using QPopupMenu::itemHeight;
+};
+
+
+}
+
+#endif /*ROSEGARDENPOPUPMENU_H_*/
diff --git a/src/gui/widgets/Rotary.cpp b/src/gui/widgets/Rotary.cpp
new file mode 100644
index 0000000..36d5817
--- /dev/null
+++ b/src/gui/widgets/Rotary.cpp
@@ -0,0 +1,560 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "Rotary.h"
+
+#include "misc/Debug.h"
+#include "gui/dialogs/FloatEdit.h"
+#include "gui/general/GUIPalette.h"
+#include "TextFloat.h"
+#include <kapplication.h>
+#include <klocale.h>
+#include <qbrush.h>
+#include <qcolor.h>
+#include <qdialog.h>
+#include <qimage.h>
+#include <qpainter.h>
+#include <qpalette.h>
+#include <qpen.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include <qtimer.h>
+#include <qtooltip.h>
+#include <qwidget.h>
+#include <cmath>
+
+
+namespace Rosegarden
+{
+
+#define ROTARY_MIN (0.25 * M_PI)
+#define ROTARY_MAX (1.75 * M_PI)
+#define ROTARY_RANGE (ROTARY_MAX - ROTARY_MIN)
+
+static TextFloat* _float = 0;
+static QTimer *_floatTimer = 0;
+
+Rotary::PixmapCache Rotary::m_pixmaps;
+
+
+Rotary::Rotary(QWidget *parent,
+ float minValue,
+ float maxValue,
+ float step,
+ float pageStep,
+ float initialPosition,
+ int size,
+ TickMode ticks,
+ bool snapToTicks,
+ bool centred,
+ bool logarithmic) :
+ QWidget(parent),
+ m_minValue(minValue),
+ m_maxValue(maxValue),
+ m_step(step),
+ m_pageStep(pageStep),
+ m_size(size),
+ m_tickMode(ticks),
+ m_snapToTicks(snapToTicks),
+ m_centred(centred),
+ m_position(initialPosition),
+ m_snapPosition(m_position),
+ m_initialPosition(initialPosition),
+ m_buttonPressed(false),
+ m_lastY(0),
+ m_lastX(0),
+ m_knobColour(0, 0, 0),
+ m_logarithmic(logarithmic)
+{
+ setBackgroundMode(Qt::NoBackground);
+
+ if (!_float)
+ _float = new TextFloat(this);
+
+ if (!_floatTimer) {
+ _floatTimer = new QTimer();
+ }
+
+ // connect timer
+ connect(_floatTimer, SIGNAL(timeout()), this,
+ SLOT(slotFloatTimeout()));
+ _float->hide();
+
+ QToolTip::add
+ (this,
+ i18n("Click and drag up and down or left and right to modify.\nDouble click to edit value directly."));
+ setFixedSize(size, size);
+
+ emit valueChanged(m_snapPosition);
+}
+
+Rotary::~Rotary()
+{
+ // Remove this connection
+ //
+ disconnect(_floatTimer, SIGNAL(timeout()), this,
+ SLOT(slotFloatTimeout()));
+
+ delete _float;
+ _float = 0;
+}
+
+void
+Rotary::slotFloatTimeout()
+{
+ if (_float)
+ _float->hide();
+}
+
+void
+Rotary::setKnobColour(const QColor &colour)
+{
+ m_knobColour = colour;
+ repaint();
+}
+
+void
+Rotary::paintEvent(QPaintEvent *)
+{
+ QPainter paint;
+
+ double angle = ROTARY_MIN // offset
+ + (ROTARY_RANGE *
+ (double(m_snapPosition - m_minValue) /
+ (double(m_maxValue) - double(m_minValue))));
+ int degrees = int(angle * 180.0 / M_PI);
+
+ // RG_DEBUG << "degrees: " << degrees << ", size " << m_size << ", pixel " << m_knobColour.pixel() << endl;
+
+ int numTicks = 0;
+ switch (m_tickMode) {
+ case LimitTicks:
+ numTicks = 2;
+ break;
+ case IntervalTicks:
+ numTicks = 5;
+ break;
+ case PageStepTicks:
+ numTicks = 1 + (m_maxValue + 0.0001 - m_minValue) / m_pageStep;
+ break;
+ case StepTicks:
+ numTicks = 1 + (m_maxValue + 0.0001 - m_minValue) / m_step;
+ break;
+ default:
+ break;
+ }
+
+ CacheIndex index(m_size, m_knobColour.pixel(), degrees, numTicks, m_centred);
+
+ if (m_pixmaps.find(index) != m_pixmaps.end()) {
+ paint.begin(this);
+ paint.drawPixmap(0, 0, m_pixmaps[index]);
+ paint.end();
+ return ;
+ }
+
+ int scale = 4;
+ int width = m_size * scale;
+ QPixmap map(width, width);
+ map.fill(paletteBackgroundColor());
+ paint.begin(&map);
+
+ QPen pen;
+ pen.setColor(kapp->palette().color(QPalette::Active, QColorGroup::Dark));
+ pen.setWidth(scale);
+ paint.setPen(pen);
+
+ if (m_knobColour != Qt::black) {
+ paint.setBrush(m_knobColour);
+ } else {
+ paint.setBrush(
+ kapp->palette().color(QPalette::Active, QColorGroup::Base));
+ }
+
+ QColor c(m_knobColour);
+ pen.setColor(c);
+ paint.setPen(pen);
+
+ int indent = width * 0.15 + 1;
+
+ paint.drawEllipse(indent, indent, width - 2*indent, width - 2*indent);
+
+ pen.setWidth(2 * scale);
+ int pos = indent + (width - 2 * indent) / 8;
+ int darkWidth = (width - 2 * indent) * 2 / 3;
+ int darkQuote = (130 * 2 / (darkWidth ? darkWidth : 1)) + 100;
+ while (darkWidth) {
+ c = c.light(101);
+ pen.setColor(c);
+ paint.setPen(pen);
+ paint.drawEllipse(pos, pos, darkWidth, darkWidth);
+ if (!--darkWidth)
+ break;
+ paint.drawEllipse(pos, pos, darkWidth, darkWidth);
+ if (!--darkWidth)
+ break;
+ paint.drawEllipse(pos, pos, darkWidth, darkWidth);
+ ++pos;
+ --darkWidth;
+ }
+
+ paint.setBrush(QBrush::NoBrush);
+
+ pen.setColor(colorGroup().dark());
+ pen.setWidth(scale);
+ paint.setPen(pen);
+
+ for (int i = 0; i < numTicks; ++i) {
+ int div = numTicks;
+ if (div > 1)
+ --div;
+ drawTick(paint, ROTARY_MIN + (ROTARY_MAX - ROTARY_MIN) * i / div,
+ width, i != 0 && i != numTicks - 1);
+ }
+
+ // now the bright metering bit
+
+ pen.setColor(GUIPalette::getColour(GUIPalette::RotaryMeter));
+ pen.setWidth(indent);
+ paint.setPen(pen);
+
+ if (m_centred) {
+ paint.drawArc(indent / 2, indent / 2, width - indent, width - indent,
+ 90 * 16, -(degrees - 180) * 16);
+ } else {
+ paint.drawArc(indent / 2, indent / 2, width - indent, width - indent,
+ (180 + 45) * 16, -(degrees - 45) * 16);
+ }
+
+ pen.setWidth(scale);
+ paint.setPen(pen);
+
+ int shadowAngle = -720;
+ c = colorGroup().dark();
+ for (int arc = 120; arc < 2880; arc += 240) {
+ pen.setColor(c);
+ paint.setPen(pen);
+ paint.drawArc(indent, indent, width - 2*indent, width - 2*indent, shadowAngle + arc, 240);
+ paint.drawArc(indent, indent, width - 2*indent, width - 2*indent, shadowAngle - arc, 240);
+ c = c.light( 110 );
+ }
+
+ shadowAngle = 2160;
+ c = colorGroup().dark();
+ for (int arc = 120; arc < 2880; arc += 240) {
+ pen.setColor(c);
+ paint.setPen(pen);
+ paint.drawArc(scale / 2, scale / 2, width - scale, width - scale, shadowAngle + arc, 240);
+ paint.drawArc(scale / 2, scale / 2, width - scale, width - scale, shadowAngle - arc, 240);
+ c = c.light( 109 );
+ }
+
+ // and un-draw the bottom part
+ pen.setColor(paletteBackgroundColor());
+ paint.setPen(pen);
+ paint.drawArc(scale / 2, scale / 2, width - scale, width - scale,
+ -45 * 16, -90 * 16);
+
+ double hyp = double(width) / 2.0;
+ double len = hyp - indent;
+ --len;
+
+ double x0 = hyp;
+ double y0 = hyp;
+
+ double x = hyp - len * sin(angle);
+ double y = hyp + len * cos(angle);
+
+ pen.setWidth(scale * 2);
+ pen.setColor(colorGroup().dark());
+ paint.setPen(pen);
+
+ paint.drawLine(int(x0), int(y0), int(x), int(y));
+
+ paint.end();
+
+ QImage i = map.convertToImage().smoothScale(m_size, m_size);
+ m_pixmaps[index] = QPixmap(i);
+ paint.begin(this);
+ paint.drawPixmap(0, 0, m_pixmaps[index]);
+ paint.end();
+}
+
+void
+Rotary::drawTick(QPainter &paint, double angle, int size, bool internal)
+{
+ double hyp = double(size) / 2.0;
+ double x0 = hyp - (hyp - 1) * sin(angle);
+ double y0 = hyp + (hyp - 1) * cos(angle);
+
+ if (internal) {
+
+ double len = hyp / 4;
+ double x1 = hyp - (hyp - len) * sin(angle);
+ double y1 = hyp + (hyp - len) * cos(angle);
+
+ paint.drawLine(int(x0), int(y0), int(x1), int(y1));
+
+ } else {
+
+ double len = hyp / 4;
+ double x1 = hyp - (hyp + len) * sin(angle);
+ double y1 = hyp + (hyp + len) * cos(angle);
+
+ paint.drawLine(int(x0), int(y0), int(x1), int(y1));
+ }
+}
+
+void
+Rotary::snapPosition()
+{
+ m_snapPosition = m_position;
+
+ if (m_snapToTicks) {
+
+ switch (m_tickMode) {
+
+ case NoTicks:
+ break; // meaningless
+
+ case LimitTicks:
+ if (m_position < (m_minValue + m_maxValue) / 2.0) {
+ m_snapPosition = m_minValue;
+ } else {
+ m_snapPosition = m_maxValue;
+ }
+ break;
+
+ case IntervalTicks:
+ m_snapPosition = m_minValue +
+ (m_maxValue - m_minValue) / 4.0 *
+ int((m_snapPosition - m_minValue) /
+ ((m_maxValue - m_minValue) / 4.0));
+ break;
+
+ case PageStepTicks:
+ m_snapPosition = m_minValue +
+ m_pageStep *
+ int((m_snapPosition - m_minValue) / m_pageStep);
+ break;
+
+ case StepTicks:
+ m_snapPosition = m_minValue +
+ m_step *
+ int((m_snapPosition - m_minValue) / m_step);
+ break;
+ }
+ }
+}
+
+void
+Rotary::mousePressEvent(QMouseEvent *e)
+{
+ if (e->button() == LeftButton) {
+ m_buttonPressed = true;
+ m_lastY = e->y();
+ m_lastX = e->x();
+ } else if (e->button() == MidButton) // reset to default
+ {
+ m_position = m_initialPosition;
+ snapPosition();
+ update();
+ emit valueChanged(m_snapPosition);
+ } else if (e->button() == RightButton) // reset to centre position
+ {
+ m_position = (m_maxValue + m_minValue) / 2.0;
+ snapPosition();
+ update();
+ emit valueChanged(m_snapPosition);
+ }
+
+ QPoint totalPos = mapTo(topLevelWidget(), QPoint(0, 0));
+
+ if (!_float)
+ _float = new TextFloat(this);
+ _float->reparent(this);
+ _float->move(totalPos + QPoint(width() + 2, -height() / 2));
+ if (m_logarithmic) {
+ _float->setText(QString("%1").arg(powf(10, m_position)));
+ } else {
+ _float->setText(QString("%1").arg(m_position));
+ }
+ _float->show();
+
+// std::cerr << "Rotary::mousePressEvent: logarithmic = " << m_logarithmic
+// << ", position = " << m_position << std::endl;
+
+ if (e->button() == RightButton || e->button() == MidButton) {
+ // one shot, 500ms
+ _floatTimer->start(500, true);
+ }
+}
+
+void
+Rotary::mouseDoubleClickEvent(QMouseEvent * /*e*/)
+{
+ float minv = m_minValue;
+ float maxv = m_maxValue;
+ float val = m_position;
+ float step = m_step;
+
+ if (m_logarithmic) {
+ minv = powf(10, minv);
+ maxv = powf(10, maxv);
+ val = powf(10, val);
+ step = powf(10, step);
+ if (step > 0.001) step = 0.001;
+ }
+
+ FloatEdit dialog(this,
+ i18n("Select a new value"),
+ i18n("Enter a new value"),
+ minv,
+ maxv,
+ val,
+ step);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ float newval = dialog.getValue();
+ if (m_logarithmic) {
+ if (m_position < powf(10, -10)) m_position = -10;
+ else m_position = log10f(newval);
+ } else {
+ m_position = newval;
+ }
+ snapPosition();
+ update();
+
+ emit valueChanged(m_snapPosition);
+ }
+}
+
+void
+Rotary::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (e->button() == LeftButton) {
+ m_buttonPressed = false;
+ m_lastY = 0;
+ m_lastX = 0;
+
+ // Hide the float text
+ //
+ if (_float)
+ _float->hide();
+ }
+}
+
+void
+Rotary::mouseMoveEvent(QMouseEvent *e)
+{
+ if (m_buttonPressed) {
+ // Dragging by x or y axis when clicked modifies value
+ //
+ float newValue = m_position +
+ (m_lastY - float(e->y()) + float(e->x()) - m_lastX) * m_step;
+
+ if (newValue > m_maxValue)
+ m_position = m_maxValue;
+ else
+ if (newValue < m_minValue)
+ m_position = m_minValue;
+ else
+ m_position = newValue;
+
+ m_lastY = e->y();
+ m_lastX = e->x();
+
+ snapPosition();
+
+ // don't update if there's nothing to update
+ // if (m_lastPosition == m_snapPosition) return;
+
+ update();
+
+ emit valueChanged(m_snapPosition);
+
+ // draw on the float text
+ if (m_logarithmic) {
+ _float->setText(QString("%1").arg(powf(10, m_snapPosition)));
+ } else {
+ _float->setText(QString("%1").arg(m_snapPosition));
+ }
+ }
+}
+
+void
+Rotary::wheelEvent(QWheelEvent *e)
+{
+ if (e->delta() > 0)
+ m_position -= m_pageStep;
+ else
+ m_position += m_pageStep;
+
+ if (m_position > m_maxValue)
+ m_position = m_maxValue;
+
+ if (m_position < m_minValue)
+ m_position = m_minValue;
+
+ snapPosition();
+ update();
+
+ if (!_float)
+ _float = new TextFloat(this);
+
+ // draw on the float text
+ if (m_logarithmic) {
+ _float->setText(QString("%1").arg(powf(10, m_snapPosition)));
+ } else {
+ _float->setText(QString("%1").arg(m_snapPosition));
+ }
+
+ // Reposition - we need to sum the relative positions up to the
+ // topLevel or dialog to please move(). Move just top/right of the rotary
+ //
+ QPoint totalPos = mapTo(topLevelWidget(), QPoint(0, 0));
+ _float->reparent(this);
+ _float->move(totalPos + QPoint(width() + 2, -height() / 2));
+ _float->show();
+
+ // one shot, 500ms
+ _floatTimer->start(500, true);
+
+ // set it to show for a timeout value
+ emit valueChanged(m_snapPosition);
+}
+
+void
+Rotary::setPosition(float position)
+{
+ m_position = position;
+
+ snapPosition();
+ update();
+}
+
+}
+#include "Rotary.moc"
diff --git a/src/gui/widgets/Rotary.h b/src/gui/widgets/Rotary.h
new file mode 100644
index 0000000..2efacf9
--- /dev/null
+++ b/src/gui/widgets/Rotary.h
@@ -0,0 +1,167 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ROSEGARDENROTARY_H_
+#define _RG_ROSEGARDENROTARY_H_
+
+#include <map>
+#include <qcolor.h>
+#include <qwidget.h>
+
+
+class QWheelEvent;
+class QPaintEvent;
+class QPainter;
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+
+
+class Rotary : public QWidget
+{
+ Q_OBJECT
+public:
+
+ enum TickMode {
+ NoTicks, // plain circle with no marks for end points etc
+ LimitTicks, // marks at end points but not any intermediate points
+ IntervalTicks, // end points plus quarter, half, three-quarters
+ PageStepTicks, // end points plus every page-step interval
+ StepTicks // end points plus every step interval
+ };
+
+ Rotary(QWidget *parent,
+ float minValue = 0.0,
+ float maxValue = 100.0,
+ float step = 1.0,
+ float pageStep = 10.0,
+ float initialPosition = 50.0,
+ int size = 20,
+ TickMode ticks = NoTicks,
+ bool snapToTicks = false,
+ bool centred = false,
+ bool logarithmic = false); // extents are logs, exp for display
+ ~Rotary();
+
+ void setMinValue(float min) { m_minValue = min; }
+ float getMinValue() const { return m_minValue; }
+
+ void setMaxValue(float max) { m_maxValue = max; }
+ float getMaxValue() const { return m_maxValue; }
+
+ void setStep(float step) { m_step = step; }
+ float getStep() const { return m_step; }
+
+ void setPageStep(float step) { m_pageStep = step; }
+ float getPageStep() const { return m_pageStep; }
+
+ int getSize() const { return m_size; }
+
+ // Position
+ //
+ float getPosition() const { return m_position; }
+ void setPosition(float position);
+
+ // Set the colour of the knob
+ //
+ void setKnobColour(const QColor &colour);
+ QColor getKnobColour() const { return m_knobColour; }
+
+signals:
+ void valueChanged(float);
+
+protected slots:
+ void slotFloatTimeout();
+
+protected:
+ virtual void paintEvent(QPaintEvent *e);
+ virtual void mousePressEvent(QMouseEvent *e);
+ virtual void mouseReleaseEvent(QMouseEvent *e);
+ virtual void mouseMoveEvent(QMouseEvent *e);
+ virtual void mouseDoubleClickEvent(QMouseEvent *e);
+ virtual void wheelEvent(QWheelEvent *e);
+
+ void snapPosition();
+ void drawPosition();
+ void drawTick(QPainter &paint, double angle, int size, bool internal);
+
+ float m_minValue;
+ float m_maxValue;
+ float m_step;
+ float m_pageStep;
+ int m_size;
+ TickMode m_tickMode;
+ bool m_snapToTicks;
+ bool m_centred;
+ bool m_logarithmic;
+
+ float m_position;
+ float m_snapPosition;
+ float m_initialPosition;
+ bool m_buttonPressed;
+ int m_lastY;
+ int m_lastX;
+
+ QColor m_knobColour;
+
+ struct CacheIndex {
+
+ CacheIndex(int _s, int _c, int _a, int _n, int _ct) :
+ size(_s), colour(_c), angle(_a), numTicks(_n), centred(_ct) { }
+
+ bool operator<(const CacheIndex &i) const {
+ // woo!
+ if (size < i.size) return true;
+ else if (size > i.size) return false;
+ else if (colour < i.colour) return true;
+ else if (colour > i.colour) return false;
+ else if (angle < i.angle) return true;
+ else if (angle > i.angle) return false;
+ else if (numTicks < i.numTicks) return true;
+ else if (numTicks > i.numTicks) return false;
+ else if (centred == i.centred) return false;
+ else if (!centred) return true;
+ return false;
+ }
+
+ int size;
+ unsigned int colour;
+ int angle;
+ int numTicks;
+ bool centred;
+ };
+
+ typedef std::map<CacheIndex, QPixmap> PixmapCache;
+ static PixmapCache m_pixmaps;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/ScrollBox.cpp b/src/gui/widgets/ScrollBox.cpp
new file mode 100644
index 0000000..b409209
--- /dev/null
+++ b/src/gui/widgets/ScrollBox.cpp
@@ -0,0 +1,159 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ This file is based on code from KGhostView, Copyright 1997-2002
+ Markkhu Hihnala <mah@ee.oulu.fi>
+ and the KGhostView authors.
+
+ 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 "ScrollBox.h"
+
+#include <qapplication.h>
+#include <qframe.h>
+#include <qimage.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+ScrollBox::ScrollBox(QWidget* parent, SizeMode sizeMode, const char* name) :
+ QFrame(parent, name),
+ m_sizeMode(sizeMode)
+{
+ setFrameStyle(Panel | Sunken);
+}
+
+void ScrollBox::mousePressEvent(QMouseEvent* e)
+{
+ m_mouse = e->pos();
+ if (e->button() == RightButton)
+ emit button3Pressed();
+ if (e->button() == MidButton)
+ emit button2Pressed();
+}
+
+void ScrollBox::mouseMoveEvent(QMouseEvent* e)
+{
+ if (e->state() != LeftButton)
+ return ;
+
+ int dx = (e->pos().x() - m_mouse.x()) * m_pagesize.width() / width();
+ int dy = (e->pos().y() - m_mouse.y()) * m_pagesize.height() / height();
+
+ emit valueChanged(QPoint(m_viewpos.x() + dx, m_viewpos.y() + dy));
+ emit valueChangedRelative(dx, dy);
+
+ m_mouse = e->pos();
+}
+
+void ScrollBox::drawContents(QPainter* paint)
+{
+ if (m_pagesize.isEmpty())
+ return ;
+
+ QRect c(contentsRect());
+
+ paint->setPen(Qt::red);
+
+ int len = m_pagesize.width();
+ int x = c.x() + c.width() * m_viewpos.x() / len;
+ int w = c.width() * m_viewsize.width() / len ;
+ if (w > c.width())
+ w = c.width();
+
+ len = m_pagesize.height();
+ int y = c.y() + c.height() * m_viewpos.y() / len;
+ int h = c.height() * m_viewsize.height() / len;
+ if (h > c.height())
+ h = c.height();
+
+ paint->drawRect(x, y, w, h);
+}
+
+void ScrollBox::setPageSize(const QSize& s)
+{
+ m_pagesize = s;
+
+ setFixedWidth(100);
+ setFixedHeight(100);
+
+ int maxWidth = int(QApplication::desktop()->width() * 0.75);
+ int maxHeight = int(QApplication::desktop()->height() * 0.75);
+
+ if (m_sizeMode == FixWidth) {
+ int height = s.height() * width() / s.width();
+ if (height > maxHeight) {
+ setFixedWidth(width() * maxHeight / height);
+ height = maxHeight;
+ }
+ setFixedHeight(height);
+ } else {
+ int width = s.width() * height() / s.height();
+ if (width > maxWidth) {
+ setFixedHeight(height() * maxWidth / width);
+ width = maxWidth;
+ }
+ setFixedWidth(width);
+ }
+
+ repaint();
+}
+
+void ScrollBox::setViewSize(const QSize& s)
+{
+ m_viewsize = s;
+ repaint();
+}
+
+void ScrollBox::setViewPos(const QPoint& pos)
+{
+ m_viewpos = pos;
+ repaint();
+}
+
+void ScrollBox::setViewX(int x)
+{
+ m_viewpos = QPoint(x, m_viewpos.y());
+ repaint();
+}
+
+void ScrollBox::setViewY(int y)
+{
+ m_viewpos = QPoint(m_viewpos.x(), y);
+ repaint();
+}
+
+void ScrollBox::setThumbnail(QPixmap img)
+{
+ setPaletteBackgroundPixmap(img.convertToImage().smoothScale(size()));
+}
+
+}
+#include "ScrollBox.moc"
diff --git a/src/gui/widgets/ScrollBox.h b/src/gui/widgets/ScrollBox.h
new file mode 100644
index 0000000..3f8140e
--- /dev/null
+++ b/src/gui/widgets/ScrollBox.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ This file is based on code from KGhostView, Copyright 1997-2002
+ Markkhu Hihnala <mah@ee.oulu.fi>
+ and the KGhostView authors.
+
+ 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_SCROLLBOX_H_
+#define _RG_SCROLLBOX_H_
+
+#include <qframe.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qsize.h>
+
+
+class QWidget;
+class QPainter;
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class ScrollBox: public QFrame
+{
+ Q_OBJECT
+
+public:
+ enum SizeMode { FixWidth, FixHeight };
+
+ ScrollBox(QWidget *parent = 0,
+ SizeMode mode = FixWidth,
+ const char *name = 0);
+
+public slots:
+ void setPageSize(const QSize&);
+ void setViewSize(const QSize&);
+ void setViewPos(const QPoint&);
+ void setViewPos(int x, int y) { setViewPos(QPoint(x, y)); }
+ void setViewX(int x);
+ void setViewY(int y);
+ void setThumbnail(QPixmap img);
+
+signals:
+ void valueChanged(const QPoint&);
+ void valueChangedRelative(int dx, int dy);
+ void button2Pressed();
+ void button3Pressed();
+
+protected:
+ void mousePressEvent(QMouseEvent *);
+ void mouseMoveEvent(QMouseEvent *);
+ void drawContents(QPainter *);
+
+private:
+ QPoint m_viewpos;
+ QPoint m_mouse;
+ QSize m_pagesize;
+ QSize m_viewsize;
+ SizeMode m_sizeMode;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/ScrollBoxDialog.cpp b/src/gui/widgets/ScrollBoxDialog.cpp
new file mode 100644
index 0000000..e442985
--- /dev/null
+++ b/src/gui/widgets/ScrollBoxDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ScrollBoxDialog.h"
+
+#include "ScrollBox.h"
+#include <kdialog.h>
+#include <qframe.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qsize.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+ScrollBoxDialog::ScrollBoxDialog(QWidget *parent,
+ ScrollBox::SizeMode sizeMode,
+ const char *name,
+ WFlags flags) :
+ KDialog(parent, name, flags),
+ m_scrollbox(new ScrollBox(this, sizeMode))
+{ }
+
+
+ScrollBoxDialog::~ScrollBoxDialog()
+{ }
+
+
+void ScrollBoxDialog::closeEvent(QCloseEvent *e)
+{
+ e->accept();
+ emit closed();
+}
+
+void ScrollBoxDialog::setPageSize(const QSize& s)
+{
+ m_scrollbox->setPageSize(s);
+ setFixedHeight(m_scrollbox->height());
+ setFixedWidth(m_scrollbox->width());
+}
+
+}
+#include "ScrollBoxDialog.moc"
diff --git a/src/gui/widgets/ScrollBoxDialog.h b/src/gui/widgets/ScrollBoxDialog.h
new file mode 100644
index 0000000..8da1771
--- /dev/null
+++ b/src/gui/widgets/ScrollBoxDialog.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_SCROLLBOXDIALOG_H_
+#define _RG_SCROLLBOXDIALOG_H_
+
+#include "ScrollBox.h"
+#include <kdialog.h>
+
+
+class QWidget;
+class QSize;
+class QCloseEvent;
+
+
+namespace Rosegarden
+{
+
+
+
+class ScrollBoxDialog : public KDialog
+{
+ Q_OBJECT
+
+public:
+ ScrollBoxDialog(QWidget *parent = 0,
+ ScrollBox::SizeMode mode = ScrollBox::FixWidth,
+ const char *name = 0,
+ WFlags flags = 0);
+ ~ScrollBoxDialog();
+
+ ScrollBox *scrollbox() { return m_scrollbox; }
+ void setPageSize(const QSize&);
+
+protected:
+ virtual void closeEvent(QCloseEvent * e);
+
+signals:
+ void closed();
+
+private:
+ ScrollBox *m_scrollbox;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/SpinBox.cpp b/src/gui/widgets/SpinBox.cpp
new file mode 100644
index 0000000..10963c8
--- /dev/null
+++ b/src/gui/widgets/SpinBox.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "SpinBox.h"
+#include "misc/Strings.h"
+
+#include <qspinbox.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+SpinBox::SpinBox(QWidget *parent, const char *name):
+ QSpinBox(parent, name), m_doubleValue(0)
+{}
+
+QString
+SpinBox::mapValueToText(int value)
+{
+ QString doubleStr;
+
+ // Assume we want to show the precision
+ //
+ if ((int)m_doubleValue != value)
+ m_doubleValue = (double) value;
+
+ doubleStr.sprintf("%4.6f", m_doubleValue);
+
+ // clear any special value
+ //setSpecialValueText("");
+
+ return doubleStr;
+}
+
+int
+SpinBox::mapTextToValue(bool * /*ok*/)
+{
+ double number = qstrtodouble(text());
+
+ if (number) {
+ m_doubleValue = number;
+ return ((int)number);
+ }
+
+ return 120; // default
+}
+
+}
+#include "SpinBox.moc"
diff --git a/src/gui/widgets/SpinBox.h b/src/gui/widgets/SpinBox.h
new file mode 100644
index 0000000..a0dfffe
--- /dev/null
+++ b/src/gui/widgets/SpinBox.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ROSEGARDENSPINBOX_H_
+#define _RG_ROSEGARDENSPINBOX_H_
+
+#include <qspinbox.h>
+#include <qstring.h>
+
+
+class QWidget;
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * A Combobox that just about handles doubles - you have
+ * to set the precision outside of this class if you're
+ * using it with Qt designer. Urch.
+ */
+class SpinBox : public QSpinBox
+{
+ Q_OBJECT
+public:
+ SpinBox(QWidget *parent = 0, const char *name=0);
+
+ double getDoubleValue() const { return m_doubleValue; }
+
+protected:
+ virtual QString mapValueToText (int value);
+ virtual int mapTextToValue(bool *ok);
+
+ double m_doubleValue;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/TextFloat.cpp b/src/gui/widgets/TextFloat.cpp
new file mode 100644
index 0000000..5e3ddbc
--- /dev/null
+++ b/src/gui/widgets/TextFloat.cpp
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "TextFloat.h"
+#include <kapplication.h>
+
+#include "gui/general/GUIPalette.h"
+#include <qfontmetrics.h>
+#include <qpainter.h>
+#include <qpalette.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+TextFloat::TextFloat(QWidget *parent):
+ QWidget(parent, "TextFloat",
+ WStyle_Customize | WStyle_NoBorder | WStyle_StaysOnTop),
+ m_text("")
+{
+ reparent(parentWidget());
+ resize(20, 20);
+}
+
+void
+TextFloat::reparent(QWidget *newParent)
+{
+ QPoint position = newParent->pos();
+
+ // Get position and reparent to either top level or dialog
+ //
+ while (newParent->parentWidget() && !newParent->isTopLevel()
+ && !newParent->isDialog()) {
+ newParent = newParent->parentWidget();
+ position += newParent->pos();
+ }
+
+ // Position this widget to the right of the parent
+ //
+ //move(pos + QPoint(parent->width() + 5, 5));
+
+ QWidget::reparent(newParent,
+ WStyle_Customize | WStyle_NoBorder | WStyle_StaysOnTop,
+ position + QPoint(20, 5));
+}
+
+void
+TextFloat::paintEvent(QPaintEvent *e)
+{
+ QPainter paint(this);
+
+ paint.setClipRegion(e->region());
+ paint.setClipRect(e->rect().normalize());
+
+ paint.setPen(kapp->palette().color(QPalette::Active, QColorGroup::Dark));
+
+ paint.setPen(GUIPalette::getColour(GUIPalette::RotaryFloatForeground));
+ paint.setBrush(GUIPalette::getColour(GUIPalette::RotaryFloatBackground));
+
+ QFontMetrics metrics(paint.fontMetrics());
+
+ QRect r = metrics.boundingRect(0, 0, 400, 400, Qt::AlignAuto, m_text);
+ resize(r.width() + 7, r.height() + 7);
+ paint.drawRect(0, 0, r.width() + 6, r.height() + 6);
+ paint.setPen(Qt::black);
+ paint.drawText(QRect(3, 3, r.width(), r.height()), Qt::AlignAuto, m_text);
+
+ /*
+ QRect textBound = metrics.boundingRect(m_text);
+
+ resize(textBound.width() + 7, textBound.height() + 7);
+ paint.drawRect(0, 0, textBound.width() + 6, textBound.height() + 6);
+
+ paint.setPen(Qt::black);
+ paint.drawText(3, textBound.height() + 3, m_text);
+ */
+}
+
+void
+TextFloat::setText(const QString &text)
+{
+ m_text = text;
+ repaint();
+}
+
+}
diff --git a/src/gui/widgets/TextFloat.h b/src/gui/widgets/TextFloat.h
new file mode 100644
index 0000000..d604a83
--- /dev/null
+++ b/src/gui/widgets/TextFloat.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ROSEGARDENTEXTFLOAT_H_
+#define _RG_ROSEGARDENTEXTFLOAT_H_
+
+#include <qstring.h>
+#include <qwidget.h>
+
+
+class QPaintEvent;
+
+
+namespace Rosegarden
+{
+
+
+
+class TextFloat : public QWidget
+{
+public:
+ TextFloat(QWidget *parent);
+ virtual ~TextFloat() {;}
+
+ void setText(const QString &text);
+
+ // Reparent the float correctly by context
+ //
+ void reparent(QWidget *newParent);
+
+protected:
+ virtual void paintEvent(QPaintEvent *e);
+
+ QString m_text;
+};
+
+
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/TimeWidget.cpp b/src/gui/widgets/TimeWidget.cpp
new file mode 100644
index 0000000..b9bad6f
--- /dev/null
+++ b/src/gui/widgets/TimeWidget.cpp
@@ -0,0 +1,668 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "TimeWidget.h"
+#include <qlayout.h>
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "base/Composition.h"
+#include "base/NotationTypes.h"
+#include "base/RealTime.h"
+#include "gui/editors/notation/NotationStrings.h"
+#include "gui/editors/notation/NotePixmapFactory.h"
+#include <qcombobox.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qpixmap.h>
+#include <qspinbox.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+TimeWidget::TimeWidget(QString title,
+ QWidget *parent,
+ Composition *composition,
+ timeT absTime,
+ bool editable,
+ bool constrainToCompositionDuration) :
+ QGroupBox(1, Horizontal, title, parent),
+ m_composition(composition),
+ m_isDuration(false),
+ m_constrain(constrainToCompositionDuration),
+ m_time(absTime),
+ m_startTime(0),
+ m_defaultTime(absTime)
+{
+ init(editable);
+}
+
+TimeWidget::TimeWidget(QString title,
+ QWidget *parent,
+ Composition *composition,
+ timeT startTime,
+ timeT duration,
+ bool editable,
+ bool constrainToCompositionDuration) :
+ QGroupBox(1, Horizontal, title, parent),
+ m_composition(composition),
+ m_isDuration(true),
+ m_constrain(constrainToCompositionDuration),
+ m_time(duration),
+ m_startTime(startTime),
+ m_defaultTime(duration)
+{
+ init(editable);
+}
+
+void
+TimeWidget::init(bool editable)
+{
+ int denoms[] = {
+ 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128
+ };
+
+ bool savedEditable = editable;
+ editable = true;
+
+ QFrame *frame = new QFrame(this);
+ QGridLayout *layout = new QGridLayout(frame, 7, 3, 5, 5);
+ QLabel *label = 0;
+
+ if (m_isDuration) {
+
+ label = new QLabel(i18n("Note:"), frame);
+ label->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ layout->addWidget(label, 0, 0);
+
+ if (editable) {
+ m_note = new QComboBox(frame);
+ m_noteDurations.push_back(0);
+ m_note->insertItem(i18n("<inexact>"));
+ for (size_t i = 0; i < sizeof(denoms) / sizeof(denoms[0]); ++i) {
+
+ timeT duration =
+ Note(Note::Breve).getDuration() / denoms[i];
+
+ if (denoms[i] > 1 && denoms[i] < 128 && (denoms[i] % 3) != 0) {
+ // not breve or hemidemi, not a triplet
+ timeT dottedDuration = duration * 3 / 2;
+ m_noteDurations.push_back(dottedDuration);
+ timeT error = 0;
+ QString label = NotationStrings::makeNoteMenuLabel
+ (dottedDuration, false, error);
+ QPixmap pmap = NotePixmapFactory::toQPixmap
+ (NotePixmapFactory::makeNoteMenuPixmap(dottedDuration, error));
+ m_note->insertItem(pmap, label); // ignore error
+ }
+
+ m_noteDurations.push_back(duration);
+ timeT error = 0;
+ QString label = NotationStrings::makeNoteMenuLabel
+ (duration, false, error);
+ QPixmap pmap = NotePixmapFactory::toQPixmap
+ (NotePixmapFactory::makeNoteMenuPixmap(duration, error));
+ m_note->insertItem(pmap, label); // ignore error
+ }
+ connect(m_note, SIGNAL(activated(int)),
+ this, SLOT(slotNoteChanged(int)));
+ layout->addMultiCellWidget(m_note, 0, 0, 1, 3);
+
+ } else {
+
+ m_note = 0;
+ timeT error = 0;
+ QString label = NotationStrings::makeNoteMenuLabel
+ (m_time, false, error);
+ if (error != 0)
+ label = i18n("<inexact>");
+ QLineEdit *le = new QLineEdit(label, frame);
+ le->setReadOnly(true);
+ layout->addMultiCellWidget(le, 0, 0, 1, 3);
+ }
+
+ label = new QLabel(i18n("Units:"), frame);
+ label->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ layout->addWidget(label, 0, 4);
+
+ if (editable) {
+ m_timeT = new QSpinBox(frame);
+ m_timeT->setLineStep
+ (Note(Note::Shortest).getDuration());
+ connect(m_timeT, SIGNAL(valueChanged(int)),
+ this, SLOT(slotTimeTChanged(int)));
+ layout->addWidget(m_timeT, 0, 5);
+ } else {
+ m_timeT = 0;
+ QLineEdit *le = new QLineEdit(QString("%1").arg(m_time), frame);
+ le->setReadOnly(true);
+ layout->addWidget(le, 0, 5);
+ }
+
+ } else {
+
+ m_note = 0;
+
+ label = new QLabel(i18n("Time:"), frame);
+ label->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ layout->addWidget(label, 0, 0);
+
+ if (editable) {
+ m_timeT = new QSpinBox(frame);
+ m_timeT->setLineStep
+ (Note(Note::Shortest).getDuration());
+ connect(m_timeT, SIGNAL(valueChanged(int)),
+ this, SLOT(slotTimeTChanged(int)));
+ layout->addWidget(m_timeT, 0, 1);
+ layout->addWidget(new QLabel(i18n("units"), frame), 0, 2);
+ } else {
+ m_timeT = 0;
+ QLineEdit *le = new QLineEdit(QString("%1").arg(m_time), frame);
+ le->setReadOnly(true);
+ layout->addWidget(le, 0, 2);
+ }
+ }
+
+ label = new QLabel(m_isDuration ? i18n("Measures:") : i18n("Measure:"), frame);
+ label->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ layout->addWidget(label, 1, 0);
+
+ if (editable) {
+ m_barLabel = 0;
+ m_bar = new QSpinBox(frame);
+ if (m_isDuration)
+ m_bar->setMinValue(0);
+ connect(m_bar, SIGNAL(valueChanged(int)),
+ this, SLOT(slotBarBeatOrFractionChanged(int)));
+ layout->addWidget(m_bar, 1, 1);
+ } else {
+ m_bar = 0;
+ m_barLabel = new QLineEdit(frame);
+ m_barLabel->setReadOnly(true);
+ layout->addWidget(m_barLabel, 1, 1);
+ }
+
+ label = new QLabel(m_isDuration ? i18n("beats:") : i18n("beat:"), frame);
+ label->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ layout->addWidget(label, 1, 2);
+
+ if (editable) {
+ m_beatLabel = 0;
+ m_beat = new QSpinBox(frame);
+ m_beat->setMinValue(1);
+ connect(m_beat, SIGNAL(valueChanged(int)),
+ this, SLOT(slotBarBeatOrFractionChanged(int)));
+ layout->addWidget(m_beat, 1, 3);
+ } else {
+ m_beat = 0;
+ m_beatLabel = new QLineEdit(frame);
+ m_beatLabel->setReadOnly(true);
+ layout->addWidget(m_beatLabel, 1, 3);
+ }
+
+ label = new QLabel(i18n("%1:").arg(NotationStrings::getShortNoteName
+ (Note
+ (Note::Shortest), true)),
+ frame);
+ label->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ layout->addWidget(label, 1, 4);
+
+ if (editable) {
+ m_fractionLabel = 0;
+ m_fraction = new QSpinBox(frame);
+ m_fraction->setMinValue(1);
+ connect(m_fraction, SIGNAL(valueChanged(int)),
+ this, SLOT(slotBarBeatOrFractionChanged(int)));
+ layout->addWidget(m_fraction, 1, 5);
+ } else {
+ m_fraction = 0;
+ m_fractionLabel = new QLineEdit(frame);
+ m_fractionLabel->setReadOnly(true);
+ layout->addWidget(m_fractionLabel, 1, 5);
+ }
+
+ m_timeSig = new QLabel(frame);
+ layout->addWidget(m_timeSig, 1, 6);
+
+ label = new QLabel(i18n("Seconds:"), frame);
+ label->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ layout->addWidget(label, 2, 0);
+
+ if (editable) {
+ m_secLabel = 0;
+ m_sec = new QSpinBox(frame);
+ if (m_isDuration)
+ m_sec->setMinValue(0);
+ connect(m_sec, SIGNAL(valueChanged(int)),
+ this, SLOT(slotSecOrMSecChanged(int)));
+ layout->addWidget(m_sec, 2, 1);
+ } else {
+ m_sec = 0;
+ m_secLabel = new QLineEdit(frame);
+ m_secLabel->setReadOnly(true);
+ layout->addWidget(m_secLabel, 2, 1);
+ }
+
+ label = new QLabel(i18n("msec:"), frame);
+ label->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
+ layout->addWidget(label, 2, 2);
+
+ if (editable) {
+ m_msecLabel = 0;
+ m_msec = new QSpinBox(frame);
+ m_msec->setMinValue(0);
+ m_msec->setLineStep(10);
+ connect(m_msec, SIGNAL(valueChanged(int)),
+ this, SLOT(slotSecOrMSecChanged(int)));
+ layout->addWidget(m_msec, 2, 3);
+ } else {
+ m_msec = 0;
+ m_msecLabel = new QLineEdit(frame);
+ m_msecLabel->setReadOnly(true);
+ layout->addWidget(m_msecLabel, 2, 3);
+ }
+
+ if (m_isDuration) {
+ m_tempo = new QLabel(frame);
+ layout->addWidget(m_tempo, 2, 6);
+ } else {
+ m_tempo = 0;
+ }
+
+ if (!savedEditable) {
+ if (m_note)
+ m_note ->setEnabled(false);
+ if (m_timeT)
+ m_timeT ->setEnabled(false);
+ if (m_bar)
+ m_bar ->setEnabled(false);
+ if (m_beat)
+ m_beat ->setEnabled(false);
+ if (m_fraction)
+ m_fraction ->setEnabled(false);
+ if (m_sec)
+ m_sec ->setEnabled(false);
+ if (m_msec)
+ m_msec ->setEnabled(false);
+ }
+
+ populate();
+}
+
+void
+TimeWidget::populate()
+{
+ // populate everything from m_time and m_startTime
+
+ if (m_note)
+ m_note ->blockSignals(true);
+ if (m_timeT)
+ m_timeT ->blockSignals(true);
+ if (m_bar)
+ m_bar ->blockSignals(true);
+ if (m_beat)
+ m_beat ->blockSignals(true);
+ if (m_fraction)
+ m_fraction ->blockSignals(true);
+ if (m_sec)
+ m_sec ->blockSignals(true);
+ if (m_msec)
+ m_msec ->blockSignals(true);
+
+ if (m_isDuration) {
+
+ if (m_time + m_startTime > m_composition->getEndMarker()) {
+ m_time = m_composition->getEndMarker() - m_startTime;
+ }
+
+ if (m_timeT) {
+ m_timeT->setMinValue(0);
+ if (m_constrain) {
+ m_timeT->setMaxValue(m_composition->getEndMarker() - m_startTime);
+ } else {
+ m_timeT->setMaxValue(INT_MAX);
+ }
+ m_timeT->setValue(m_time);
+ }
+
+ if (m_note) {
+ m_note->setCurrentItem(0);
+ for (size_t i = 0; i < m_noteDurations.size(); ++i) {
+ if (m_time == m_noteDurations[i]) {
+ m_note->setCurrentItem(i);
+ break;
+ }
+ }
+ }
+
+ // the bar/beat etc timings are considered to be times of a note
+ // starting at the start of a bar, in the time signature in effect
+ // at m_startTime
+
+ int bars = 0, beats = 0, hemidemis = 0, remainder = 0;
+ m_composition->getMusicalTimeForDuration(m_startTime, m_time,
+ bars, beats, hemidemis, remainder);
+ TimeSignature timeSig =
+ m_composition->getTimeSignatureAt(m_startTime);
+
+ if (m_bar) {
+ m_bar->setMinValue(0);
+ if (m_constrain) {
+ m_bar->setMaxValue
+ (m_composition->getBarNumber(m_composition->getEndMarker()) -
+ m_composition->getBarNumber(m_startTime));
+ } else {
+ m_bar->setMaxValue(9999);
+ }
+ m_bar->setValue(bars);
+ } else {
+ m_barLabel->setText(QString("%1").arg(bars));
+ }
+
+ if (m_beat) {
+ m_beat->setMinValue(0);
+ m_beat->setMaxValue(timeSig.getBeatsPerBar() - 1);
+ m_beat->setValue(beats);
+ } else {
+ m_beatLabel->setText(QString("%1").arg(beats));
+ }
+
+ if (m_fraction) {
+ m_fraction->setMinValue(0);
+ m_fraction->setMaxValue(timeSig.getBeatDuration() /
+ Note(Note::Shortest).
+ getDuration() - 1);
+ m_fraction->setValue(hemidemis);
+ } else {
+ m_fractionLabel->setText(QString("%1").arg(hemidemis));
+ }
+
+ m_timeSig->setText(i18n("(%1/%2 time)").arg(timeSig.getNumerator()).
+ arg(timeSig.getDenominator()));
+
+ timeT endTime = m_startTime + m_time;
+
+ RealTime rt = m_composition->getRealTimeDifference
+ (m_startTime, endTime);
+
+ if (m_sec) {
+ m_sec->setMinValue(0);
+ if (m_constrain) {
+ m_sec->setMaxValue(m_composition->getRealTimeDifference
+ (m_startTime, m_composition->getEndMarker()).sec);
+ } else {
+ m_sec->setMaxValue(9999);
+ }
+ m_sec->setValue(rt.sec);
+ } else {
+ m_secLabel->setText(QString("%1").arg(rt.sec));
+ }
+
+ if (m_msec) {
+ m_msec->setMinValue(0);
+ m_msec->setMaxValue(999);
+ m_msec->setValue(rt.msec());
+ } else {
+ m_msecLabel->setText(QString("%1").arg(rt.msec()));
+ }
+
+ bool change = (m_composition->getTempoChangeNumberAt(endTime) !=
+ m_composition->getTempoChangeNumberAt(m_startTime));
+
+ //!!! imprecise -- better to work from tempoT directly
+ double tempo = m_composition->getTempoQpm(m_composition->getTempoAtTime(m_startTime));
+
+ int qpmc = int(tempo * 100.0);
+ int bpmc = qpmc;
+ if (timeSig.getBeatDuration()
+ != Note(Note::Crotchet).getDuration()) {
+ bpmc = int(tempo * 100.0 *
+ Note(Note::Crotchet).getDuration() /
+ timeSig.getBeatDuration());
+ }
+ if (change) {
+ if (bpmc != qpmc) {
+ m_tempo->setText(i18n("(starting %1.%2 qpm, %2.%3 bpm)").
+ arg(qpmc / 100).
+ arg(qpmc % 100).
+ arg(bpmc / 100).
+ arg(bpmc % 100));
+ } else {
+ m_tempo->setText(i18n("(starting %1.%2 bpm)").
+ arg(bpmc / 100).
+ arg(bpmc % 100));
+ }
+ } else {
+ if (bpmc != qpmc) {
+ m_tempo->setText(i18n("(%1.%2 qpm, %2.%3 bpm)").
+ arg(qpmc / 100).
+ arg(qpmc % 100).
+ arg(bpmc / 100).
+ arg(bpmc % 100));
+ } else {
+ m_tempo->setText(i18n("(%1.%2 bpm)").
+ arg(bpmc / 100).
+ arg(bpmc % 100));
+ }
+ }
+
+ } else {
+
+ if (m_time > m_composition->getEndMarker()) {
+ m_time = m_composition->getEndMarker();
+ }
+
+ if (m_timeT) {
+ if (m_constrain) {
+ m_timeT->setMinValue(m_composition->getStartMarker());
+ m_timeT->setMaxValue(m_composition->getEndMarker());
+ } else {
+ m_timeT->setMinValue(INT_MIN);
+ m_timeT->setMaxValue(INT_MAX);
+ }
+ m_timeT->setValue(m_time);
+ }
+
+ int bar = 1, beat = 1, hemidemis = 0, remainder = 0;
+ m_composition->getMusicalTimeForAbsoluteTime
+ (m_time, bar, beat, hemidemis, remainder);
+
+ TimeSignature timeSig =
+ m_composition->getTimeSignatureAt(m_time);
+
+ if (m_bar) {
+ m_bar->setMinValue(INT_MIN);
+ if (m_constrain) {
+ m_bar->setMaxValue(m_composition->getBarNumber
+ (m_composition->getEndMarker()));
+ } else {
+ m_bar->setMaxValue(9999);
+ }
+ m_bar->setValue(bar + 1);
+ } else {
+ m_barLabel->setText(QString("%1").arg(bar + 1));
+ }
+
+ if (m_beat) {
+ m_beat->setMinValue(1);
+ m_beat->setMaxValue(timeSig.getBeatsPerBar());
+ m_beat->setValue(beat);
+ } else {
+ m_beatLabel->setText(QString("%1").arg(beat));
+ }
+
+ if (m_fraction) {
+ m_fraction->setMinValue(0);
+ m_fraction->setMaxValue(timeSig.getBeatDuration() /
+ Note(Note::Shortest).
+ getDuration() - 1);
+ m_fraction->setValue(hemidemis);
+ } else {
+ m_fractionLabel->setText(QString("%1").arg(hemidemis));
+ }
+
+ m_timeSig->setText(i18n("(%1/%2 time)").arg(timeSig.getNumerator()).
+ arg(timeSig.getDenominator()));
+
+ RealTime rt = m_composition->getElapsedRealTime(m_time);
+
+ if (m_sec) {
+ m_sec->setMinValue(INT_MIN);
+ if (m_constrain) {
+ m_sec->setMaxValue(m_composition->getElapsedRealTime
+ (m_composition->getEndMarker()).sec);
+ } else {
+ m_sec->setMaxValue(9999);
+ }
+ m_sec->setValue(rt.sec);
+ } else {
+ m_secLabel->setText(QString("%1").arg(rt.sec));
+ }
+
+ if (m_msec) {
+ m_msec->setMinValue(0);
+ m_msec->setMaxValue(999);
+ m_msec->setValue(rt.msec());
+ } else {
+ m_msecLabel->setText(QString("%1").arg(rt.msec()));
+ }
+ }
+
+ if (m_note)
+ m_note ->blockSignals(false);
+ if (m_timeT)
+ m_timeT ->blockSignals(false);
+ if (m_bar)
+ m_bar ->blockSignals(false);
+ if (m_beat)
+ m_beat ->blockSignals(false);
+ if (m_fraction)
+ m_fraction ->blockSignals(false);
+ if (m_sec)
+ m_sec ->blockSignals(false);
+ if (m_msec)
+ m_msec ->blockSignals(false);
+}
+
+timeT
+TimeWidget::getTime()
+{
+ return m_time;
+}
+
+RealTime
+TimeWidget::getRealTime()
+{
+ if (m_isDuration) {
+ return m_composition->getRealTimeDifference(m_startTime,
+ m_startTime + m_time);
+ } else {
+ return m_composition->getElapsedRealTime(m_time);
+ }
+}
+
+void
+TimeWidget::slotSetTime(timeT t)
+{
+ bool change = (m_time != t);
+ if (!change)
+ return ;
+ m_time = t;
+ populate();
+ emit timeChanged(getTime());
+ emit realTimeChanged(getRealTime());
+}
+
+void
+TimeWidget::slotSetRealTime(RealTime rt)
+{
+ if (m_isDuration) {
+ RealTime startRT = m_composition->getElapsedRealTime(m_startTime);
+ if (rt >= RealTime::zeroTime) {
+ slotSetTime(m_composition->getElapsedTimeForRealTime(startRT + rt) -
+ m_startTime);
+ } else {
+ RG_DEBUG << "WARNING: TimeWidget::slotSetRealTime: rt must be >0 for duration widget (was " << rt << ")" << endl;
+ }
+ } else {
+ slotSetTime(m_composition->getElapsedTimeForRealTime(rt));
+ }
+}
+
+void
+TimeWidget::slotResetToDefault()
+{
+ slotSetTime(m_defaultTime);
+}
+
+void
+TimeWidget::slotNoteChanged(int n)
+{
+ if (n > 0) {
+ slotSetTime(m_noteDurations[n]);
+ }
+}
+
+void
+TimeWidget::slotTimeTChanged(int t)
+{
+ RG_DEBUG << "slotTimeTChanged: t is " << t << ", value is " << m_timeT->value() << endl;
+
+ slotSetTime(t);
+}
+
+void
+TimeWidget::slotBarBeatOrFractionChanged(int)
+{
+ int bar = m_bar->value();
+ int beat = m_beat->value();
+ int fraction = m_fraction->value();
+
+ if (m_isDuration) {
+ slotSetTime(m_composition->getDurationForMusicalTime
+ (m_startTime, bar, beat, fraction, 0));
+
+ } else {
+ slotSetTime(m_composition->getAbsoluteTimeForMusicalTime
+ (bar, beat, fraction, 0));
+ }
+}
+
+void
+TimeWidget::slotSecOrMSecChanged(int)
+{
+ int sec = m_sec->value();
+ int msec = m_msec->value();
+
+ slotSetRealTime(RealTime(sec, msec * 1000000));
+}
+
+}
+#include "TimeWidget.moc"
diff --git a/src/gui/widgets/TimeWidget.h b/src/gui/widgets/TimeWidget.h
new file mode 100644
index 0000000..5ffa17a
--- /dev/null
+++ b/src/gui/widgets/TimeWidget.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ROSEGARDENTIMEWIDGET_H_
+#define _RG_ROSEGARDENTIMEWIDGET_H_
+
+#include "base/RealTime.h"
+#include <qgroupbox.h>
+#include <qstring.h>
+#include <vector>
+#include "base/Event.h"
+
+
+class QWidget;
+class QSpinBox;
+class QLineEdit;
+class QLabel;
+class QComboBox;
+
+
+namespace Rosegarden
+{
+
+class Composition;
+
+
+class TimeWidget : public QGroupBox
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor for absolute time widget
+ */
+ TimeWidget(QString title,
+ QWidget *parent,
+ Composition *composition, // for bar/beat/msec
+ timeT initialTime,
+ bool editable = true,
+ bool constrainToCompositionDuration = true);
+
+ /**
+ * Constructor for duration widget. startTime is the absolute time
+ * at which this duration begins, necessary so that we can show the
+ * correct real-time (based on tempo at startTime) etc.
+ */
+ TimeWidget(QString title,
+ QWidget *parent,
+ Composition *composition, // for bar/beat/msec
+ timeT startTime,
+ timeT initialDuration,
+ bool editable = true,
+ bool constrainToCompositionDuration = true);
+
+ timeT getTime();
+ RealTime getRealTime();
+
+signals:
+ void timeChanged(timeT);
+ void realTimeChanged(RealTime);
+
+public slots:
+ void slotSetTime(timeT);
+ void slotSetRealTime(RealTime);
+ void slotResetToDefault();
+
+ void slotNoteChanged(int);
+ void slotTimeTChanged(int);
+ void slotBarBeatOrFractionChanged(int);
+ void slotSecOrMSecChanged(int);
+
+private:
+ Composition *m_composition;
+ bool m_isDuration;
+ bool m_constrain;
+ timeT m_time;
+ timeT m_startTime;
+ timeT m_defaultTime;
+
+ QComboBox *m_note;
+ QSpinBox *m_timeT;
+ QSpinBox *m_bar;
+ QSpinBox *m_beat;
+ QSpinBox *m_fraction;
+ QLineEdit *m_barLabel;
+ QLineEdit *m_beatLabel;
+ QLineEdit *m_fractionLabel;
+ QLabel *m_timeSig;
+ QSpinBox *m_sec;
+ QSpinBox *m_msec;
+ QLineEdit *m_secLabel;
+ QLineEdit *m_msecLabel;
+ QLabel *m_tempo;
+
+ void init(bool editable);
+ void populate();
+
+ std::vector<timeT> m_noteDurations;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/TristateCheckBox.cpp b/src/gui/widgets/TristateCheckBox.cpp
new file mode 100644
index 0000000..89ade5d
--- /dev/null
+++ b/src/gui/widgets/TristateCheckBox.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "TristateCheckBox.h"
+
+#include <qcheckbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+void
+TristateCheckBox::mouseReleaseEvent(QMouseEvent *)
+{}
+
+TristateCheckBox::~TristateCheckBox()
+{}
+
+}
+#include "TristateCheckBox.moc"
diff --git a/src/gui/widgets/TristateCheckBox.h b/src/gui/widgets/TristateCheckBox.h
new file mode 100644
index 0000000..699252a
--- /dev/null
+++ b/src/gui/widgets/TristateCheckBox.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ROSEGARDENTRISTATECHECKBOX_H_
+#define _RG_ROSEGARDENTRISTATECHECKBOX_H_
+
+#include <qcheckbox.h>
+
+
+class QWidget;
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+
+
+/** Create out own check box which is always Tristate
+ * and allows us to click only between on and off
+ * and only to _show_ the third ("Some") state
+ */
+class TristateCheckBox : public QCheckBox
+{
+Q_OBJECT
+public:
+ TristateCheckBox(QWidget *parent=0,
+ const char *name=0):QCheckBox(parent, name)
+ { setTristate(true) ;}
+
+ virtual ~TristateCheckBox();
+
+protected:
+ // don't emit when the button is released
+ virtual void mouseReleaseEvent(QMouseEvent *);
+
+private:
+};
+
+
+// A label that emits a double click signal and provides scroll wheel information.
+//
+//
+
+}
+
+#endif
diff --git a/src/gui/widgets/VUMeter.cpp b/src/gui/widgets/VUMeter.cpp
new file mode 100644
index 0000000..ae9fe84
--- /dev/null
+++ b/src/gui/widgets/VUMeter.cpp
@@ -0,0 +1,694 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "VUMeter.h"
+
+#include "misc/Debug.h"
+#include "base/AudioLevel.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/rulers/VelocityColour.h"
+#include <qbrush.h>
+#include <qcolor.h>
+#include <qlabel.h>
+#include <qpainter.h>
+#include <qtimer.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+VUMeter::VUMeter(QWidget *parent,
+ VUMeterType type,
+ bool stereo,
+ bool hasRecord,
+ int width,
+ int height,
+ VUAlignment alignment,
+ const char *name):
+ QLabel(parent, name),
+ m_originalHeight(height),
+ m_active(true),
+ m_type(type),
+ m_alignment(alignment),
+ m_levelLeft(0),
+ m_recordLevelLeft(0),
+ m_peakLevelLeft(0),
+ m_levelStepLeft(m_baseLevelStep),
+ m_recordLevelStepLeft(m_baseLevelStep),
+ m_fallTimerLeft(0),
+ m_peakTimerLeft(0),
+ m_levelRight(0),
+ m_recordLevelRight(0),
+ m_peakLevelRight(0),
+ m_levelStepRight(0),
+ m_recordLevelStepRight(0),
+ m_fallTimerRight(0),
+ m_peakTimerRight(0),
+ m_showPeakLevel(true),
+ m_baseLevelStep(3),
+ m_stereo(stereo),
+ m_hasRecord(hasRecord)
+{
+ // Work out if we need peak hold first
+ //
+ switch (m_type) {
+ case PeakHold:
+ case AudioPeakHoldShort:
+ case AudioPeakHoldLong:
+ case AudioPeakHoldIEC:
+ case AudioPeakHoldIECLong:
+ case FixedHeightVisiblePeakHold:
+ m_showPeakLevel = true;
+ break;
+
+ default:
+ case Plain:
+ m_showPeakLevel = false;
+ break;
+ }
+
+ // Always init the left fall timer
+ //
+ m_fallTimerLeft = new QTimer();
+
+ connect(m_fallTimerLeft, SIGNAL(timeout()),
+ this, SLOT(slotReduceLevelLeft()));
+
+ if (m_showPeakLevel) {
+ m_peakTimerLeft = new QTimer();
+
+ connect(m_peakTimerLeft, SIGNAL(timeout()),
+ this, SLOT(slotStopShowingPeakLeft()));
+ }
+
+ if (stereo) {
+ m_fallTimerRight = new QTimer();
+
+ connect(m_fallTimerRight, SIGNAL(timeout()),
+ this, SLOT(slotReduceLevelRight()));
+
+ if (m_showPeakLevel) {
+ m_peakTimerRight = new QTimer();
+ connect(m_peakTimerRight, SIGNAL(timeout()),
+ this, SLOT(slotStopShowingPeakRight()));
+ }
+
+ }
+
+ setMinimumSize(width, m_originalHeight);
+ setMaximumSize(width, m_originalHeight);
+
+ if (m_alignment == Vertical)
+ m_maxLevel = height;
+ else
+ m_maxLevel = width;
+
+ int max = m_maxLevel;
+ int red, orange, green;
+
+ if (m_type == AudioPeakHoldShort) {
+ red = AudioLevel::dB_to_fader( 0.0, max, AudioLevel::ShortFader);
+ orange = AudioLevel::dB_to_fader( -2.0, max, AudioLevel::ShortFader);
+ green = AudioLevel::dB_to_fader( -10.0, max, AudioLevel::ShortFader);
+ m_background = QColor(50, 50, 50);
+ } else if (m_type == AudioPeakHoldLong) {
+ red = AudioLevel::dB_to_fader( 0.0, max, AudioLevel::LongFader);
+ orange = AudioLevel::dB_to_fader( -2.0, max, AudioLevel::LongFader);
+ green = AudioLevel::dB_to_fader( -10.0, max, AudioLevel::LongFader);
+ m_background = QColor(50, 50, 50);
+ } else if (m_type == AudioPeakHoldIEC) {
+ red = AudioLevel::dB_to_fader( -0.1, max, AudioLevel::IEC268Meter);
+ orange = AudioLevel::dB_to_fader( -6.0, max, AudioLevel::IEC268Meter);
+ green = AudioLevel::dB_to_fader( -10.0, max, AudioLevel::IEC268Meter);
+ m_background = QColor(50, 50, 50);
+ } else if (m_type == AudioPeakHoldIECLong) {
+ red = AudioLevel::dB_to_fader( 0.0, max, AudioLevel::IEC268LongMeter);
+ orange = AudioLevel::dB_to_fader( -6.0, max, AudioLevel::IEC268LongMeter);
+ green = AudioLevel::dB_to_fader( -10.0, max, AudioLevel::IEC268LongMeter);
+ m_background = QColor(50, 50, 50);
+ } else {
+ red = max * 92 / 100;
+ orange = max * 60 / 100;
+ green = max * 10 / 100;
+ m_background = Qt::black;
+ }
+
+ if (m_type == AudioPeakHoldLong ||
+ m_type == AudioPeakHoldShort ||
+ m_type == AudioPeakHoldIEC ||
+ m_type == AudioPeakHoldIECLong) {
+ m_velocityColour =
+ new VelocityColour(GUIPalette::getColour(GUIPalette::LevelMeterSolidRed),
+ GUIPalette::getColour(GUIPalette::LevelMeterSolidOrange),
+ GUIPalette::getColour(GUIPalette::LevelMeterSolidGreen),
+ max, red, orange, green);
+ } else {
+ m_velocityColour =
+ new VelocityColour(GUIPalette::getColour(GUIPalette::LevelMeterRed),
+ GUIPalette::getColour(GUIPalette::LevelMeterOrange),
+ GUIPalette::getColour(GUIPalette::LevelMeterGreen),
+ max, red, orange, green);
+ }
+}
+
+VUMeter::~VUMeter()
+{
+ delete m_velocityColour;
+ delete m_peakTimerRight;
+ delete m_peakTimerLeft;
+ delete m_fallTimerRight;
+ delete m_fallTimerLeft;
+}
+
+void
+VUMeter::setLevel(double level)
+{
+ setLevel(level, level, false);
+}
+
+void
+VUMeter::setLevel(double leftLevel, double rightLevel)
+{
+ setLevel(leftLevel, rightLevel, false);
+}
+
+void
+VUMeter::setRecordLevel(double level)
+{
+ setLevel(level, level, true);
+}
+
+void
+VUMeter::setRecordLevel(double leftLevel, double rightLevel)
+{
+ setLevel(leftLevel, rightLevel, true);
+}
+
+void
+VUMeter::setLevel(double leftLevel, double rightLevel, bool record)
+{
+ if (!isVisible())
+ return ;
+
+ // RG_DEBUG << "setLevel(" << (void *)this << "): record=" << record << ", leftLevel=" << leftLevel << ", hasRecord=" << m_hasRecord << endl;
+
+ if (record && !m_hasRecord)
+ return ;
+
+ short &ll = (record ? m_recordLevelLeft : m_levelLeft);
+ short &lr = (record ? m_recordLevelRight : m_levelRight);
+
+ switch (m_type) {
+
+ case AudioPeakHoldShort:
+ ll = AudioLevel::dB_to_fader
+ (leftLevel, m_maxLevel, AudioLevel::ShortFader);
+ lr = AudioLevel::dB_to_fader
+ (rightLevel, m_maxLevel, AudioLevel::ShortFader);
+ break;
+
+ case AudioPeakHoldLong:
+ ll = AudioLevel::dB_to_fader
+ (leftLevel, m_maxLevel, AudioLevel::LongFader);
+ lr = AudioLevel::dB_to_fader
+ (rightLevel, m_maxLevel, AudioLevel::LongFader);
+ break;
+
+ case AudioPeakHoldIEC:
+ ll = AudioLevel::dB_to_fader
+ (leftLevel, m_maxLevel, AudioLevel::IEC268Meter);
+ lr = AudioLevel::dB_to_fader
+ (rightLevel, m_maxLevel, AudioLevel::IEC268Meter);
+ break;
+
+ case AudioPeakHoldIECLong:
+ ll = AudioLevel::dB_to_fader
+ (leftLevel, m_maxLevel, AudioLevel::IEC268LongMeter);
+ lr = AudioLevel::dB_to_fader
+ (rightLevel, m_maxLevel, AudioLevel::IEC268LongMeter);
+ break;
+
+ default:
+ ll = (int)(double(m_maxLevel) * leftLevel);
+ lr = (int)(double(m_maxLevel) * rightLevel);
+ };
+
+ if (ll < 0)
+ ll = 0;
+ if (ll > m_maxLevel)
+ ll = m_maxLevel;
+ if (lr < 0)
+ lr = 0;
+ if (lr > m_maxLevel)
+ lr = m_maxLevel;
+
+ if (record) {
+ m_recordLevelStepLeft = m_baseLevelStep;
+ m_recordLevelStepRight = m_baseLevelStep;
+ } else {
+ m_levelStepLeft = m_baseLevelStep;
+ m_levelStepRight = m_baseLevelStep;
+ }
+
+ // Only start the timer when we need it
+ if (ll > 0) {
+ if (m_fallTimerLeft->isActive() == false) {
+ m_fallTimerLeft->start(40); // 40 ms per level fall iteration
+ meterStart();
+ }
+ }
+
+ if (lr > 0) {
+ if (m_fallTimerRight && m_fallTimerRight->isActive() == false) {
+ m_fallTimerRight->start(40); // 40 ms per level fall iteration
+ meterStart();
+ }
+ }
+
+ if (!record) {
+
+ // Reset level and reset timer if we're exceeding the
+ // current peak
+ //
+ if (ll >= m_peakLevelLeft && m_showPeakLevel) {
+ m_peakLevelLeft = ll;
+
+ if (m_peakTimerLeft->isActive())
+ m_peakTimerLeft->stop();
+
+ m_peakTimerLeft->start(1000); // milliseconds of peak hold
+ }
+
+ if (lr >= m_peakLevelRight && m_showPeakLevel) {
+ m_peakLevelRight = lr;
+
+ if (m_peakTimerRight) {
+ if (m_peakTimerRight->isActive())
+ m_peakTimerRight->stop();
+
+ m_peakTimerRight->start(1000); // milliseconds of peak hold
+ }
+ }
+ }
+
+ if (m_active) {
+ QPainter paint(this);
+ drawMeterLevel(&paint);
+ }
+}
+
+void
+VUMeter::paintEvent(QPaintEvent *e)
+{
+ // RG_DEBUG << "VUMeter::paintEvent - height = " << height() << endl;
+ QPainter paint(this);
+
+ if (m_type == VUMeter::AudioPeakHoldShort ||
+ m_type == VUMeter::AudioPeakHoldLong ||
+ m_type == VUMeter::AudioPeakHoldIEC ||
+ m_type == VUMeter::AudioPeakHoldIECLong) {
+ paint.setPen(m_background);
+ paint.setBrush(m_background);
+ paint.drawRect(0, 0, width(), height());
+
+ drawMeterLevel(&paint);
+
+ paint.setPen(colorGroup().background());
+ paint.drawPoint(0, 0);
+ paint.drawPoint(width() - 1, 0);
+ paint.drawPoint(0, height() - 1);
+ paint.drawPoint(width() - 1, height() - 1);
+ } else if (m_type == VUMeter::FixedHeightVisiblePeakHold) {
+ paint.setPen(m_background);
+ paint.setBrush(m_background);
+ paint.drawRect(0, 0, width(), height());
+
+ if (m_fallTimerLeft->isActive())
+ drawMeterLevel(&paint);
+ else {
+ meterStop();
+ drawFrame(&paint);
+ drawContents(&paint);
+ }
+ } else {
+ if (m_fallTimerLeft->isActive()) {
+ paint.setPen(m_background);
+ paint.setBrush(m_background);
+ paint.drawRect(0, 0, width(), height());
+ drawMeterLevel(&paint);
+ } else {
+ meterStop();
+ drawFrame(&paint);
+ drawContents(&paint);
+ }
+ }
+}
+
+void
+VUMeter::drawColouredBar(QPainter *paint, int channel,
+ int x, int y, int w, int h)
+{
+ if (m_type == AudioPeakHoldLong ||
+ m_type == AudioPeakHoldShort ||
+ m_type == AudioPeakHoldIEC ||
+ m_type == AudioPeakHoldIECLong) {
+
+ Qt::BrushStyle style = Qt::SolidPattern;
+
+ int medium = m_velocityColour->getMediumKnee(),
+ loud = m_velocityColour->getLoudKnee();
+
+ if (m_alignment == Vertical) {
+ if (h > loud) {
+ paint->setPen(m_velocityColour->getLoudColour());
+ paint->setBrush(QBrush(m_velocityColour->getLoudColour(),
+ style));
+ paint->drawRect(x, y, w, h - loud);
+ }
+ } else {
+ if (w > loud) {
+ paint->setPen(m_velocityColour->getLoudColour());
+ paint->setBrush(QBrush(m_velocityColour->getLoudColour(),
+ style));
+ paint->drawRect(x + loud, y, w - loud, h);
+ }
+ }
+
+ if (m_alignment == Vertical) {
+ if (h > medium) {
+ paint->setPen(m_velocityColour->getMediumColour());
+ paint->setBrush(QBrush(m_velocityColour->getMediumColour(),
+ style));
+ paint->drawRect(x, y + (h > loud ? (h - loud) : 0),
+ w, std::min(h - medium, loud - medium));
+ }
+ } else {
+ if (w > medium) {
+ paint->setPen(m_velocityColour->getMediumColour());
+ paint->setBrush(QBrush(m_velocityColour->getMediumColour(),
+ style));
+ paint->drawRect(x + medium, y,
+ std::min(w - medium, loud - medium), h);
+ }
+ }
+
+ if (m_alignment == Vertical) {
+ paint->setPen(m_velocityColour->getQuietColour());
+ paint->setBrush(QBrush(m_velocityColour->getQuietColour(),
+ style));
+ paint->drawRect(x, y + (h > medium ? (h - medium) : 0),
+ w, std::min(h, medium));
+ } else {
+ paint->setPen(m_velocityColour->getQuietColour());
+ paint->setBrush(QBrush(m_velocityColour->getQuietColour(),
+ style));
+ paint->drawRect(x, y, std::min(w, medium), h);
+ }
+
+ } else {
+
+ if (channel == 0) {
+
+ QColor mixedColour = m_velocityColour->getColour(m_levelLeft);
+
+ paint->setPen(mixedColour);
+ paint->setBrush(mixedColour);
+
+ } else {
+
+ QColor mixedColour = m_velocityColour->getColour(m_levelRight);
+
+ paint->setPen(mixedColour);
+ paint->setBrush(mixedColour);
+ }
+
+ // RG_DEBUG << "VUMeter::drawColouredBar - level = " << m_levelLeft << endl;
+
+ paint->drawRect(x, y, w, h);
+ }
+}
+
+void
+VUMeter::drawMeterLevel(QPainter* paint)
+{
+ int medium = m_velocityColour->getMediumKnee(),
+ loud = m_velocityColour->getLoudKnee();
+
+ if (m_stereo) {
+ if (m_alignment == VUMeter::Vertical) {
+ int hW = width() / 2;
+
+ int midWidth = 1;
+ if (m_hasRecord) {
+ if (width() > 10) {
+ midWidth = 2;
+ }
+ }
+
+ // Draw the left bar
+ //
+ int y = height() - (m_levelLeft * height()) / m_maxLevel;
+ int ry = height() - (m_recordLevelLeft * height()) / m_maxLevel;
+
+ drawColouredBar(paint, 0, 0, y, hW - midWidth, height() - y);
+
+ if (m_hasRecord) {
+ drawColouredBar(paint, 0, hW - midWidth, ry, midWidth + 1, height() - ry);
+ }
+
+ paint->setPen(m_background);
+ paint->setBrush(m_background);
+ paint->drawRect(0, 0, hW - midWidth, y);
+
+ if (m_hasRecord) {
+ paint->drawRect(hW - midWidth, 0, midWidth + 1, ry);
+ }
+
+ if (m_showPeakLevel) {
+ int h = (m_peakLevelLeft * height()) / m_maxLevel;
+ y = height() - h;
+
+ if (h > loud) {
+ paint->setPen(Qt::red); // brighter than the red meter bar
+ paint->drawLine(0, y - 1, hW - midWidth - 1, y - 1);
+ paint->drawLine(0, y + 1, hW - midWidth - 1, y + 1);
+ }
+
+ paint->setPen(Qt::white);
+ paint->drawLine(0, y, hW - midWidth - 1, y);
+ }
+
+ // Draw the right bar
+ //
+ y = height() - (m_levelRight * height()) / m_maxLevel;
+ ry = height() - (m_recordLevelRight * height()) / m_maxLevel;
+ drawColouredBar(paint, 1, hW + midWidth, y, hW - midWidth, height() - y);
+
+ if (m_hasRecord) {
+ drawColouredBar(paint, 1, hW, ry, midWidth + 1, height() - ry);
+ }
+
+ paint->setPen(m_background);
+ paint->setBrush(m_background);
+ paint->drawRect(hW + midWidth, 0, hW - midWidth + 1, y);
+
+ if (m_hasRecord) {
+ paint->drawRect(hW, 0, midWidth, ry);
+ }
+
+ if (m_showPeakLevel) {
+ int h = (m_peakLevelRight * height()) / m_maxLevel;
+ y = height() - h;
+
+ if (h > loud) {
+ paint->setPen(Qt::red); // brighter than the red meter bar
+ paint->drawLine(hW + midWidth, y - 1, width(), y - 1);
+ paint->drawLine(hW + midWidth, y + 1, width(), y + 1);
+ }
+
+ paint->setPen(Qt::white);
+ paint->setBrush(Qt::white);
+
+ paint->drawLine(hW + midWidth, y, width(), y);
+ }
+ } else // horizontal
+ {
+ paint->setPen(m_background);
+ paint->setBrush(m_background);
+ paint->drawRect(0, 0, width(), height());
+
+ int x = (m_levelLeft * width()) / m_maxLevel;
+ if (x > 0)
+ paint->drawRect(0, 0, x, height());
+
+ if (m_showPeakLevel) {
+ paint->setPen(Qt::white);
+ paint->setBrush(Qt::white);
+
+ // show peak level
+ x = m_peakLevelLeft * width() / m_maxLevel;
+ if (x < (width() - 1))
+ x++;
+ else
+ x = width() - 1;
+
+ paint->drawLine(x, 0, x, height());
+ }
+ }
+ } else {
+ // Paint a vertical meter according to type
+ //
+ if (m_alignment == VUMeter::Vertical) {
+ int y = height() - (m_levelLeft * height()) / m_maxLevel;
+ drawColouredBar(paint, 0, 0, y, width(), height());
+
+ paint->setPen(m_background);
+ paint->setBrush(m_background);
+ paint->drawRect(0, 0, width(), y);
+
+ /*
+ RG_DEBUG << "VUMeter::drawMeterLevel - height = " << height()
+ << ", vertical rect height = " << y << endl;
+ */
+
+ if (m_showPeakLevel) {
+ paint->setPen(Qt::white);
+ paint->setBrush(Qt::white);
+
+ y = height() - (m_peakLevelLeft * height()) / m_maxLevel;
+
+ paint->drawLine(0, y, width(), y);
+ }
+ } else {
+ int x = (m_levelLeft * width()) / m_maxLevel;
+ if (x > 0)
+ drawColouredBar(paint, 0, 0, 0, x, height());
+
+ paint->setPen(m_background);
+ paint->setBrush(m_background);
+ paint->drawRect(x, 0, width() - x, height());
+
+ if (m_showPeakLevel) {
+ paint->setPen(Qt::white);
+ paint->setBrush(Qt::white);
+
+ // show peak level
+ x = (m_peakLevelLeft * width()) / m_maxLevel;
+ if (x < (width() - 1))
+ x++;
+ else
+ x = width() - 1;
+
+ paint->drawLine(x, 0, x, height());
+ }
+ }
+ }
+}
+
+void
+VUMeter::slotReduceLevelRight()
+{
+ m_levelStepRight = int(m_levelRight) * m_baseLevelStep / 100 + 1;
+ if (m_levelStepRight < 1)
+ m_levelStepRight = 1;
+
+ m_recordLevelStepRight = int(m_recordLevelRight) * m_baseLevelStep / 100 + 1;
+ if (m_recordLevelStepRight < 1)
+ m_recordLevelStepRight = 1;
+
+ if (m_levelRight > 0)
+ m_levelRight -= m_levelStepRight;
+ if (m_recordLevelRight > 0)
+ m_recordLevelRight -= m_recordLevelStepRight;
+
+ if (m_levelRight <= 0) {
+ m_levelRight = 0;
+ m_peakLevelRight = 0;
+ }
+
+ if (m_recordLevelRight <= 0)
+ m_recordLevelRight = 0;
+
+ if (m_levelRight == 0 && m_recordLevelRight == 0) {
+ // Always stop the timer when we don't need it
+ if (m_fallTimerRight)
+ m_fallTimerRight->stop();
+ meterStop();
+ }
+
+ QPainter paint(this);
+ drawMeterLevel(&paint);
+}
+
+void
+VUMeter::slotReduceLevelLeft()
+{
+ m_levelStepLeft = int(m_levelLeft) * m_baseLevelStep / 100 + 1;
+ if (m_levelStepLeft < 1)
+ m_levelStepLeft = 1;
+
+ m_recordLevelStepLeft = int(m_recordLevelLeft) * m_baseLevelStep / 100 + 1;
+ if (m_recordLevelStepLeft < 1)
+ m_recordLevelStepLeft = 1;
+
+ if (m_levelLeft > 0)
+ m_levelLeft -= m_levelStepLeft;
+ if (m_recordLevelLeft > 0)
+ m_recordLevelLeft -= m_recordLevelStepLeft;
+
+ if (m_levelLeft <= 0) {
+ m_levelLeft = 0;
+ m_peakLevelLeft = 0;
+ }
+
+ if (m_recordLevelLeft <= 0)
+ m_recordLevelLeft = 0;
+
+ if (m_levelLeft == 0 && m_recordLevelLeft == 0) {
+ // Always stop the timer when we don't need it
+ if (m_fallTimerLeft)
+ m_fallTimerLeft->stop();
+ meterStop();
+ }
+
+ QPainter paint(this);
+ drawMeterLevel(&paint);
+}
+
+void
+VUMeter::slotStopShowingPeakRight()
+{
+ m_peakLevelRight = 0;
+}
+
+void
+VUMeter::slotStopShowingPeakLeft()
+{
+ m_peakLevelLeft = 0;
+}
+
+}
+#include "VUMeter.moc"
diff --git a/src/gui/widgets/VUMeter.h b/src/gui/widgets/VUMeter.h
new file mode 100644
index 0000000..0a06dfd
--- /dev/null
+++ b/src/gui/widgets/VUMeter.h
@@ -0,0 +1,154 @@
+
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_VUMETER_H_
+#define _RG_VUMETER_H_
+
+#include <qcolor.h>
+#include <qlabel.h>
+
+
+class QWidget;
+class QTimer;
+class QPaintEvent;
+class QPainter;
+
+
+namespace Rosegarden
+{
+
+class VelocityColour;
+
+
+class VUMeter : public QLabel
+{
+Q_OBJECT
+
+public:
+ typedef enum
+ {
+ Plain,
+ PeakHold,
+ AudioPeakHoldShort,
+ AudioPeakHoldLong,
+ AudioPeakHoldIEC,
+ AudioPeakHoldIECLong,
+ FixedHeightVisiblePeakHold
+ } VUMeterType;
+
+ typedef enum
+ {
+ Horizontal,
+ Vertical
+ } VUAlignment;
+
+ // Mono and stereo level setting. The AudioPeakHold meter types
+ // expect levels in dB; other types expect levels between 0 and 1.
+ //
+ void setLevel(double level);
+ void setLevel(double leftLevel, double rightLevel);
+
+ // Mono and stereo record level setting. Same units. Only
+ // applicable if hasRecord true in constructor.
+ //
+ void setRecordLevel(double level);
+ void setRecordLevel(double leftLevel, double rightLevel);
+
+ virtual void paintEvent(QPaintEvent*);
+
+protected:
+ // Constructor is protected - we can only create an object
+ // from a sub-class of this type from a sub-class.
+ //
+ VUMeter(QWidget *parent = 0,
+ VUMeterType type = Plain,
+ bool stereo = false,
+ bool hasRecord = false,
+ int width = 0,
+ int height = 0,
+ VUAlignment alignment = Horizontal,
+ const char *name = 0);
+ ~VUMeter();
+
+ virtual void meterStart() = 0;
+ virtual void meterStop() = 0;
+
+ int m_originalHeight;
+ bool m_active;
+
+ void setLevel(double leftLevel, double rightLevel, bool record);
+
+private slots:
+ void slotReduceLevelLeft();
+ void slotStopShowingPeakLeft();
+
+ void slotReduceLevelRight();
+ void slotStopShowingPeakRight();
+
+private:
+
+ void drawMeterLevel(QPainter *paint);
+ void drawColouredBar(QPainter *paint, int channel,
+ int x, int y, int w, int h);
+
+ VUMeterType m_type;
+ VUAlignment m_alignment;
+ QColor m_background;
+
+ short m_maxLevel;
+
+ short m_levelLeft;
+ short m_recordLevelLeft;
+ short m_peakLevelLeft;
+ short m_levelStepLeft;
+ short m_recordLevelStepLeft;
+ QTimer *m_fallTimerLeft;
+ QTimer *m_peakTimerLeft;
+
+ short m_levelRight;
+ short m_recordLevelRight;
+ short m_peakLevelRight;
+ short m_levelStepRight;
+ short m_recordLevelStepRight;
+ QTimer *m_fallTimerRight;
+ QTimer *m_peakTimerRight;
+
+ bool m_showPeakLevel;
+ short m_baseLevelStep;
+
+ bool m_stereo;
+ bool m_hasRecord;
+
+ // We use this to work out our colours
+ //
+ VelocityColour *m_velocityColour;
+
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/widgets/WheelyButton.cpp b/src/gui/widgets/WheelyButton.cpp
new file mode 100644
index 0000000..e63cb3b
--- /dev/null
+++ b/src/gui/widgets/WheelyButton.cpp
@@ -0,0 +1,35 @@
+/* -*- 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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "WheelyButton.h"
+
+#include <qpushbutton.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+}
+#include "WheelyButton.moc"
diff --git a/src/gui/widgets/WheelyButton.h b/src/gui/widgets/WheelyButton.h
new file mode 100644
index 0000000..83de80d
--- /dev/null
+++ b/src/gui/widgets/WheelyButton.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_WHEELYBUTTON_H_
+#define _RG_WHEELYBUTTON_H_
+
+#include <qpushbutton.h>
+
+
+class QWidget;
+class QWheelEvent;
+
+
+namespace Rosegarden
+{
+
+
+
+class WheelyButton : public QPushButton
+{
+ Q_OBJECT
+
+public:
+ WheelyButton(QWidget *w) : QPushButton(w) { }
+ virtual ~WheelyButton() { }
+
+signals:
+ void wheel(bool up);
+
+protected:
+ void wheelEvent(QWheelEvent *e) {
+ emit wheel(e->delta() > 0);
+ }
+};
+
+
+
+// A specialised menu for selecting audio inputs or outputs, that
+// queries the studio and instrument to find out what it should show.
+// Available in a "compact" size, which is a push button with a popup
+// menu attached, or a regular size which is a combobox.
+//
+
+}
+
+#endif
diff --git a/src/gui/widgets/ZoomSlider.cpp b/src/gui/widgets/ZoomSlider.cpp
new file mode 100644
index 0000000..1986635
--- /dev/null
+++ b/src/gui/widgets/ZoomSlider.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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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 "ZoomSlider.h"
+
+#include <qslider.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+}
diff --git a/src/gui/widgets/ZoomSlider.h b/src/gui/widgets/ZoomSlider.h
new file mode 100644
index 0000000..635d074
--- /dev/null
+++ b/src/gui/widgets/ZoomSlider.h
@@ -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 <glaurent@telegraph-road.org>,
+ Chris Cannam <cannam@all-day-breakfast.com>,
+ Richard Bown <richard.bown@ferventsoftware.com>
+
+ 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_ZOOMSLIDER_H_
+#define _RG_ZOOMSLIDER_H_
+
+#include <qslider.h>
+#include <vector>
+
+
+class T;
+class QWidget;
+
+
+namespace Rosegarden
+{
+
+
+
+template <class T>
+class ZoomSlider : public QSlider
+{
+public:
+
+ /**
+ * Construct a ZoomSlider offering a selection from the
+ * given set of sizes.
+ *
+ * A ZoomSlider is a not-very-well-named slider widget that
+ * offers the user an integral range of values (say, 1..3)
+ * but maps those values internally onto a list of "sizes",
+ * which may be any values of any type (for example the
+ * strings "small", "medium" or "large" or the doubles 1.0,
+ * 1.2 and 1.5). It may be useful where a GUI wants to
+ * offer a fairly limited range of sizes or options that
+ * may actually be arbitrary values chosen because they
+ * work well for some internal reason but that should appear
+ * to the user as a nice continuous range.
+ */
+ ZoomSlider(const std::vector<T> &sizes, T defaultValue,
+ Orientation, QWidget * parent, const char * name=0);
+
+ virtual ~ZoomSlider();
+
+ void reinitialise(const std::vector<T> &sizes, T defaultValue);
+
+ const T &getCurrentSize() const;
+ const T &getDefault() const;
+
+public slots:
+ void setToDefault(); // restore the initial value
+ void setSize(T size);
+ void increment();
+ void decrement();
+
+protected:
+ static int getIndex(const std::vector<T> &, T size);
+ std::vector<T> m_sizes;
+ T m_defaultValue;
+};
+
+
+template<class T>
+ZoomSlider<T>::ZoomSlider(const std::vector<T> &sizes,
+ T initialSize, Orientation o,
+ QWidget *parent, const char *name) :
+ QSlider(0, sizes.size()-1, 1,
+ getIndex(sizes, initialSize), o, parent, name),
+ m_sizes(sizes),
+ m_defaultValue(initialSize)
+{
+ setTracking(false);
+ setFixedWidth(150);
+ setFixedHeight(15);
+ setLineStep(1);
+ setTickmarks(Below);
+}
+
+template<class T>
+ZoomSlider<T>::~ZoomSlider() { }
+
+template<class T>
+int
+ZoomSlider<T>::getIndex(const std::vector<T> &sizes, T size)
+{
+ for (unsigned int i = 0; i < sizes.size(); ++i) {
+ if (sizes[i] == size) return i;
+ }
+ return sizes.size()/2;
+}
+
+template<class T>
+void
+ZoomSlider<T>::reinitialise(const std::vector<T> &sizes, T size)
+{
+ m_sizes = sizes;
+ setMinValue(0);
+ setMaxValue(sizes.size()-1);
+ setValue(getIndex(sizes, size));
+ setLineStep(1);
+ setTickmarks(Below);
+}
+
+template<class T>
+void
+ZoomSlider<T>::setToDefault()
+{
+ setValue(getIndex(m_sizes, m_defaultValue));
+}
+
+template <class T>
+const T &
+ZoomSlider<T>::getCurrentSize() const
+{
+ return m_sizes[value()];
+}
+
+template <class T>
+void
+ZoomSlider<T>::setSize(T size)
+{
+ setValue(getIndex(m_sizes, size));
+}
+
+template <class T>
+void
+ZoomSlider<T>::increment()
+{
+ if (value() + 1 >= m_sizes.size()) return;
+ setValue(value() + 1);
+}
+
+template <class T>
+void
+ZoomSlider<T>::decrement()
+{
+ if (value() <= 0) return;
+ setValue(value() - 1);
+}
+
+template <class T>
+const T &
+ZoomSlider<T>::getDefault() const
+{
+ return m_defaultValue;
+}
+
+
+
+
+}
+
+#endif