You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rosegarden/src/gui/editors/matrix/MatrixView.cpp

3075 lines
96 KiB

/*
Rosegarden
A MIDI and audio sequencer and musical notation editor.
This program is Copyright 2000-2008
Guillaume Laurent <glaurent@telegraph-road.org>,
Chris Cannam <cannam@all-day-breakfast.com>,
Richard Bown <richard.bown@ferventsoftware.com>
The moral rights of Guillaume Laurent, Chris Cannam, and Richard
Bown to claim authorship of this work have been asserted.
Other copyrights also apply to some parts of this work. Please
see the AUTHORS file and individual file headers for details.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either 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 <tdelocale.h>
#include <kstddirs.h>
#include <tdeaction.h>
#include <kcombobox.h>
#include <tdeconfig.h>
#include <kdockwidget.h>
#include <tdeglobal.h>
#include <tdemessagebox.h>
#include <kstatusbar.h>
#include <tdetoolbar.h>
#include <kxmlguiclient.h>
#include <tqcanvas.h>
#include <tqcursor.h>
#include <tqdialog.h>
#include <tqlayout.h>
#include <tqiconset.h>
#include <tqlabel.h>
#include <tqpixmap.h>
#include <tqpoint.h>
#include <tqscrollview.h>
#include <tqsize.h>
#include <tqslider.h>
#include <tqstring.h>
#include <tqwidget.h>
#include <tqwmatrix.h>
namespace Rosegarden
{
static double xorigin = 0.0;
MatrixView::MatrixView(RosegardenGUIDoc *doc,
std::vector<Segment *> segments,
TQWidget *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";
TQString pixmapDir = TDEGlobal::dirs()->findResource("appdata", "pixmaps/toolbar");
TQPixmap 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, TQ_SIGNAL(iMBeingClosed()),
this, TQ_SLOT(slotParametersClosed()));
connect(m_dockLeft, TQ_SIGNAL(hasUndocked()),
this, TQ_SLOT(slotParametersClosed()));
// Apparently, hasUndocked() is emitted when the dock widget's
// 'close' button on the dock handle is clicked.
connect(m_mainDockWidget, TQ_SIGNAL(docking(KDockWidget*, KDockWidget::DockPosition)),
this, TQ_SLOT(slotParametersDockedBack(KDockWidget*, KDockWidget::DockPosition)));
Composition &comp = doc->getComposition();
m_toolBox = new MatrixToolBox(this);
initStatusBar();
connect(m_toolBox, TQ_SIGNAL(showContextHelp(const TQString &)),
this, TQ_SLOT(slotToolHelpChanged(const TQString &)));
TQCanvas *tCanvas = new TQCanvas(this);
m_config->setGroup(MatrixViewConfigGroup);
if (m_config->readBoolEntry("backgroundtextures-1.6-plus", true)) {
TQPixmap background;
TQString pixmapDir =
TDEGlobal::dirs()->findResource("appdata", "pixmaps/");
// We now use a lined background for the non-percussion matrix,
// suggested and supplied by Alessandro Preziosi
TQString backgroundPixmap = isDrumMode() ? "bg-paper-white.xpm" : "bg-matrix-lines.xpm";
if (background.load(TQString("%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());
TQWidget* 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(TQScrollView::AlwaysOff);
m_pianoView->setHScrollBarMode(TQScrollView::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,
TQ_SIGNAL(pluginSelected(InstrumentId, int, int)),
m_parameterBox,
TQ_SLOT(slotPluginSelected(InstrumentId, int, int)));
connect(app,
TQ_SIGNAL(pluginBypassed(InstrumentId, int, bool)),
m_parameterBox,
TQ_SLOT(slotPluginBypassed(InstrumentId, int, bool)));
connect(app,
TQ_SIGNAL(instrumentParametersChanged(InstrumentId)),
m_parameterBox,
TQ_SLOT(slotInstrumentParametersChanged(InstrumentId)));
connect(m_parameterBox,
TQ_SIGNAL(instrumentParametersChanged(InstrumentId)),
app,
TQ_SIGNAL(instrumentParametersChanged(InstrumentId)));
connect(m_parameterBox,
TQ_SIGNAL(selectPlugin(TQWidget *, InstrumentId, int)),
app,
TQ_SLOT(slotShowPluginDialog(TQWidget *, InstrumentId, int)));
connect(m_parameterBox,
TQ_SIGNAL(showPluginGUI(InstrumentId, int)),
app,
TQ_SLOT(slotShowPluginGUI(InstrumentId, int)));
connect(parent, // RosegardenGUIView
TQ_SIGNAL(checkTrackAssignments()),
this,
TQ_SLOT(slotCheckTrackAssignments()));
// Assign the instrument
//
m_parameterBox->useInstrument(instr);
if (m_drumMode) {
connect(m_parameterBox,
TQ_SIGNAL(instrumentPercussionSetChanged(Instrument *)),
this,
TQ_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(), TQ_SIGNAL(valueChanged(int)),
this, TQ_SLOT(slotVerticalScrollPianoKeyboard(int)));
connect(m_canvasView->verticalScrollBar(), TQ_SIGNAL(sliderMoved(int)),
this, TQ_SLOT(slotVerticalScrollPianoKeyboard(int)));
connect(m_canvasView, TQ_SIGNAL(zoomIn()), this, TQ_SLOT(slotZoomIn()));
connect(m_canvasView, TQ_SIGNAL(zoomOut()), this, TQ_SLOT(slotZoomOut()));
connect(m_pianoView, TQ_SIGNAL(gotWheelEvent(TQWheelEvent*)),
m_canvasView, TQ_SLOT(slotExternalWheelEvent(TQWheelEvent*)));
// ensure the piano keyb keeps the right margins when the user toggles
// the canvas view rulers
//
connect(m_canvasView, TQ_SIGNAL(bottomWidgetHeightChanged(int)),
this, TQ_SLOT(slotCanvasBottomWidgetHeightChanged(int)));
connect(m_canvasView, TQ_SIGNAL(mouseEntered()),
this, TQ_SLOT(slotMouseEnteredCanvasView()));
connect(m_canvasView, TQ_SIGNAL(mouseLeft()),
this, TQ_SLOT(slotMouseLeftCanvasView()));
/*
TQObject::connect
(getCanvasView(), TQ_SIGNAL(activeItemPressed(TQMouseEvent*, TQCanvasItem*)),
this, TQ_SLOT (activeItemPressed(TQMouseEvent*, TQCanvasItem*)));
*/
TQObject::connect
(getCanvasView(),
TQ_SIGNAL(mousePressed(timeT,
int, TQMouseEvent*, MatrixElement*)),
this,
TQ_SLOT(slotMousePressed(timeT,
int, TQMouseEvent*, MatrixElement*)));
TQObject::connect
(getCanvasView(),
TQ_SIGNAL(mouseMoved(timeT, int, TQMouseEvent*)),
this,
TQ_SLOT(slotMouseMoved(timeT, int, TQMouseEvent*)));
TQObject::connect
(getCanvasView(),
TQ_SIGNAL(mouseReleased(timeT, int, TQMouseEvent*)),
this,
TQ_SLOT(slotMouseReleased(timeT, int, TQMouseEvent*)));
TQObject::connect
(getCanvasView(), TQ_SIGNAL(hoveredOverNoteChanged(int, bool, timeT)),
this, TQ_SLOT(slotHoveredOverNoteChanged(int, bool, timeT)));
TQObject::connect
(m_pitchRuler, TQ_SIGNAL(hoveredOverKeyChanged(unsigned int)),
this, TQ_SLOT (slotHoveredOverKeyChanged(unsigned int)));
TQObject::connect
(m_pitchRuler, TQ_SIGNAL(keyPressed(unsigned int, bool)),
this, TQ_SLOT (slotKeyPressed(unsigned int, bool)));
TQObject::connect
(m_pitchRuler, TQ_SIGNAL(keySelected(unsigned int, bool)),
this, TQ_SLOT (slotKeySelected(unsigned int, bool)));
TQObject::connect
(m_pitchRuler, TQ_SIGNAL(keyReleased(unsigned int, bool)),
this, TQ_SLOT (slotKeyReleased(unsigned int, bool)));
TQObject::connect
(getCanvasView(), TQ_SIGNAL(hoveredOverAbsoluteTimeChanged(unsigned int)),
this, TQ_SLOT (slotHoveredOverAbsoluteTimeChanged(unsigned int)));
TQObject::connect
(doc, TQ_SIGNAL(pointerPositionChanged(timeT)),
this, TQ_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
TQObject::disconnect
(topStandardRuler->getLoopRuler(),
TQ_SIGNAL(setPointerPosition(timeT)), 0, 0);
TQObject::connect
(topStandardRuler->getLoopRuler(),
TQ_SIGNAL(setPointerPosition(timeT)),
this, TQ_SLOT(slotSetInsertCursorPosition(timeT)));
TQObject::connect
(topStandardRuler,
TQ_SIGNAL(dragPointerToPosition(timeT)),
this, TQ_SLOT(slotSetInsertCursorPosition(timeT)));
topStandardRuler->getLoopRuler()->setBackgroundColor
(GUIPalette::getColour(GUIPalette::InsertCursorRuler));
connect(topStandardRuler->getLoopRuler(), TQ_SIGNAL(startMouseMove(int)),
m_canvasView, TQ_SLOT(startAutoScroll(int)));
connect(topStandardRuler->getLoopRuler(), TQ_SIGNAL(stopMouseMove()),
m_canvasView, TQ_SLOT(stopAutoScroll()));
connect(bottomStandardRuler->getLoopRuler(), TQ_SIGNAL(startMouseMove(int)),
m_canvasView, TQ_SLOT(startAutoScroll(int)));
connect(bottomStandardRuler->getLoopRuler(), TQ_SIGNAL(stopMouseMove()),
m_canvasView, TQ_SLOT(stopAutoScroll()));
connect(m_bottomStandardRuler, TQ_SIGNAL(dragPointerToPosition(timeT)),
this, TQ_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
// ~TQCanvasView so we can't do anything about it). This calls
// TQCanvasView::updateContentsSize(), which in turn updates the
// view's scrollbars, hence calling TQScrollBar::setValue(), and
// sending the TQSCrollbar::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...)
//
TDERadioAction* toolAction = 0;
TQString pixmapDir = TDEGlobal::dirs()->findResource("appdata", "pixmaps/");
TQIconSet icon(TQPixmap(pixmapDir + "/toolbar/select.xpm"));
toolAction = new TDERadioAction(i18n("&Select and Edit"), icon, Key_F2,
this, TQ_SLOT(slotSelectSelected()),
actionCollection(), "select");
toolAction->setExclusiveGroup("tools");
toolAction = new TDERadioAction(i18n("&Draw"), "pencil", Key_F3,
this, TQ_SLOT(slotPaintSelected()),
actionCollection(), "draw");
toolAction->setExclusiveGroup("tools");
toolAction = new TDERadioAction(i18n("&Erase"), "eraser", Key_F4,
this, TQ_SLOT(slotEraseSelected()),
actionCollection(), "erase");
toolAction->setExclusiveGroup("tools");
toolAction = new TDERadioAction(i18n("&Move"), "move", Key_F5,
this, TQ_SLOT(slotMoveSelected()),
actionCollection(), "move");
toolAction->setExclusiveGroup("tools");
TQCanvasPixmap pixmap(pixmapDir + "/toolbar/resize.xpm");
icon = TQIconSet(pixmap);
toolAction = new TDERadioAction(i18n("Resi&ze"), icon, Key_F6,
this, TQ_SLOT(slotResizeSelected()),
actionCollection(), "resize");
toolAction->setExclusiveGroup("tools");
icon = TQIconSet(NotePixmapFactory::toTQPixmap(NotePixmapFactory::makeToolbarPixmap("chord")));
(new TDEToggleAction(i18n("C&hord Insert Mode"), icon, Key_H,
this, TQ_SLOT(slotUpdateInsertModeStatus()),
actionCollection(), "chord_mode"))->
setChecked(false);
pixmap.load(pixmapDir + "/toolbar/step_by_step.xpm");
icon = TQIconSet(pixmap);
new TDEToggleAction(i18n("Ste&p Recording"), icon, 0, this,
TQ_SLOT(slotToggleStepByStep()), actionCollection(),
"toggle_step_by_step");
pixmap.load(pixmapDir + "/toolbar/quantize.png");
icon = TQIconSet(pixmap);
new TDEAction(EventQuantizeCommand::getGlobalName(), icon, Key_Equal, this,
TQ_SLOT(slotTransformsQuantize()), actionCollection(),
"quantize");
new TDEAction(i18n("Repeat Last Quantize"), Key_Plus, this,
TQ_SLOT(slotTransformsRepeatQuantize()), actionCollection(),
"repeat_quantize");
new TDEAction(CollapseNotesCommand::getGlobalName(), Key_Equal + CTRL, this,
TQ_SLOT(slotTransformsCollapseNotes()), actionCollection(),
"collapse_notes");
new TDEAction(i18n("&Legato"), Key_Minus, this,
TQ_SLOT(slotTransformsLegato()), actionCollection(),
"legatoize");
new TDEAction(ChangeVelocityCommand::getGlobalName(10), 0,
Key_Up + SHIFT, this,
TQ_SLOT(slotVelocityUp()), actionCollection(),
"velocity_up");
new TDEAction(ChangeVelocityCommand::getGlobalName( -10), 0,
Key_Down + SHIFT, this,
TQ_SLOT(slotVelocityDown()), actionCollection(),
"velocity_down");
new TDEAction(i18n("Set to Current Velocity"), 0, this,
TQ_SLOT(slotSetVelocitiesToCurrent()), actionCollection(),
"set_to_current_velocity");
new TDEAction(i18n("Set Event &Velocities..."), 0, this,
TQ_SLOT(slotSetVelocities()), actionCollection(),
"set_velocities");
new TDEAction(i18n("Trigger Se&gment..."), 0, this,
TQ_SLOT(slotTriggerSegment()), actionCollection(),
"trigger_segment");
new TDEAction(i18n("Remove Triggers..."), 0, this,
TQ_SLOT(slotRemoveTriggers()), actionCollection(),
"remove_trigger");
new TDEAction(i18n("Select &All"), Key_A + CTRL, this,
TQ_SLOT(slotSelectAll()), actionCollection(),
"select_all");
new TDEAction(i18n("&Delete"), Key_Delete, this,
TQ_SLOT(slotEditDelete()), actionCollection(),
"delete");
new TDEAction(i18n("Cursor &Back"), 0, Key_Left, this,
TQ_SLOT(slotStepBackward()), actionCollection(),
"cursor_back");
new TDEAction(i18n("Cursor &Forward"), 0, Key_Right, this,
TQ_SLOT(slotStepForward()), actionCollection(),
"cursor_forward");
new TDEAction(i18n("Cursor Ba&ck Bar"), 0, Key_Left + CTRL, this,
TQ_SLOT(slotJumpBackward()), actionCollection(),
"cursor_back_bar");
new TDEAction(i18n("Cursor For&ward Bar"), 0, Key_Right + CTRL, this,
TQ_SLOT(slotJumpForward()), actionCollection(),
"cursor_forward_bar");
new TDEAction(i18n("Cursor Back and Se&lect"), SHIFT + Key_Left, this,
TQ_SLOT(slotExtendSelectionBackward()), actionCollection(),
"extend_selection_backward");
new TDEAction(i18n("Cursor Forward and &Select"), SHIFT + Key_Right, this,
TQ_SLOT(slotExtendSelectionForward()), actionCollection(),
"extend_selection_forward");
new TDEAction(i18n("Cursor Back Bar and Select"), SHIFT + CTRL + Key_Left, this,
TQ_SLOT(slotExtendSelectionBackwardBar()), actionCollection(),
"extend_selection_backward_bar");
new TDEAction(i18n("Cursor Forward Bar and Select"), SHIFT + CTRL + Key_Right, this,
TQ_SLOT(slotExtendSelectionForwardBar()), actionCollection(),
"extend_selection_forward_bar");
new TDEAction(i18n("Cursor to St&art"), 0,
/* #1025717: conflicting meanings for ctrl+a - dupe with Select All
Key_A + CTRL, */ this,
TQ_SLOT(slotJumpToStart()), actionCollection(),
"cursor_start");
new TDEAction(i18n("Cursor to &End"), 0, Key_E + CTRL, this,
TQ_SLOT(slotJumpToEnd()), actionCollection(),
"cursor_end");
icon = TQIconSet(NotePixmapFactory::toTQPixmap(NotePixmapFactory::makeToolbarPixmap
("transport-cursor-to-pointer")));
new TDEAction(i18n("Cursor to &Playback Pointer"), icon, 0, this,
TQ_SLOT(slotJumpCursorToPlayback()), actionCollection(),
"cursor_to_playback_pointer");
icon = TQIconSet(NotePixmapFactory::toTQPixmap(NotePixmapFactory::makeToolbarPixmap
("transport-play")));
TDEAction *play = new TDEAction(i18n("&Play"), icon, Key_Enter, this,
TQ_SIGNAL(play()), actionCollection(), "play");
// Alternative shortcut for Play
TDEShortcut playShortcut = play->shortcut();
playShortcut.append( KKey(Key_Return + CTRL) );
play->setShortcut(playShortcut);
icon = TQIconSet(NotePixmapFactory::toTQPixmap(NotePixmapFactory::makeToolbarPixmap
("transport-stop")));
new TDEAction(i18n("&Stop"), icon, Key_Insert, this,
TQ_SIGNAL(stop()), actionCollection(), "stop");
icon = TQIconSet(NotePixmapFactory::toTQPixmap(NotePixmapFactory::makeToolbarPixmap
("transport-rewind")));
new TDEAction(i18n("Re&wind"), icon, Key_End, this,
TQ_SIGNAL(rewindPlayback()), actionCollection(),
"playback_pointer_back_bar");
icon = TQIconSet(NotePixmapFactory::toTQPixmap(NotePixmapFactory::makeToolbarPixmap
("transport-ffwd")));
new TDEAction(i18n("&Fast Forward"), icon, Key_PageDown, this,
TQ_SIGNAL(fastForwardPlayback()), actionCollection(),
"playback_pointer_forward_bar");
icon = TQIconSet(NotePixmapFactory::toTQPixmap(NotePixmapFactory::makeToolbarPixmap
("transport-rewind-end")));
new TDEAction(i18n("Rewind to &Beginning"), icon, 0, this,
TQ_SIGNAL(rewindPlaybackToBeginning()), actionCollection(),
"playback_pointer_start");
icon = TQIconSet(NotePixmapFactory::toTQPixmap(NotePixmapFactory::makeToolbarPixmap
("transport-ffwd-end")));
new TDEAction(i18n("Fast Forward to &End"), icon, 0, this,
TQ_SIGNAL(fastForwardPlaybackToEnd()), actionCollection(),
"playback_pointer_end");
icon = TQIconSet(NotePixmapFactory::toTQPixmap(NotePixmapFactory::makeToolbarPixmap
("transport-pointer-to-cursor")));
new TDEAction(i18n("Playback Pointer to &Cursor"), icon, 0, this,
TQ_SLOT(slotJumpPlaybackToCursor()), actionCollection(),
"playback_pointer_to_cursor");
icon = TQIconSet(NotePixmapFactory::toTQPixmap(NotePixmapFactory::makeToolbarPixmap
("transport-solo")));
new TDEToggleAction(i18n("&Solo"), icon, 0, this,
TQ_SLOT(slotToggleSolo()), actionCollection(),
"toggle_solo");
icon = TQIconSet(NotePixmapFactory::toTQPixmap(NotePixmapFactory::makeToolbarPixmap
("transport-tracking")));
(new TDEToggleAction(i18n("Scro&ll to Follow Playback"), icon, Key_Pause, this,
TQ_SLOT(slotToggleTracking()), actionCollection(),
"toggle_tracking"))->setChecked(m_playTracking);
icon = TQIconSet(NotePixmapFactory::toTQPixmap(NotePixmapFactory::makeToolbarPixmap
("transport-panic")));
new TDEAction(i18n("Panic"), icon, Key_P + CTRL + ALT, this,
TQ_SIGNAL(panic()), actionCollection(), "panic");
new TDEAction(i18n("Set Loop to Selection"), Key_Semicolon + CTRL, this,
TQ_SLOT(slotPreviewSelection()), actionCollection(),
"preview_selection");
new TDEAction(i18n("Clear L&oop"), Key_Colon + CTRL, this,
TQ_SLOT(slotClearLoop()), actionCollection(),
"clear_loop");
new TDEAction(i18n("Clear Selection"), Key_Escape, this,
TQ_SLOT(slotClearSelection()), actionCollection(),
"clear_selection");
// icon = TQIconSet(TQCanvasPixmap(pixmapDir + "/toolbar/eventfilter.xpm"));
new TDEAction(i18n("&Filter Selection"), "filter", Key_F + CTRL, this,
TQ_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 TDEAction(i18n("&No Snap"), 0, this,
TQ_SLOT(slotSetSnapFromAction()),
actionCollection(), "snap_none");
} else if (d == SnapGrid::SnapToUnit) {
} else if (d == SnapGrid::SnapToBeat) {
new TDEAction(i18n("Snap to Bea&t"), Key_1, this,
TQ_SLOT(slotSetSnapFromAction()),
actionCollection(), "snap_beat");
} else if (d == SnapGrid::SnapToBar) {
new TDEAction(i18n("Snap to &Bar"), Key_5, this,
TQ_SLOT(slotSetSnapFromAction()),
actionCollection(), "snap_bar");
} else {
timeT err = 0;
TQString label = NotationStrings::makeNoteMenuLabel(d, true, err);
TQPixmap pixmap = NotePixmapFactory::toTQPixmap
(NotePixmapFactory::makeNoteMenuPixmap(d, err));
TDEShortcut 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;
TQString actionName = TQString("snap_%1").arg(int((crotchetDuration * 4) / d));
if (d == (crotchetDuration * 3) / 2) actionName = "snap_3";
new TDEAction(i18n("Snap to %1").arg(label), pixmap, cut, this,
TQ_SLOT(slotSetSnapFromAction()), actionCollection(),
actionName.ascii());
}
}
//
// Settings menu
//
new TDEAction(i18n("Show Instrument Parameters"), 0, this,
TQ_SLOT(slotDockParametersBack()),
actionCollection(),
"show_inst_parameters");
new TDEToggleAction(i18n("Show Ch&ord Name Ruler"), 0, this,
TQ_SLOT(slotToggleChordsRuler()),
actionCollection(), "show_chords_ruler");
new TDEToggleAction(i18n("Show &Tempo Ruler"), 0, this,
TQ_SLOT(slotToggleTempoRuler()),
actionCollection(), "show_tempo_ruler");
createGUI(getRCFileName(), false);
if (getSegmentsOnlyRestsAndClefs())
actionCollection()->action("draw")->activate();
else
actionCollection()->action("select")->activate();
}
bool
MatrixView::isInChordMode()
{
return ((TDEToggleAction *)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 TQLabel(sb);
m_hoveredOverNoteName = new TQLabel(sb);
m_hoveredOverAbsoluteTime->setMinimumWidth(175);
m_hoveredOverNoteName->setMinimumWidth(65);
sb->addWidget(m_hoveredOverAbsoluteTime);
sb->addWidget(m_hoveredOverNoteName);
m_insertModeLabel = new TQLabel(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 TQLabel(sb);
sb->addWidget(m_selectionCounter);
}
void MatrixView::slotToolHelpChanged(const TQString &s)
{
TQString 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();
}
TQSize MatrixView::getViewSize()
{
return canvas()->size();
}
void MatrixView::setViewSize(TQSize 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, TQ_SIGNAL(gotSelection()),
this, TQ_SLOT(slotNewSelection()));
connect(selector, TQ_SIGNAL(editTriggerSegment(int)),
this, TQ_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() == TQDialog::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,
TQMouseEvent* 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() != TQt::RightButton) {
getCanvasView()->startAutoScroll();
}
// play a preview
//playPreview(pitch);
}
void MatrixView::slotMouseMoved(timeT time, int pitch, TQMouseEvent* 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, TQMouseEvent* 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();
TQString msg = i18n("Note: %1 (%2.%3s)")
.arg(TQString("%1-%2-%3-%4")
.arg(TQString("%1").arg(bar + 1).rightJustify(3, '0'))
.arg(TQString("%1").arg(beat).rightJustify(2, '0'))
.arg(TQString("%1").arg(fraction).rightJustify(2, '0'))
.arg(TQString("%1").arg(remainder).rightJustify(2, '0')))
.arg(rt.sec)
.arg(TQString("%1").arg(ms).rightJustify(3, '0'));
m_hoveredOverAbsoluteTime->setText(msg);
}
m_haveHoveredOverNote = false;
m_hoveredOverNoteName->setText(i18n("%1 (%2)")
.arg(label.getTQString())
.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(TQString("%1 (%2)").
arg(label.getTQString()).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/tqstring.html#sprintf
// we replaced this TQString format("%ld (%ld.%03lds)");
// to support Unicode
TQString message = i18n("Time: %1 (%2.%3s)")
.arg(TQString("%1-%2-%3-%4")
.arg(TQString("%1").arg(bar + 1).rightJustify(3, '0'))
.arg(TQString("%1").arg(beat).rightJustify(2, '0'))
.arg(TQString("%1").arg(fraction).rightJustify(2, '0'))
.arg(TQString("%1").arg(remainder).rightJustify(2, '0')))
.arg(rt.sec)
.arg(TQString("%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 TQObject *s = sender();
TQString 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 TQObject *s = sender();
TQString 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;
TDEToolBar *actionsToolbar = toolBar("Actions Toolbar");
if (!actionsToolbar) {
MATRIX_DEBUG << "MatrixView::initActionsToolbar - "
<< "tool bar not found" << endl;
return ;
}
// The SnapGrid combo and Snap To... menu items
//
TQLabel *sLabel = new TQLabel(i18n(" Grid: "), actionsToolbar, "tde toolbar widget");
sLabel->setIndent(10);
TQPixmap noMap = NotePixmapFactory::toTQPixmap(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;
TQString label = NotationStrings::makeNoteMenuLabel(d, true, err);
TQPixmap pixmap = NotePixmapFactory::toTQPixmap
(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, TQ_SIGNAL(activated(int)),
this, TQ_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
TQLabel *vlabel = new TQLabel(i18n(" Velocity: "), actionsToolbar, "tde toolbar widget");
vlabel->setIndent(10);
m_velocityCombo = new KComboBox(actionsToolbar);
for (int i = 0; i <= 127; ++i) {
m_velocityCombo->insertItem(TQString("%1").arg(i));
}
m_velocityCombo->setCurrentItem(100); //!!! associate with segment
// Quantize combo
//
TQLabel *qLabel = new TQLabel(i18n(" Quantize: "), actionsToolbar, "tde 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;
TQString label = NotationStrings::makeNoteMenuLabel(time, true, error);
TQPixmap pmap = NotePixmapFactory::toTQPixmap(NotePixmapFactory::makeNoteMenuPixmap(time, error));
m_quantizeCombo->insertItem(error ? noMap : pmap, label);
}
m_quantizeCombo->insertItem(noMap, i18n("Off"));
connect(m_quantizeCombo, TQ_SIGNAL(activated(int)),
this, TQ_SLOT(slotQuantizeSelection(int)));
}
void
MatrixView::initZoomToolbar()
{
MATRIX_DEBUG << "MatrixView::initZoomToolbar" << endl;
TDEToolBar *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, TQt::Horizontal, zoomToolbar, "tde toolbar widget");
m_hZoomSlider->setTracking(true);
m_hZoomSlider->setFocusPolicy(TQWidget::NoFocus);
m_zoomLabel = new TQLabel(zoomToolbar, "tde toolbar widget");
m_zoomLabel->setIndent(10);
m_zoomLabel->setFixedWidth(80);
connect(m_hZoomSlider,
TQ_SIGNAL(valueChanged(int)),
TQ_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
//
TQWMatrix 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() == TQDialog::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(TQSize(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() == TQDialog::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() != TQDialog::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(TQPaintEvent* 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
//
TQString 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 ;
}
TDEToggleAction *action = dynamic_cast<TDEToggleAction *>
(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()
{
TDEToggleAction *action = dynamic_cast<TDEToggleAction *>
(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()
{
TQString message;
if (isInChordMode()) {
message = i18n(" Chord ");
} else {
message = "";
}
m_insertModeLabel->setText(message);
}
void
MatrixView::slotStepByStepTargetRequested(TQObject *obj)
{
TDEToggleAction *action = dynamic_cast<TDEToggleAction *>
(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;
TQWidget *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);
}
TQObject::connect
(pitchRuler, TQ_SIGNAL(hoveredOverKeyChanged(unsigned int)),
this, TQ_SLOT (slotHoveredOverKeyChanged(unsigned int)));
TQObject::connect
(pitchRuler, TQ_SIGNAL(keyPressed(unsigned int, bool)),
this, TQ_SLOT (slotKeyPressed(unsigned int, bool)));
TQObject::connect
(pitchRuler, TQ_SIGNAL(keySelected(unsigned int, bool)),
this, TQ_SLOT (slotKeySelected(unsigned int, bool)));
TQObject::connect
(pitchRuler, TQ_SIGNAL(keyReleased(unsigned int, bool)),
this, TQ_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"