summaryrefslogtreecommitdiffstats
path: root/src/gui/editors/matrix/MatrixView.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/editors/matrix/MatrixView.cpp')
-rw-r--r--src/gui/editors/matrix/MatrixView.cpp3076
1 files changed, 3076 insertions, 0 deletions
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"