summaryrefslogtreecommitdiffstats
path: root/src/gui/general
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/general')
-rw-r--r--src/gui/general/ActiveItem.cpp32
-rw-r--r--src/gui/general/ActiveItem.h55
-rw-r--r--src/gui/general/BarLine.cpp165
-rw-r--r--src/gui/general/BarLine.h64
-rw-r--r--src/gui/general/BaseTool.cpp89
-rw-r--r--src/gui/general/BaseTool.h112
-rw-r--r--src/gui/general/BaseToolBox.cpp58
-rw-r--r--src/gui/general/BaseToolBox.h69
-rw-r--r--src/gui/general/CanvasCursor.cpp52
-rw-r--r--src/gui/general/CanvasCursor.h55
-rw-r--r--src/gui/general/CanvasItemGC.cpp64
-rw-r--r--src/gui/general/CanvasItemGC.h85
-rw-r--r--src/gui/general/CategoryElement.cpp61
-rw-r--r--src/gui/general/CategoryElement.h71
-rw-r--r--src/gui/general/ClefIndex.cpp100
-rw-r--r--src/gui/general/ClefIndex.h59
-rw-r--r--src/gui/general/EditTool.cpp143
-rw-r--r--src/gui/general/EditTool.h166
-rw-r--r--src/gui/general/EditToolBox.cpp56
-rw-r--r--src/gui/general/EditToolBox.h65
-rw-r--r--src/gui/general/EditView.cpp1717
-rw-r--r--src/gui/general/EditView.h405
-rw-r--r--src/gui/general/EditViewBase.cpp711
-rw-r--r--src/gui/general/EditViewBase.h396
-rw-r--r--src/gui/general/EditViewTimeSigNotifier.h56
-rw-r--r--src/gui/general/GUIPalette.cpp311
-rw-r--r--src/gui/general/GUIPalette.h185
-rw-r--r--src/gui/general/HZoomable.cpp32
-rw-r--r--src/gui/general/HZoomable.h53
-rw-r--r--src/gui/general/LinedStaff.cpp1217
-rw-r--r--src/gui/general/LinedStaff.h759
-rw-r--r--src/gui/general/LinedStaffManager.cpp33
-rw-r--r--src/gui/general/LinedStaffManager.h61
-rw-r--r--src/gui/general/MidiPitchLabel.cpp74
-rw-r--r--src/gui/general/MidiPitchLabel.h57
-rw-r--r--src/gui/general/PixmapFunctions.cpp271
-rw-r--r--src/gui/general/PixmapFunctions.h107
-rw-r--r--src/gui/general/PresetElement.cpp68
-rw-r--r--src/gui/general/PresetElement.h82
-rw-r--r--src/gui/general/PresetGroup.cpp269
-rw-r--r--src/gui/general/PresetGroup.h105
-rw-r--r--src/gui/general/PresetHandlerDialog.cpp281
-rw-r--r--src/gui/general/PresetHandlerDialog.h107
-rw-r--r--src/gui/general/ProgressReporter.cpp53
-rw-r--r--src/gui/general/ProgressReporter.h80
-rw-r--r--src/gui/general/RosegardenCanvasView.cpp485
-rw-r--r--src/gui/general/RosegardenCanvasView.h197
-rw-r--r--src/gui/general/RosegardenScrollView.cpp416
-rw-r--r--src/gui/general/RosegardenScrollView.h183
-rw-r--r--src/gui/general/Spline.cpp130
-rw-r--r--src/gui/general/Spline.h71
-rw-r--r--src/gui/general/StaffLine.cpp64
-rw-r--r--src/gui/general/StaffLine.h78
53 files changed, 10735 insertions, 0 deletions
diff --git a/src/gui/general/ActiveItem.cpp b/src/gui/general/ActiveItem.cpp
new file mode 100644
index 0000000..00d967f
--- /dev/null
+++ b/src/gui/general/ActiveItem.cpp
@@ -0,0 +1,32 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "ActiveItem.h"
+
+
+
+namespace Rosegarden
+{
+}
diff --git a/src/gui/general/ActiveItem.h b/src/gui/general/ActiveItem.h
new file mode 100644
index 0000000..f8f5339
--- /dev/null
+++ b/src/gui/general/ActiveItem.h
@@ -0,0 +1,55 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_ACTIVEITEM_H_
+#define _RG_ACTIVEITEM_H_
+
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * An interface for canvas items which are capable of handling
+ * mouse events
+ */
+class ActiveItem
+{
+public:
+ virtual void handleMousePress(QMouseEvent*) = 0;
+ virtual void handleMouseMove(QMouseEvent*) = 0;
+ virtual void handleMouseRelease(QMouseEvent*) = 0;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/general/BarLine.cpp b/src/gui/general/BarLine.cpp
new file mode 100644
index 0000000..42bb936
--- /dev/null
+++ b/src/gui/general/BarLine.cpp
@@ -0,0 +1,165 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "BarLine.h"
+
+#include <qpainter.h>
+
+namespace Rosegarden {
+
+void
+BarLine::drawShape(QPainter &painter)
+{
+ int bx = int(x());
+ int by = int(y());
+
+ switch (m_style) {
+
+ case LinedStaff::PlainBar:
+ painter.drawRect(bx, by, m_baseBarThickness, m_barLineHeight);
+ break;
+
+ case LinedStaff::DoubleBar:
+ painter.drawRect(bx, by, m_baseBarThickness, m_barLineHeight);
+ painter.drawRect(bx + m_baseBarThickness * 3, by,
+ m_baseBarThickness, m_barLineHeight);
+ break;
+
+ case LinedStaff::HeavyDoubleBar:
+ bx -= m_baseBarThickness * 5;
+ painter.drawRect(bx, by, m_baseBarThickness, m_barLineHeight);
+ painter.drawRect(bx + m_baseBarThickness * 3, by,
+ m_baseBarThickness * 3, m_barLineHeight);
+ break;
+
+ case LinedStaff::RepeatEndBar:
+ bx -= m_baseBarThickness * 5 + m_lineSpacing * 2 / 3;
+ painter.drawEllipse(bx, by + m_barLineHeight / 2 - (m_lineSpacing * 2 / 3),
+ m_lineSpacing / 3, m_lineSpacing / 3);
+ painter.drawEllipse(bx, by + m_barLineHeight / 2 + (m_lineSpacing / 3),
+ m_lineSpacing / 3, m_lineSpacing / 3);
+ bx += m_lineSpacing * 2 / 3;
+ painter.drawRect(bx, by, m_baseBarThickness, m_barLineHeight);
+ painter.drawRect(bx + m_baseBarThickness * 3, by,
+ m_baseBarThickness * 3, m_barLineHeight);
+ break;
+
+ case LinedStaff::RepeatStartBar:
+
+ if (m_inset > 0) {
+ painter.drawRect(bx, by, m_baseBarThickness, m_barLineHeight);
+ bx += m_inset;
+ }
+
+ painter.drawRect(bx, by, m_baseBarThickness * 3, m_barLineHeight);
+ painter.drawRect(bx + m_baseBarThickness * 5, by,
+ m_baseBarThickness, m_barLineHeight);
+ bx += m_baseBarThickness * 6 + (m_lineSpacing / 3);
+ painter.drawEllipse(bx, by + m_barLineHeight / 2 - (m_lineSpacing * 2 / 3),
+ m_lineSpacing / 3, m_lineSpacing / 3);
+ painter.drawEllipse(bx, by + m_barLineHeight / 2 + (m_lineSpacing / 3),
+ m_lineSpacing / 3, m_lineSpacing / 3);
+ break;
+
+ case LinedStaff::RepeatBothBar:
+
+ if (m_inset > 0) {
+ painter.drawRect(bx, by, m_baseBarThickness, m_barLineHeight);
+ bx += m_inset;
+ }
+
+ bx -= m_baseBarThickness * 4 + m_lineSpacing * 2 / 3;
+ painter.drawEllipse(bx, by + m_barLineHeight / 2 - (m_lineSpacing * 2 / 3),
+ m_lineSpacing / 3, m_lineSpacing / 3);
+ painter.drawEllipse(bx, by + m_barLineHeight / 2 + (m_lineSpacing / 3),
+ m_lineSpacing / 3, m_lineSpacing / 3);
+ bx += m_lineSpacing * 2 / 3;
+ painter.drawRect(bx, by, m_baseBarThickness, m_barLineHeight);
+ painter.drawRect(bx + m_baseBarThickness * 3, by,
+ m_baseBarThickness * 3, m_barLineHeight);
+ painter.drawRect(bx + m_baseBarThickness * 8, by,
+ m_baseBarThickness, m_barLineHeight);
+ bx += m_baseBarThickness * 9 + (m_lineSpacing / 3);
+ painter.drawEllipse(bx, by + m_barLineHeight / 2 - (m_lineSpacing * 2 / 3),
+ m_lineSpacing / 3, m_lineSpacing / 3);
+ painter.drawEllipse(bx, by + m_barLineHeight / 2 + (m_lineSpacing / 3),
+ m_lineSpacing / 3, m_lineSpacing / 3);
+
+ break;
+
+ case LinedStaff::NoVisibleBar:
+ break;
+ }
+}
+
+QPointArray
+BarLine::areaPoints() const
+{
+ int bx = int(x());
+ int by = int(y());
+ int x0 = bx, y0 = by, x1, y1 = by + m_barLineHeight;
+
+ switch (m_style) {
+
+ case LinedStaff::PlainBar:
+ x1 = x0 + m_baseBarThickness;
+ break;
+
+ case LinedStaff::DoubleBar:
+ x1 = x0 + m_baseBarThickness * 4;
+ break;
+
+ case LinedStaff::HeavyDoubleBar:
+ x0 -= m_baseBarThickness * 6;
+ x1 = bx;
+ break;
+
+ case LinedStaff::RepeatEndBar:
+ x0 -= m_baseBarThickness * 6 + m_lineSpacing * 2 / 3;
+ x1 = bx;
+ break;
+
+ case LinedStaff::RepeatStartBar:
+ x1 = x0 + m_baseBarThickness * 6 + m_lineSpacing * 2 / 3 + m_inset;
+ break;
+
+ case LinedStaff::RepeatBothBar:
+ x0 -= m_baseBarThickness * 4 + m_lineSpacing * 2 / 3;
+ x1 = x0 + m_baseBarThickness * 9 + m_lineSpacing * 2 / 3 + m_inset;
+ break;
+
+ case LinedStaff::NoVisibleBar:
+ x1 = x0 + 1;
+ break;
+ }
+
+ QPointArray p(4);
+ p[0] = QPoint(x0, y0);
+ p[1] = QPoint(x1, y0);
+ p[2] = QPoint(x1, y1);
+ p[3] = QPoint(x0, y1);
+ return p;
+}
+
+}
diff --git a/src/gui/general/BarLine.h b/src/gui/general/BarLine.h
new file mode 100644
index 0000000..124bc06
--- /dev/null
+++ b/src/gui/general/BarLine.h
@@ -0,0 +1,64 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_BARLINE_H_
+#define _RG_BARLINE_H_
+
+#include "LinedStaff.h"
+#include <qcanvas.h>
+
+namespace Rosegarden {
+
+class BarLine : public QCanvasPolygonalItem
+{
+public:
+ BarLine(QCanvas *canvas, double layoutX,
+ int barLineHeight, int baseBarThickness, int lineSpacing,
+ int inset, LinedStaff::BarStyle style) :
+ QCanvasPolygonalItem(canvas),
+ m_layoutX(layoutX),
+ m_barLineHeight(barLineHeight),
+ m_baseBarThickness(baseBarThickness),
+ m_lineSpacing(lineSpacing),
+ m_inset(inset),
+ m_style(style) { }
+
+ double getLayoutX() const { return m_layoutX; }
+
+ virtual QPointArray areaPoints() const;
+ virtual void drawShape(QPainter &);
+
+protected:
+ double m_layoutX;
+ int m_barLineHeight;
+ int m_baseBarThickness;
+ int m_lineSpacing;
+ int m_inset;
+ LinedStaff::BarStyle m_style;
+};
+
+}
+
+#endif /*BARLINE_H_*/
diff --git a/src/gui/general/BaseTool.cpp b/src/gui/general/BaseTool.cpp
new file mode 100644
index 0000000..4c33610
--- /dev/null
+++ b/src/gui/general/BaseTool.cpp
@@ -0,0 +1,89 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "BaseTool.h"
+
+#include "misc/Debug.h"
+#include <kxmlguifactory.h>
+#include <qcursor.h>
+#include <qobject.h>
+#include <qpopupmenu.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+BaseTool::BaseTool(const QString& menuName, KXMLGUIFactory* factory, QObject* parent)
+ : QObject(parent),
+ m_menuName(menuName),
+ m_menu(0),
+ m_parentFactory(factory)
+{}
+
+BaseTool::~BaseTool()
+{
+ RG_DEBUG << "BaseTool::~BaseTool()\n";
+
+ // delete m_menu;
+ // m_parentView->factory()->removeClient(this);
+ // m_instance = 0;
+}
+
+void BaseTool::ready()
+{}
+
+void BaseTool::stow()
+{}
+
+void BaseTool::showMenu()
+{
+ if (!hasMenu())
+ return ;
+
+ if (!m_menu)
+ createMenu();
+
+ if (m_menu)
+ m_menu->exec(QCursor::pos());
+ else
+ RG_DEBUG << "BaseTool::showMenu() : no menu to show\n";
+}
+
+QString BaseTool::getCurrentContextHelp() const
+{
+ return m_contextHelp;
+}
+
+void BaseTool::setContextHelp(const QString &help)
+{
+ m_contextHelp = help;
+ emit showContextHelp(m_contextHelp);
+}
+
+}
+
+#include "BaseTool.moc"
+
diff --git a/src/gui/general/BaseTool.h b/src/gui/general/BaseTool.h
new file mode 100644
index 0000000..71e6410
--- /dev/null
+++ b/src/gui/general/BaseTool.h
@@ -0,0 +1,112 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_BASETOOL_H_
+#define _RG_BASETOOL_H_
+
+#include <qobject.h>
+#include <qstring.h>
+
+
+class QPopupMenu;
+class KXMLGUIFactory;
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * BaseTool : base tool class, just handles RMB menu creation and
+ * handling by a BaseToolBox
+ *
+ */
+class BaseTool : public QObject
+{
+ Q_OBJECT
+
+ friend class BaseToolBox;
+
+public:
+
+ virtual ~BaseTool();
+
+ /**
+ * Is called by the parent View (EditView or SegmentCanvas) when
+ * the tool is set as current.
+ * Add any setup here (e.g. setting the mouse cursor shape)
+ */
+ virtual void ready();
+
+ /**
+ * Is called by the parent View (EditView or SegmentCanvas) after
+ * the tool is put away.
+ * Add any cleanup here
+ */
+ virtual void stow();
+
+ /**
+ * Show the menu if there is one
+ */
+ virtual void showMenu();
+
+ /**
+ * Retrieve current status-line type help for the tool, if any
+ */
+ virtual QString getCurrentContextHelp() const;
+
+signals:
+ void showContextHelp(const QString &);
+
+protected:
+ /**
+ * Create a new BaseTool
+ *
+ * \a menuName : the name of the menu defined in the XML rc file
+ */
+ BaseTool(const QString& menuName, KXMLGUIFactory*, QObject* parent);
+
+ virtual void createMenu() = 0;
+ virtual bool hasMenu() { return false; }
+
+ virtual void setContextHelp(const QString &help);
+ virtual void clearContextHelp() { setContextHelp(""); }
+
+ //--------------- Data members ---------------------------------
+
+ QString m_menuName;
+ QPopupMenu* m_menu;
+
+ KXMLGUIFactory* m_parentFactory;
+
+ QString m_contextHelp;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/general/BaseToolBox.cpp b/src/gui/general/BaseToolBox.cpp
new file mode 100644
index 0000000..9e2fda9
--- /dev/null
+++ b/src/gui/general/BaseToolBox.cpp
@@ -0,0 +1,58 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "BaseToolBox.h"
+
+#include "BaseTool.h"
+#include <qobject.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+BaseToolBox::BaseToolBox(QWidget* parent)
+ : QObject(parent),
+ m_tools(17, // default size, from the Qt docs
+ false) // but we want it to be case insensitive
+{
+ //m_tools.setAutoDelete(true);
+}
+
+BaseTool* BaseToolBox::getTool(const QString& toolName)
+{
+ BaseTool* tool = m_tools[toolName];
+
+ if (!tool) tool = createTool(toolName);
+
+ connect(tool, SIGNAL(showContextHelp(const QString &)),
+ this, SIGNAL(showContextHelp(const QString &)));
+
+ return tool;
+}
+
+}
+#include "BaseToolBox.moc"
diff --git a/src/gui/general/BaseToolBox.h b/src/gui/general/BaseToolBox.h
new file mode 100644
index 0000000..2a0242f
--- /dev/null
+++ b/src/gui/general/BaseToolBox.h
@@ -0,0 +1,69 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_BASETOOLBOX_H_
+#define _RG_BASETOOLBOX_H_
+
+#include <qdict.h>
+#include <qobject.h>
+
+
+class QWidget;
+class QString;
+
+
+namespace Rosegarden
+{
+
+class BaseTool;
+
+
+/**
+ * BaseToolBox : maintains a single instance of each registered tool
+ *
+ * Tools are fetched from a name
+ */
+class BaseToolBox : public QObject
+{
+ Q_OBJECT
+
+public:
+ BaseToolBox(QWidget* parent);
+
+ virtual BaseTool* getTool(const QString& toolName);
+
+signals:
+ void showContextHelp(const QString &);
+
+protected:
+ virtual BaseTool* createTool(const QString& toolName) = 0;
+
+ QDict<BaseTool> m_tools;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/general/CanvasCursor.cpp b/src/gui/general/CanvasCursor.cpp
new file mode 100644
index 0000000..5f04794
--- /dev/null
+++ b/src/gui/general/CanvasCursor.cpp
@@ -0,0 +1,52 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "CanvasCursor.h"
+
+#include "GUIPalette.h"
+#include <qcanvas.h>
+#include <qpen.h>
+
+
+namespace Rosegarden
+{
+
+CanvasCursor::CanvasCursor(QCanvas* c, int width)
+ : QCanvasRectangle(c),
+ m_width(width)
+{
+ QPen pen(GUIPalette::getColour(GUIPalette::Pointer));
+ // pen.setWidth(width);
+ setPen(pen);
+ setBrush(GUIPalette::getColour(GUIPalette::Pointer));
+}
+
+void CanvasCursor::updateHeight()
+{
+ setSize(m_width, canvas()->height());
+ // setPoints(0, 0, 0, canvas()->height());
+}
+
+}
diff --git a/src/gui/general/CanvasCursor.h b/src/gui/general/CanvasCursor.h
new file mode 100644
index 0000000..694e9df
--- /dev/null
+++ b/src/gui/general/CanvasCursor.h
@@ -0,0 +1,55 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_CANVASCURSOR_H_
+#define _RG_CANVASCURSOR_H_
+
+
+#include <qcanvas.h>
+
+class QCanvas;
+
+
+namespace Rosegarden
+{
+
+
+
+class CanvasCursor : public QCanvasRectangle
+{
+public:
+ CanvasCursor(QCanvas*, int width);
+ void updateHeight();
+// virtual QRect boundingRect() const;
+protected:
+ int m_width;
+};
+
+
+
+
+}
+
+#endif
diff --git a/src/gui/general/CanvasItemGC.cpp b/src/gui/general/CanvasItemGC.cpp
new file mode 100644
index 0000000..6e6afb2
--- /dev/null
+++ b/src/gui/general/CanvasItemGC.cpp
@@ -0,0 +1,64 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "CanvasItemGC.h"
+
+#include "misc/Debug.h"
+
+#include <qcanvas.h>
+
+namespace Rosegarden
+{
+
+void CanvasItemGC::mark(QCanvasItem* item)
+{
+ if (!item)
+ return ;
+
+ item->hide();
+ // RG_DEBUG << "CanvasItemGC::mark() : "
+ // << item << std::endl;
+ m_garbage.push_back(item);
+}
+
+void CanvasItemGC::gc()
+{
+ for (unsigned int i = 0; i < m_garbage.size(); ++i) {
+ // RG_DEBUG << "CanvasItemGC::gc() : delete "
+ // << m_garbage[i] << "\n";
+ delete m_garbage[i];
+ }
+
+ m_garbage.clear();
+}
+
+void CanvasItemGC::flush()
+{
+ m_garbage.clear();
+}
+
+std::vector<QCanvasItem*> CanvasItemGC::m_garbage;
+
+}
diff --git a/src/gui/general/CanvasItemGC.h b/src/gui/general/CanvasItemGC.h
new file mode 100644
index 0000000..db1833b
--- /dev/null
+++ b/src/gui/general/CanvasItemGC.h
@@ -0,0 +1,85 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_CANVASITEMGC_H_
+#define _RG_CANVASITEMGC_H_
+
+#include <vector>
+
+
+class QCanvasItem;
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * A pseudo GC in which CanvasItems whose ownership isn't clear cut
+ * can be put for periodical removal.
+ *
+ * This is especially for SegmentItems which can put their repeat
+ * rectangles when they're being deleted.
+ *
+ * The problem this solves is a classic ownership/double deletion
+ * case. The SegmentCanvas deletes all its items on destruction. But
+ * the SegmentItems have an auxiliary "repeat rectangle" which is a
+ * QCanvasRectangle, that needs to be deleted when the SegmentItem is
+ * itself deleted.
+ *
+ * However, if the SegmentItem deletes its repeat rectangle, then when
+ * the SegmentCanvas destruction occurs, the SegmentCanvas dtor will
+ * get a list of all its children (QCanvas::allItems()), containing
+ * both SegmentItems and their repeat rectangles. Deleting a
+ * SegmentItem will delete its repeat rectangle, which will still be
+ * present in the all children list which the SegmentCanvas dtor is
+ * iterating over.
+ *
+ * So a solution is simply to push to-be-deleted repeat rectangles on
+ * this GC, which should be processed on canvas updates, for instance.
+ *
+ */
+class CanvasItemGC
+{
+public:
+ /// mark the given item for GC
+ static void mark(QCanvasItem*);
+
+ /// GC all marked items
+ static void gc();
+
+ /// Forget all marked items - don't delete them
+ static void flush();
+
+protected:
+ static std::vector<QCanvasItem*> m_garbage;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/general/CategoryElement.cpp b/src/gui/general/CategoryElement.cpp
new file mode 100644
index 0000000..2199ce6
--- /dev/null
+++ b/src/gui/general/CategoryElement.cpp
@@ -0,0 +1,61 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "CategoryElement.h"
+
+#include "misc/Debug.h"
+#include "PresetElement.h"
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+CategoryElement::CategoryElement(QString name) :
+ m_name(name)
+{}
+
+CategoryElement::~CategoryElement()
+{
+ // nothing to do
+}
+
+void
+CategoryElement::addPreset(QString name,
+ int clef,
+ int transpose,
+ int highAm,
+ int lowAm,
+ int highPro,
+ int lowPro)
+{
+ RG_DEBUG << "CategoryElement::addPreset(...): adding new PresetElement" << endl;
+
+ PresetElement e(name, clef, transpose, highAm, lowAm,
+ highPro, lowPro);
+ m_categoryPresets.push_back(e);
+}
+
+}
diff --git a/src/gui/general/CategoryElement.h b/src/gui/general/CategoryElement.h
new file mode 100644
index 0000000..eeb88a5
--- /dev/null
+++ b/src/gui/general/CategoryElement.h
@@ -0,0 +1,71 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_CATEGORYELEMENT_H_
+#define _RG_CATEGORYELEMENT_H_
+
+#include "PresetElement.h"
+#include <qstring.h>
+
+
+
+
+namespace Rosegarden
+{
+
+
+/*
+ * A container class for storing a collection of PresetElement objects grouped
+ * into the same musical category (eg. Flutes, Clarinets, Strings)
+ */
+class CategoryElement
+{
+public:
+ CategoryElement(QString name);
+ ~CategoryElement();
+
+ void addPreset(QString name,
+ int clef,
+ int transpose,
+ int highAm,
+ int lowAm,
+ int highPro,
+ int lowPro);
+
+ QString getName() { return m_name; }
+
+ ElementContainer getPresets() { return m_categoryPresets; }
+ PresetElement getPresetByIndex(int index) { return m_categoryPresets [index]; }
+
+private:
+ QString m_name;
+ ElementContainer m_categoryPresets;
+}; // CategoryElement
+
+typedef std::vector<CategoryElement> CategoriesContainer;
+
+}
+
+#endif
diff --git a/src/gui/general/ClefIndex.cpp b/src/gui/general/ClefIndex.cpp
new file mode 100644
index 0000000..68ad488
--- /dev/null
+++ b/src/gui/general/ClefIndex.cpp
@@ -0,0 +1,100 @@
+// -*- c-basic-offset: 4 -*-
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral right of the authors to claim authorship of this work
+ has been asserted.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "gui/general/ClefIndex.h"
+#include "misc/Debug.h"
+
+namespace Rosegarden
+{
+
+const Rosegarden::Clef clefIndexToClef(int index)
+{
+ Rosegarden::Clef clef;
+
+ // insert an initial clef from track parameters
+ switch (index) {
+ case TrebleClef: clef = Clef(Clef::Treble); break;
+ case BassClef: clef = Clef(Clef::Bass); break;
+ case CrotalesClef: clef = Clef(Clef::Treble, 2); break;
+ case XylophoneClef: clef = Clef(Clef::Treble, 1); break;
+ case GuitarClef: clef = Clef(Clef::Treble, -1); break;
+ case ContrabassClef: clef = Clef(Clef::Bass, -1); break;
+ case CelestaClef: clef = Clef(Clef::Bass, 2); break;
+ case OldCelestaClef: clef = Clef(Clef::Bass, 1); break;
+ case FrenchClef: clef = Clef(Clef::French); break;
+ case SopranoClef: clef = Clef(Clef::Soprano); break;
+ case MezzosopranoClef: clef = Clef(Clef::Mezzosoprano); break;
+ case AltoClef: clef = Clef(Clef::Alto); break;
+ case TenorClef: clef = Clef(Clef::Tenor); break;
+ case BaritoneClef: clef = Clef(Clef::Baritone); break;
+ case VarbaritoneClef: clef = Clef(Clef::Varbaritone); break;
+ case SubbassClef: clef = Clef(Clef::Subbass); break;
+ default: clef = Clef(Clef::Treble); break;
+ }
+ return clef;
+}
+
+const int clefNameToClefIndex(QString s)
+{
+ int m_elClef = 0;
+ if (s) {
+ if (s == "treble")
+ m_elClef = TrebleClef;
+ else if (s == "bass")
+ m_elClef = BassClef;
+ else if (s == "crotales")
+ m_elClef = CrotalesClef;
+ else if (s == "xylophone")
+ m_elClef = XylophoneClef;
+ else if (s == "guitar")
+ m_elClef = GuitarClef;
+ else if (s == "contrabass")
+ m_elClef = ContrabassClef;
+ else if (s == "celesta")
+ m_elClef = CelestaClef;
+ else if (s == "oldCelesta")
+ m_elClef = OldCelestaClef;
+ else if (s == "french")
+ m_elClef = FrenchClef;
+ else if (s == "soprano")
+ m_elClef = SopranoClef;
+ else if (s == "mezzosoprano")
+ m_elClef = MezzosopranoClef;
+ else if (s == "alto")
+ m_elClef = AltoClef;
+ else if (s == "tenor")
+ m_elClef = TenorClef;
+ else if (s == "baritone")
+ m_elClef = BaritoneClef;
+ else if (s == "varbaritone")
+ m_elClef = VarbaritoneClef;
+ else if (s == "subbass")
+ m_elClef = SubbassClef;
+ else if (s == "two-bar")
+ m_elClef = TwoBarClef;
+ else {
+ RG_DEBUG << "startElement: processed unrecognized clef type: " << s << endl;
+ }
+ }
+ return m_elClef;
+}
+
+} \ No newline at end of file
diff --git a/src/gui/general/ClefIndex.h b/src/gui/general/ClefIndex.h
new file mode 100644
index 0000000..74e3fc8
--- /dev/null
+++ b/src/gui/general/ClefIndex.h
@@ -0,0 +1,59 @@
+// -*- c-basic-offset: 4 -*-
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral right of the authors to claim authorship of this work
+ has been asserted.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#ifndef _CLEF_INDEX_H_
+#define _CLEF_INDEX_H_
+
+#include "base/NotationTypes.h"
+#include <qstring.h>
+
+// used variously by TPB, SPB, PresetHandler to correlate combo box indices to
+// clef types
+enum { TrebleClef = 0, // G clef, line 2
+ BassClef, // F clef, line 4
+ CrotalesClef, // G clef, line 2, 15 above
+ XylophoneClef, // G clef, line 2, 8 above
+ GuitarClef, // G clef, line 2, 8 below
+ ContrabassClef, // F clef, line 4, 8 below
+ CelestaClef, // F clef, line 4, 15 above
+ OldCelestaClef, // F clef, line 4, 8 above
+ FrenchClef, // G clef, line 1
+ SopranoClef, // C clef, line 1
+ MezzosopranoClef, // C clef, line 2
+ AltoClef, // C clef, line 3
+ TenorClef, // C clef, line 4
+ BaritoneClef, // C clef, line 5
+ VarbaritoneClef, // F clef, line 3
+ SubbassClef, // F clef, line 5
+ TwoBarClef // percussion clef //!!! doesn't exist yet!
+ };
+
+namespace Rosegarden
+{
+
+const Clef clefIndexToClef(int index);
+
+const int clefNameToClefIndex(QString s);
+
+}
+
+#endif // _CLEF_INDEX_H_
diff --git a/src/gui/general/EditTool.cpp b/src/gui/general/EditTool.cpp
new file mode 100644
index 0000000..52e7b11
--- /dev/null
+++ b/src/gui/general/EditTool.cpp
@@ -0,0 +1,143 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "EditTool.h"
+
+#include "misc/Debug.h"
+#include "base/Event.h"
+#include "BaseTool.h"
+#include "base/ViewElement.h"
+#include "EditView.h"
+#include "RosegardenCanvasView.h"
+#include <kxmlguiclient.h>
+#include <qevent.h>
+#include <qpopupmenu.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+EditTool::EditTool(const QString& menuName, EditView* view)
+ : BaseTool(menuName, view->factory(), view),
+ m_parentView(view)
+{}
+
+void EditTool::handleMousePress(timeT time,
+ int height,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement* el)
+{
+ RG_DEBUG << "EditTool::handleMousePress : mouse button = "
+ << e->button() << endl;
+
+ switch (e->button()) {
+
+ case Qt::LeftButton:
+ if (e->type() == QEvent::MouseButtonDblClick) {
+ RG_DEBUG << "EditTool::handleMousePress: it's a double-click"
+ << endl;
+ handleMouseDoubleClick(time, height, staffNo, e, el);
+ } else {
+ RG_DEBUG << "EditTool::handleMousePress: it's a single-click"
+ << endl;
+ handleLeftButtonPress(time, height, staffNo, e, el);
+ }
+ break;
+
+ case Qt::RightButton:
+ handleRightButtonPress(time, height, staffNo, e, el);
+ break;
+
+ case Qt::MidButton:
+ handleMidButtonPress(time, height, staffNo, e, el);
+ break;
+
+ default:
+ RG_DEBUG << "EditTool::handleMousePress : no button mouse press\n";
+ break;
+ }
+}
+
+void EditTool::handleMidButtonPress(timeT,
+ int, int,
+ QMouseEvent*,
+ ViewElement*)
+{}
+
+void EditTool::handleRightButtonPress(timeT,
+ int, int,
+ QMouseEvent*,
+ ViewElement*)
+{
+ showMenu();
+}
+
+void EditTool::handleMouseDoubleClick(timeT,
+ int, int,
+ QMouseEvent*,
+ ViewElement*)
+{
+ // nothing
+}
+
+int EditTool::handleMouseMove(timeT, int, QMouseEvent*)
+{
+ return RosegardenCanvasView::NoFollow;
+}
+
+void EditTool::handleMouseRelease(timeT, int, QMouseEvent*)
+{}
+
+void EditTool::createMenu(QString rcFileName)
+{
+ setRCFileName(rcFileName);
+ createMenu();
+}
+
+void EditTool::createMenu()
+{
+ RG_DEBUG << "BaseTool::createMenu() " << m_rcFileName << " - " << m_menuName << endl;
+
+ setXMLFile(m_rcFileName);
+ m_parentFactory->addClient(this);
+
+ QWidget* tmp = m_parentFactory->container(m_menuName, this);
+
+ if (!tmp)
+ RG_DEBUG << "BaseTool::createMenu(" << m_rcFileName
+ << ") : menu creation failed (name : "
+ << m_menuName << ")\n";
+
+ m_menu = dynamic_cast<QPopupMenu*>(tmp);
+}
+
+bool EditTool::hasMenu()
+{
+ return !m_rcFileName.isEmpty();
+}
+
+}
diff --git a/src/gui/general/EditTool.h b/src/gui/general/EditTool.h
new file mode 100644
index 0000000..17937d1
--- /dev/null
+++ b/src/gui/general/EditTool.h
@@ -0,0 +1,166 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_EDITTOOL_H_
+#define _RG_EDITTOOL_H_
+
+#include "BaseTool.h"
+#include <kxmlguiclient.h>
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class Event;
+class EditView;
+
+
+/**
+ * Edit tool base class.
+ *
+ * A EditTool represents one of the items on an edition view
+ * toolbar. It handles mouse click events for the EditView ('State'
+ * design pattern).
+ *
+ * A EditTool can have a menu, normally activated through a right
+ * mouse button click. This menu is defined in an XML file, see
+ * NoteInserter and noteinserter.rc for an example.
+ *
+ * This class is a "semi-singleton", that is, only one instance per
+ * EditView window is created. This is because menu creation is
+ * slow, and the fact that a tool can trigger the setting of another
+ * tool through a menu choice). This is maintained with the
+ * EditToolBox class This means we can't rely on the ctor/dtor to
+ * perform setting up, like mouse cursor changes for instance. Use the
+ * ready() and stow() method for this.
+ *
+ * @see EditView#setTool()
+ * @see EditToolBox
+ */
+class EditTool : public BaseTool, public KXMLGUIClient
+{
+ friend class EditToolBox;
+
+public:
+
+ /**
+ * Dispatch the event to Left/Middle/Right MousePress
+ */
+ virtual void handleMousePress(timeT time,
+ int height,
+ int staffNo,
+ QMouseEvent *event,
+ ViewElement*);
+
+ /**
+ * Main operation of the tool
+ */
+ virtual void handleLeftButtonPress(timeT time,
+ int height,
+ int staffNo,
+ QMouseEvent *event,
+ ViewElement*) = 0;
+
+ /**
+ * Do nothing
+ */
+ virtual void handleMidButtonPress(timeT time,
+ int height,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement*);
+
+ /**
+ * Show option menu
+ */
+ virtual void handleRightButtonPress(timeT time,
+ int height,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement*);
+
+ /**
+ * Do nothing
+ */
+ virtual void handleMouseDoubleClick(timeT time,
+ int height,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement*);
+
+ /**
+ * Do nothing.
+ * Implementations of handleMouseMove should return true if
+ * they want the canvas to scroll to the position the mouse
+ * moved to following the method's return.
+ */
+ virtual int handleMouseMove(timeT time,
+ int height,
+ QMouseEvent*);
+
+ /**
+ * Do nothing
+ */
+ virtual void handleMouseRelease(timeT time,
+ int height,
+ QMouseEvent*);
+
+ /**
+ * Respond to an event being deleted -- it may be the one the tool
+ * is remembering as the current event.
+ */
+ virtual void handleEventRemoved(Event *) { }
+
+
+protected:
+ /**
+ * Create a new EditTool
+ *
+ * \a menuName : the name of the menu defined in the XML rc file
+ */
+ EditTool(const QString& menuName, EditView*);
+
+ void setRCFileName(QString rcfilename) { m_rcFileName = rcfilename; }
+
+ virtual void createMenu();
+ virtual void createMenu(QString rcFileName);
+ virtual bool hasMenu();
+
+ //--------------- Data members ---------------------------------
+ QString m_rcFileName;
+
+ EditView* m_parentView;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/general/EditToolBox.cpp b/src/gui/general/EditToolBox.cpp
new file mode 100644
index 0000000..c2e24a9
--- /dev/null
+++ b/src/gui/general/EditToolBox.cpp
@@ -0,0 +1,56 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "EditToolBox.h"
+
+#include "BaseToolBox.h"
+#include "EditTool.h"
+#include "EditView.h"
+#include <qobject.h>
+#include <qstring.h>
+#include <kmessagebox.h>
+
+namespace Rosegarden
+{
+
+EditToolBox::EditToolBox(EditView *parent)
+ : BaseToolBox(parent),
+ m_parentView(parent)
+{
+}
+
+EditTool* EditToolBox::getTool(const QString& toolName)
+{
+ return dynamic_cast<EditTool*>(BaseToolBox::getTool(toolName));
+}
+
+EditTool* EditToolBox::createTool(const QString&)
+{
+ KMessageBox::error(0, "EditToolBox::createTool called - this should never happen");
+ return 0;
+}
+
+}
+#include "EditToolBox.moc"
diff --git a/src/gui/general/EditToolBox.h b/src/gui/general/EditToolBox.h
new file mode 100644
index 0000000..0115558
--- /dev/null
+++ b/src/gui/general/EditToolBox.h
@@ -0,0 +1,65 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_EDITTOOLBOX_H_
+#define _RG_EDITTOOLBOX_H_
+
+#include "BaseToolBox.h"
+#include "EditTool.h"
+
+
+class QString;
+
+
+namespace Rosegarden
+{
+
+class EditView;
+
+
+/**
+ * EditToolBox : specialized toolbox for EditViews (notation, matrix...)
+ *
+ */
+class EditToolBox : public BaseToolBox
+{
+ Q_OBJECT
+public:
+ EditToolBox(EditView* parent);
+
+ virtual EditTool* getTool(const QString& toolName);
+
+protected:
+ virtual EditTool* createTool(const QString& toolName);
+
+ //--------------- Data members ---------------------------------
+
+ EditView* m_parentView;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/general/EditView.cpp b/src/gui/general/EditView.cpp
new file mode 100644
index 0000000..a36b385
--- /dev/null
+++ b/src/gui/general/EditView.cpp
@@ -0,0 +1,1717 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "EditView.h"
+#include <qlayout.h>
+
+#include "base/BaseProperties.h"
+#include <klocale.h>
+#include <kconfig.h>
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "ActiveItem.h"
+#include "base/AnalysisTypes.h"
+#include "base/Composition.h"
+#include "base/CompositionTimeSliceAdapter.h"
+#include "base/Controllable.h"
+#include "base/ControlParameter.h"
+#include "base/Device.h"
+#include "base/Event.h"
+#include "base/Exception.h"
+#include "base/Instrument.h"
+#include "base/MidiDevice.h"
+#include "base/MidiProgram.h"
+#include "base/MidiTypes.h"
+#include "base/NotationTypes.h"
+#include "base/Profiler.h"
+#include "base/Property.h"
+#include "base/PropertyName.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "base/SoftSynthDevice.h"
+#include "base/Staff.h"
+#include "base/Studio.h"
+#include "base/ViewElement.h"
+#include "commands/edit/InvertCommand.h"
+#include "commands/edit/MoveCommand.h"
+#include "commands/edit/RescaleCommand.h"
+#include "commands/edit/RetrogradeCommand.h"
+#include "commands/edit/RetrogradeInvertCommand.h"
+#include "commands/edit/TransposeCommand.h"
+#include "commands/segment/AddTempoChangeCommand.h"
+#include "commands/segment/AddTimeSignatureAndNormalizeCommand.h"
+#include "commands/segment/AddTimeSignatureCommand.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "EditViewBase.h"
+#include "gui/dialogs/RescaleDialog.h"
+#include "gui/dialogs/TempoDialog.h"
+#include "gui/dialogs/IntervalDialog.h"
+#include "gui/dialogs/TimeSignatureDialog.h"
+#include "gui/rulers/StandardRuler.h"
+#include "gui/kdeext/KTmpStatusMsg.h"
+#include "gui/kdeext/QCanvasGroupableItem.h"
+#include "gui/rulers/ControllerEventsRuler.h"
+#include "gui/rulers/ControlRuler.h"
+#include "gui/rulers/PropertyControlRuler.h"
+#include "RosegardenCanvasView.h"
+#include <kaction.h>
+#include <kcommand.h>
+#include <kdockwidget.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <kstddirs.h>
+#include <ktabwidget.h>
+#include <kxmlguiclient.h>
+#include <qaccel.h>
+#include <qbutton.h>
+#include <qdialog.h>
+#include <qframe.h>
+#include <qinputdialog.h>
+#include <qlabel.h>
+#include <qobjectlist.h>
+#include <qpopupmenu.h>
+#include <qsize.h>
+#include <qstring.h>
+#include <qtabwidget.h>
+#include <qvbox.h>
+#include <qwidget.h>
+#include <qwmatrix.h>
+
+
+namespace Rosegarden
+{
+
+const unsigned int EditView::CONTROLS_ROW = 0;
+const unsigned int EditView::RULERS_ROW = CONTROLS_ROW + 1;
+const unsigned int EditView::TOPBARBUTTONS_ROW = RULERS_ROW + 1;
+const unsigned int EditView::CANVASVIEW_ROW = TOPBARBUTTONS_ROW + 1;
+const unsigned int EditView::CONTROLRULER_ROW = CANVASVIEW_ROW + 1;
+
+// Just some simple features we might want to show - make them bit maskable
+//
+static int FeatureShowVelocity = 0x00001; // show the velocity ruler
+
+EditView::EditView(RosegardenGUIDoc *doc,
+ std::vector<Segment *> segments,
+ unsigned int cols,
+ QWidget *parent, const char *name) :
+ EditViewBase(doc, segments, cols, parent, name),
+ m_currentEventSelection(0),
+ m_activeItem(0),
+ m_canvasView(0),
+ m_rulerBox(new QVBoxLayout), // top ruler box - added to grid later on
+ m_rulerBoxFiller(0), // On the left of m_rulerBox
+ m_controlBox(new QVBoxLayout), // top control ruler box - added to grid later on
+ m_bottomBox(new QVBox(this, "bottomframe")), // bottom box - added to bottom of canvas view by setCanvasView()
+ m_topStandardRuler(0),
+ m_bottomStandardRuler(0),
+ m_controlRuler(0),
+ m_controlRulers(new KTabWidget(getBottomWidget(), "controlrulers"))
+{
+ m_controlRulers->setHoverCloseButton(true);
+ m_controlRulers->setHoverCloseButtonDelayed(false);
+ connect(m_controlRulers, SIGNAL(closeRequest(QWidget*)),
+ this, SLOT(slotRemoveControlRuler(QWidget*)));
+
+ (dynamic_cast<QBoxLayout*>(m_bottomBox->layout()))->setDirection(QBoxLayout::BottomToTop);
+
+ // m_rulerBoxFiller is a white label used to keep m_rulerBox exactly
+ // above the scrolling part of the view (and never above the
+ // RosegardenCanvasView::m_leftWidget).
+ QGridLayout * gl = new QGridLayout(1, 2);
+ gl->setColStretch(0, 0);
+ gl->setColStretch(1, 1);
+ gl->addLayout(m_rulerBox, 0, 1);
+ m_rulerBoxFiller = new QLabel(getCentralWidget());
+ gl->addWidget(m_rulerBoxFiller, 0, 0);
+ m_rulerBoxFiller->hide();
+
+ m_grid->addLayout(gl, RULERS_ROW, m_mainCol);
+
+ m_grid->addMultiCellLayout(m_controlBox, CONTROLS_ROW, CONTROLS_ROW, 0, 1);
+ m_controlBox->setAlignment(AlignRight);
+ // m_grid->addWidget(m_controlRulers, CONTROLRULER_ROW, 2);
+
+ m_controlRulers->hide();
+ m_controlRulers->setTabPosition(QTabWidget::Bottom);
+}
+
+EditView::~EditView()
+{
+ delete m_currentEventSelection;
+ m_currentEventSelection = 0;
+}
+
+void EditView::updateBottomWidgetGeometry()
+{
+ getBottomWidget()->layout()->invalidate();
+ getBottomWidget()->updateGeometry();
+ getCanvasView()->updateBottomWidgetGeometry();
+}
+
+void EditView::paintEvent(QPaintEvent* e)
+{
+ RG_DEBUG << "EditView::paintEvent()\n";
+ EditViewBase::paintEvent(e);
+
+ if (m_needUpdate) {
+ RG_DEBUG << "EditView::paintEvent() - calling updateView\n";
+ updateView();
+ getCanvasView()->slotUpdate();
+
+ // update rulers
+ QLayoutIterator it = m_rulerBox->iterator();
+ QLayoutItem *child;
+ while ( (child = it.current()) != 0 ) {
+ if (child->widget())
+ child->widget()->update();
+ ++it;
+ }
+
+ updateControlRulers();
+
+ } else {
+
+ getCanvasView()->slotUpdate();
+ updateControlRulers();
+
+ }
+
+ m_needUpdate = false;
+}
+
+void EditView::updateControlRulers(bool updateHPos)
+{
+ for (int i = 0; i < m_controlRulers->count(); ++i) {
+ ControlRuler* ruler = dynamic_cast<ControlRuler*>(m_controlRulers->page(i));
+ if (ruler) {
+ if (updateHPos)
+ ruler->slotUpdateElementsHPos();
+ else
+ ruler->slotUpdate();
+ }
+ }
+}
+
+void EditView::setControlRulersZoom(QWMatrix zoomMatrix)
+{
+ m_currentRulerZoomMatrix = zoomMatrix;
+
+ for (int i = 0; i < m_controlRulers->count(); ++i) {
+ ControlRuler* ruler = dynamic_cast<ControlRuler*>(m_controlRulers->page(i));
+ if (ruler)
+ ruler->setWorldMatrix(zoomMatrix);
+ }
+}
+
+void EditView::setControlRulersCurrentSegment()
+{
+ RG_DEBUG << "EditView::setControlRulersCurrentSegment: visible is " << m_controlRulers->isVisible() << endl;
+
+ bool visible = m_controlRulers->isVisible();
+
+ delete m_controlRulers;
+ m_controlRulers = new KTabWidget(getBottomWidget(), "controlrulers");
+
+ bool haveTabs = setupControllerTabs();
+ setupAddControlRulerMenu();
+
+ if (haveTabs)
+ m_controlRulers->show();
+ else
+ m_controlRulers->hide();
+
+ updateBottomWidgetGeometry();
+
+ /*
+ for (int i = 0; i < m_controlRulers->count(); ++i) {
+
+ PropertyControlRuler *pcr = dynamic_cast<PropertyControlRuler *>
+ (m_controlRulers->page(i));
+
+ if (pcr) pcr->setStaff(getCurrentStaff());
+ else {
+
+ ControllerEventsRuler *cer = dynamic_cast<ControllerEventsRuler *>
+ (m_controlRulers->page(i));
+
+ if (cer) cer->setSegment(getCurrentSegment());
+ }
+ }
+ */
+}
+
+void EditView::setTopStandardRuler(StandardRuler* w, QWidget *leftBox)
+{
+ delete m_topStandardRuler;
+ m_topStandardRuler = w;
+
+ QGridLayout * gl = new QGridLayout(1, 2);
+ gl->setColStretch(0, 0);
+ gl->setColStretch(1, 1);
+
+ gl->addWidget(w, 0, 1);
+ if (leftBox) {
+ gl->addWidget(leftBox, 0, 0);
+ }
+
+ m_grid->addLayout(gl, TOPBARBUTTONS_ROW, m_mainCol);
+
+ if (m_canvasView) {
+ connect(m_canvasView->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ m_topStandardRuler, SLOT(slotScrollHoriz(int)));
+ connect(m_canvasView->horizontalScrollBar(), SIGNAL(sliderMoved(int)),
+ m_topStandardRuler, SLOT(slotScrollHoriz(int)));
+ }
+}
+
+void EditView::setBottomStandardRuler(StandardRuler* w)
+{
+ delete m_bottomStandardRuler;
+ m_bottomStandardRuler = w;
+
+ // m_bottomBox->insertWidget(0, w);
+
+ if (m_canvasView) {
+ connect(m_canvasView->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ m_bottomStandardRuler, SLOT(slotScrollHoriz(int)));
+ connect(m_canvasView->horizontalScrollBar(), SIGNAL(sliderMoved(int)),
+ m_bottomStandardRuler, SLOT(slotScrollHoriz(int)));
+ }
+}
+
+void EditView::setRewFFwdToAutoRepeat()
+{
+ QWidget* transportToolbar = factory()->container("Transport Toolbar", this);
+
+ if (transportToolbar) {
+ QObjectList *l = transportToolbar->queryList();
+ QObjectListIt it(*l); // iterate over the buttons
+ QObject *obj;
+
+ while ( (obj = it.current()) != 0 ) {
+ // for each found object...
+ ++it;
+ // RG_DEBUG << "EditView::setRewFFwdToAutoRepeat() : obj name : " << obj->name() << endl;
+ QString objName = obj->name();
+
+ if (objName.endsWith("playback_pointer_back_bar") || objName.endsWith("playback_pointer_forward_bar")) {
+ QButton* btn = dynamic_cast<QButton*>(obj);
+ if (!btn) {
+ RG_DEBUG << "Very strange - found widgets in Transport Toolbar which aren't buttons\n";
+
+ continue;
+ }
+ btn->setAutoRepeat(true);
+ }
+
+
+ }
+ delete l;
+
+ } else {
+ RG_DEBUG << "transportToolbar == 0\n";
+ }
+
+}
+
+void EditView::addRuler(QWidget* w)
+{
+ m_rulerBox->addWidget(w);
+
+ if (m_canvasView) {
+ connect(m_canvasView->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ w, SLOT(slotScrollHoriz(int)));
+ connect(m_canvasView->horizontalScrollBar(), SIGNAL(sliderMoved(int)),
+ w, SLOT(slotScrollHoriz(int)));
+ }
+}
+
+void EditView::addPropertyBox(QWidget *w)
+{
+ m_controlBox->addWidget(w);
+}
+
+void EditView::addControlRuler(ControlRuler* ruler)
+{
+ ruler->setWorldMatrix(m_currentRulerZoomMatrix);
+ m_controlRulers->addTab(ruler, KGlobal::iconLoader()->loadIconSet("fileclose", KIcon::Small),
+ ruler->getName());
+ m_controlRulers->showPage(ruler);
+
+ if (m_canvasView) {
+ connect(m_canvasView->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ ruler->horizontalScrollBar(), SIGNAL(valueChanged(int)));
+ connect(m_canvasView->horizontalScrollBar(), SIGNAL(sliderMoved(int)),
+ ruler->horizontalScrollBar(), SIGNAL(sliderMoved(int)));
+ }
+
+ connect(ruler, SIGNAL(stateChange(const QString&, bool)),
+ this, SLOT(slotStateChanged(const QString&, bool)));
+
+ stateChanged("have_control_ruler", KXMLGUIClient::StateReverse);
+}
+
+void EditView::readjustViewSize(QSize requestedSize, bool exact)
+{
+ Profiler profiler("EditView::readjustViewSize", true);
+
+ if (exact) {
+ RG_DEBUG << "EditView::readjustViewSize: exact size requested ("
+ << requestedSize.width() << ", " << requestedSize.height()
+ << ")\n";
+
+ setViewSize(requestedSize);
+ getCanvasView()->slotUpdate();
+ return ;
+ }
+
+ int requestedWidth = requestedSize.width(),
+ requestedHeight = requestedSize.height(),
+ windowWidth = width(),
+ windowHeight = height();
+
+ QSize newSize;
+
+ newSize.setWidth(((requestedWidth / windowWidth) + 1) * windowWidth);
+ newSize.setHeight(((requestedHeight / windowHeight) + 1) * windowHeight);
+
+ RG_DEBUG << "EditView::readjustViewSize: requested ("
+ << requestedSize.width() << ", " << requestedSize.height()
+ << "), getting (" << newSize.width() << ", "
+ << newSize.height() << ")" << endl;
+
+ setViewSize(newSize);
+
+ getCanvasView()->slotUpdate();
+}
+
+void EditView::setCanvasView(RosegardenCanvasView *canvasView)
+{
+ delete m_canvasView;
+ m_canvasView = canvasView;
+ m_grid->addWidget(m_canvasView, CANVASVIEW_ROW, m_mainCol);
+ m_canvasView->setBottomFixedWidget(m_bottomBox);
+
+ // TODO : connect canvas view's horiz. scrollbar to top/bottom bars and rulers
+
+ // m_horizontalScrollBar->setRange(m_canvasView->horizontalScrollBar()->minValue(),
+ // m_canvasView->horizontalScrollBar()->maxValue());
+
+ // m_horizontalScrollBar->setSteps(m_canvasView->horizontalScrollBar()->lineStep(),
+ // m_canvasView->horizontalScrollBar()->pageStep());
+
+ // connect(m_horizontalScrollBar, SIGNAL(valueChanged(int)),
+ // m_canvasView->horizontalScrollBar(), SIGNAL(valueChanged(int)));
+ // connect(m_horizontalScrollBar, SIGNAL(sliderMoved(int)),
+ // m_canvasView->horizontalScrollBar(), SIGNAL(sliderMoved(int)));
+
+}
+
+Device *
+EditView::getCurrentDevice()
+{
+ Segment *segment = getCurrentSegment();
+ if (!segment)
+ return 0;
+
+ Studio &studio = getDocument()->getStudio();
+ Instrument *instrument =
+ studio.getInstrumentById
+ (segment->getComposition()->getTrackById(segment->getTrack())->
+ getInstrument());
+ if (!instrument)
+ return 0;
+
+ return instrument->getDevice();
+}
+
+timeT
+EditView::getInsertionTime(Clef &clef,
+ Rosegarden::Key &key)
+{
+ timeT t = getInsertionTime();
+ Segment *segment = getCurrentSegment();
+
+ if (segment) {
+ clef = segment->getClefAtTime(t);
+ key = segment->getKeyAtTime(t);
+ } else {
+ clef = Clef();
+ key = ::Rosegarden::Key();
+ }
+
+ return t;
+}
+
+void EditView::slotActiveItemPressed(QMouseEvent* e,
+ QCanvasItem* item)
+{
+ if (!item)
+ return ;
+
+ // Check if it's a groupable item, if so get its group
+ //
+ QCanvasGroupableItem *gitem = dynamic_cast<QCanvasGroupableItem*>(item);
+ if (gitem)
+ item = gitem->group();
+
+ // Check if it's an active item
+ //
+ ActiveItem *activeItem = dynamic_cast<ActiveItem*>(item);
+
+ if (activeItem) {
+
+ setActiveItem(activeItem);
+ activeItem->handleMousePress(e);
+ updateView();
+
+ }
+}
+
+void
+EditView::slotStepBackward()
+{
+ Staff *staff = getCurrentStaff();
+ if (!staff)
+ return ;
+ ViewElementList *vel = staff->getViewElementList();
+
+ timeT time = getInsertionTime();
+ ViewElementList::iterator i = vel->findTime(time);
+
+ while (i != vel->begin() &&
+ (i == vel->end() || (*i)->getViewAbsoluteTime() >= time))
+ --i;
+
+ if (i != vel->end())
+ slotSetInsertCursorPosition((*i)->getViewAbsoluteTime());
+}
+
+void
+EditView::slotStepForward()
+{
+ Staff *staff = getCurrentStaff();
+ if (!staff)
+ return ;
+ ViewElementList *vel = staff->getViewElementList();
+
+ timeT time = getInsertionTime();
+ ViewElementList::iterator i = vel->findTime(time);
+
+ while (i != vel->end() &&
+ (*i)->getViewAbsoluteTime() <= time)
+ ++i;
+
+ if (i == vel->end()) {
+ slotSetInsertCursorPosition(staff->getSegment().getEndMarkerTime());
+ } else {
+ slotSetInsertCursorPosition((*i)->getViewAbsoluteTime());
+ }
+}
+
+void
+EditView::slotJumpBackward()
+{
+ Segment *segment = getCurrentSegment();
+ if (!segment)
+ return ;
+ timeT time = getInsertionTime();
+ time = segment->getBarStartForTime(time - 1);
+ slotSetInsertCursorPosition(time);
+}
+
+void
+EditView::slotJumpForward()
+{
+ Segment *segment = getCurrentSegment();
+ if (!segment)
+ return ;
+ timeT time = getInsertionTime();
+ time = segment->getBarEndForTime(time);
+ slotSetInsertCursorPosition(time);
+}
+
+void
+EditView::slotJumpToStart()
+{
+ Segment *segment = getCurrentSegment();
+ if (!segment)
+ return ;
+ timeT time = segment->getStartTime();
+ slotSetInsertCursorPosition(time);
+}
+
+void
+EditView::slotJumpToEnd()
+{
+ Segment *segment = getCurrentSegment();
+ if (!segment)
+ return ;
+ timeT time = segment->getEndMarkerTime();
+ slotSetInsertCursorPosition(time);
+}
+
+void EditView::slotExtendSelectionBackward()
+{
+ slotExtendSelectionBackward(false);
+}
+
+void EditView::slotExtendSelectionBackwardBar()
+{
+ slotExtendSelectionBackward(true);
+}
+
+void EditView::slotExtendSelectionBackward(bool bar)
+{
+ // If there is no current selection, or the selection is entirely
+ // to the right of the cursor, move the cursor left and add to the
+ // selection
+
+ timeT oldTime = getInsertionTime();
+ if (bar)
+ slotJumpBackward();
+ else
+ slotStepBackward();
+ timeT newTime = getInsertionTime();
+
+ Staff *staff = getCurrentStaff();
+ if (!staff)
+ return ;
+ Segment *segment = &staff->getSegment();
+ ViewElementList *vel = staff->getViewElementList();
+
+ EventSelection *es = new EventSelection(*segment);
+ if (m_currentEventSelection &&
+ &m_currentEventSelection->getSegment() == segment)
+ es->addFromSelection(m_currentEventSelection);
+
+ if (!m_currentEventSelection ||
+ &m_currentEventSelection->getSegment() != segment ||
+ m_currentEventSelection->getSegmentEvents().size() == 0 ||
+ m_currentEventSelection->getStartTime() >= oldTime) {
+
+ ViewElementList::iterator extendFrom = vel->findTime(oldTime);
+
+ while (extendFrom != vel->begin() &&
+ (*--extendFrom)->getViewAbsoluteTime() >= newTime) {
+ if ((*extendFrom)->event()->isa(Note::EventType)) {
+ es->addEvent((*extendFrom)->event());
+ }
+ }
+
+ } else { // remove an event
+
+ EventSelection::eventcontainer::iterator i =
+ es->getSegmentEvents().end();
+
+ std::vector<Event *> toErase;
+
+ while (i != es->getSegmentEvents().begin() &&
+ (*--i)->getAbsoluteTime() >= newTime) {
+ toErase.push_back(*i);
+ }
+
+ for (unsigned int j = 0; j < toErase.size(); ++j) {
+ es->removeEvent(toErase[j]);
+ }
+ }
+
+ setCurrentSelection(es);
+}
+
+void EditView::slotExtendSelectionForward()
+{
+ slotExtendSelectionForward(false);
+}
+
+void EditView::slotExtendSelectionForwardBar()
+{
+ slotExtendSelectionForward(true);
+}
+
+void EditView::slotExtendSelectionForward(bool bar)
+{
+ // If there is no current selection, or the selection is entirely
+ // to the left of the cursor, move the cursor right and add to the
+ // selection
+
+ timeT oldTime = getInsertionTime();
+ if (bar)
+ slotJumpForward();
+ else
+ slotStepForward();
+ timeT newTime = getInsertionTime();
+
+ Staff *staff = getCurrentStaff();
+ if (!staff)
+ return ;
+ Segment *segment = &staff->getSegment();
+ ViewElementList *vel = staff->getViewElementList();
+
+ EventSelection *es = new EventSelection(*segment);
+ if (m_currentEventSelection &&
+ &m_currentEventSelection->getSegment() == segment)
+ es->addFromSelection(m_currentEventSelection);
+
+ if (!m_currentEventSelection ||
+ &m_currentEventSelection->getSegment() != segment ||
+ m_currentEventSelection->getSegmentEvents().size() == 0 ||
+ m_currentEventSelection->getEndTime() <= oldTime) {
+
+ ViewElementList::iterator extendFrom = vel->findTime(oldTime);
+
+ while (extendFrom != vel->end() &&
+ (*extendFrom)->getViewAbsoluteTime() < newTime) {
+ if ((*extendFrom)->event()->isa(Note::EventType)) {
+ es->addEvent((*extendFrom)->event());
+ }
+ ++extendFrom;
+ }
+
+ } else { // remove an event
+
+ EventSelection::eventcontainer::iterator i =
+ es->getSegmentEvents().begin();
+
+ std::vector<Event *> toErase;
+
+ while (i != es->getSegmentEvents().end() &&
+ (*i)->getAbsoluteTime() < newTime) {
+ toErase.push_back(*i);
+ ++i;
+ }
+
+ for (unsigned int j = 0; j < toErase.size(); ++j) {
+ es->removeEvent(toErase[j]);
+ }
+ }
+
+ setCurrentSelection(es);
+}
+
+void
+EditView::setupActions()
+{
+ createInsertPitchActionMenu();
+
+ //
+ // Tempo and time signature changes
+ //
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QCanvasPixmap pixmap(pixmapDir + "/toolbar/event-insert-tempo.png");
+ QIconSet icon = QIconSet(pixmap);
+ new KAction(AddTempoChangeCommand::getGlobalName(),
+ icon, 0,
+ this, SLOT(slotAddTempo()),
+ actionCollection(), "add_tempo");
+
+ pixmap.load(pixmapDir + "/toolbar/event-insert-timesig.png");
+ icon = QIconSet(pixmap);
+ new KAction(AddTimeSignatureCommand::getGlobalName(),
+ icon, 0,
+ this, SLOT(slotAddTimeSignature()),
+ actionCollection(), "add_time_signature");
+
+ //
+ // Transforms
+ //
+ new KAction(i18n("&Halve Durations"), Key_H + CTRL, this,
+ SLOT(slotHalveDurations()), actionCollection(),
+ "halve_durations");
+
+ new KAction(i18n("&Double Durations"), Key_H + CTRL + SHIFT, this,
+ SLOT(slotDoubleDurations()), actionCollection(),
+ "double_durations");
+
+ new KAction(RescaleCommand::getGlobalName(), 0, this,
+ SLOT(slotRescale()), actionCollection(),
+ "rescale");
+
+ new KAction(TransposeCommand::getGlobalName(1), 0,
+ Key_Up, this,
+ SLOT(slotTransposeUp()), actionCollection(),
+ "transpose_up");
+
+ new KAction(TransposeCommand::getGlobalName(12), 0,
+ Key_Up + CTRL, this,
+ SLOT(slotTransposeUpOctave()), actionCollection(),
+ "transpose_up_octave");
+
+ new KAction(TransposeCommand::getGlobalName( -1), 0,
+ Key_Down, this,
+ SLOT(slotTransposeDown()), actionCollection(),
+ "transpose_down");
+
+ new KAction(TransposeCommand::getGlobalName( -12), 0,
+ Key_Down + CTRL, this,
+ SLOT(slotTransposeDownOctave()), actionCollection(),
+ "transpose_down_octave");
+
+ new KAction(TransposeCommand::getGlobalName(0), 0, this,
+ SLOT(slotTranspose()), actionCollection(),
+ "general_transpose");
+
+ new KAction(TransposeCommand::getDiatonicGlobalName(0,0), 0, this,
+ SLOT(slotDiatonicTranspose()), actionCollection(),
+ "general_diatonic_transpose");
+
+ new KAction(InvertCommand::getGlobalName(0), 0, this,
+ SLOT(slotInvert()), actionCollection(),
+ "invert");
+
+ new KAction(RetrogradeCommand::getGlobalName(0), 0, this,
+ SLOT(slotRetrograde()), actionCollection(),
+ "retrograde");
+
+ new KAction(RetrogradeInvertCommand::getGlobalName(0), 0, this,
+ SLOT(slotRetrogradeInvert()), actionCollection(),
+ "retrograde_invert");
+
+ new KAction(i18n("Jog &Left"), Key_Left + ALT, this,
+ SLOT(slotJogLeft()), actionCollection(),
+ "jog_left");
+
+ new KAction(i18n("Jog &Right"), Key_Right + ALT, this,
+ SLOT(slotJogRight()), actionCollection(),
+ "jog_right");
+
+ // Control rulers
+ //
+ new KAction(i18n("Show Velocity Property Ruler"), 0, this,
+ SLOT(slotShowVelocityControlRuler()), actionCollection(),
+ "show_velocity_control_ruler");
+
+ /*
+ new KAction(i18n("Show Controllers Events Ruler"), 0, this,
+ SLOT(slotShowControllerEventsRuler()), actionCollection(),
+ "show_controller_events_ruler");
+ */
+
+ // Disabled for now
+ //
+ // new KAction(i18n("Add Control Ruler..."), 0, this,
+ // SLOT(slotShowPropertyControlRuler()), actionCollection(),
+ // "add_control_ruler");
+
+ //
+ // Control Ruler context menu
+ //
+ new KAction(i18n("Insert item"), 0, this,
+ SLOT(slotInsertControlRulerItem()), actionCollection(),
+ "insert_control_ruler_item");
+
+ // This was on Key_Delete, but that conflicts with existing Delete commands
+ // on individual edit views
+ new KAction(i18n("Erase selected items"), 0, this,
+ SLOT(slotEraseControlRulerItem()), actionCollection(),
+ "erase_control_ruler_item");
+
+ new KAction(i18n("Clear ruler"), 0, this,
+ SLOT(slotClearControlRulerItem()), actionCollection(),
+ "clear_control_ruler_item");
+
+ new KAction(i18n("Insert line of controllers"), 0, this,
+ SLOT(slotStartControlLineItem()), actionCollection(),
+ "start_control_line_item");
+
+ new KAction(i18n("Flip forward"), Key_BracketRight, this,
+ SLOT(slotFlipForwards()), actionCollection(),
+ "flip_control_events_forward");
+
+ new KAction(i18n("Flip backwards"), Key_BracketLeft, this,
+ SLOT(slotFlipBackwards()), actionCollection(),
+ "flip_control_events_back");
+
+ new KAction(i18n("Draw property line"), 0, this,
+ SLOT(slotDrawPropertyLine()), actionCollection(),
+ "draw_property_line");
+
+ new KAction(i18n("Select all property values"), 0, this,
+ SLOT(slotSelectAllProperties()), actionCollection(),
+ "select_all_properties");
+}
+
+void
+EditView::setupAddControlRulerMenu()
+{
+ RG_DEBUG << "EditView::setupAddControlRulerMenu" << endl;
+
+ QPopupMenu* addControlRulerMenu = dynamic_cast<QPopupMenu*>
+ (factory()->container("add_control_ruler", this));
+
+ if (addControlRulerMenu) {
+
+ addControlRulerMenu->clear();
+
+ //!!! problem here with notation view -- current segment can
+ // change after construction, but this function isn't used again
+
+ Controllable *c =
+ dynamic_cast<MidiDevice *>(getCurrentDevice());
+ if (!c) {
+ c = dynamic_cast<SoftSynthDevice *>(getCurrentDevice());
+ if (!c)
+ return ;
+ }
+
+ const ControlList &list = c->getControlParameters();
+
+ int i = 0;
+ QString itemStr;
+
+ for (ControlList::const_iterator it = list.begin();
+ it != list.end(); ++it) {
+ if (it->getType() == Controller::EventType) {
+ QString hexValue;
+ hexValue.sprintf("(0x%x)", it->getControllerValue());
+
+ itemStr = i18n("%1 Controller %2 %3").arg(strtoqstr(it->getName()))
+ .arg(it->getControllerValue())
+ .arg(hexValue);
+
+ } else if (it->getType() == PitchBend::EventType)
+ itemStr = i18n("Pitch Bend");
+ else
+ itemStr = i18n("Unsupported Event Type");
+
+ addControlRulerMenu->insertItem(itemStr, i++);
+ }
+
+ connect(addControlRulerMenu, SIGNAL(activated(int)),
+ SLOT(slotAddControlRuler(int)));
+ }
+
+}
+
+bool
+EditView::setupControllerTabs()
+{
+ bool have = false;
+
+ // Setup control rulers the Segment already has some stored against it.
+ //
+ Segment *segment = getCurrentSegment();
+ Segment::EventRulerList list = segment->getEventRulerList();
+
+ RG_DEBUG << "EditView::setupControllerTabs - got " << list.size() << " EventRulers" << endl;
+
+ RG_DEBUG << "Segment view features: " << segment->getViewFeatures() << endl;
+ if (segment->getViewFeatures() & FeatureShowVelocity) {
+ showPropertyControlRuler(BaseProperties::VELOCITY);
+ have = true;
+ }
+
+ if (list.size()) {
+ Controllable *c =
+ dynamic_cast<MidiDevice *>(getCurrentDevice());
+ if (!c) {
+ c = dynamic_cast<SoftSynthDevice *>(getCurrentDevice());
+ if (!c)
+ return have;
+ }
+
+ have = true;
+
+ Segment::EventRulerListIterator it;
+
+ for (it = list.begin(); it != list.end(); ++it) {
+ // Get ControlParameter object from controller value
+ //
+ const ControlParameter *controlParameter =
+ c->getControlParameter((*it)->m_type,
+ MidiByte((*it)->m_controllerValue));
+
+ RG_DEBUG << "EditView::setupControllerTabs - "
+ << "Control Parameter type = " << (*it)->m_type << endl;
+
+ if (controlParameter) {
+ ControllerEventsRuler* controlRuler = makeControllerEventRuler(controlParameter);
+ addControlRuler(controlRuler);
+ RG_DEBUG << "EditView::setupControllerTabs - adding Ruler" << endl;
+ }
+ }
+
+ if (!m_controlRulers->isVisible())
+ m_controlRulers->show();
+
+ updateBottomWidgetGeometry();
+ }
+
+ return have;
+}
+
+void
+EditView::slotAddControlRuler(int controller)
+{
+ RG_DEBUG << "EditView::slotAddControlRuler - item = "
+ << controller << endl;
+
+ Controllable *c =
+ dynamic_cast<MidiDevice *>(getCurrentDevice());
+ if (!c) {
+ c = dynamic_cast<SoftSynthDevice *>(getCurrentDevice());
+ if (!c)
+ return ;
+ }
+
+ const ControlList &list = c->getControlParameters();
+ ControlParameter control = list[controller];
+
+ int index = 0;
+
+ ControlRuler* existingRuler = findRuler(control, index);
+
+ if (existingRuler) {
+
+ m_controlRulers->setCurrentPage(index);
+
+ } else {
+
+ // Create control ruler to a specific controller. This duplicates
+ // the control parameter in the supplied pointer.
+ ControllerEventsRuler* controlRuler = makeControllerEventRuler(&control);
+
+ addControlRuler(controlRuler);
+ }
+
+ if (!m_controlRulers->isVisible()) {
+ m_controlRulers->show();
+ }
+
+ updateBottomWidgetGeometry();
+
+ // Add the controller to the segment so the views can
+ // remember what we've opened against it.
+ //
+ Staff *staff = getCurrentStaff();
+ staff->getSegment().addEventRuler(control.getType(), control.getControllerValue());
+
+ getDocument()->slotDocumentModified();
+}
+
+void EditView::slotRemoveControlRuler(QWidget* w)
+{
+ ControllerEventsRuler* ruler = dynamic_cast<ControllerEventsRuler*>(w);
+
+ if (ruler) {
+ ControlParameter *controller = ruler->getControlParameter();
+
+ // remove the control parameter from the "showing controllers" list on the segment
+ //
+ if (controller) {
+ Staff *staff = getCurrentStaff();
+ bool value = staff->getSegment().
+ deleteEventRuler(controller->getType(), controller->getControllerValue());
+
+ if (value)
+ RG_DEBUG << "slotRemoveControlRuler : removed controller from segment\n";
+ else
+ RG_DEBUG << "slotRemoveControlRuler : couldn't remove controller from segment - "
+ << int(controller->getControllerValue())
+ << endl;
+
+ }
+ } else { // else it's probably a velocity ruler
+ PropertyControlRuler *propertyRuler = dynamic_cast<PropertyControlRuler*>(w);
+
+ if (propertyRuler) {
+ Segment &seg = getCurrentStaff()->getSegment();
+ seg.setViewFeatures(0); // for the moment we only have one view feature so
+ // we can just blank it out
+
+ RG_DEBUG << "slotRemoveControlRuler : removed velocity ruler" << endl;
+ }
+ }
+
+ delete w;
+
+ if (m_controlRulers->count() == 0) {
+ m_controlRulers->hide();
+ updateBottomWidgetGeometry();
+ }
+
+ getDocument()->slotDocumentModified();
+}
+
+void
+EditView::createInsertPitchActionMenu()
+{
+ QString notePitchNames[] = {
+ i18n("I"), i18n("II"), i18n("III"), i18n("IV"),
+ i18n("V"), i18n("VI"), i18n("VII"), i18n("VIII")
+ };
+ QString flat = i18n("%1 flat");
+ QString sharp = i18n("%1 sharp");
+
+ const Key notePitchKeys[3][7] = {
+ {
+ Key_A, Key_S, Key_D, Key_F, Key_J, Key_K, Key_L,
+ },
+ {
+ Key_Q, Key_W, Key_E, Key_R, Key_U, Key_I, Key_O,
+ },
+ {
+ Key_Z, Key_X, Key_C, Key_V, Key_B, Key_N, Key_M,
+ },
+ };
+
+ KActionMenu *insertPitchActionMenu =
+ new KActionMenu(i18n("&Insert Note"), this, "insert_note_actionmenu");
+
+ for (int octave = 0; octave <= 2; ++octave) {
+
+ KActionMenu *menu = insertPitchActionMenu;
+ if (octave == 1) {
+ menu = new KActionMenu(i18n("&Upper Octave"), this,
+ "insert_note_actionmenu_upper_octave");
+ insertPitchActionMenu->insert(new KActionSeparator(this));
+ insertPitchActionMenu->insert(menu);
+ } else if (octave == 2) {
+ menu = new KActionMenu(i18n("&Lower Octave"), this,
+ "insert_note_actionmenu_lower_octave");
+ insertPitchActionMenu->insert(menu);
+ }
+
+ for (unsigned int i = 0; i < 7; ++i) {
+
+ KAction *insertPitchAction = 0;
+
+ QString octaveSuffix;
+ if (octave == 1)
+ octaveSuffix = "_high";
+ else if (octave == 2)
+ octaveSuffix = "_low";
+
+ // do and fa lack a flat
+
+ if (i != 0 && i != 3) {
+
+ insertPitchAction =
+ new KAction
+ (flat.arg(notePitchNames[i]),
+ CTRL + SHIFT + notePitchKeys[octave][i],
+ this, SLOT(slotInsertNoteFromAction()), actionCollection(),
+ QString("insert_%1_flat%2").arg(i).arg(octaveSuffix));
+
+ menu->insert(insertPitchAction);
+ }
+
+ insertPitchAction =
+ new KAction
+ (notePitchNames[i],
+ notePitchKeys[octave][i],
+ this, SLOT(slotInsertNoteFromAction()), actionCollection(),
+ QString("insert_%1%2").arg(i).arg(octaveSuffix));
+
+ menu->insert(insertPitchAction);
+
+ // and mi and ti lack a sharp
+
+ if (i != 2 && i != 6) {
+
+ insertPitchAction =
+ new KAction
+ (sharp.arg(notePitchNames[i]),
+ SHIFT + notePitchKeys[octave][i],
+ this, SLOT(slotInsertNoteFromAction()), actionCollection(),
+ QString("insert_%1_sharp%2").arg(i).arg(octaveSuffix));
+
+ menu->insert(insertPitchAction);
+ }
+
+ if (i < 6)
+ menu->insert(new KActionSeparator(this));
+ }
+ }
+
+ actionCollection()->insert(insertPitchActionMenu);
+}
+
+int
+EditView::getPitchFromNoteInsertAction(QString name,
+ Accidental &accidental,
+ const Clef &clef,
+ const ::Rosegarden::Key &key)
+{
+ using namespace Accidentals;
+
+ accidental = NoAccidental;
+
+ if (name.left(7) == "insert_") {
+
+ name = name.right(name.length() - 7);
+
+ int modify = 0;
+ int octave = 0;
+
+ if (name.right(5) == "_high") {
+
+ octave = 1;
+ name = name.left(name.length() - 5);
+
+ } else if (name.right(4) == "_low") {
+
+ octave = -1;
+ name = name.left(name.length() - 4);
+ }
+
+ if (name.right(6) == "_sharp") {
+
+ modify = 1;
+ accidental = Sharp;
+ name = name.left(name.length() - 6);
+
+ } else if (name.right(5) == "_flat") {
+
+ modify = -1;
+ accidental = Flat;
+ name = name.left(name.length() - 5);
+ }
+
+ int scalePitch = name.toInt();
+
+ if (scalePitch < 0 || scalePitch > 7) {
+ NOTATION_DEBUG << "EditView::getPitchFromNoteInsertAction: pitch "
+ << scalePitch << " out of range, using 0" << endl;
+ scalePitch = 0;
+ }
+
+ Pitch pitch
+ (scalePitch, 4 + octave + clef.getOctave(), key, accidental);
+ return pitch.getPerformancePitch();
+
+ } else {
+
+ throw Exception("Not an insert action",
+ __FILE__, __LINE__);
+ }
+}
+
+void EditView::slotAddTempo()
+{
+ timeT insertionTime = getInsertionTime();
+
+ TempoDialog tempoDlg(this, getDocument());
+
+ connect(&tempoDlg,
+ SIGNAL(changeTempo(timeT,
+ tempoT,
+ tempoT,
+ TempoDialog::TempoDialogAction)),
+ this,
+ SIGNAL(changeTempo(timeT,
+ tempoT,
+ tempoT,
+ TempoDialog::TempoDialogAction)));
+
+ tempoDlg.setTempoPosition(insertionTime);
+ tempoDlg.exec();
+}
+
+void EditView::slotAddTimeSignature()
+{
+ Segment *segment = getCurrentSegment();
+ if (!segment)
+ return ;
+ Composition *composition = segment->getComposition();
+ timeT insertionTime = getInsertionTime();
+
+ TimeSignatureDialog *dialog = 0;
+ int timeSigNo = composition->getTimeSignatureNumberAt(insertionTime);
+
+ if (timeSigNo >= 0) {
+
+ dialog = new TimeSignatureDialog
+ (this, composition, insertionTime,
+ composition->getTimeSignatureAt(insertionTime));
+
+ } else {
+
+ timeT endTime = composition->getDuration();
+ if (composition->getTimeSignatureCount() > 0) {
+ endTime = composition->getTimeSignatureChange(0).first;
+ }
+
+ CompositionTimeSliceAdapter adapter
+ (composition, insertionTime, endTime);
+ AnalysisHelper helper;
+ TimeSignature timeSig = helper.guessTimeSignature(adapter);
+
+ dialog = new TimeSignatureDialog
+ (this, composition, insertionTime, timeSig, false,
+ i18n("Estimated time signature shown"));
+ }
+
+ if (dialog->exec() == QDialog::Accepted) {
+
+ insertionTime = dialog->getTime();
+
+ if (dialog->shouldNormalizeRests()) {
+
+ addCommandToHistory(new AddTimeSignatureAndNormalizeCommand
+ (composition, insertionTime,
+ dialog->getTimeSignature()));
+
+ } else {
+
+ addCommandToHistory(new AddTimeSignatureCommand
+ (composition, insertionTime,
+ dialog->getTimeSignature()));
+ }
+ }
+
+ delete dialog;
+}
+
+void EditView::showPropertyControlRuler(PropertyName propertyName)
+{
+ int index = 0;
+
+ ControlRuler* existingRuler = findRuler(propertyName, index);
+
+ if (existingRuler) {
+
+ m_controlRulers->setCurrentPage(index);
+
+ } else {
+
+ PropertyControlRuler* controlRuler = makePropertyControlRuler(propertyName);
+ addControlRuler(controlRuler);
+ }
+
+ if (!m_controlRulers->isVisible()) {
+ m_controlRulers->show();
+ }
+
+ updateBottomWidgetGeometry();
+}
+
+void EditView::slotShowVelocityControlRuler()
+{
+ showPropertyControlRuler(BaseProperties::VELOCITY);
+ Segment &seg = getCurrentStaff()->getSegment();
+ seg.setViewFeatures(seg.getViewFeatures() | FeatureShowVelocity);
+ getDocument()->slotDocumentModified();
+}
+
+void EditView::slotShowControllerEventsRuler()
+{
+
+ // int index = 0;
+
+ // ControlRuler* existingRuler = findRuler(propertyName, index);
+
+ // if (existingRuler) {
+
+ // m_controlRulers->setCurrentPage(index);
+
+ // } else {
+
+ // ControllerEventsRuler* controlRuler = makeControllerEventRuler();
+ // addControlRuler(controlRuler);
+ // }
+
+ // if (!m_controlRulers->isVisible()) {
+ // m_controlRulers->show();
+ // }
+
+ // updateBottomWidgetGeometry();
+}
+
+void EditView::slotShowPropertyControlRuler()
+{
+ /*
+ KDialogBase propChooserDialog(this, "propertychooserdialog", true, i18n("Select event property"),
+ KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok);
+
+ KListBox* propList = new KListBox(propChooserDialog.makeVBoxMainWidget());
+ new QListBoxRGProperty(propList, BaseProperties::VELOCITY.c_str());
+
+ int rc = propChooserDialog.exec();
+ if (rc == QDialog::Accepted) {
+ // fix for KDE 3.0
+ //QListBoxRGProperty* item = dynamic_cast<QListBoxRGProperty*>(propList->selectedItem());
+ QListBoxRGProperty* item = dynamic_cast<QListBoxRGProperty*>
+ (propList->item(propList->currentItem()));
+
+ if (item) {
+ PropertyName property = item->getPropertyName();
+ showPropertyControlRuler(property);
+ }
+ }
+ */
+}
+
+void
+EditView::slotInsertControlRulerItem()
+{
+ ControllerEventsRuler* ruler = dynamic_cast<ControllerEventsRuler*>(getCurrentControlRuler());
+ if (ruler)
+ ruler->insertControllerEvent();
+}
+
+void
+EditView::slotEraseControlRulerItem()
+{
+ ControllerEventsRuler* ruler = dynamic_cast<ControllerEventsRuler*>(getCurrentControlRuler());
+ if (ruler)
+ ruler->eraseControllerEvent();
+}
+
+void
+EditView::slotStartControlLineItem()
+{
+ ControllerEventsRuler* ruler = dynamic_cast<ControllerEventsRuler*>(getCurrentControlRuler());
+ if (ruler)
+ ruler->startControlLine();
+}
+
+void
+EditView::slotDrawPropertyLine()
+{
+ int index = 0;
+ PropertyControlRuler* ruler = dynamic_cast<PropertyControlRuler*>
+ (findRuler(BaseProperties::VELOCITY, index));
+
+ if (ruler)
+ ruler->startPropertyLine();
+}
+
+void
+EditView::slotSelectAllProperties()
+{
+ int index = 0;
+ PropertyControlRuler* ruler = dynamic_cast<PropertyControlRuler*>
+ (findRuler(BaseProperties::VELOCITY, index));
+
+ if (ruler)
+ ruler->selectAllProperties();
+}
+
+void
+EditView::slotClearControlRulerItem()
+{
+ ControllerEventsRuler* ruler = dynamic_cast<ControllerEventsRuler*>(getCurrentControlRuler());
+ if (ruler)
+ ruler->clearControllerEvents();
+}
+
+void
+EditView::slotHalveDurations()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ KTmpStatusMsg msg(i18n("Halving durations..."), this);
+
+ addCommandToHistory(
+ new RescaleCommand(*m_currentEventSelection,
+ m_currentEventSelection->getTotalDuration() / 2,
+ false));
+}
+
+void
+EditView::slotDoubleDurations()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ KTmpStatusMsg msg(i18n("Doubling durations..."), this);
+
+ addCommandToHistory(
+ new RescaleCommand(*m_currentEventSelection,
+ m_currentEventSelection->getTotalDuration() * 2,
+ false));
+}
+
+void
+EditView::slotRescale()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ RescaleDialog dialog
+ (this,
+ &getDocument()->getComposition(),
+ m_currentEventSelection->getStartTime(),
+ m_currentEventSelection->getEndTime() -
+ m_currentEventSelection->getStartTime(),
+ true,
+ true);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ KTmpStatusMsg msg(i18n("Rescaling..."), this);
+ addCommandToHistory(new RescaleCommand
+ (*m_currentEventSelection,
+ dialog.getNewDuration(),
+ dialog.shouldCloseGap()));
+ }
+}
+
+void EditView::slotTranspose()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ m_config->setGroup(EditViewConfigGroup);
+
+ int dialogDefault = m_config->readNumEntry("lasttransposition", 0);
+
+ bool ok = false;
+ int semitones = QInputDialog::getInteger
+ (i18n("Transpose"),
+ i18n("By number of semitones: "),
+ dialogDefault, -127, 127, 1, &ok, this);
+ if (!ok || semitones == 0) return;
+
+ m_config->setGroup(EditViewConfigGroup);
+ m_config->writeEntry("lasttransposition", semitones);
+
+ KTmpStatusMsg msg(i18n("Transposing..."), this);
+ addCommandToHistory(new TransposeCommand
+ (semitones, *m_currentEventSelection));
+}
+
+void EditView::slotDiatonicTranspose()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ m_config->setGroup(EditViewConfigGroup);
+
+ IntervalDialog intervalDialog(this);
+ int ok = intervalDialog.exec();
+ //int dialogDefault = m_config->readNumEntry("lasttransposition", 0);
+ int semitones = intervalDialog.getChromaticDistance();
+ int steps = intervalDialog.getDiatonicDistance();
+
+ if (!ok || (semitones == 0 && steps == 0)) return;
+
+ m_config->setGroup(EditViewConfigGroup);
+
+ KTmpStatusMsg msg(i18n("Transposing..."), this);
+ if (intervalDialog.getChangeKey())
+ {
+ std::cout << "Transposing changing keys is not currently supported on selections" << std::endl;
+ }
+ else
+ {
+ // Transpose within key
+ //std::cout << "Transposing semitones, steps: " << semitones << ", " << steps << std::endl;
+ addCommandToHistory(new TransposeCommand
+ (semitones, steps, *m_currentEventSelection));
+ }
+}
+
+void EditView::slotTransposeUp()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Transposing up one semitone..."), this);
+
+ addCommandToHistory(new TransposeCommand(1, *m_currentEventSelection));
+}
+
+void EditView::slotTransposeUpOctave()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Transposing up one octave..."), this);
+
+ addCommandToHistory(new TransposeCommand(12, *m_currentEventSelection));
+}
+
+void EditView::slotTransposeDown()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Transposing down one semitone..."), this);
+
+ addCommandToHistory(new TransposeCommand( -1, *m_currentEventSelection));
+}
+
+void EditView::slotTransposeDownOctave()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Transposing down one octave..."), this);
+
+ addCommandToHistory(new TransposeCommand( -12, *m_currentEventSelection));
+}
+
+void EditView::slotInvert()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ int semitones = 0;
+
+ KTmpStatusMsg msg(i18n("Inverting..."), this);
+ addCommandToHistory(new InvertCommand
+ (semitones, *m_currentEventSelection));
+}
+
+void EditView::slotRetrograde()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ int semitones = 0;
+
+ KTmpStatusMsg msg(i18n("Retrograding..."), this);
+ addCommandToHistory(new RetrogradeCommand
+ (semitones, *m_currentEventSelection));
+}
+
+void EditView::slotRetrogradeInvert()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ int semitones = 0;
+
+ KTmpStatusMsg msg(i18n("Retrograde inverting..."), this);
+ addCommandToHistory(new RetrogradeInvertCommand
+ (semitones, *m_currentEventSelection));
+}
+
+void EditView::slotJogLeft()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Jogging left..."), this);
+
+ RG_DEBUG << "EditView::slotJogLeft" << endl;
+
+ addCommandToHistory(
+ new MoveCommand(*getCurrentSegment(),
+ -Note(Note::Demisemiquaver).getDuration(),
+ false, // don't use notation timings
+ *m_currentEventSelection));
+}
+
+void EditView::slotJogRight()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Jogging right..."), this);
+
+ RG_DEBUG << "EditView::slotJogRight" << endl;
+
+ addCommandToHistory(
+ new MoveCommand(*getCurrentSegment(),
+ Note(Note::Demisemiquaver).getDuration(),
+ false, // don't use notation timings
+ *m_currentEventSelection));
+}
+
+void
+EditView::slotFlipForwards()
+{
+ RG_DEBUG << "EditView::slotFlipForwards" << endl;
+ ControlRuler* ruler = getCurrentControlRuler();
+ if (ruler) ruler->flipForwards();
+}
+
+void
+EditView::slotFlipBackwards()
+{
+ RG_DEBUG << "EditView::slotFlipBackwards" << endl;
+ ControlRuler* ruler = getCurrentControlRuler();
+ if (ruler) ruler->flipBackwards();
+}
+
+ControlRuler* EditView::getCurrentControlRuler()
+{
+ return dynamic_cast<ControlRuler*>(m_controlRulers->currentPage());
+}
+
+ControlRuler* EditView::findRuler(PropertyName propertyName, int &index)
+{
+ for(index = 0; index < m_controlRulers->count(); ++index) {
+ PropertyControlRuler* ruler = dynamic_cast<PropertyControlRuler*>(m_controlRulers->page(index));
+ if (ruler && ruler->getPropertyName() == propertyName) return ruler;
+ }
+
+ return 0;
+}
+
+ControlRuler* EditView::findRuler(const ControlParameter& controller, int &index)
+{
+ for(index = 0; index < m_controlRulers->count(); ++index) {
+ ControllerEventsRuler* ruler = dynamic_cast<ControllerEventsRuler*>(m_controlRulers->page(index));
+ if (ruler && *(ruler->getControlParameter()) == controller) return ruler;
+ }
+
+ return 0;
+}
+
+PropertyControlRuler* EditView::makePropertyControlRuler(PropertyName propertyName)
+{
+ QCanvas* controlRulerCanvas = new QCanvas(this);
+ QSize viewSize = getViewSize();
+ controlRulerCanvas->resize(viewSize.width(), ControlRuler::DefaultRulerHeight); // TODO - keep it in sync with main canvas size
+
+// QCanvas* controlRulerCanvas = ControlRulerCanvasRepository::getCanvas(getCurrentSegment(), propertyName,
+// getViewSize());
+
+ PropertyControlRuler* controlRuler = new PropertyControlRuler
+ (propertyName, getCurrentStaff(), getHLayout(), this,
+ controlRulerCanvas, m_controlRulers);
+
+ controlRuler->setMainHorizontalScrollBar(m_canvasView->horizontalScrollBar());
+
+ return controlRuler;
+}
+
+ControllerEventsRuler* EditView::makeControllerEventRuler(const ControlParameter *controller)
+{
+ QCanvas* controlRulerCanvas = new QCanvas(this);
+ QSize viewSize = getViewSize();
+ controlRulerCanvas->resize(viewSize.width(), ControlRuler::DefaultRulerHeight); // TODO - keep it in sync with main canvas size
+// QCanvas* controlRulerCanvas = ControlRulerCanvasRepository::getCanvas(getCurrentSegment(), controller,
+// getViewSize());
+
+
+ ControllerEventsRuler* controlRuler = new ControllerEventsRuler
+ (getCurrentSegment(), getHLayout(), this,
+ controlRulerCanvas, m_controlRulers, controller);
+
+ controlRuler->setMainHorizontalScrollBar(m_canvasView->horizontalScrollBar());
+
+ return controlRuler;
+}
+
+RosegardenCanvasView* EditView::getCanvasView()
+{
+ return m_canvasView;
+}
+
+}
+#include "EditView.moc"
diff --git a/src/gui/general/EditView.h b/src/gui/general/EditView.h
new file mode 100644
index 0000000..da18982
--- /dev/null
+++ b/src/gui/general/EditView.h
@@ -0,0 +1,405 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_EDITVIEW_H_
+#define _RG_EDITVIEW_H_
+
+#include "base/PropertyName.h"
+#include "EditViewBase.h"
+#include "gui/dialogs/TempoDialog.h"
+#include <qsize.h>
+#include <qstring.h>
+#include <qwmatrix.h>
+#include <vector>
+#include "base/Event.h"
+
+
+class QWidget;
+class QVBoxLayout;
+class QVBox;
+class QPaintEvent;
+class QMouseEvent;
+class QCanvasItem;
+class KTabWidget;
+class Accidental;
+
+
+namespace Rosegarden
+{
+
+class Staff;
+class Segment;
+class RulerScale;
+class RosegardenGUIDoc;
+class RosegardenCanvasView;
+class PropertyControlRuler;
+class Key;
+class EventSelection;
+class Device;
+class ControlRuler;
+class ControlParameter;
+class ControllerEventsRuler;
+class Clef;
+class StandardRuler;
+class ActiveItem;
+
+
+class EditView : public EditViewBase
+{
+ Q_OBJECT
+
+public:
+
+ /**
+ * Create an EditView for the segments \a segments from document \a doc.
+ *
+ * \arg cols : number of columns, main column is always rightmost
+ *
+ */
+ EditView(RosegardenGUIDoc *doc,
+ std::vector<Segment *> segments,
+ unsigned int cols,
+ QWidget *parent,
+ const char *name = 0);
+
+ virtual ~EditView();
+
+ /**
+ * "Clever" readjustment of the view size
+ * If the new size is larger, enlarge to that size plus a margin
+ * If it is smaller, only shrink if the reduction is significant
+ * (e.g. new size is less than 75% of the old one)
+ *
+ * @arg exact if true, then set to newSize exactly
+ */
+ virtual void readjustViewSize(QSize newSize, bool exact = false);
+
+ /**
+ * Return the active item
+ */
+ ActiveItem* activeItem() { return m_activeItem; }
+
+ /**
+ * Set the active item
+ */
+ void setActiveItem(ActiveItem* i) { m_activeItem = i; }
+
+ /**
+ * Set the current event selection.
+ *
+ * If preview is true, sound the selection as well.
+ *
+ * If redrawNow is true, recolour the elements on the canvas;
+ * otherwise just line up a refresh for the next paint event.
+ *
+ * (If the selection has changed as part of a modification to a
+ * segment, redrawNow should be unnecessary and undesirable, as a
+ * paint event will occur in the next event loop following the
+ * command invocation anyway.)
+ */
+ virtual void setCurrentSelection(EventSelection* s,
+ bool preview = false,
+ bool redrawNow = false) = 0;
+
+ EventSelection* getCurrentSelection()
+ { return m_currentEventSelection; }
+
+ RosegardenCanvasView* getRawCanvasView() { return m_canvasView; }
+ virtual RosegardenCanvasView* getCanvasView();
+
+signals:
+ void changeTempo(timeT, // tempo change time
+ tempoT, // tempo value
+ tempoT, // target value
+ TempoDialog::TempoDialogAction); // tempo action
+
+public slots:
+ /**
+ * Called when a mouse press occurred on an active canvas item
+ *
+ * @see ActiveItem
+ * @see QCanvasItem#setActive
+ */
+ virtual void slotActiveItemPressed(QMouseEvent*, QCanvasItem*);
+
+ virtual void slotSetInsertCursorPosition(timeT position) = 0;
+
+ void slotExtendSelectionBackward();
+ void slotExtendSelectionForward();
+ void slotExtendSelectionBackwardBar();
+ void slotExtendSelectionForwardBar();
+ void slotExtendSelectionBackward(bool bar);
+ void slotExtendSelectionForward(bool bar);
+
+ virtual void slotStepBackward(); // default is event-by-event
+ virtual void slotStepForward(); // default is event-by-event
+ void slotJumpBackward();
+ void slotJumpForward();
+ void slotJumpToStart();
+ void slotJumpToEnd();
+
+ void slotAddTempo();
+ void slotAddTimeSignature();
+
+ virtual void slotShowVelocityControlRuler();
+ virtual void slotShowControllerEventsRuler();
+ virtual void slotShowPropertyControlRuler();
+
+ // rescale
+ void slotHalveDurations();
+ void slotDoubleDurations();
+ void slotRescale();
+
+ // transpose
+ void slotTransposeUp();
+ void slotTransposeUpOctave();
+ void slotTransposeDown();
+ void slotTransposeDownOctave();
+ void slotTranspose();
+ void slotDiatonicTranspose();
+
+ // invert
+ void slotInvert();
+ void slotRetrograde();
+ void slotRetrogradeInvert();
+
+ // jog events
+ void slotJogLeft();
+ void slotJogRight();
+
+ // Control ruler actions
+ //
+ void slotInsertControlRulerItem();
+ void slotEraseControlRulerItem();
+ void slotClearControlRulerItem();
+ void slotStartControlLineItem();
+ void slotFlipForwards();
+ void slotFlipBackwards();
+
+ // Property ruler actions
+ //
+ void slotDrawPropertyLine();
+ void slotSelectAllProperties();
+
+ // add control ruler
+ void slotAddControlRuler(int);
+ void slotRemoveControlRuler(QWidget*);
+
+protected:
+ virtual RulerScale* getHLayout() = 0;
+
+ QVBox* getBottomWidget() { return m_bottomBox; }
+
+ virtual void updateBottomWidgetGeometry();
+
+ virtual void paintEvent(QPaintEvent* e);
+
+ /**
+ * Locate the given widgets in the top bar-buttons position and
+ * connect up its scrolling signals.
+ */
+ void setTopStandardRuler(StandardRuler*, QWidget *leftBox = NULL);
+
+ /**
+ * Locate the given widget in the bottom bar-buttons position and
+ * connect up its scrolling signals.
+ */
+ void setBottomStandardRuler(StandardRuler*);
+
+ /**
+ * Set the 'Rewind' and 'Fast Forward' buttons in the transport
+ * toolbar to AutoRepeat
+ */
+ void setRewFFwdToAutoRepeat();
+
+ /**
+ * Locate the given widget right above the top bar-buttons and
+ * connect up its scrolling signals.
+ * The widget has to have a slotScrollHoriz(int) slot
+ */
+ void addRuler(QWidget*);
+
+ /**
+ * Add a ruler control box
+ */
+ void addPropertyBox(QWidget*);
+
+ /**
+ * Make a control ruler for the given property,
+ */
+ PropertyControlRuler* makePropertyControlRuler(PropertyName propertyName);
+
+ /**
+ * Make a ruler for controller events
+ */
+ ControllerEventsRuler* makeControllerEventRuler(const ControlParameter *controller = 0);
+
+ /**
+ * Add control ruler
+ */
+ void addControlRuler(ControlRuler* ruler);
+
+ /**
+ * Update all control rulers
+ */
+ void updateControlRulers(bool updateHPos=false);
+
+ /**
+ * Set zoom factor of control rulers
+ */
+ void setControlRulersZoom(QWMatrix);
+
+ /**
+ * Set current segment for control rulers
+ */
+ void setControlRulersCurrentSegment();
+
+ /**
+ * Find the control ruler for the given property name
+ * if it's already been created, return 0 otherwise
+ */
+ ControlRuler* findRuler(PropertyName propertyName, int &index);
+
+ /**
+ * Find the control ruler for the given controller
+ * if it's already been created, return 0 otherwise
+ */
+ ControlRuler* findRuler(const ControlParameter& controller, int &index);
+
+ /**
+ * Show a control ruler for the given property
+ * If the ruler already exists, activate the tab it's in,
+ * otherwise create the ruler and add it to the control rulers tab
+ * widget
+ */
+ void showPropertyControlRuler(PropertyName propertyName);
+
+ /**
+ * Return the control ruler currently displayed, or 0 if none exist
+ */
+ ControlRuler* getCurrentControlRuler();
+
+ /**
+ * Set up those actions common to any EditView (e.g. note insertion,
+ * time signatures etc)
+ */
+ void setupActions();
+
+ /**
+ * Set up the 'Add control ruler' sub-menu
+ */
+ void setupAddControlRulerMenu();
+
+ /**
+ * Do this after any other segment setup in a subordinate view.
+ * Returns true if there were any tabs to set up.
+ */
+ bool setupControllerTabs();
+
+ /**
+ * Create an action menu for inserting notes from the PC keyboard,
+ * and add it to the action collection. This is one of the methods
+ * called by setupActions().
+ */
+ void createInsertPitchActionMenu();
+
+ /**
+ * Get a note pitch from an action name (where the action is one of
+ * those created by createInsertPitchActionMenu). Can throw an
+ * Exception to mean that the action is not an insert one. Also
+ * returns any specified accidental through the reference arg.
+ */
+ int getPitchFromNoteInsertAction(QString actionName,
+ Accidental &acc,
+ const Clef &clef,
+ const ::Rosegarden::Key &key);
+
+ /**
+ * Abstract method to get the view size
+ * Typically implemented as canvas()->size().
+ */
+ virtual QSize getViewSize() = 0;
+
+ /**
+ * Abstract method to set the view size
+ * Typically implemented as canvas()->resize().
+ */
+ virtual void setViewSize(QSize) = 0;
+
+ /**
+ * Abstract method to get current insert-pointer time
+ */
+ virtual timeT getInsertionTime() = 0;
+
+ /**
+ * Return the time at which the insert cursor may be found,
+ * and the time signature, clef and key at that time. Default
+ * implementation is okay but slow.
+ */
+ virtual timeT getInsertionTime(Clef &clef, ::Rosegarden::Key &key);
+
+ /**
+ * Abstract method to get current staff (the returned staff will be
+ * that representing the segment of getCurrentSegment())
+ */
+ virtual Staff *getCurrentStaff() = 0;
+
+ /**
+ * Return the device of the current segment, if any
+ */
+ Device *getCurrentDevice();
+
+ virtual void setCanvasView(RosegardenCanvasView *cv);
+
+ //--------------- Data members ---------------------------------
+
+ /// The current selection of Events (for cut/copy/paste)
+ EventSelection* m_currentEventSelection;
+
+ ActiveItem* m_activeItem;
+
+ RosegardenCanvasView *m_canvasView;
+
+ QVBoxLayout *m_rulerBox;
+ QLabel *m_rulerBoxFiller;
+ QVBoxLayout *m_controlBox;
+ QVBox *m_bottomBox;
+ StandardRuler *m_topStandardRuler;
+ StandardRuler *m_bottomStandardRuler;
+ ControlRuler *m_controlRuler;
+ KTabWidget *m_controlRulers;
+ QWMatrix m_currentRulerZoomMatrix;
+
+ static const unsigned int RULERS_ROW;
+ static const unsigned int CONTROLS_ROW;
+ static const unsigned int TOPBARBUTTONS_ROW;
+ static const unsigned int CANVASVIEW_ROW;
+ static const unsigned int CONTROLRULER_ROW;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/general/EditViewBase.cpp b/src/gui/general/EditViewBase.cpp
new file mode 100644
index 0000000..0193beb
--- /dev/null
+++ b/src/gui/general/EditViewBase.cpp
@@ -0,0 +1,711 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "EditViewBase.h"
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include <klocale.h>
+#include <kstddirs.h>
+#include "misc/Debug.h"
+#include "base/Clipboard.h"
+#include "base/Event.h"
+#include "base/NotationTypes.h"
+#include "base/Segment.h"
+#include "commands/segment/SegmentReconfigureCommand.h"
+#include "document/MultiViewCommandHistory.h"
+#include "document/RosegardenGUIDoc.h"
+#include "EditToolBox.h"
+#include "EditTool.h"
+#include "EditView.h"
+#include "gui/dialogs/ConfigureDialog.h"
+#include "gui/dialogs/TimeDialog.h"
+#include "gui/general/EditViewTimeSigNotifier.h"
+#include "gui/kdeext/KTmpStatusMsg.h"
+#include <kaction.h>
+#include <kcommand.h>
+#include <kconfig.h>
+#include <kdockwidget.h>
+#include <kedittoolbar.h>
+#include <kglobal.h>
+#include <kkeydialog.h>
+#include <kmainwindow.h>
+#include <kstatusbar.h>
+#include <kstdaccel.h>
+#include <kstdaction.h>
+#include <kxmlguiclient.h>
+#include <qaccel.h>
+#include <qcanvas.h>
+#include <qdialog.h>
+#include <qframe.h>
+#include <qiconset.h>
+#include <qobject.h>
+#include <qpixmap.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+bool EditViewBase::m_inPaintEvent = false;
+const unsigned int EditViewBase::ID_STATUS_MSG = 1;
+const unsigned int EditViewBase::NbLayoutRows = 6;
+
+EditViewBase::EditViewBase(RosegardenGUIDoc *doc,
+ std::vector<Segment *> segments,
+ unsigned int cols,
+ QWidget *parent, const char *name) :
+ KDockMainWindow(parent, name),
+ m_viewNumber( -1),
+ m_viewLocalPropertyPrefix(makeViewLocalPropertyPrefix()),
+ m_config(kapp->config()),
+ m_doc(doc),
+ m_segments(segments),
+ m_tool(0),
+ m_toolBox(0),
+ m_mainDockWidget(0),
+ m_centralFrame(0),
+ m_grid(0),
+ m_mainCol(cols - 1),
+ m_compositionRefreshStatusId(doc->getComposition().getNewRefreshStatusId()),
+ m_needUpdate(false),
+ m_pendingPaintEvent(0),
+ m_havePendingPaintEvent(false),
+ m_accelerators(0),
+ m_configDialogPageIndex(0),
+ m_inCtor(true),
+ m_timeSigNotifier(new EditViewTimeSigNotifier(doc))
+{
+
+ QPixmap dummyPixmap; // any icon will do
+ m_mainDockWidget = createDockWidget("Rosegarden EditView DockWidget", dummyPixmap,
+ 0L, "editview_dock_widget");
+ // allow others to dock to the left and right sides only
+ m_mainDockWidget->setDockSite(KDockWidget::DockLeft | KDockWidget::DockRight);
+ // forbit docking abilities of m_mainDockWidget itself
+ m_mainDockWidget->setEnableDocking(KDockWidget::DockNone);
+ setView(m_mainDockWidget); // central widget in a KDE mainwindow
+ setMainDockWidget(m_mainDockWidget); // master dockwidget
+
+ m_centralFrame = new QFrame(m_mainDockWidget, "centralframe");
+ m_grid = new QGridLayout(m_centralFrame, NbLayoutRows, cols);
+
+ m_mainDockWidget->setWidget(m_centralFrame);
+
+ initSegmentRefreshStatusIds();
+
+ m_doc->attachEditView(this);
+
+ QObject::connect
+ (getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(update()));
+
+ QObject::connect
+ (getCommandHistory(), SIGNAL(commandExecuted()),
+ this, SLOT(slotTestClipboard()));
+
+ // create accelerators
+ //
+ m_accelerators = new QAccel(this);
+}
+
+EditViewBase::~EditViewBase()
+{
+ delete m_timeSigNotifier;
+
+ m_doc->detachEditView(this);
+
+ getCommandHistory()->detachView(actionCollection());
+ m_viewNumberPool.erase(m_viewNumber);
+ slotSaveOptions();
+}
+
+void EditViewBase::slotSaveOptions()
+{}
+
+void EditViewBase::readOptions()
+{
+ getToggleAction("options_show_statusbar")->setChecked(!statusBar()->isHidden());
+ getToggleAction("options_show_toolbar")->setChecked(!toolBar()->isHidden());
+}
+
+void EditViewBase::setupActions(QString rcFileName, bool haveClipboard)
+{
+ setRCFileName(rcFileName);
+
+ // Actions all edit views will have
+
+ KStdAction::showToolbar(this, SLOT(slotToggleToolBar()),
+ actionCollection(), "options_show_toolbar");
+
+ KStdAction::showStatusbar(this, SLOT(slotToggleStatusBar()),
+ actionCollection(), "options_show_statusbar");
+
+ KStdAction::preferences(this,
+ SLOT(slotConfigure()),
+ actionCollection());
+
+ KStdAction::keyBindings(this,
+ SLOT(slotEditKeys()),
+ actionCollection());
+
+ KStdAction::configureToolbars(this,
+ SLOT(slotEditToolbars()),
+ actionCollection());
+
+
+ // File menu
+ KStdAction::save (this, SIGNAL(saveFile()), actionCollection());
+ KStdAction::close(this, SLOT(slotCloseWindow()), actionCollection());
+
+ if (haveClipboard) {
+ KStdAction::cut (this, SLOT(slotEditCut()), actionCollection());
+ KStdAction::copy (this, SLOT(slotEditCopy()), actionCollection());
+ KStdAction::paste (this, SLOT(slotEditPaste()), actionCollection());
+ }
+
+ new KToolBarPopupAction(i18n("Und&o"),
+ "undo",
+ KStdAccel::key(KStdAccel::Undo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Undo));
+
+ new KToolBarPopupAction(i18n("Re&do"),
+ "redo",
+ KStdAccel::key(KStdAccel::Redo),
+ actionCollection(),
+ KStdAction::stdName(KStdAction::Redo));
+
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+
+ QCanvasPixmap pixmap(pixmapDir + "/toolbar/matrix.png");
+ QIconSet icon = QIconSet(pixmap);
+ new KAction(i18n("Open in Matri&x Editor"), icon, 0, this,
+ SLOT(slotOpenInMatrix()), actionCollection(),
+ "open_in_matrix");
+
+ pixmap.load(pixmapDir + "/toolbar/matrix-percussion.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Open in &Percussion Matrix Editor"), icon, 0, this,
+ SLOT(slotOpenInPercussionMatrix()), actionCollection(),
+ "open_in_percussion_matrix");
+
+ pixmap.load(pixmapDir + "/toolbar/notation.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Open in &Notation Editor"), icon, 0, this,
+ SLOT(slotOpenInNotation()), actionCollection(),
+ "open_in_notation");
+
+ pixmap.load(pixmapDir + "/toolbar/eventlist.png");
+ icon = QIconSet(pixmap);
+ new KAction(i18n("Open in &Event List Editor"), icon, 0, this,
+ SLOT(slotOpenInEventList()), actionCollection(),
+ "open_in_event_list");
+
+ new KAction(i18n("Set Segment Start Time..."), 0, this,
+ SLOT(slotSetSegmentStartTime()), actionCollection(),
+ "set_segment_start");
+
+ new KAction(i18n("Set Segment Duration..."), 0, this,
+ SLOT(slotSetSegmentDuration()), actionCollection(),
+ "set_segment_duration");
+
+ // add undo and redo to edit menu and toolbar
+ getCommandHistory()->attachView(actionCollection());
+
+}
+
+void EditViewBase::slotConfigure()
+{
+ ConfigureDialog *configDlg =
+ new ConfigureDialog(getDocument(), m_config, this);
+
+ configDlg->showPage(getConfigDialogPageIndex());
+ configDlg->show();
+}
+
+void EditViewBase::slotEditKeys()
+{
+ KKeyDialog::configure(actionCollection());
+}
+
+void EditViewBase::slotEditToolbars()
+{
+ KEditToolbar dlg(actionCollection(), getRCFileName());
+
+ connect(&dlg, SIGNAL(newToolbarConfig()),
+ SLOT(slotUpdateToolbars()));
+
+ dlg.exec();
+}
+
+void EditViewBase::slotUpdateToolbars()
+{
+ createGUI(getRCFileName());
+ //m_viewToolBar->setChecked(!toolBar()->isHidden());
+}
+
+void
+EditViewBase::slotOpenInNotation()
+{
+
+ emit openInNotation(m_segments);
+}
+
+void
+EditViewBase::slotOpenInMatrix()
+{
+ emit openInMatrix(m_segments);
+}
+
+void
+EditViewBase::slotOpenInPercussionMatrix()
+{
+ emit openInPercussionMatrix(m_segments);
+}
+
+void
+EditViewBase::slotOpenInEventList()
+{
+ emit openInEventList(m_segments);
+}
+
+std::set<int> EditViewBase::m_viewNumberPool;
+
+std::string
+EditViewBase::makeViewLocalPropertyPrefix()
+{
+ static char buffer[100];
+ int i = 0;
+ while (m_viewNumberPool.find(i) != m_viewNumberPool.end())
+ ++i;
+ m_viewNumber = i;
+ m_viewNumberPool.insert(i);
+ sprintf(buffer, "View%d::", i);
+ return buffer;
+}
+
+void EditViewBase::paintEvent(QPaintEvent* e)
+{
+ // It is possible for this function to be called re-entrantly,
+ // because a re-layout procedure may deliberately ask the event
+ // loop to process some more events so as to keep the GUI looking
+ // responsive. If that happens, we remember the events that came
+ // in in the middle of one paintEvent call and process their union
+ // again at the end of the call.
+ /*
+ if (m_inPaintEvent) {
+ NOTATION_DEBUG << "EditViewBase::paintEvent: in paint event already" << endl;
+ if (e) {
+ if (m_havePendingPaintEvent) {
+ if (m_pendingPaintEvent) {
+ QRect r = m_pendingPaintEvent->rect().unite(e->rect());
+ *m_pendingPaintEvent = QPaintEvent(r);
+ } else {
+ m_pendingPaintEvent = new QPaintEvent(*e);
+ }
+ } else {
+ m_pendingPaintEvent = new QPaintEvent(*e);
+ }
+ }
+ m_havePendingPaintEvent = true;
+ return;
+ }
+ */
+ //!!! m_inPaintEvent = true;
+
+ if (isCompositionModified()) {
+
+ // Check if one of the segments we display has been removed
+ // from the composition.
+ //
+ // For the moment we'll have to close the view if any of the
+ // segments we handle has been deleted.
+
+ for (unsigned int i = 0; i < m_segments.size(); ++i) {
+
+ if (!m_segments[i]->getComposition()) {
+ // oops, I think we've been deleted
+ close();
+ return ;
+ }
+ }
+ }
+
+
+ m_needUpdate = false;
+
+ // Scan all segments and check if they've been modified.
+ //
+ // If we have more than one segment modified, we need to update
+ // them all at once with the same time range, otherwise we can run
+ // into problems when the layout of one depends on the others. So
+ // we use updateStart/End to calculate a bounding range for all
+ // modifications.
+
+ timeT updateStart = 0, updateEnd = 0;
+ int segmentsToUpdate = 0;
+ Segment *singleSegment = 0;
+
+ for (unsigned int i = 0; i < m_segments.size(); ++i) {
+
+ Segment* segment = m_segments[i];
+ unsigned int refreshStatusId = m_segmentsRefreshStatusIds[i];
+ SegmentRefreshStatus &refreshStatus =
+ segment->getRefreshStatus(refreshStatusId);
+
+ if (refreshStatus.needsRefresh() && isCompositionModified()) {
+
+ // if composition is also modified, relayout everything
+ refreshSegment(0);
+ segmentsToUpdate = 0;
+ break;
+
+ } else if (m_timeSigNotifier->hasTimeSigChanged()) {
+
+ // not exactly optimal!
+ refreshSegment(0);
+ segmentsToUpdate = 0;
+ m_timeSigNotifier->reset();
+ break;
+
+ } else if (refreshStatus.needsRefresh()) {
+
+ timeT startTime = refreshStatus.from(),
+ endTime = refreshStatus.to();
+
+ if (segmentsToUpdate == 0 || startTime < updateStart) {
+ updateStart = startTime;
+ }
+ if (segmentsToUpdate == 0 || endTime > updateEnd) {
+ updateEnd = endTime;
+ }
+ singleSegment = segment;
+ ++segmentsToUpdate;
+
+ refreshStatus.setNeedsRefresh(false);
+ m_needUpdate = true;
+ }
+ }
+
+ if (segmentsToUpdate > 1) {
+ refreshSegment(0, updateStart, updateEnd);
+ } else if (segmentsToUpdate > 0) {
+ refreshSegment(singleSegment, updateStart, updateEnd);
+ }
+
+ if (e)
+ KMainWindow::paintEvent(e);
+
+ // moved this to the end of the method so that things called
+ // from this method can still test whether the composition had
+ // been modified (it's sometimes useful to know whether e.g.
+ // any time signatures have changed)
+ setCompositionModified(false);
+
+ //!!! m_inPaintEvent = false;
+ /*
+ if (m_havePendingPaintEvent) {
+ e = m_pendingPaintEvent;
+ m_havePendingPaintEvent = false;
+ m_pendingPaintEvent = 0;
+ paintEvent(e);
+ delete e;
+ }
+ */
+}
+
+void EditViewBase::closeEvent(QCloseEvent* e)
+{
+ RG_DEBUG << "EditViewBase::closeEvent()\n";
+
+ if (isInCtor()) {
+ RG_DEBUG << "EditViewBase::closeEvent() : is in ctor, ignoring close event\n";
+ e->ignore();
+ } else {
+ KMainWindow::closeEvent(e);
+ }
+}
+
+void EditViewBase::addCommandToHistory(KCommand *command)
+{
+ getCommandHistory()->addCommand(command);
+}
+
+void EditViewBase::setTool(EditTool* tool)
+{
+ if (m_tool)
+ m_tool->stow();
+
+ m_tool = tool;
+
+ if (m_tool)
+ m_tool->ready();
+
+}
+
+void EditViewBase::slotCloseWindow()
+{
+ close();
+}
+
+void EditViewBase::slotToggleToolBar()
+{
+ KTmpStatusMsg msg(i18n("Toggle the toolbar..."), this);
+
+ if (toolBar()->isVisible())
+ toolBar()->hide();
+ else
+ toolBar()->show();
+}
+
+void EditViewBase::slotToggleStatusBar()
+{
+ KTmpStatusMsg msg(i18n("Toggle the statusbar..."), this);
+
+ if (statusBar()->isVisible())
+ statusBar()->hide();
+ else
+ statusBar()->show();
+}
+
+void EditViewBase::slotStatusMsg(const QString &text)
+{
+ ///////////////////////////////////////////////////////////////////
+ // change status message permanently
+ statusBar()->clear();
+ statusBar()->changeItem(text, ID_STATUS_MSG);
+}
+
+void EditViewBase::slotStatusHelpMsg(const QString &text)
+{
+ ///////////////////////////////////////////////////////////////////
+ // change status message of whole statusbar temporary (text, msec)
+ statusBar()->message(text, 2000);
+}
+
+void EditViewBase::initSegmentRefreshStatusIds()
+{
+ for (unsigned int i = 0; i < m_segments.size(); ++i) {
+
+ unsigned int rid = m_segments[i]->getNewRefreshStatusId();
+ m_segmentsRefreshStatusIds.push_back(rid);
+ }
+}
+
+bool EditViewBase::isCompositionModified()
+{
+ return getDocument()->getComposition().getRefreshStatus
+ (m_compositionRefreshStatusId).needsRefresh();
+}
+
+void EditViewBase::setCompositionModified(bool c)
+{
+ getDocument()->getComposition().getRefreshStatus
+ (m_compositionRefreshStatusId).setNeedsRefresh(c);
+}
+
+bool EditViewBase::getSegmentsOnlyRestsAndClefs()
+{
+ using Rosegarden::Segment;
+
+ for (unsigned int i = 0; i < m_segments.size(); ++i) {
+
+ Segment* segment = m_segments[i];
+
+ for (Segment::iterator iter = segment->begin();
+ iter != segment->end(); ++iter) {
+
+ if (((*iter)->getType() != Note::EventRestType)
+ && ((*iter)->getType() != Clef::EventType))
+ return false;
+ }
+
+ }
+
+ return true;
+
+}
+
+void EditViewBase::toggleWidget(QWidget* widget,
+ const QString& toggleActionName)
+{
+ KToggleAction* toggleAction = getToggleAction(toggleActionName);
+
+ if (!toggleAction) {
+ RG_DEBUG << "!!! Unknown toggle action : " << toggleActionName << endl;
+ return ;
+ }
+
+ widget->setShown(toggleAction->isChecked());
+}
+
+void
+EditViewBase::slotTestClipboard()
+{
+ if (getDocument()->getClipboard()->isEmpty()) {
+ RG_DEBUG << "EditViewBase::slotTestClipboard(): empty" << endl;
+
+ stateChanged("have_clipboard", KXMLGUIClient::StateReverse);
+ stateChanged("have_clipboard_single_segment",
+ KXMLGUIClient::StateReverse);
+ } else {
+ RG_DEBUG << "EditViewBase::slotTestClipboard(): not empty" << endl;
+
+ stateChanged("have_clipboard", KXMLGUIClient::StateNoReverse);
+ stateChanged("have_clipboard_single_segment",
+ (getDocument()->getClipboard()->isSingleSegment() ?
+ KXMLGUIClient::StateNoReverse :
+ KXMLGUIClient::StateReverse));
+ }
+}
+
+void
+EditViewBase::slotToggleSolo()
+{
+ KToggleAction* toggleSoloAction = getToggleAction("toggle_solo");
+ if (!toggleSoloAction)
+ return ;
+
+ bool newSoloState = toggleSoloAction->isChecked();
+
+ RG_DEBUG << "EditViewBase::slotToggleSolo() : solo = " << newSoloState << endl;
+ emit toggleSolo(newSoloState);
+
+ if (newSoloState) {
+ emit selectTrack(getCurrentSegment()->getTrack());
+ }
+
+}
+
+void
+EditViewBase::slotStateChanged(const QString& s,
+ bool noReverse)
+{
+ RG_DEBUG << "EditViewBase::slotStateChanged " << s << ", " << noReverse << endl;
+ stateChanged(s, noReverse ? KXMLGUIClient::StateNoReverse : KXMLGUIClient::StateReverse);
+}
+
+void
+EditViewBase::slotSetSegmentStartTime()
+{
+ Segment *s = getCurrentSegment();
+ if (!s)
+ return ;
+
+ TimeDialog dialog(this, i18n("Segment Start Time"),
+ &getDocument()->getComposition(),
+ s->getStartTime(), false);
+
+ if (dialog.exec() == QDialog::Accepted) {
+
+ SegmentReconfigureCommand *command =
+ new SegmentReconfigureCommand(i18n("Set Segment Start Time"));
+
+ command->addSegment
+ (s, dialog.getTime(),
+ s->getEndMarkerTime() - s->getStartTime() + dialog.getTime(),
+ s->getTrack());
+
+ addCommandToHistory(command);
+ }
+}
+
+void
+EditViewBase::slotSetSegmentDuration()
+{
+ Segment *s = getCurrentSegment();
+ if (!s)
+ return ;
+
+ TimeDialog dialog(this, i18n("Segment Duration"),
+ &getDocument()->getComposition(),
+ s->getStartTime(),
+ s->getEndMarkerTime() - s->getStartTime(), false);
+
+ if (dialog.exec() == QDialog::Accepted) {
+
+ SegmentReconfigureCommand *command =
+ new SegmentReconfigureCommand(i18n("Set Segment Duration"));
+
+ command->addSegment
+ (s, s->getStartTime(),
+ s->getStartTime() + dialog.getTime(),
+ s->getTrack());
+
+ addCommandToHistory(command);
+ }
+}
+
+void EditViewBase::slotCompositionStateUpdate()
+{
+ // update state of 'solo' toggle
+ //
+ KToggleAction* toggleSolo = getToggleAction("toggle_solo");
+ if (!toggleSolo)
+ return ;
+
+ if (getDocument()->getComposition().isSolo()) {
+ bool s = m_segments[0]->getTrack() == getDocument()->getComposition().getSelectedTrack();
+ RG_DEBUG << "EditViewBase::slotCompositionStateUpdate() : set solo to " << s << endl;
+ toggleSolo->setChecked(s);
+ } else {
+ toggleSolo->setChecked(false);
+ RG_DEBUG << "EditViewBase::slotCompositionStateUpdate() : set solo to false\n";
+ }
+
+ // update the window caption
+ //
+ updateViewCaption();
+}
+
+void
+EditViewBase::windowActivationChange(bool oldState)
+{
+ if (isActiveWindow()) {
+ emit windowActivated();
+ }
+}
+
+void
+EditViewBase::handleEventRemoved(Event *event)
+{
+ if (m_tool)
+ m_tool->handleEventRemoved(event);
+}
+
+MultiViewCommandHistory* EditViewBase::getCommandHistory()
+{
+ return getDocument()->getCommandHistory();
+}
+
+KToggleAction* EditViewBase::getToggleAction(const QString& actionName)
+{
+ return dynamic_cast<KToggleAction*>(actionCollection()->action(actionName));
+}
+
+}
+#include "EditViewBase.moc"
diff --git a/src/gui/general/EditViewBase.h b/src/gui/general/EditViewBase.h
new file mode 100644
index 0000000..03784cb
--- /dev/null
+++ b/src/gui/general/EditViewBase.h
@@ -0,0 +1,396 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_EDITVIEWBASE_H_
+#define _RG_EDITVIEWBASE_H_
+
+#include <set>
+#include <string>
+#include <kdockwidget.h>
+#include <qstring.h>
+#include <vector>
+#include "base/Event.h"
+
+
+class QWidget;
+class QPaintEvent;
+class QGridLayout;
+class QFrame;
+class QCloseEvent;
+class QAccel;
+class KToggleAction;
+class KConfig;
+class KCommand;
+namespace Rosegarden { class EditViewTimeSigNotifier; }
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class RosegardenGUIDoc;
+class MultiViewCommandHistory;
+class Event;
+class EditToolBox;
+class EditTool;
+
+
+class EditViewBase : public KDockMainWindow
+{
+
+ Q_OBJECT
+
+public:
+
+ /**
+ * Create an EditViewBase for the segments \a segments from document \a doc.
+ *
+ * \arg cols : number of columns, main column is always rightmost
+ *
+ */
+ EditViewBase(RosegardenGUIDoc *doc,
+ std::vector<Segment *> segments,
+ unsigned int cols,
+ QWidget *parent,
+ const char *name = 0);
+
+ virtual ~EditViewBase();
+
+ const RosegardenGUIDoc *getDocument() const { return m_doc; }
+ RosegardenGUIDoc *getDocument() { return m_doc; }
+
+ /**
+ * Refresh part of a Segment following a modification made in this
+ * or another view. The startTime and endTime give the extents of
+ * the modified region. This method is called following a
+ * modification to any Segment; no attempt has been made to check
+ * that the given Segment is actually shown in this view, so take
+ * care.
+ *
+ * If segment is null, refresh all segments.
+ * If the startTime and endTime are equal, refresh the whole of
+ * the relevant segments.
+ */
+ virtual void refreshSegment(Segment *segment,
+ timeT startTime = 0,
+ timeT endTime = 0) = 0;
+
+ /**
+ * Get the document's global command history
+ */
+ virtual MultiViewCommandHistory *getCommandHistory();
+
+ /**
+ * Add a Command to the history
+ */
+ virtual void addCommandToHistory(KCommand *);
+
+ /**
+ * Update the view
+ */
+ virtual void updateView() = 0;
+
+ /**
+ * Return our local accelerator object
+ */
+ QAccel* getAccelerators() { return m_accelerators; }
+
+ /**
+ * Return a string unique to this view (amongst views currently
+ * extant) that can be used (e.g. as a prefix) to distinguish
+ * view-local properties. It's up to the subclass or other user
+ * of this string to manage the properties correctly, for example
+ * by deleting them from the events when the view closes.
+ */
+ std::string getViewLocalPropertyPrefix() {
+ return m_viewLocalPropertyPrefix;
+ }
+
+ /*
+ * So that other people can create tools against our view
+ *
+ */
+ EditToolBox* getToolBox() { return m_toolBox; }
+
+ /**
+ * Let tools know if their current element has gone
+ */
+ virtual void handleEventRemoved(Event *event);
+
+ static const unsigned int ID_STATUS_MSG;
+ static const unsigned int NbLayoutRows;
+
+
+signals:
+ /**
+ * Tell the app to save the file.
+ */
+ void saveFile();
+
+ /**
+ * Reopen the given segments in another sort of editor.
+ */
+ void openInNotation(std::vector<Segment *>);
+ void openInMatrix(std::vector<Segment *>);
+ void openInPercussionMatrix(std::vector<Segment *>);
+ void openInEventList(std::vector<Segment *>);
+
+ /**
+ * Tell the main view that the track being edited is the
+ * current selected track
+ * This is used by #slotToggleSolo
+ */
+ void selectTrack(int);
+
+ /**
+ * Tell the main view that the solo status has changed (the user clicked on the 'solo' toggle)
+ */
+ void toggleSolo(bool);
+
+ void windowActivated();
+
+public slots:
+ /**
+ * close window
+ */
+ virtual void slotCloseWindow();
+
+ /**
+ * put the indicationed text/object into the clipboard and remove * it
+ * from the document
+ */
+ virtual void slotEditCut() = 0;
+
+ /**
+ * put the indicationed text/object into the clipboard
+ */
+ virtual void slotEditCopy() = 0;
+
+ /**
+ * paste the clipboard into the document
+ */
+ virtual void slotEditPaste() = 0;
+
+ /**
+ * toggles the main toolbar
+ */
+ virtual void slotToggleToolBar();
+
+ /**
+ * toggles the statusbar
+ */
+ virtual void slotToggleStatusBar();
+
+ /**
+ * Changes the statusbar contents for the standard label permanently,
+ * used to indicate current actions.
+ *
+ * @param text the text that is displayed in the statusbar
+ */
+ virtual void slotStatusMsg(const QString &text);
+
+ /**
+ * Changes the status message of the whole statusbar for two
+ * seconds, then restores the last status. This is used to display
+ * statusbar messages that give information about actions for
+ * toolbar icons and menuentries.
+ *
+ * @param text the text that is displayed in the statusbar
+ */
+ virtual void slotStatusHelpMsg(const QString &text);
+
+ /**
+ * A command has happened; check the clipboard in case we
+ * need to change state
+ */
+ virtual void slotTestClipboard();
+
+ /**
+ * Connected to this view's toolbar 'solo' button
+ */
+ virtual void slotToggleSolo();
+
+ void slotStateChanged(const QString&, bool noReverse);
+
+ virtual void slotOpenInMatrix();
+ virtual void slotOpenInPercussionMatrix();
+ virtual void slotOpenInNotation();
+ virtual void slotOpenInEventList();
+
+ /**
+ * Set the start time of the current segment
+ */
+ void slotSetSegmentStartTime();
+
+ /**
+ * Set the duration of the current segment
+ */
+ void slotSetSegmentDuration();
+
+ /**
+ * Global composition updates from the main view (track selection, solo, etc...)
+ */
+ virtual void slotCompositionStateUpdate();
+
+protected:
+
+ virtual void windowActivationChange(bool);
+
+ virtual void paintEvent(QPaintEvent* e);
+
+ /**
+ * @see #setInCtor
+ */
+ virtual void closeEvent(QCloseEvent* e);
+
+ /**
+ * ignore close events while we're in ctor
+ */
+ void setOutOfCtor() { m_inCtor = false; }
+
+ /**
+ * Check if we're still in ctor
+ */
+ bool isInCtor() { return m_inCtor; }
+
+ /**
+ * Set the current Notation tool (note inserter, rest inserter, eraser...)
+ *
+ * Called when the user selects a new item on one of the notation toolbars
+ * (notes toolbars, rests toolbars...)
+ */
+ void setTool(EditTool*);
+
+ /**
+ * read general Options again and initialize all variables like the recent file list
+ */
+ virtual void readOptions();
+
+ /**
+ * create menus and toolbars
+ */
+ virtual void setupActions(QString rcFileName, bool haveClipboard = true);
+
+ /**
+ * setup status bar
+ */
+ virtual void initStatusBar() = 0;
+
+ /**
+ * Abstract method to get current segment
+ */
+ virtual Segment *getCurrentSegment() = 0;
+
+ /**
+ * Set the caption of the view's window
+ */
+ virtual void updateViewCaption() = 0;
+
+protected slots:
+ /**
+ * save general Options like all bar positions and status as well
+ * as the geometry and the recent file list to the configuration
+ * file
+ */
+ virtual void slotSaveOptions();
+ virtual void slotConfigure();
+ virtual void slotEditKeys();
+ virtual void slotEditToolbars();
+ virtual void slotUpdateToolbars();
+
+protected:
+ QWidget* getCentralWidget() { return m_centralFrame; }
+
+ void initSegmentRefreshStatusIds();
+
+ bool isCompositionModified();
+ void setCompositionModified(bool);
+
+ /**
+ * Returns true if all of the segments contain
+ * only rests and clefs events
+ */
+ bool getSegmentsOnlyRestsAndClefs();
+
+ /// Convenience function around actionCollection()->action()
+ KToggleAction* getToggleAction(const QString& actionName);
+
+ /**
+ * Make a widget visible depending on the state of a
+ * KToggleAction
+ */
+ virtual void toggleWidget(QWidget* widget, const QString& toggleActionName);
+
+ void setRCFileName(QString s) { m_rcFileName = s; }
+ QString getRCFileName() { return m_rcFileName; }
+
+ /**
+ * Set the page index of the config dialog which corresponds to
+ * this editview
+ */
+ void setConfigDialogPageIndex(int p) { m_configDialogPageIndex = p; }
+ int getConfigDialogPageIndex() { return m_configDialogPageIndex; }
+
+ //--------------- Data members ---------------------------------
+ QString m_rcFileName;
+
+ static std::set<int> m_viewNumberPool;
+ std::string makeViewLocalPropertyPrefix();
+ int m_viewNumber;
+ std::string m_viewLocalPropertyPrefix;
+
+ KConfig* m_config;
+
+ RosegardenGUIDoc* m_doc;
+ std::vector<Segment *> m_segments;
+ std::vector<unsigned int> m_segmentsRefreshStatusIds;
+
+ EditTool* m_tool;
+ EditToolBox* m_toolBox;
+
+ KDockWidget *m_mainDockWidget;
+ QFrame *m_centralFrame;
+ QGridLayout *m_grid;
+
+ unsigned int m_mainCol;
+ unsigned int m_compositionRefreshStatusId;
+ bool m_needUpdate;
+
+ QPaintEvent *m_pendingPaintEvent;
+ bool m_havePendingPaintEvent;
+ static bool m_inPaintEvent; // true if _any_ edit view is in a paint event
+
+ QAccel *m_accelerators;
+
+ int m_configDialogPageIndex;
+
+ bool m_inCtor;
+
+ EditViewTimeSigNotifier *m_timeSigNotifier;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/general/EditViewTimeSigNotifier.h b/src/gui/general/EditViewTimeSigNotifier.h
new file mode 100644
index 0000000..679494d
--- /dev/null
+++ b/src/gui/general/EditViewTimeSigNotifier.h
@@ -0,0 +1,56 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#ifndef _RG_EDITVIEWTIMESIGNOTIFIER_H_
+#define _RG_EDITVIEWTIMESIGNOTIFIER_H_
+
+namespace Rosegarden {
+
+class EditViewTimeSigNotifier : public Rosegarden::CompositionObserver
+{
+public:
+ EditViewTimeSigNotifier(RosegardenGUIDoc *doc) :
+ m_composition(&doc->getComposition()),
+ m_timeSigChanged(false) {
+ m_composition->addObserver(this);
+ }
+ virtual ~EditViewTimeSigNotifier() {
+ if (!isCompositionDeleted()) m_composition->removeObserver(this);
+ }
+ virtual void timeSignatureChanged(const Rosegarden::Composition *c) {
+ if (c == m_composition) m_timeSigChanged = true;
+ }
+
+ bool hasTimeSigChanged() const { return m_timeSigChanged; }
+ void reset() { m_timeSigChanged = false; }
+
+protected:
+ Rosegarden::Composition *m_composition;
+ bool m_timeSigChanged;
+};
+
+}
+
+#endif /*EDITVIEWTIMESIGNOTIFIER_H_*/
diff --git a/src/gui/general/GUIPalette.cpp b/src/gui/general/GUIPalette.cpp
new file mode 100644
index 0000000..4705c21
--- /dev/null
+++ b/src/gui/general/GUIPalette.cpp
@@ -0,0 +1,311 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "GUIPalette.h"
+#include <kapplication.h>
+
+#include "base/Colour.h"
+#include "document/ConfigGroups.h"
+#include <kconfig.h>
+#include <qcolor.h>
+
+
+namespace Rosegarden
+{
+
+QColor GUIPalette::getColour(const char* const colourName)
+{
+ KConfig* config = kapp->config();
+ config->setGroup(ColoursConfigGroup);
+
+ QColor res = getInstance()->m_defaultsMap[colourName];
+ config->readColorEntry(colourName, &res);
+ return res;
+}
+
+Colour GUIPalette::convertColour(const QColor &input)
+{
+ int r, g, b;
+ input.rgb(&r, &g, &b);
+ return Colour(r, g, b);
+}
+
+QColor GUIPalette::convertColour(const Colour& input)
+{
+ return QColor(input.getRed(), input.getGreen(), input.getBlue());
+}
+
+GUIPalette::GUIPalette()
+{
+ m_defaultsMap[ActiveRecordTrack] = Qt::red;
+
+ m_defaultsMap[SegmentCanvas] = QColor(230, 230, 230);
+ m_defaultsMap[SegmentBorder] = Qt::black;
+
+ // 1.0 colors
+ // m_defaultsMap[RecordingInternalSegmentBlock] = QColor(255, 182, 193);
+ // m_defaultsMap[RecordingAudioSegmentBlock] = QColor(182, 222, 255);
+
+ // MIDI recording preview (pale yellow)
+ m_defaultsMap[RecordingInternalSegmentBlock] = QColor(255, 234, 182);
+
+ // audio recording preview (pale red)
+ m_defaultsMap[RecordingAudioSegmentBlock] = QColor(255, 182, 193);
+
+ m_defaultsMap[RecordingSegmentBorder] = Qt::black;
+
+ m_defaultsMap[RepeatSegmentBorder] = QColor(130, 133, 170);
+
+ m_defaultsMap[SegmentAudioPreview] = QColor(39, 71, 22);
+ m_defaultsMap[SegmentInternalPreview] = Qt::white;
+ m_defaultsMap[SegmentLabel] = Qt::black;
+ m_defaultsMap[SegmentSplitLine] = Qt::black;
+
+ m_defaultsMap[MatrixElementBorder] = Qt::black;
+ m_defaultsMap[MatrixElementBlock] = QColor(98, 128, 232);
+ m_defaultsMap[MatrixOverlapBlock] = Qt::black;
+
+ m_defaultsMap[LoopRulerBackground] = QColor(120, 120, 120);
+ m_defaultsMap[LoopRulerForeground] = Qt::white;
+ m_defaultsMap[LoopHighlight] = Qt::white;
+
+ m_defaultsMap[TempoBase] = QColor(197, 211, 125);
+
+ //m_defaultsMap[TextRulerBackground] = QColor(60, 205, 230, QColor::Hsv);
+ // m_defaultsMap[TextRulerBackground] = QColor(120, 90, 238, QColor::Hsv);
+ // m_defaultsMap[TextRulerBackground] = QColor(210, 220, 140);
+ m_defaultsMap[TextRulerBackground] = QColor(226, 232, 187);
+ m_defaultsMap[TextRulerForeground] = Qt::white;
+
+ m_defaultsMap[ChordNameRulerBackground] = QColor(230, 230, 230);
+ m_defaultsMap[ChordNameRulerForeground] = Qt::black;
+
+ m_defaultsMap[RawNoteRulerBackground] = QColor(240, 240, 240);
+ m_defaultsMap[RawNoteRulerForeground] = Qt::black;
+
+ m_defaultsMap[LevelMeterGreen] = QColor(0, 200, 0);
+ m_defaultsMap[LevelMeterOrange] = QColor(255, 165, 0);
+ m_defaultsMap[LevelMeterRed] = QColor(200, 0, 0);
+
+ // m_defaultsMap[LevelMeterSolidGreen] = QColor(0, 140, 0);
+ m_defaultsMap[LevelMeterSolidGreen] = QColor(84, 177, 248); // blue!
+ // m_defaultsMap[LevelMeterSolidOrange] = QColor(220, 120, 0);
+ m_defaultsMap[LevelMeterSolidOrange] = QColor(255, 225, 0);
+ // m_defaultsMap[LevelMeterSolidRed] = QColor(255, 50, 50);
+ m_defaultsMap[LevelMeterSolidRed] = QColor(255, 0, 0);
+
+ m_defaultsMap[BarLine] = Qt::black;
+ m_defaultsMap[BarLineIncorrect] = QColor(211, 0, 31);
+ m_defaultsMap[BeatLine] = QColor(100, 100, 100);
+ m_defaultsMap[SubBeatLine] = QColor(212, 212, 212);
+ m_defaultsMap[StaffConnectingLine] = QColor(192, 192, 192);
+ m_defaultsMap[StaffConnectingTerminatingLine] = QColor(128, 128, 128);
+
+ m_defaultsMap[Pointer] = Qt::darkBlue;
+ m_defaultsMap[PointerRuler] = QColor(100, 100, 100);
+
+ m_defaultsMap[InsertCursor] = QColor(160, 104, 186);
+ m_defaultsMap[InsertCursorRuler] = QColor(160, 136, 170);
+
+ m_defaultsMap[TrackDivider] = QColor(145, 145, 145);
+ //m_defaultsMap[MovementGuide] = QColor(172, 230, 139);
+ m_defaultsMap[MovementGuide] = QColor(62, 161, 194);
+ //m_defaultsMap[MovementGuide] = QColor(255, 189, 89);
+ m_defaultsMap[SelectionRectangle] = QColor(103, 128, 211);
+ m_defaultsMap[SelectedElement] = QColor(0, 54, 232);
+
+ const int SelectedElementHue = 225;
+ const int SelectedElementMinValue = 220;
+ const int HighlightedElementHue = 25;
+ const int HighlightedElementMinValue = 220;
+ const int QuantizedNoteHue = 69;
+ const int QuantizedNoteMinValue = 140;
+ const int TriggerNoteHue = 4;
+ const int TriggerNoteMinValue = 140;
+ const int OutRangeNoteHue = 0;
+ const int OutRangeNoteMinValue = 200;
+
+ m_defaultsMap[TextAnnotationBackground] = QColor(255, 255, 180);
+
+ m_defaultsMap[TextLilyPondDirectiveBackground] = QColor(95, 157, 87);
+
+ m_defaultsMap[AudioCountdownBackground] = Qt::darkGray;
+ m_defaultsMap[AudioCountdownForeground] = Qt::red;
+
+// m_defaultsMap[RotaryFloatBackground] = Qt::cyan;
+ m_defaultsMap[RotaryFloatBackground] = QColor(182, 222, 255);
+ m_defaultsMap[RotaryFloatForeground] = Qt::black;
+
+ m_defaultsMap[RotaryPastelBlue] = QColor(205, 212, 255);
+ m_defaultsMap[RotaryPastelRed] = QColor(255, 168, 169);
+ m_defaultsMap[RotaryPastelGreen] = QColor(231, 255, 223);
+ m_defaultsMap[RotaryPastelOrange] = QColor(255, 233, 208);
+ m_defaultsMap[RotaryPastelYellow] = QColor(249, 255, 208);
+
+ m_defaultsMap[MatrixKeyboardFocus] = QColor(224, 112, 8);
+
+ // m_defaultsMap[RotaryPlugin] = QColor(185, 255, 248);
+ m_defaultsMap[RotaryPlugin] = QColor(185, 200, 248);
+ // m_defaultsMap[RotaryPlugin] = QColor(185, 185, 185);
+
+ m_defaultsMap[RotaryMeter] = QColor(255, 100, 0);
+
+ m_defaultsMap[MarkerBackground] = QColor(185, 255, 248);
+
+ m_defaultsMap[QuickMarker] = Qt::red;
+
+ // m_defaultsMap[MuteTrackLED] = QColor(218, 190, 230, QColor::Hsv);
+ m_defaultsMap[MuteTrackLED] = QColor(211, 194, 238, QColor::Hsv);
+ m_defaultsMap[RecordMIDITrackLED] = QColor(45, 250, 225, QColor::Hsv);
+ m_defaultsMap[RecordAudioTrackLED] = QColor(0, 250, 225, QColor::Hsv);
+
+ m_defaultsMap[PlaybackFaderOutline] = QColor(211, 194, 238, QColor::Hsv);
+ m_defaultsMap[RecordFaderOutline] = QColor(0, 250, 225, QColor::Hsv);
+}
+
+GUIPalette* GUIPalette::getInstance()
+{
+ if (!m_instance) m_instance = new GUIPalette();
+ return m_instance;
+}
+
+const char* const GUIPalette::ActiveRecordTrack = "activerecordtrack";
+
+
+const char* const GUIPalette::SegmentCanvas = "segmentcanvas";
+const char* const GUIPalette::SegmentBorder = "segmentborder";
+const char* const GUIPalette::RecordingInternalSegmentBlock = "recordinginternalsegmentblock";
+const char* const GUIPalette::RecordingAudioSegmentBlock = "recordingaudiosegmentblock";
+const char* const GUIPalette::RecordingSegmentBorder = "recordingsegmentborder";
+
+const char* const GUIPalette::RepeatSegmentBorder = "repeatsegmentborder";
+
+const char* const GUIPalette::SegmentAudioPreview = "segmentaudiopreview";
+const char* const GUIPalette::SegmentInternalPreview = "segmentinternalpreview";
+const char* const GUIPalette::SegmentLabel = "segmentlabel";
+const char* const GUIPalette::SegmentSplitLine = "segmentsplitline";
+
+const char* const GUIPalette::MatrixElementBorder = "matrixelementborder";
+const char* const GUIPalette::MatrixElementBlock = "matrixelementblock";
+const char* const GUIPalette::MatrixOverlapBlock = "matrixoverlapblock";
+
+const char* const GUIPalette::LoopRulerBackground = "looprulerbackground";
+const char* const GUIPalette::LoopRulerForeground = "looprulerforeground";
+const char* const GUIPalette::LoopHighlight = "loophighlight";
+
+const char* const GUIPalette::TempoBase = "tempobase";
+
+const char* const GUIPalette::TextRulerBackground = "textrulerbackground";
+const char* const GUIPalette::TextRulerForeground = "textrulerforeground";
+
+const char* const GUIPalette::ChordNameRulerBackground = "chordnamerulerbackground";
+const char* const GUIPalette::ChordNameRulerForeground = "chordnamerulerforeground";
+
+const char* const GUIPalette::RawNoteRulerBackground = "rawnoterulerbackground";
+const char* const GUIPalette::RawNoteRulerForeground = "rawnoterulerforeground";
+
+const char* const GUIPalette::LevelMeterGreen = "levelmetergreen";
+const char* const GUIPalette::LevelMeterOrange = "levelmeterorange";
+const char* const GUIPalette::LevelMeterRed = "levelmeterred";
+
+const char* const GUIPalette::LevelMeterSolidGreen = "levelmetersolidgreen";
+const char* const GUIPalette::LevelMeterSolidOrange = "levelmetersolidorange";
+const char* const GUIPalette::LevelMeterSolidRed = "levelmetersolidred";
+
+const char* const GUIPalette::BarLine = "barline";
+const char* const GUIPalette::BarLineIncorrect = "barlineincorrect";
+const char* const GUIPalette::BeatLine = "beatline";
+const char* const GUIPalette::SubBeatLine = "subbeatline";
+const char* const GUIPalette::StaffConnectingLine = "staffconnectingline";
+const char* const GUIPalette::StaffConnectingTerminatingLine = "staffconnectingterminatingline";
+
+const char* const GUIPalette::Pointer = "pointer";
+const char* const GUIPalette::PointerRuler = "pointerruler";
+
+const char* const GUIPalette::InsertCursor = "insertcursor";
+const char* const GUIPalette::InsertCursorRuler = "insertcursorruler";
+
+const char* const GUIPalette::TrackDivider = "trackdivider";
+const char* const GUIPalette::MovementGuide = "movementguide";
+const char* const GUIPalette::SelectionRectangle = "selectionrectangle";
+const char* const GUIPalette::SelectedElement = "selectedelement";
+
+const int GUIPalette::SelectedElementHue = 225;
+const int GUIPalette::SelectedElementMinValue = 220;
+const int GUIPalette::HighlightedElementHue = 25;
+const int GUIPalette::HighlightedElementMinValue = 220;
+const int GUIPalette::QuantizedNoteHue = 69;
+const int GUIPalette::QuantizedNoteMinValue = 140;
+const int GUIPalette::TriggerNoteHue = 4;
+const int GUIPalette::TriggerNoteMinValue = 140;
+const int GUIPalette::OutRangeNoteHue = 0;
+const int GUIPalette::OutRangeNoteMinValue = 200;
+
+const int GUIPalette::CollisionHaloHue = 42;
+const int GUIPalette::CollisionHaloSaturation = 200;
+
+const char* const GUIPalette::TextAnnotationBackground = "textannotationbackground";
+
+const char* const GUIPalette::TextLilyPondDirectiveBackground = "textlilyponddirectivebackground";
+
+const char* const GUIPalette::AudioCountdownBackground = "audiocountdownbackground";
+const char* const GUIPalette::AudioCountdownForeground = "audiocountdownforeground";
+
+const char* const GUIPalette::RotaryFloatBackground = "rotaryfloatbackground";
+const char* const GUIPalette::RotaryFloatForeground = "rotaryfloatforeground";
+
+const char* const GUIPalette::RotaryPastelBlue = "rotarypastelblue";
+const char* const GUIPalette::RotaryPastelRed = "rotarypastelred";
+const char* const GUIPalette::RotaryPastelGreen = "rotarypastelgreen";
+const char* const GUIPalette::RotaryPastelOrange = "rotarypastelorange";
+const char* const GUIPalette::RotaryPastelYellow = "rotarypastelyellow";
+
+const char* const GUIPalette::MatrixKeyboardFocus = "matrixkeyboardfocus";
+
+const char* const GUIPalette::RotaryPlugin = "rotaryplugin";
+
+const char* const GUIPalette::RotaryMeter = "rotarymeter";
+
+const char* const GUIPalette::MarkerBackground = "markerbackground";
+
+const char* const GUIPalette::QuickMarker = "quickmarker";
+
+const char* const GUIPalette::MuteTrackLED = "mutetrackled";
+const char* const GUIPalette::RecordMIDITrackLED = "recordmiditrackled";
+const char* const GUIPalette::RecordAudioTrackLED = "recordaudiotrackled";
+
+const char* const GUIPalette::PlaybackFaderOutline = "playbackfaderoutline";
+const char* const GUIPalette::RecordFaderOutline = "recordfaderoutline";
+
+
+GUIPalette* GUIPalette::m_instance = 0;
+
+// defines which index in the document's colourmap should be used as the color
+// when creating new audio segments from recordings, or inserting from the
+// audio file manager (presumes a file derived from the updated autoload.rg
+// that shipped along with this change)
+const int GUIPalette::AudioDefaultIndex = 1;
+
+}
diff --git a/src/gui/general/GUIPalette.h b/src/gui/general/GUIPalette.h
new file mode 100644
index 0000000..c8760fb
--- /dev/null
+++ b/src/gui/general/GUIPalette.h
@@ -0,0 +1,185 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_GUIPALETTE_H_
+#define _RG_GUIPALETTE_H_
+
+#include "base/Colour.h"
+#include <map>
+#include <qcolor.h>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * Definitions of colours to be used throughout the Rosegarden GUI.
+ *
+ * They're in this file not so much to allow them to be easily
+ * changed, as to ensure a certain amount of consistency between
+ * colours for different functions that might end up being seen
+ * at the same time.
+ */
+
+class GUIPalette
+{
+public:
+ static QColor getColour(const char* const colourName);
+
+ static Colour convertColour(const QColor &input);
+ static QColor convertColour(const Colour &input);
+
+ static const char* const ActiveRecordTrack;
+
+ static const char* const SegmentCanvas;
+ static const char* const SegmentBorder;
+
+ static const char* const RecordingInternalSegmentBlock;
+ static const char* const RecordingAudioSegmentBlock;
+ static const char* const RecordingSegmentBorder;
+
+ static const char* const RepeatSegmentBorder;
+
+ static const char* const SegmentAudioPreview;
+ static const char* const SegmentInternalPreview;
+ static const char* const SegmentLabel;
+ static const char* const SegmentSplitLine;
+
+ static const char* const MatrixElementBorder;
+ static const char* const MatrixElementBlock;
+ static const char* const MatrixOverlapBlock;
+
+ static const char* const LoopRulerBackground;
+ static const char* const LoopRulerForeground;
+ static const char* const LoopHighlight;
+
+ static const char* const TempoBase;
+
+ static const char* const TextRulerBackground;
+ static const char* const TextRulerForeground;
+
+ static const char* const ChordNameRulerBackground;
+ static const char* const ChordNameRulerForeground;
+
+ static const char* const RawNoteRulerBackground;
+ static const char* const RawNoteRulerForeground;
+
+ static const char* const LevelMeterGreen;
+ static const char* const LevelMeterOrange;
+ static const char* const LevelMeterRed;
+
+ static const char* const LevelMeterSolidGreen;
+ static const char* const LevelMeterSolidOrange;
+ static const char* const LevelMeterSolidRed;
+
+ static const char* const BarLine;
+ static const char* const BarLineIncorrect;
+ static const char* const BeatLine;
+ static const char* const SubBeatLine;
+ static const char* const StaffConnectingLine;
+ static const char* const StaffConnectingTerminatingLine;
+
+ static const char* const Pointer;
+ static const char* const PointerRuler;
+
+ static const char* const InsertCursor;
+ static const char* const InsertCursorRuler;
+
+ static const char* const TrackDivider;
+ static const char* const MovementGuide;
+ static const char* const SelectionRectangle;
+ static const char* const SelectedElement;
+
+ static const int SelectedElementHue;
+ static const int SelectedElementMinValue;
+ static const int HighlightedElementHue;
+ static const int HighlightedElementMinValue;
+ static const int QuantizedNoteHue;
+ static const int QuantizedNoteMinValue;
+ static const int TriggerNoteHue;
+ static const int TriggerNoteMinValue;
+ static const int OutRangeNoteHue;
+ static const int OutRangeNoteMinValue;
+
+ static const int CollisionHaloHue;
+ static const int CollisionHaloSaturation;
+
+ static const char* const TextAnnotationBackground;
+
+ static const char* const TextLilyPondDirectiveBackground;
+
+ static const char* const AudioCountdownBackground;
+ static const char* const AudioCountdownForeground;
+
+ static const char* const RotaryFloatBackground;
+ static const char* const RotaryFloatForeground;
+
+ static const char* const RotaryPastelBlue;
+ static const char* const RotaryPastelRed;
+ static const char* const RotaryPastelGreen;
+ static const char* const RotaryPastelOrange;
+ static const char* const RotaryPastelYellow;
+
+ static const char* const MatrixKeyboardFocus;
+
+ static const char* const RotaryPlugin;
+
+ static const char* const RotaryMeter;
+
+ static const char* const MarkerBackground;
+
+ static const char* const QuickMarker;
+
+ static const char* const MuteTrackLED;
+ static const char* const RecordMIDITrackLED;
+ static const char* const RecordAudioTrackLED;
+
+ static const char* const PlaybackFaderOutline;
+ static const char* const RecordFaderOutline;
+
+ static const int AudioDefaultIndex;
+
+protected:
+ GUIPalette();
+ QColor getDefaultColour(const char* const colourName);
+
+ //--------------- Data members ---------------------------------
+ static GUIPalette* getInstance();
+ static GUIPalette* m_instance;
+
+ typedef std::map<const char* const, QColor> colourmap;
+
+ colourmap m_defaultsMap;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/general/HZoomable.cpp b/src/gui/general/HZoomable.cpp
new file mode 100644
index 0000000..ff81f6c
--- /dev/null
+++ b/src/gui/general/HZoomable.cpp
@@ -0,0 +1,32 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "HZoomable.h"
+
+
+
+namespace Rosegarden
+{
+}
diff --git a/src/gui/general/HZoomable.h b/src/gui/general/HZoomable.h
new file mode 100644
index 0000000..82e9aa9
--- /dev/null
+++ b/src/gui/general/HZoomable.h
@@ -0,0 +1,53 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_HZOOMABLE_H_
+#define _RG_HZOOMABLE_H_
+
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+class HZoomable
+{
+public:
+ HZoomable() : m_hScaleFactor(1.0) {}
+
+ void setHScaleFactor(double dy) { m_hScaleFactor = dy; }
+ double getHScaleFactor() const { return m_hScaleFactor; }
+
+protected:
+ double m_hScaleFactor;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/general/LinedStaff.cpp b/src/gui/general/LinedStaff.cpp
new file mode 100644
index 0000000..e2e5d12
--- /dev/null
+++ b/src/gui/general/LinedStaff.cpp
@@ -0,0 +1,1217 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "LinedStaff.h"
+
+#include "misc/Debug.h"
+#include "base/Event.h"
+#include "base/LayoutEngine.h"
+#include "base/NotationTypes.h"
+#include "base/Profiler.h"
+#include "base/Segment.h"
+#include "base/SnapGrid.h"
+#include "base/Staff.h"
+#include "base/ViewElement.h"
+#include "GUIPalette.h"
+#include "BarLine.h"
+#include <qcanvas.h>
+#include <qcolor.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qpen.h>
+#include <qrect.h>
+#include <qstring.h>
+#include <algorithm>
+
+
+namespace Rosegarden
+{
+
+// width of pointer
+//
+const int pointerWidth = 3;
+
+
+LinedStaff::LinedStaff(QCanvas *canvas, Segment *segment,
+ SnapGrid *snapGrid, int id,
+ int resolution, int lineThickness) :
+ Staff(*segment),
+ m_canvas(canvas),
+ m_snapGrid(snapGrid),
+ m_id(id),
+ m_x(0.0),
+ m_y(0),
+ m_margin(0.0),
+ m_titleHeight(0),
+ m_resolution(resolution),
+ m_lineThickness(lineThickness),
+ m_pageMode(LinearMode),
+ m_pageWidth(2000.0), // fairly arbitrary, but we need something non-zero
+ m_rowsPerPage(0),
+ m_rowSpacing(0),
+ m_connectingLineLength(0),
+ m_startLayoutX(0),
+ m_endLayoutX(0),
+ m_current(false),
+ m_pointer(new QCanvasLine(canvas)),
+ m_insertCursor(new QCanvasLine(canvas)),
+ m_insertCursorTime(segment->getStartTime()),
+ m_insertCursorTimeValid(false)
+{
+ initCursors();
+}
+
+LinedStaff::LinedStaff(QCanvas *canvas, Segment *segment,
+ SnapGrid *snapGrid,
+ int id, int resolution, int lineThickness,
+ double pageWidth, int rowsPerPage, int rowSpacing) :
+ Staff(*segment),
+ m_canvas(canvas),
+ m_snapGrid(snapGrid),
+ m_id(id),
+ m_x(0.0),
+ m_y(0),
+ m_margin(0.0),
+ m_titleHeight(0),
+ m_resolution(resolution),
+ m_lineThickness(lineThickness),
+ m_pageMode(rowsPerPage ? MultiPageMode : ContinuousPageMode),
+ m_pageWidth(pageWidth),
+ m_rowsPerPage(rowsPerPage),
+ m_rowSpacing(rowSpacing),
+ m_connectingLineLength(0),
+ m_startLayoutX(0),
+ m_endLayoutX(0),
+ m_current(false),
+ m_pointer(new QCanvasLine(canvas)),
+ m_insertCursor(new QCanvasLine(canvas)),
+ m_insertCursorTime(segment->getStartTime()),
+ m_insertCursorTimeValid(false)
+{
+ initCursors();
+}
+
+LinedStaff::LinedStaff(QCanvas *canvas, Segment *segment,
+ SnapGrid *snapGrid,
+ int id, int resolution, int lineThickness,
+ PageMode pageMode, double pageWidth, int rowsPerPage,
+ int rowSpacing) :
+ Staff(*segment),
+ m_canvas(canvas),
+ m_snapGrid(snapGrid),
+ m_id(id),
+ m_x(0.0),
+ m_y(0),
+ m_margin(0.0),
+ m_titleHeight(0),
+ m_resolution(resolution),
+ m_lineThickness(lineThickness),
+ m_pageMode(pageMode),
+ m_pageWidth(pageWidth),
+ m_rowsPerPage(rowsPerPage),
+ m_rowSpacing(rowSpacing),
+ m_connectingLineLength(0),
+ m_startLayoutX(0),
+ m_endLayoutX(0),
+ m_current(false),
+ m_pointer(new QCanvasLine(canvas)),
+ m_insertCursor(new QCanvasLine(canvas)),
+ m_insertCursorTime(segment->getStartTime()),
+ m_insertCursorTimeValid(false)
+{
+ initCursors();
+}
+
+LinedStaff::~LinedStaff()
+{
+ /*!!! No, the canvas items are all deleted by the canvas on destruction.
+
+ deleteBars();
+ for (int i = 0; i < (int)m_staffLines.size(); ++i) clearStaffLineRow(i);
+ */
+}
+
+void
+LinedStaff::initCursors()
+{
+ QPen pen(GUIPalette::getColour(GUIPalette::Pointer));
+ pen.setWidth(pointerWidth);
+
+ m_pointer->setPen(pen);
+ m_pointer->setBrush(GUIPalette::getColour(GUIPalette::Pointer));
+
+ pen.setColor(GUIPalette::getColour(GUIPalette::InsertCursor));
+
+ m_insertCursor->setPen(pen);
+ m_insertCursor->setBrush(GUIPalette::getColour(GUIPalette::InsertCursor));
+}
+
+void
+LinedStaff::setResolution(int resolution)
+{
+ m_resolution = resolution;
+}
+
+void
+LinedStaff::setLineThickness(int lineThickness)
+{
+ m_lineThickness = lineThickness;
+}
+
+void
+LinedStaff::setPageMode(PageMode pageMode)
+{
+ m_pageMode = pageMode;
+}
+
+void
+LinedStaff::setPageWidth(double pageWidth)
+{
+ m_pageWidth = pageWidth;
+}
+
+void
+LinedStaff::setRowsPerPage(int rowsPerPage)
+{
+ m_rowsPerPage = rowsPerPage;
+}
+
+void
+LinedStaff::setRowSpacing(int rowSpacing)
+{
+ m_rowSpacing = rowSpacing;
+}
+
+void
+LinedStaff::setConnectingLineLength(int connectingLineLength)
+{
+ m_connectingLineLength = connectingLineLength;
+}
+
+int
+LinedStaff::getId() const
+{
+ return m_id;
+}
+
+void
+LinedStaff::setX(double x)
+{
+ m_x = x;
+}
+
+double
+LinedStaff::getX() const
+{
+ return m_x;
+}
+
+void
+LinedStaff::setY(int y)
+{
+ m_y = y;
+}
+
+int
+LinedStaff::getY() const
+{
+ return m_y;
+}
+
+void
+LinedStaff::setMargin(double margin)
+{
+ m_margin = margin;
+}
+
+double
+LinedStaff::getMargin() const
+{
+ if (m_pageMode != MultiPageMode)
+ return 0;
+ return m_margin;
+}
+
+void
+LinedStaff::setTitleHeight(int titleHeight)
+{
+ m_titleHeight = titleHeight;
+}
+
+int
+LinedStaff::getTitleHeight() const
+{
+ return m_titleHeight;
+}
+
+double
+LinedStaff::getTotalWidth() const
+{
+ switch (m_pageMode) {
+
+ case ContinuousPageMode:
+ return getCanvasXForRightOfRow(getRowForLayoutX(m_endLayoutX)) - m_x;
+
+ case MultiPageMode:
+ return getCanvasXForRightOfRow(getRowForLayoutX(m_endLayoutX)) + m_margin - m_x;
+
+ case LinearMode:
+ default:
+ return getCanvasXForLayoutX(m_endLayoutX) - m_x;
+ }
+}
+
+int
+LinedStaff::getTotalHeight() const
+{
+ switch (m_pageMode) {
+
+ case ContinuousPageMode:
+ return getCanvasYForTopOfStaff(getRowForLayoutX(m_endLayoutX)) +
+ getHeightOfRow() - m_y;
+
+ case MultiPageMode:
+ return getCanvasYForTopOfStaff(m_rowsPerPage - 1) +
+ getHeightOfRow() - m_y;
+
+ case LinearMode:
+ default:
+ return getCanvasYForTopOfStaff(0) + getHeightOfRow() - m_y;
+ }
+}
+
+int
+LinedStaff::getHeightOfRow() const
+{
+ return getTopLineOffset() + getLegerLineCount() * getLineSpacing()
+ + getBarLineHeight() + m_lineThickness;
+}
+
+bool
+LinedStaff::containsCanvasCoords(double x, int y) const
+{
+ switch (m_pageMode) {
+
+ case ContinuousPageMode:
+
+ for (int row = getRowForLayoutX(m_startLayoutX);
+ row <= getRowForLayoutX(m_endLayoutX); ++row) {
+ if (y >= getCanvasYForTopOfStaff(row) &&
+ y < getCanvasYForTopOfStaff(row) + getHeightOfRow()) {
+ return true;
+ }
+ }
+
+ return false;
+
+ case MultiPageMode:
+
+ for (int row = getRowForLayoutX(m_startLayoutX);
+ row <= getRowForLayoutX(m_endLayoutX); ++row) {
+ if (y >= getCanvasYForTopOfStaff(row) &&
+ y < getCanvasYForTopOfStaff(row) + getHeightOfRow() &&
+ x >= getCanvasXForLeftOfRow(row) &&
+ x <= getCanvasXForRightOfRow(row)) {
+ return true;
+ }
+ }
+
+ return false;
+
+ case LinearMode:
+ default:
+
+ return (y >= getCanvasYForTopOfStaff() &&
+ y < getCanvasYForTopOfStaff() + getHeightOfRow());
+ }
+}
+
+int
+LinedStaff::getCanvasYForHeight(int h, double baseX, int baseY) const
+{
+ int y;
+
+ // NOTATION_DEBUG << "LinedStaff::getCanvasYForHeight(" << h << "," << baseY
+ // << ")" << endl;
+
+ if (baseX < 0)
+ baseX = getX() + getMargin();
+
+ if (baseY >= 0) {
+ y = getCanvasYForTopLine(getRowForCanvasCoords(baseX, baseY));
+ } else {
+ y = getCanvasYForTopLine();
+ }
+
+ y += getLayoutYForHeight(h);
+
+ return y;
+}
+
+int
+LinedStaff::getLayoutYForHeight(int h) const
+{
+ int y = ((getTopLineHeight() - h) * getLineSpacing()) / getHeightPerLine();
+ if (h < getTopLineHeight() && (h % getHeightPerLine() != 0))
+ ++y;
+
+ return y;
+}
+
+int
+LinedStaff::getHeightAtCanvasCoords(double x, int y) const
+{
+ //!!! the lazy route: approximate, then get the right value
+ // by calling getCanvasYForHeight a few times... ugh
+
+ // RG_DEBUG << "\nNotationStaff::heightOfYCoord: y = " << y
+ // << ", getTopLineOffset() = " << getTopLineOffset()
+ // << ", getLineSpacing() = " << m_npf->getLineSpacing()
+ // << endl;
+
+ if (x < 0)
+ x = getX() + getMargin();
+
+ int row = getRowForCanvasCoords(x, y);
+ int ph = (y - getCanvasYForTopLine(row)) * getHeightPerLine() /
+ getLineSpacing();
+ ph = getTopLineHeight() - ph;
+
+ int i;
+ int mi = -2;
+ int md = getLineSpacing() * 2;
+
+ int testi = -2;
+ int testMd = 1000;
+
+ for (i = -1; i <= 1; ++i) {
+ int d = y - getCanvasYForHeight(ph + i, x, y);
+ if (d < 0)
+ d = -d;
+ if (d < md) {
+ md = d;
+ mi = i;
+ }
+ if (d < testMd) {
+ testMd = d;
+ testi = i;
+ }
+ }
+
+ if (mi > -2) {
+ // RG_DEBUG << "LinedStaff::getHeightAtCanvasCoords: " << y
+ // << " -> " << (ph + mi) << " (mi is " << mi << ", distance "
+ // << md << ")" << endl;
+ // if (mi == 0) {
+ // RG_DEBUG << "GOOD APPROXIMATION" << endl;
+ // } else {
+ // RG_DEBUG << "BAD APPROXIMATION" << endl;
+ // }
+ return ph + mi;
+ } else {
+ RG_DEBUG << "LinedStaff::getHeightAtCanvasCoords: heuristic got " << ph << ", nothing within range (closest was " << (ph + testi) << " which is " << testMd << " away)" << endl;
+ return 0;
+ }
+}
+
+QRect
+LinedStaff::getBarExtents(double x, int y) const
+{
+ int row = getRowForCanvasCoords(x, y);
+
+ for (int i = 1; i < m_barLines.size(); ++i) {
+
+ double layoutX = m_barLines[i]->getLayoutX();
+ int barRow = getRowForLayoutX(layoutX);
+
+ if (m_pageMode != LinearMode && (barRow < row))
+ continue;
+
+ BarLine *line = m_barLines[i];
+
+ if (line) {
+ if (line->x() <= x)
+ continue;
+
+ return QRect(int(m_barLines[i -1]->x()),
+ getCanvasYForTopOfStaff(barRow),
+ int(line->x() - m_barLines[i - 1]->x()),
+ getHeightOfRow());
+ }
+ }
+
+ // failure
+ return QRect(int(getX() + getMargin()), getCanvasYForTopOfStaff(), 4, getHeightOfRow());
+}
+
+double
+LinedStaff::getCanvasXForLayoutX(double x) const
+{
+ switch (m_pageMode) {
+
+ case ContinuousPageMode:
+ return m_x + x - (m_pageWidth * getRowForLayoutX(x));
+
+ case MultiPageMode: {
+ int pageNo = getRowForLayoutX(x) / getRowsPerPage();
+ double cx = m_x + x - (m_pageWidth * getRowForLayoutX(x));
+ cx += m_margin + (m_margin * 2 + m_pageWidth) * pageNo;
+ return cx;
+ }
+
+ case LinearMode:
+ default:
+ return m_x + x;
+ }
+}
+
+LinedStaff::LinedStaffCoords
+
+LinedStaff::getLayoutCoordsForCanvasCoords(double x, int y) const
+{
+ int row = getRowForCanvasCoords(x, y);
+ return LinedStaffCoords
+ ((row * m_pageWidth) + x - getCanvasXForLeftOfRow(row),
+ y - getCanvasYForTopOfStaff(row));
+}
+
+LinedStaff::LinedStaffCoords
+
+LinedStaff::getCanvasCoordsForLayoutCoords(double x, int y) const
+{
+ int row = getRowForLayoutX(x);
+ return LinedStaffCoords
+ (getCanvasXForLayoutX(x), getCanvasYForTopLine(row) + y);
+}
+
+int
+LinedStaff::getRowForCanvasCoords(double x, int y) const
+{
+ switch (m_pageMode) {
+
+ case ContinuousPageMode:
+ return ((y - m_y) / m_rowSpacing);
+
+ case MultiPageMode: {
+ int px = int(x - m_x - m_margin);
+ int pw = int(m_margin * 2 + m_pageWidth);
+ if (px < pw)
+ y -= m_titleHeight;
+ return (getRowsPerPage() * (px / pw)) + ((y - m_y) / m_rowSpacing);
+ }
+
+ case LinearMode:
+ default:
+ return (int)((x - m_x) / m_pageWidth);
+ }
+}
+
+int
+LinedStaff::getCanvasYForTopOfStaff(int row) const
+{
+ switch (m_pageMode) {
+
+ case ContinuousPageMode:
+ if (row <= 0)
+ return m_y;
+ else
+ return m_y + (row * m_rowSpacing);
+
+ case MultiPageMode:
+ if (row <= 0)
+ return m_y + m_titleHeight;
+ else if (row < getRowsPerPage())
+ return m_y + ((row % getRowsPerPage()) * m_rowSpacing) + m_titleHeight;
+ else
+ return m_y + ((row % getRowsPerPage()) * m_rowSpacing);
+
+ case LinearMode:
+ default:
+ return m_y;
+ }
+}
+
+double
+LinedStaff::getCanvasXForLeftOfRow(int row) const
+{
+ switch (m_pageMode) {
+
+ case ContinuousPageMode:
+ return m_x;
+
+ case MultiPageMode:
+ return m_x + m_margin +
+ (m_margin*2 + m_pageWidth) * (row / getRowsPerPage());
+
+ case LinearMode:
+ default:
+ return m_x + (row * m_pageWidth);
+ }
+}
+
+void
+LinedStaff::sizeStaff(HorizontalLayoutEngine &layout)
+{
+ Profiler profiler("LinedStaff::sizeStaff", true);
+
+ deleteBars();
+ deleteRepeatedClefsAndKeys();
+ deleteTimeSignatures();
+
+ // RG_DEBUG << "LinedStaff::sizeStaff" << endl;
+
+ int lastBar = layout.getLastVisibleBarOnStaff(*this);
+
+ double xleft = 0, xright = 0;
+ bool haveXLeft = false;
+
+ xright = layout.getBarPosition(lastBar) - 1;
+
+ TimeSignature currentTimeSignature;
+
+ for (int barNo = layout.getFirstVisibleBarOnStaff(*this);
+ barNo <= lastBar; ++barNo) {
+
+ double x = layout.getBarPosition(barNo);
+
+ if (!haveXLeft) {
+ xleft = x;
+ haveXLeft = true;
+ }
+
+ double timeSigX = 0;
+ TimeSignature timeSig;
+ bool isNew = layout.getTimeSignaturePosition(*this, barNo, timeSig, timeSigX);
+
+ if (isNew && barNo < lastBar) {
+ currentTimeSignature = timeSig;
+ insertTimeSignature(timeSigX, currentTimeSignature);
+ RG_DEBUG << "LinedStaff[" << this << "]::sizeStaff: bar no " << barNo << " has time signature at " << timeSigX << endl;
+ }
+
+ RG_DEBUG << "LinedStaff::sizeStaff: inserting bar at " << x << " on staff " << this << " (isNew " << isNew << ", timeSigX " << timeSigX << ")" << endl;
+
+ bool showBarNo =
+ (showBarNumbersEvery() > 0 &&
+ ((barNo + 1) % showBarNumbersEvery()) == 0);
+
+ insertBar(x,
+ ((barNo == lastBar) ? 0 :
+ (layout.getBarPosition(barNo + 1) - x)),
+ layout.isBarCorrectOnStaff(*this, barNo - 1),
+ currentTimeSignature,
+ barNo,
+ showBarNo);
+ }
+
+ m_startLayoutX = xleft;
+ m_endLayoutX = xright;
+
+ drawStaffName();
+ resizeStaffLines();
+}
+
+void
+LinedStaff::deleteBars()
+{
+ for (BarLineList::iterator i = m_barLines.begin();
+ i != m_barLines.end(); ++i) {
+ (*i)->hide();
+ delete *i;
+ }
+
+ for (LineRecList::iterator i = m_beatLines.begin();
+ i != m_beatLines.end(); ++i) {
+ i->second->hide();
+ delete i->second;
+ }
+
+ for (LineRecList::iterator i = m_barConnectingLines.begin();
+ i != m_barConnectingLines.end(); ++i) {
+ i->second->hide();
+ delete i->second;
+ }
+
+ for (ItemList::iterator i = m_barNumbers.begin();
+ i != m_barNumbers.end(); ++i) {
+ (*i)->hide();
+ delete *i;
+ }
+
+ m_barLines.clear();
+ m_beatLines.clear();
+ m_barConnectingLines.clear();
+ m_barNumbers.clear();
+}
+
+void
+LinedStaff::insertBar(double layoutX, double width, bool isCorrect,
+ const TimeSignature &timeSig,
+ int barNo, bool showBarNo)
+{
+ // RG_DEBUG << "insertBar: " << layoutX << ", " << width
+ // << ", " << isCorrect << endl;
+
+ int barThickness = m_lineThickness * 5 / 4;
+
+ // hack to ensure the bar line appears on the correct row in
+ // notation page layouts, with a conditional to prevent us from
+ // moving the bar and beat lines in the matrix
+ if (!showBeatLines()) {
+ if (width > 0.01) { // not final bar in staff
+ layoutX += 1;
+ } else {
+ layoutX -= 1;
+ }
+ }
+
+ int row = getRowForLayoutX(layoutX);
+ double x = getCanvasXForLayoutX(layoutX);
+ int y = getCanvasYForTopLine(row);
+
+ bool firstBarInRow = false, lastBarInRow = false;
+
+ if (m_pageMode != LinearMode &&
+ (getRowForLayoutX(layoutX) >
+ getRowForLayoutX(layoutX - getMargin() - 2)))
+ firstBarInRow = true;
+
+ if (m_pageMode != LinearMode &&
+ width > 0.01 && // width == 0 for final bar in staff
+ (getRowForLayoutX(layoutX) <
+ getRowForLayoutX(layoutX + width + getMargin() + 2)))
+ lastBarInRow = true;
+
+ BarStyle style = getBarStyle(barNo);
+
+ if (style == RepeatBothBar && firstBarInRow)
+ style = RepeatStartBar;
+
+ if (firstBarInRow)
+ insertRepeatedClefAndKey(layoutX, barNo);
+
+ // If we're supposed to be hiding bar lines, we do just that --
+ // create them as normal, then hide them. We can't simply not
+ // create them because we rely on this to find bar extents for
+ // things like double-click selection in notation.
+ bool hidden = false;
+ if (style == PlainBar && timeSig.hasHiddenBars())
+ hidden = true;
+
+ double inset = 0.0;
+ if (style == RepeatStartBar || style == RepeatBothBar) {
+ inset = getBarInset(barNo, firstBarInRow);
+ }
+
+ BarLine *line = new BarLine(m_canvas, layoutX,
+ getBarLineHeight(), barThickness, getLineSpacing(),
+ (int)inset, style);
+
+ line->moveBy(x, y);
+
+ if (isCorrect) {
+ line->setPen(GUIPalette::getColour(GUIPalette::BarLine));
+ line->setBrush(GUIPalette::getColour(GUIPalette::BarLine));
+ } else {
+ line->setPen(GUIPalette::getColour(GUIPalette::BarLineIncorrect));
+ line->setBrush(GUIPalette::getColour(GUIPalette::BarLineIncorrect));
+ }
+
+ line->setZ( -1);
+ if (hidden)
+ line->hide();
+ else
+ line->show();
+
+ // The bar lines have to be in order of layout-x (there's no
+ // such interesting stipulation for beat or connecting lines)
+ BarLineList::iterator insertPoint = lower_bound
+ (m_barLines.begin(), m_barLines.end(), line, compareBars);
+ m_barLines.insert(insertPoint, line);
+
+ if (lastBarInRow) {
+
+ double xe = x + width - barThickness;
+ style = getBarStyle(barNo + 1);
+ if (style == RepeatBothBar)
+ style = RepeatEndBar;
+
+ BarLine *eline = new BarLine(m_canvas, layoutX,
+ getBarLineHeight(), barThickness, getLineSpacing(),
+ 0, style);
+ eline->moveBy(xe, y);
+
+ eline->setPen(GUIPalette::getColour(GUIPalette::BarLine));
+ eline->setBrush(GUIPalette::getColour(GUIPalette::BarLine));
+
+ eline->setZ( -1);
+ if (hidden)
+ eline->hide();
+ else
+ eline->show();
+
+ BarLineList::iterator insertPoint = lower_bound
+ (m_barLines.begin(), m_barLines.end(), eline, compareBars);
+ m_barLines.insert(insertPoint, eline);
+ }
+
+ if (showBarNo) {
+
+ QFont font;
+ font.setPixelSize(m_resolution * 3 / 2);
+ QFontMetrics metrics(font);
+ QString text = QString("%1").arg(barNo + 1);
+
+ QCanvasItem *barNoText = new QCanvasText(text, font, m_canvas);
+ barNoText->setX(x);
+ barNoText->setY(y - metrics.height() - m_resolution * 2);
+ barNoText->setZ( -1);
+ if (hidden)
+ barNoText->hide();
+ else
+ barNoText->show();
+
+ m_barNumbers.push_back(barNoText);
+ }
+
+ QCanvasRectangle *rect = 0;
+
+ if (showBeatLines()) {
+
+ double gridLines; // number of grid lines per bar may be fractional
+
+ // If the snap time is zero we default to beat markers
+ //
+ if (m_snapGrid && m_snapGrid->getSnapTime(x))
+ gridLines = double(timeSig.getBarDuration()) /
+ double(m_snapGrid->getSnapTime(x));
+ else
+ gridLines = timeSig.getBeatsPerBar();
+
+ double dx = width / gridLines;
+
+ for (int gridLine = hidden ? 0 : 1; gridLine < gridLines; ++gridLine) {
+
+ rect = new QCanvasRectangle
+ (0, 0, barThickness, getBarLineHeight(), m_canvas);
+
+ rect->moveBy(x + gridLine * dx, y);
+
+ double currentGrid = gridLines / double(timeSig.getBeatsPerBar());
+
+ rect->setPen(GUIPalette::getColour(GUIPalette::BeatLine));
+ rect->setBrush(GUIPalette::getColour(GUIPalette::BeatLine));
+
+ // Reset to SubBeatLine colour if we're not a beat line - avoid div by zero!
+ //
+ if (currentGrid > 1.0 && double(gridLine) / currentGrid != gridLine / int(currentGrid)) {
+ rect->setPen(GUIPalette::getColour(GUIPalette::SubBeatLine));
+ rect->setBrush(GUIPalette::getColour(GUIPalette::SubBeatLine));
+ }
+
+ rect->setZ( -1);
+ rect->show(); // show beat lines even if the bar lines are hidden
+
+ LineRec beatLine(layoutX + gridLine * dx, rect);
+ m_beatLines.push_back(beatLine);
+ }
+ }
+
+ if (m_connectingLineLength > 0) {
+
+ rect = new QCanvasRectangle
+ (0, 0, barThickness, m_connectingLineLength, m_canvas);
+
+ rect->moveBy(x, y);
+
+ rect->setPen(GUIPalette::getColour(GUIPalette::StaffConnectingLine));
+ rect->setBrush(GUIPalette::getColour(GUIPalette::StaffConnectingLine));
+ rect->setZ( -3);
+ if (hidden)
+ rect->hide();
+ else
+ rect->show();
+
+ LineRec connectingLine(layoutX, rect);
+ m_barConnectingLines.push_back(connectingLine);
+ }
+}
+
+bool
+LinedStaff::compareBars(const BarLine *barLine1, const BarLine *barLine2)
+{
+ return (barLine1->getLayoutX() < barLine2->getLayoutX());
+}
+
+bool
+LinedStaff::compareBarToLayoutX(const BarLine *barLine1, int x)
+{
+ return (barLine1->getLayoutX() < x);
+}
+
+void
+LinedStaff::deleteTimeSignatures()
+{
+ // default implementation is empty
+}
+
+void
+LinedStaff::insertTimeSignature(double, const TimeSignature &)
+{
+ // default implementation is empty
+}
+
+void
+LinedStaff::deleteRepeatedClefsAndKeys()
+{
+ // default implementation is empty
+}
+
+void
+LinedStaff::insertRepeatedClefAndKey(double, int)
+{
+ // default implementation is empty
+}
+
+void
+LinedStaff::drawStaffName()
+{
+ // default implementation is empty
+}
+
+void
+LinedStaff::resizeStaffLines()
+{
+ int firstRow = getRowForLayoutX(m_startLayoutX);
+ int lastRow = getRowForLayoutX(m_endLayoutX);
+
+ RG_DEBUG << "LinedStaff::resizeStaffLines: firstRow "
+ << firstRow << ", lastRow " << lastRow
+ << " (startLayoutX " << m_startLayoutX
+ << ", endLayoutX " << m_endLayoutX << ")" << endl;
+
+ assert(lastRow >= firstRow);
+
+ int i;
+ while ((int)m_staffLines.size() <= lastRow) {
+ m_staffLines.push_back(ItemList());
+ m_staffConnectingLines.push_back(0);
+ }
+
+ // Remove all the staff lines that precede the start of the staff
+
+ for (i = 0; i < firstRow; ++i)
+ clearStaffLineRow(i);
+
+ // now i == firstRow
+
+ while (i <= lastRow) {
+
+ double x0;
+ double x1;
+
+ if (i == firstRow) {
+ x0 = getCanvasXForLayoutX(m_startLayoutX);
+ } else {
+ x0 = getCanvasXForLeftOfRow(i);
+ }
+
+ if (i == lastRow) {
+ x1 = getCanvasXForLayoutX(m_endLayoutX);
+ } else {
+ x1 = getCanvasXForRightOfRow(i);
+ }
+
+ resizeStaffLineRow(i, x0, x1 - x0);
+
+ ++i;
+ }
+
+ // now i == lastRow + 1
+
+ while (i < (int)m_staffLines.size())
+ clearStaffLineRow(i++);
+}
+
+void
+LinedStaff::clearStaffLineRow(int row)
+{
+ for (int h = 0; h < (int)m_staffLines[row].size(); ++h) {
+ delete m_staffLines[row][h];
+ }
+ m_staffLines[row].clear();
+
+ delete m_staffConnectingLines[row];
+ m_staffConnectingLines[row] = 0;
+}
+
+void
+LinedStaff::resizeStaffLineRow(int row, double x, double length)
+{
+ // RG_DEBUG << "LinedStaff::resizeStaffLineRow: row "
+ // << row << ", x " << x << ", length "
+ // << length << endl;
+
+
+ // If the resolution is 8 or less, we want to reduce the blackness
+ // of the staff lines somewhat to make them less intrusive
+
+ int level = 0;
+ int z = 2;
+ if (m_resolution < 6) {
+ z = -1;
+ level = (9 - m_resolution) * 32;
+ if (level > 200)
+ level = 200;
+ }
+
+ QColor lineColour(level, level, level);
+
+ int h;
+
+ /*!!! No longer really good enough. But we could potentially use the
+ bar positions to sort this out
+
+ if (m_pageMode && row > 0 && offset == 0.0) {
+ offset = (double)m_npf->getBarMargin() / 2;
+ length -= offset;
+ }
+ */
+
+ int y;
+
+ delete m_staffConnectingLines[row];
+
+ if (m_pageMode != LinearMode && m_connectingLineLength > 0.1) {
+
+ // rather arbitrary (dup in insertBar)
+ int barThickness = m_resolution / 12 + 1;
+ y = getCanvasYForTopLine(row);
+ QCanvasRectangle *line = new QCanvasRectangle
+ (int(x + length), y, barThickness, m_connectingLineLength, m_canvas);
+ line->setPen(GUIPalette::getColour(GUIPalette::StaffConnectingTerminatingLine));
+ line->setBrush(GUIPalette::getColour(GUIPalette::StaffConnectingTerminatingLine));
+ line->setZ( -2);
+ line->show();
+ m_staffConnectingLines[row] = line;
+
+ } else {
+ m_staffConnectingLines[row] = 0;
+ }
+
+ while ((int)m_staffLines[row].size() <= getLineCount() * m_lineThickness) {
+ m_staffLines[row].push_back(0);
+ }
+
+ int lineIndex = 0;
+
+ for (h = 0; h < getLineCount(); ++h) {
+
+ y = getCanvasYForHeight
+ (getBottomLineHeight() + getHeightPerLine() * h,
+ x, getCanvasYForTopLine(row));
+
+ if (elementsInSpaces()) {
+ y -= getLineSpacing() / 2 + 1;
+ }
+
+ // RG_DEBUG << "LinedStaff: drawing line from ("
+ // << x << "," << y << ") to (" << (x+length-1)
+ // << "," << y << ")" << endl;
+
+ QCanvasItem *line;
+ delete m_staffLines[row][lineIndex];
+ m_staffLines[row][lineIndex] = 0;
+
+ if (m_lineThickness > 1) {
+ QCanvasRectangle *rline = new QCanvasRectangle
+ (int(x), y, int(length), m_lineThickness, m_canvas);
+ rline->setPen(lineColour);
+ rline->setBrush(lineColour);
+ line = rline;
+ } else {
+ QCanvasLine *lline = new QCanvasLine(m_canvas);
+ lline->setPoints(int(x), y, int(x + length), y);
+ lline->setPen(lineColour);
+ line = lline;
+ }
+
+ // if (j > 0) line->setSignificant(false);
+
+ line->setZ(z);
+ m_staffLines[row][lineIndex] = line;
+ line->show();
+
+ ++lineIndex;
+ }
+
+ while (lineIndex < (int)m_staffLines[row].size()) {
+ delete m_staffLines[row][lineIndex];
+ m_staffLines[row][lineIndex] = 0;
+ ++lineIndex;
+ }
+}
+
+void
+LinedStaff::setCurrent(bool current)
+{
+ m_current = current;
+ if (m_current) {
+ m_insertCursor->show();
+ } else {
+ m_insertCursor->hide();
+ }
+}
+
+double
+LinedStaff::getLayoutXOfPointer() const
+{
+ double x = m_pointer->x();
+ int row = getRowForCanvasCoords(x, int(m_pointer->y()));
+ return getLayoutCoordsForCanvasCoords(x, getCanvasYForTopLine(row)).first;
+}
+
+void
+LinedStaff::getPointerPosition(double &cx, int &cy) const
+{
+ cx = m_pointer->x();
+ cy = getCanvasYForTopOfStaff(getRowForCanvasCoords(cx, int(m_pointer->y())));
+}
+
+double
+LinedStaff::getLayoutXOfInsertCursor() const
+{
+ if (!m_current) return -1;
+ double x = m_insertCursor->x();
+ int row = getRowForCanvasCoords(x, int(m_insertCursor->y()));
+ return getLayoutCoordsForCanvasCoords(x, getCanvasYForTopLine(row)).first;
+}
+
+timeT
+LinedStaff::getInsertCursorTime(HorizontalLayoutEngine &layout) const
+{
+ if (m_insertCursorTimeValid) return m_insertCursorTime;
+ return layout.getTimeForX(getLayoutXOfInsertCursor());
+}
+
+void
+LinedStaff::getInsertCursorPosition(double &cx, int &cy) const
+{
+ if (!m_current) {
+ cx = -1;
+ cy = -1;
+ return ;
+ }
+ cx = m_insertCursor->x();
+ cy = getCanvasYForTopOfStaff(getRowForCanvasCoords(cx, int(m_insertCursor->y())));
+}
+
+void
+LinedStaff::setPointerPosition(double canvasX, int canvasY)
+{
+ int row = getRowForCanvasCoords(canvasX, canvasY);
+ canvasY = getCanvasYForTopOfStaff(row);
+ m_pointer->setX(int(canvasX));
+ m_pointer->setY(int(canvasY));
+ m_pointer->setZ( -30); // behind everything else
+ m_pointer->setPoints(0, 0, 0, getHeightOfRow() /* - 1 */);
+ m_pointer->show();
+}
+
+void
+LinedStaff::setPointerPosition(HorizontalLayoutEngine &layout,
+ timeT time)
+{
+ setPointerPosition(layout.getXForTime(time));
+}
+
+void
+LinedStaff::setPointerPosition(double layoutX)
+{
+ LinedStaffCoords coords = getCanvasCoordsForLayoutCoords(layoutX, 0);
+ setPointerPosition(coords.first, coords.second);
+}
+
+void
+LinedStaff::hidePointer()
+{
+ m_pointer->hide();
+}
+
+void
+LinedStaff::setInsertCursorPosition(double canvasX, int canvasY)
+{
+ if (!m_current) return;
+
+ int row = getRowForCanvasCoords(canvasX, canvasY);
+ canvasY = getCanvasYForTopOfStaff(row);
+ m_insertCursor->setX(canvasX);
+ m_insertCursor->setY(canvasY);
+ m_insertCursor->setZ( -28); // behind everything else except playback pointer
+ m_insertCursor->setPoints(0, 0, 0, getHeightOfRow() - 1);
+ m_insertCursor->show();
+ m_insertCursorTimeValid = false;
+}
+
+void
+LinedStaff::setInsertCursorPosition(HorizontalLayoutEngine &layout,
+ timeT time)
+{
+ double x = layout.getXForTime(time);
+ LinedStaffCoords coords = getCanvasCoordsForLayoutCoords(x, 0);
+ setInsertCursorPosition(coords.first, coords.second);
+ m_insertCursorTime = time;
+ m_insertCursorTimeValid = true;
+}
+
+void
+LinedStaff::hideInsertCursor()
+{
+ m_insertCursor->hide();
+}
+
+void
+LinedStaff::renderElements(ViewElementList::iterator,
+ ViewElementList::iterator)
+{
+ // nothing -- we assume rendering will be done by the implementation
+ // of positionElements
+}
+
+void
+LinedStaff::renderAllElements()
+{
+ renderElements(getViewElementList()->begin(),
+ getViewElementList()->end());
+}
+
+void
+LinedStaff::positionAllElements()
+{
+ positionElements(getSegment().getStartTime(),
+ getSegment().getEndTime());
+}
+
+}
diff --git a/src/gui/general/LinedStaff.h b/src/gui/general/LinedStaff.h
new file mode 100644
index 0000000..1444bd2
--- /dev/null
+++ b/src/gui/general/LinedStaff.h
@@ -0,0 +1,759 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_LINEDSTAFF_H_
+#define _RG_LINEDSTAFF_H_
+
+#include "base/Event.h"
+#include "base/FastVector.h"
+#include "base/Staff.h"
+#include "base/ViewElement.h"
+#include <qrect.h>
+#include <utility>
+#include <vector>
+
+
+class QCanvasLine;
+class QCanvasItem;
+class QCanvas;
+class isFirstBarInRow;
+class barNo;
+
+
+namespace Rosegarden
+{
+
+class BarLine;
+class TimeSignature;
+class SnapGrid;
+class Segment;
+class HorizontalLayoutEngine;
+class Event;
+
+
+/**
+ * LinedStaff is a base class for implementations of Staff that
+ * display the contents of a Segment on a set of horizontal lines
+ * with optional vertical bar lines.
+ * Likely subclasses include the notation and piano-roll staffs.
+ *
+ * In general, this class handles x coordinates in floating-point,
+ * but y-coordinates as integers because of the requirement that
+ * staff lines be a precise integral distance apart.
+ */
+
+class LinedStaff : public Staff
+{
+public:
+ typedef std::pair<double, int> LinedStaffCoords;
+
+ enum PageMode {
+ LinearMode = 0,
+ ContinuousPageMode,
+ MultiPageMode
+ };
+
+ enum BarStyle {
+ PlainBar = 0,
+ DoubleBar,
+ HeavyDoubleBar,
+ RepeatEndBar,
+ RepeatStartBar,
+ RepeatBothBar,
+ NoVisibleBar
+ };
+
+protected:
+ /**
+ * Create a new LinedStaff for the given Segment, with a
+ * linear layout.
+ *
+ * \a id is an arbitrary id for the staff in its view,
+ * not used within the LinedStaff implementation but
+ * queryable via getId
+ *
+ * \a resolution is the number of blank pixels between
+ * staff lines
+ *
+ * \a lineThickness is the number of pixels thick a
+ * staff line should be
+ */
+ LinedStaff(QCanvas *, Segment *, SnapGrid *,
+ int id, int resolution, int lineThickness);
+
+ /**
+ * Create a new LinedStaff for the given Segment, with a
+ * page layout.
+ *
+ * \a id is an arbitrary id for the staff in its view,
+ * not used within the LinedStaff implementation but
+ * queryable via getId
+ *
+ * \a resolution is the number of blank pixels between
+ * staff lines
+ *
+ * \a lineThickness is the number of pixels thick a
+ * staff line should be
+ *
+ * \a pageWidth is the width of a page, to determine
+ * when to break lines for page layout
+ *
+ * \a rowsPerPage is the number of rows to a page, or zero
+ * for a single continuous page
+ *
+ * \a rowSpacing is the distance in pixels between
+ * the tops of consecutive rows on this staff
+ */
+ LinedStaff(QCanvas *, Segment *, SnapGrid *,
+ int id, int resolution, int lineThickness,
+ double pageWidth, int rowsPerPage, int rowSpacing);
+
+ /**
+ * Create a new LinedStaff for the given Segment, with
+ * either page or linear layout.
+ */
+ LinedStaff(QCanvas *, Segment *, SnapGrid *,
+ int id, int resolution, int lineThickness, PageMode pageMode,
+ double pageWidth, int rowsPerPage, int rowSpacing);
+
+public:
+ virtual ~LinedStaff();
+
+protected:
+ // Methods required to define the type of staff this is
+
+ /**
+ * Returns the number of visible staff lines
+ */
+ virtual int getLineCount() const = 0;
+
+ /**
+ * Returns the number of invisible staff lines
+ * to leave space for above (and below) the visible staff
+ */
+ virtual int getLegerLineCount() const = 0;
+
+ /**
+ * Returns the height-on-staff value for
+ * the bottom visible staff line (a shorthand means for
+ * referring to staff lines)
+ */
+ virtual int getBottomLineHeight() const = 0;
+
+ /**
+ * Returns the difference between the height-on-
+ * staff value of one visible staff line and the next one
+ * above it
+ */
+ virtual int getHeightPerLine() const = 0;
+
+ /**
+ * Returns the height-on-staff value for the top visible
+ * staff line. This is deliberately not virtual.
+ */
+ int getTopLineHeight() const {
+ return getBottomLineHeight() +
+ (getLineCount() - 1) * getHeightPerLine();
+ }
+
+ /**
+ * Returns true if elements fill the spaces between lines,
+ * false if elements can fall on lines. If true, the lines
+ * will be displaced vertically by half a line spacing.
+ */
+ virtual bool elementsInSpaces() const {
+ return false;
+ }
+
+ /**
+ * Returns true if the staff should draw a faint vertical line at
+ * each beat, in between the (darker) bar lines.
+ */
+ virtual bool showBeatLines() const {
+ return false;
+ }
+
+ /**
+ * Returns the number of bars between bar-line numbers, or zero if
+ * bar lines should not be numbered. For example, if this
+ * function returns 5, every 5th bar (starting at bar 5) will be
+ * numbered.
+ */
+ virtual int showBarNumbersEvery() const {
+ return 0;
+ }
+
+ /**
+ * Returns the bar line / repeat style for the start of the given bar.
+ */
+ virtual BarStyle getBarStyle(int /* barNo */) const {
+ return PlainBar;
+ }
+
+ /**
+ * Returns the distance the opening (repeat) bar is inset from the
+ * nominal barline position. This is to accommodate the situation
+ * where a repeat bar has to appear after the clef and key.
+ */
+ virtual double getBarInset(int /* barNo */, bool /* isFirstBarInRow */) const {
+ return 0;
+ }
+
+protected:
+ /// Subclass may wish to expose this
+ virtual void setResolution(int resolution);
+
+ /// Subclass may wish to expose this
+ virtual void setLineThickness(int lineThickness);
+
+ /// Subclass may wish to expose this
+ virtual void setPageMode(PageMode pageMode);
+
+ /// Subclass may wish to expose this
+ virtual void setPageWidth(double pageWidth);
+
+ /// Subclass may wish to expose this
+ virtual void setRowsPerPage(int rowsPerPage);
+
+ /// Subclass may wish to expose this
+ virtual void setRowSpacing(int rowSpacing);
+
+ /// Subclass may wish to expose this. Default is zero
+ virtual void setConnectingLineLength(int length);
+
+public:
+ /**
+ * Return the id of the staff. This is only useful to external
+ * agents, it isn't used by the LinedStaff itself.
+ */
+ virtual int getId() const;
+
+ /**
+ * Set the canvas x-coordinate of the left-hand end of the staff.
+ * This does not move any canvas items that have already been
+ * created; it should be called before the sizeStaff/positionElements
+ * procedure begins.
+ */
+ virtual void setX(double x);
+
+ /**
+ * Get the canvas x-coordinate of the left-hand end of the staff.
+ */
+ virtual double getX() const;
+
+ /**
+ * Set the canvas y-coordinate of the top of the first staff row.
+ * This does not move any canvas items that have already been
+ * created; it should be called before the sizeStaff/positionElements
+ * procedure begins.
+ */
+ virtual void setY(int y);
+
+ /**
+ * Get the canvas y-coordinate of the top of the first staff row.
+ */
+ virtual int getY() const;
+
+ /**
+ * Set the canvas width of the margin to left and right of the
+ * staff on each page (used only in MultiPageMode). Each staff
+ * row will still be pageWidth wide (that is, the margin is in
+ * addition to the pageWidth, not included in it). This does not
+ * move any canvas items that have already been created; it should
+ * be called before the sizeStaff/positionElements procedure
+ * begins.
+ */
+ virtual void setMargin(double m);
+
+ /**
+ * Get the canvas width of the left and right margins.
+ */
+ virtual double getMargin() const;
+
+ /**
+ * Set the canvas height of the area at the top of the first page
+ * reserved for the composition title and composer's name (used
+ * only in MultiPageMode).
+ */
+ virtual void setTitleHeight(int h);
+
+ /**
+ * Get the canvas height of the title area.
+ */
+ virtual int getTitleHeight() const;
+
+ /**
+ * Returns the width of the entire staff after layout. Call
+ * this only after you've done the full sizeStaff/positionElements
+ * procedure.
+ */
+ virtual double getTotalWidth() const;
+
+ /**
+ * Returns the height of the entire staff after layout. Call
+ * this only after you've done the full sizeStaff/positionElements
+ * procedure. If there are multiple rows, this will be the
+ * height of all rows, including any space between rows that
+ * is used to display other staffs.
+ */
+ virtual int getTotalHeight() const;
+
+ /**
+ * Returns the total number of pages used by the staff.
+ */
+ int getPageCount() const {
+ if (m_pageMode != MultiPageMode) return 1;
+ else return 1 + (getRowForLayoutX(m_endLayoutX) / getRowsPerPage());
+ }
+
+ /**
+ * Returns the difference between the y coordinates of
+ * neighbouring visible staff lines. Deliberately non-virtual
+ */
+ int getLineSpacing() const {
+ return m_resolution + m_lineThickness;
+ }
+
+ /**
+ * Returns the total height of a single staff row, including ruler
+ */
+ virtual int getHeightOfRow() const;
+
+ /**
+ * Returns true if the given canvas coordinates fall within
+ * (any of the rows of) this staff. False if they fall in the
+ * gap between two rows.
+ */
+ virtual bool containsCanvasCoords(double canvasX, int canvasY) const;
+
+ /**
+ * Returns the canvas y coordinate of the specified line on the
+ * staff. baseX/baseY are a canvas coordinates somewhere on the
+ * correct row, or -1 for the default row.
+ */
+ virtual int getCanvasYForHeight(int height, double baseX = -1, int baseY = -1) const;
+
+ /**
+ * Returns the y coordinate of the specified line on the
+ * staff, relative to the top of the row.
+ */
+ virtual int getLayoutYForHeight(int height) const;
+
+ /**
+ * Returns the height-on-staff value nearest to the given
+ * canvas coordinates.
+ */
+ virtual int getHeightAtCanvasCoords(double x, int y) const;
+
+ /**
+ * Return the full width, height and origin of the bar containing
+ * the given canvas cooordinates.
+ */
+ virtual QRect getBarExtents(double x, int y) const;
+
+ /**
+ * Set whether this is the current staff or not. A staff that is
+ * current will differ visually from non-current staffs.
+ *
+ * The owner of the staffs should normally ensure that one staff
+ * is current (the default is non-current, even if there only is
+ * one staff) and that only one staff is current at once.
+ */
+ virtual void setCurrent(bool current);
+
+ /**
+ * Move the playback pointer to the layout-X coordinate
+ * corresponding to the given time, and show it.
+ */
+ virtual void setPointerPosition
+ (HorizontalLayoutEngine&, timeT);
+
+ /**
+ * Move the playback pointer to the layout-X coordinate
+ * corresponding to the given canvas coordinates, and show it.
+ */
+ virtual void setPointerPosition(double x, int y);
+
+ /**
+ * Move the playback pointer to the given layout-X
+ * coordinate, and show it.
+ */
+ virtual void setPointerPosition(double x);
+
+ /**
+ * Returns the layout-X coordinate corresponding to the current
+ * position of the playback pointer.
+ */
+ virtual double getLayoutXOfPointer() const;
+
+ /**
+ * Returns the canvas coordinates of the top of the playback
+ * pointer.
+ */
+ virtual void getPointerPosition(double &x, int &y) const;
+
+ /**
+ * Hide the playback pointer.
+ */
+ virtual void hidePointer();
+
+ /**
+ * Move the insertion cursor to the layout-X coordinate
+ * corresponding to the given time, and show it.
+ */
+ virtual void setInsertCursorPosition(HorizontalLayoutEngine&, timeT);
+
+ /**
+ * Move the insertion cursor to the layout-X coordinate
+ * corresponding to the given canvas coordinates, and show it.
+ */
+ virtual void setInsertCursorPosition(double x, int y);
+
+ /**
+ * Returns the layout-X coordinate corresponding to the current
+ * position of the insertion cursor. Returns -1 if this staff
+ * is not current or there is some other problem.
+ */
+ virtual double getLayoutXOfInsertCursor() const;
+
+ /**
+ * Return the time of the insert cursor.
+ */
+ virtual timeT getInsertCursorTime(HorizontalLayoutEngine&) const;
+
+ /**
+ * Return the canvas coordinates of the top of the insert
+ * cursor.
+ */
+ virtual void getInsertCursorPosition(double &x, int &y) const;
+
+ /**
+ * Hide the insert cursor.
+ */
+ virtual void hideInsertCursor();
+
+ /**
+ * Query the given horizontal layout object (which is assumed to
+ * have just completed its layout procedure) to determine the
+ * required extents of the staff and the positions of the bars,
+ * and create the bars and staff lines accordingly. It may be
+ * called either before or after renderElements and/or
+ * positionElements.
+ *
+ * No bars or staff lines will appear unless this method has
+ * been called.
+ */
+ virtual void sizeStaff(HorizontalLayoutEngine& layout);
+
+ /**
+ * Generate or re-generate sprites for all the elements between
+ * from and to. See subclasses for specific detailed comments.
+ *
+ * A very simplistic staff subclass may choose not to
+ * implement this (the default implementation is empty) and to
+ * do all the rendering work in positionElements. If rendering
+ * elements is slow, however, it makes sense to do it here
+ * because this method may be called less often.
+ */
+ virtual void renderElements(ViewElementList::iterator from,
+ ViewElementList::iterator to);
+
+ /**
+ * Call renderElements(from, to) on the whole staff.
+ */
+ virtual void renderAllElements();
+
+ /**
+ * Assign suitable coordinates to the elements on the staff
+ * between the start and end times, based entirely on the layout
+ * X and Y coordinates they were given by the horizontal and
+ * vertical layout processes.
+ *
+ * The implementation is free to render any elements it
+ * chooses in this method as well.
+ */
+ virtual void positionElements(timeT from,
+ timeT to) = 0;
+
+ /**
+ * Call positionElements(from, to) on the whole staff.
+ */
+ virtual void positionAllElements();
+
+
+ /* Some optional methods for the subclass. */
+
+
+ /**
+ * Return an iterator pointing to the nearest view element to the
+ * given canvas coordinates.
+ *
+ * If notesAndRestsOnly is true, do not return any view element
+ * other than a note or rest.
+ *
+ * If the closest view element is further away than
+ * proximityThreshold pixels in either x or y axis, return end().
+ * If proximityThreshold is less than zero, treat it as infinite.
+ *
+ * Also return the clef and key in force at these coordinates.
+ *
+ * The default implementation should suit for subclasses that only
+ * show a single element per layout X coordinate.
+ */
+ virtual ViewElementList::iterator getClosestElementToCanvasCoords
+ (double x, int y,
+ Event *&clef, Event *&key,
+ bool notesAndRestsOnly = false, int proximityThreshold = 10) {
+ LinedStaffCoords layoutCoords = getLayoutCoordsForCanvasCoords(x, y);
+ return getClosestElementToLayoutX
+ (layoutCoords.first, clef, key,
+ notesAndRestsOnly, proximityThreshold);
+ }
+
+ /**
+ * Return an iterator pointing to the nearest view element to the
+ * given layout x-coordinate.
+ *
+ * If notesAndRestsOnly is true, do not return any view element
+ * other than a note or rest.
+ *
+ * If the closest view element is further away than
+ * proximityThreshold pixels in either x or y axis, return end().
+ * If proximityThreshold is less than zero, treat it as infinite.
+ *
+ * Also return the clef and key in force at these coordinates.
+ *
+ * The subclass may decide whether to implement this method or not
+ * based on the semantics and intended usage of the class.
+ */
+ virtual ViewElementList::iterator getClosestElementToLayoutX
+ (double x,
+ Event *&clef, Event *&key,
+ bool notesAndRestsOnly = false, int proximityThreshold = 10) {
+ return getViewElementList()->end();
+ }
+
+ /**
+ * Return an iterator pointing to the element "under" the given
+ * canvas coordinates.
+ *
+ * Return end() if there is no such element.
+ *
+ * Also return the clef and key in force at these coordinates.
+ *
+ *
+ * The default implementation should suit for subclasses that only
+ * show a single element per layout X coordinate.
+ */
+ virtual ViewElementList::iterator getElementUnderCanvasCoords
+ (double x, int y, Event *&clef, Event *&key) {
+ LinedStaffCoords layoutCoords = getLayoutCoordsForCanvasCoords(x, y);
+ return getElementUnderLayoutX(layoutCoords.first, clef, key);
+ }
+
+ /**
+ * Return an iterator pointing to the element "under" the given
+ * canvas coordinates.
+ *
+ * Return end() if there is no such element.
+ *
+ * Also return the clef and key in force at these coordinates.
+ *
+ * The subclass may decide whether to implement this method or not
+ * based on the semantics and intended usage of the class.
+ */
+ virtual ViewElementList::iterator getElementUnderLayoutX
+ (double x, Event *&clef, Event *&key) {
+ return getViewElementList()->end();
+ }
+
+ // The default implementation of the following is empty. The
+ // subclass is presumed to know what the staff's name is and
+ // where to put it; this is simply called at some point during
+ // the staff-drawing process.
+ virtual void drawStaffName();
+
+
+public:
+ // This should not really be public -- it should be one of the
+ // protected methods below -- but we have some code that needs
+ // it and hasn't been supplied with a proper way to do without.
+ // Please try to avoid calling this method.
+ //!!! fix NotationView::doDeferredCursorMove
+
+ // This should not really be public -- it should be one of the
+ // protected methods below -- but we have some code that needs
+ // it and hasn't been supplied with a proper way to do without.
+ // Please try to avoid calling this method.
+ //!!! fix NotationView::getStaffForCanvasCoords
+ LinedStaffCoords
+ getLayoutCoordsForCanvasCoords(double x, int y) const;
+
+ // This should not really be public -- it should be one of the
+ // protected methods below -- but we have some code that needs
+ // it and hasn't been supplied with a proper way to do without.
+ // Please try to avoid calling this method.
+ //!!! fix NotationView::scrollToTime
+ LinedStaffCoords
+ getCanvasCoordsForLayoutCoords(double x, int y) const;//!!!
+
+ // This should not really be public -- it should be one of the
+ // protected methods below -- but we have some code that needs
+ // it and hasn't been supplied with a proper way to do without.
+ // Please try to avoid calling this method.
+ //!!! fix NotationView::print etc
+ int getRowSpacing() { return m_rowSpacing; }
+
+protected:
+ // Methods that the subclass may (indeed, should) use to convert
+ // between the layout coordinates of elements and their canvas
+ // coordinates. These are deliberately not virtual.
+
+ // Note that even linear-layout staffs have multiple rows; their
+ // rows all have the same y coordinate but increasing x
+ // coordinates, instead of the other way around. (The only reason
+ // for this is that it seems to be more efficient from the QCanvas
+ // perspective to create and manipulate many relatively short
+ // canvas lines rather than a smaller number of very long ones.)
+
+ int getTopLineOffset() const {
+ return getLineSpacing() * getLegerLineCount();
+ }
+
+ int getBarLineHeight() const {
+ return getLineSpacing() * (getLineCount() - 1) + m_lineThickness;
+ }
+
+ int getRowForLayoutX(double x) const {
+ return (int)(x / m_pageWidth);
+ }
+
+ int getRowForCanvasCoords(double x, int y) const;
+
+ int getCanvasYForTopOfStaff(int row = -1) const;
+
+ int getCanvasYForTopLine(int row = -1) const {
+ return getCanvasYForTopOfStaff(row) + getTopLineOffset();
+ }
+
+ double getCanvasXForLeftOfRow(int row) const;
+
+ double getCanvasXForRightOfRow(int row) const {
+ return getCanvasXForLeftOfRow(row) + m_pageWidth;
+ }
+
+ LinedStaffCoords
+ getCanvasOffsetsForLayoutCoords(double x, int y) const {
+ LinedStaffCoords cc = getCanvasCoordsForLayoutCoords(x, y);
+ return LinedStaffCoords(cc.first - x, cc.second - y);
+ }
+
+ double getCanvasXForLayoutX(double x) const;
+
+ int getRowsPerPage() const {
+ return m_rowsPerPage;
+ }
+
+protected:
+ // Actual implementation methods. The default implementation
+ // shows staff lines, connecting lines (where appropriate) and bar
+ // lines, but does not show time signatures. To see time
+ // signatures, override the deleteTimeSignatures and
+ // insertTimeSignature methods. For repeated clefs and keys at
+ // the start of each row, override deleteRepeatedClefsAndKeys
+ // and insertRepeatedClefAndKey, but note that your layout class
+ // will need to allot the space for them separately.
+
+ virtual void resizeStaffLines();
+ virtual void clearStaffLineRow(int row);
+ virtual void resizeStaffLineRow(int row, double offset, double length);
+
+ virtual void deleteBars();
+ virtual void insertBar(double layoutX, double width, bool isCorrect,
+ const TimeSignature &,
+ int barNo, bool showBarNo);
+
+ // The default implementations of the following two are empty.
+ virtual void deleteTimeSignatures();
+ virtual void insertTimeSignature(double layoutX,
+ const TimeSignature &);
+
+ // The default implementations of the following two are empty.
+ virtual void deleteRepeatedClefsAndKeys();
+ virtual void insertRepeatedClefAndKey(double layoutX, int barNo);
+
+ void initCursors();
+
+protected:
+
+ //--------------- Data members ---------------------------------
+
+ QCanvas *m_canvas;
+ SnapGrid *m_snapGrid;
+
+ int m_id;
+
+ double m_x;
+ int m_y;
+ double m_margin;
+ int m_titleHeight;
+ int m_resolution;
+ int m_lineThickness;
+
+ PageMode m_pageMode;
+ double m_pageWidth;
+ int m_rowsPerPage;
+ int m_rowSpacing;
+ int m_connectingLineLength;
+
+ double m_startLayoutX;
+ double m_endLayoutX;
+
+ bool m_current;
+
+ typedef std::vector<QCanvasItem *> ItemList;
+ typedef std::vector<ItemList> ItemMatrix;
+ ItemMatrix m_staffLines;
+ ItemList m_staffConnectingLines;
+
+ typedef std::pair<double, QCanvasItem *> LineRec; // layout-x, line
+ typedef FastVector<LineRec> LineRecList;
+ typedef FastVector<BarLine *> BarLineList;//!!! should be multiset I reckon
+ static bool compareBars(const BarLine *, const BarLine *);
+ static bool compareBarToLayoutX(const BarLine *, int);
+ BarLineList m_barLines;
+ LineRecList m_beatLines;
+ LineRecList m_barConnectingLines;
+ ItemList m_barNumbers;
+
+ QCanvasLine *m_pointer;
+ QCanvasLine *m_insertCursor;
+ timeT m_insertCursorTime;
+ bool m_insertCursorTimeValid;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/general/LinedStaffManager.cpp b/src/gui/general/LinedStaffManager.cpp
new file mode 100644
index 0000000..b1b92d2
--- /dev/null
+++ b/src/gui/general/LinedStaffManager.cpp
@@ -0,0 +1,33 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "LinedStaffManager.h"
+
+#include "LinedStaff.h"
+
+
+namespace Rosegarden
+{
+}
diff --git a/src/gui/general/LinedStaffManager.h b/src/gui/general/LinedStaffManager.h
new file mode 100644
index 0000000..44338f1
--- /dev/null
+++ b/src/gui/general/LinedStaffManager.h
@@ -0,0 +1,61 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_LINEDSTAFFMANAGER_H_
+#define _RG_LINEDSTAFFMANAGER_H_
+
+
+
+
+
+namespace Rosegarden
+{
+
+class LinedStaff;
+
+
+/**
+ * LinedStaffManager is a trivial abstract base for classes that own
+ * and position sets of LinedStaffs, as a convenient API to permit
+ * clients (such as canvas implementations) to discover which staff
+ * lies where.
+ *
+ * LinedStaffManager is not used by LinedStaff.
+ */
+
+class LinedStaff;
+
+class LinedStaffManager
+{
+public:
+ virtual ~LinedStaffManager() {}
+ virtual LinedStaff *getStaffForCanvasCoords(int x, int y) const = 0;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/general/MidiPitchLabel.cpp b/src/gui/general/MidiPitchLabel.cpp
new file mode 100644
index 0000000..47a748b
--- /dev/null
+++ b/src/gui/general/MidiPitchLabel.cpp
@@ -0,0 +1,74 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "MidiPitchLabel.h"
+#include <kapplication.h>
+
+#include "document/ConfigGroups.h"
+#include <kconfig.h>
+#include <klocale.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+static QString notes[] = {
+ i18n("C%1"), i18n("C#%1"), i18n("D%1"), i18n("D#%1"),
+ i18n("E%1"), i18n("F%1"), i18n("F#%1"), i18n("G%1"),
+ i18n("G#%1"), i18n("A%1"), i18n("A#%1"), i18n("B%1")
+};
+
+
+MidiPitchLabel::MidiPitchLabel(int pitch)
+{
+ if (pitch < 0 || pitch > 127) {
+
+ m_midiNote = "";
+
+ } else {
+
+ KConfig *config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+ int baseOctave = config->readNumEntry("midipitchoctave", -2);
+
+ int octave = (int)(((float)pitch) / 12.0) + baseOctave;
+ m_midiNote = notes[pitch % 12].arg(octave);
+ }
+}
+
+std::string
+MidiPitchLabel::getString() const
+{
+ return std::string(m_midiNote.utf8().data());
+}
+
+QString
+MidiPitchLabel::getQString() const
+{
+ return m_midiNote;
+}
+
+}
diff --git a/src/gui/general/MidiPitchLabel.h b/src/gui/general/MidiPitchLabel.h
new file mode 100644
index 0000000..9abcc11
--- /dev/null
+++ b/src/gui/general/MidiPitchLabel.h
@@ -0,0 +1,57 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_MIDIPITCHLABEL_H_
+#define _RG_MIDIPITCHLABEL_H_
+
+#include <string>
+#include <qstring.h>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+class MidiPitchLabel
+{
+public:
+ MidiPitchLabel(int pitch);
+
+ std::string getString() const;
+ QString getQString() const;
+
+private:
+ QString m_midiNote;
+
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/general/PixmapFunctions.cpp b/src/gui/general/PixmapFunctions.cpp
new file mode 100644
index 0000000..d297dad
--- /dev/null
+++ b/src/gui/general/PixmapFunctions.cpp
@@ -0,0 +1,271 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "PixmapFunctions.h"
+
+#include <qbitmap.h>
+#include <qcolor.h>
+#include <qimage.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+
+#include <iostream>
+
+namespace Rosegarden
+{
+
+QBitmap
+PixmapFunctions::generateMask(const QPixmap &map, const QRgb &px)
+{
+ QImage i(map.convertToImage());
+ QImage im(i.width(), i.height(), 1, 2, QImage::LittleEndian);
+
+ for (int y = 0; y < i.height(); ++y) {
+ for (int x = 0; x < i.width(); ++x) {
+ if (i.pixel(x, y) != px) {
+ im.setPixel(x, y, 1);
+ } else {
+ im.setPixel(x, y, 0);
+ }
+ }
+ }
+
+ QBitmap m;
+ m.convertFromImage(im);
+ return m;
+}
+
+QBitmap
+PixmapFunctions::generateMask(const QPixmap &map)
+{
+ QImage i(map.convertToImage());
+ QImage im(i.width(), i.height(), 1, 2, QImage::LittleEndian);
+
+ QRgb px0(i.pixel(0, 0));
+ QRgb px1(i.pixel(i.width() - 1, 0));
+ QRgb px2(i.pixel(i.width() - 1, i.height() - 1));
+ QRgb px3(i.pixel(0, i.height() - 1));
+
+ QRgb px(px0);
+ if (px0 != px2 && px1 == px3)
+ px = px1;
+
+ for (int y = 0; y < i.height(); ++y) {
+ for (int x = 0; x < i.width(); ++x) {
+ if (i.pixel(x, y) != px) {
+ im.setPixel(x, y, 1);
+ } else {
+ im.setPixel(x, y, 0);
+ }
+ }
+ }
+
+ QBitmap m;
+ m.convertFromImage(im);
+ return m;
+}
+
+QPixmap
+PixmapFunctions::colourPixmap(const QPixmap &map, int hue, int minValue)
+{
+ // assumes pixmap is currently in shades of grey; maps black ->
+ // solid colour and greys -> shades of colour
+
+ QImage image = map.convertToImage();
+
+ int s, v;
+
+ bool warned = false;
+
+ for (int y = 0; y < image.height(); ++y) {
+ for (int x = 0; x < image.width(); ++x) {
+
+ QColor pixel(image.pixel(x, y));
+
+ int oldHue;
+ pixel.hsv(&oldHue, &s, &v);
+
+ if (oldHue >= 0) {
+ if (!warned) {
+ std::cerr << "PixmapFunctions::recolour: Not a greyscale pixmap "
+ << "(found rgb value " << pixel.red() << ","
+ << pixel.green() << "," << pixel.blue()
+ << "), hoping for the best" << std::endl;
+ warned = true;
+ }
+ }
+
+ image.setPixel
+ (x, y, QColor(hue,
+ 255 - v,
+ v > minValue ? v : minValue,
+ QColor::Hsv).rgb());
+ }
+ }
+
+ QPixmap rmap;
+ rmap.convertFromImage(image);
+ if (map.mask())
+ rmap.setMask(*map.mask());
+ return rmap;
+}
+
+QPixmap
+PixmapFunctions::shadePixmap(const QPixmap &map)
+{
+ QImage image = map.convertToImage();
+
+ int h, s, v;
+
+ for (int y = 0; y < image.height(); ++y) {
+ for (int x = 0; x < image.width(); ++x) {
+
+ QColor pixel(image.pixel(x, y));
+
+ pixel.hsv(&h, &s, &v);
+
+ image.setPixel
+ (x, y, QColor(h,
+ s,
+ 255 - ((255 - v) / 2),
+ QColor::Hsv).rgb());
+ }
+ }
+
+ QPixmap rmap;
+ rmap.convertFromImage(image);
+ if (map.mask())
+ rmap.setMask(*map.mask());
+ return rmap;
+}
+
+QPixmap
+PixmapFunctions::flipVertical(const QPixmap &map)
+{
+ QPixmap rmap;
+ QImage i(map.convertToImage());
+ rmap.convertFromImage(i.mirror(false, true));
+
+ if (map.mask()) {
+ QImage im(map.mask()->convertToImage());
+ QBitmap newMask;
+ newMask.convertFromImage(im.mirror(false, true));
+ rmap.setMask(newMask);
+ }
+
+ return rmap;
+}
+
+QPixmap
+PixmapFunctions::flipHorizontal(const QPixmap &map)
+{
+ QPixmap rmap;
+ QImage i(map.convertToImage());
+ rmap.convertFromImage(i.mirror(true, false));
+
+ if (map.mask()) {
+ QImage im(map.mask()->convertToImage());
+ QBitmap newMask;
+ newMask.convertFromImage(im.mirror(true, false));
+ rmap.setMask(newMask);
+ }
+
+ return rmap;
+}
+
+std::pair<QPixmap, QPixmap>
+PixmapFunctions::splitPixmap(const QPixmap &pixmap, int x)
+{
+ QPixmap left(x, pixmap.height(), pixmap.depth());
+ QBitmap leftMask(left.width(), left.height());
+
+ QPixmap right(pixmap.width() - x, pixmap.height(), pixmap.depth());
+ QBitmap rightMask(right.width(), right.height());
+
+ QPainter paint;
+
+ paint.begin(&left);
+ paint.drawPixmap(0, 0, pixmap, 0, 0, left.width(), left.height());
+ paint.end();
+
+ paint.begin(&leftMask);
+ paint.drawPixmap(0, 0, *pixmap.mask(), 0, 0, left.width(), left.height());
+ paint.end();
+
+ left.setMask(leftMask);
+
+ paint.begin(&right);
+ paint.drawPixmap(0, 0, pixmap, left.width(), 0, right.width(), right.height());
+ paint.end();
+
+ paint.begin(&rightMask);
+ paint.drawPixmap(0, 0, *pixmap.mask(), left.width(), 0, right.width(), right.height());
+ paint.end();
+
+ right.setMask(rightMask);
+
+ return std::pair<QPixmap, QPixmap>(left, right);
+}
+
+void
+PixmapFunctions::drawPixmapMasked(QPixmap &dest, QBitmap &destMask,
+ int x0, int y0,
+ const QPixmap &src)
+{
+ QImage idp(dest.convertToImage());
+ QImage idm(destMask.convertToImage());
+ QImage isp(src.convertToImage());
+ QImage ism(src.mask()->convertToImage());
+
+ for (int y = 0; y < isp.height(); ++y) {
+ for (int x = 0; x < isp.width(); ++x) {
+
+ if (x >= ism.width())
+ continue;
+ if (y >= ism.height())
+ continue;
+
+ if (ism.depth() == 1 && ism.pixel(x, y) == 0)
+ continue;
+ if (ism.pixel(x, y) == Qt::white.rgb())
+ continue;
+
+ int x1 = x + x0;
+ int y1 = y + y0;
+ if (x1 < 0 || x1 >= idp.width())
+ continue;
+ if (y1 < 0 || y1 >= idp.height())
+ continue;
+
+ idp.setPixel(x1, y1, isp.pixel(x, y));
+ idm.setPixel(x1, y1, 1);
+ }
+ }
+
+ dest.convertFromImage(idp);
+ destMask.convertFromImage(idm);
+}
+
+}
diff --git a/src/gui/general/PixmapFunctions.h b/src/gui/general/PixmapFunctions.h
new file mode 100644
index 0000000..22da0f0
--- /dev/null
+++ b/src/gui/general/PixmapFunctions.h
@@ -0,0 +1,107 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_PIXMAPFUNCTIONS_H_
+#define _RG_PIXMAPFUNCTIONS_H_
+
+#include <qbitmap.h>
+#include <qpixmap.h>
+#include <utility>
+
+
+namespace Rosegarden
+{
+
+
+
+class PixmapFunctions
+{
+public:
+ /**
+ * Generate a heuristic mask for the given pixmap. Unlike
+ * QPixmap::createHeuristicMask, this removes from the mask all
+ * pixels that are apparently "background" even if they appear in
+ * holes in the middle of the image. This is more usually what we
+ * want than the default behaviour of createHeuristicMask.
+ *
+ * The rgb value specifies the colour to treat as background.
+ *
+ * This function is slow.
+ */
+ static QBitmap generateMask(const QPixmap &map, const QRgb &rgb);
+
+ /**
+ * Generate a heuristic mask for the given pixmap. Unlike
+ * QPixmap::createHeuristicMask, this removes from the mask all
+ * pixels that are apparently "background" even if they appear in
+ * holes in the middle of the image. This is more usually what we
+ * want than the default behaviour of createHeuristicMask.
+ *
+ * This function calculates its own estimated colour to match as
+ * background.
+ *
+ * This function is slow.
+ */
+ static QBitmap generateMask(const QPixmap &map);
+
+ /**
+ * Colour a greyscale pixmap with the given hue.
+ * minValue specifies the minimum value (in the HSV sense) that
+ * will be used for any recoloured pixel.
+ */
+ static QPixmap colourPixmap(const QPixmap &map, int hue, int minValue);
+
+ /**
+ * Make a pixmap grey, or otherwise reduce its intensity.
+ */
+ static QPixmap shadePixmap(const QPixmap &map);
+
+ /// Return a QPixmap that is a mirror image of map (including mask)
+ static QPixmap flipVertical(const QPixmap &map);
+
+ /// Return a QPixmap that is a mirror image of map (including mask)
+ static QPixmap flipHorizontal(const QPixmap &map);
+
+ /// Return left and right parts of the QPixmap
+ static std::pair<QPixmap, QPixmap> splitPixmap(const QPixmap &original, int x);
+
+ /**
+ * Using QPainter::drawPixmap to draw one pixmap on another does
+ * not appear to take the mask into account properly. Background
+ * pixels in the second pixmap erase foreground pixels in the
+ * first one, regardless of whether they're masked or not. This
+ * function does what I expect.
+ *
+ * Note that the source pixmap _must_ have a mask.
+ */
+ static void drawPixmapMasked(QPixmap &dest, QBitmap &destMask,
+ int x, int y,
+ const QPixmap &source);
+};
+
+
+}
+
+#endif
diff --git a/src/gui/general/PresetElement.cpp b/src/gui/general/PresetElement.cpp
new file mode 100644
index 0000000..4158d69
--- /dev/null
+++ b/src/gui/general/PresetElement.cpp
@@ -0,0 +1,68 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ This file is Copyright 2006
+ D. Michael McIntyre <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "PresetElement.h"
+
+#include "misc/Debug.h"
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+PresetElement::PresetElement(QString name,
+ int clef,
+ int transpose,
+ int highAm,
+ int lowAm,
+ int highPro,
+ int lowPro) :
+ m_name (name),
+ m_clef (clef),
+ m_transpose (transpose),
+ m_highAm (highAm),
+ m_lowAm (lowAm),
+ m_highPro (highPro),
+ m_lowPro (lowPro)
+{
+ RG_DEBUG << "PresetElement::PresetElement(" << endl
+ << " name = " << name << endl
+ << " clef = " << clef << endl
+ << " trns.= " << transpose << endl
+ << " higH = " << highAm << endl
+ << " lowA = " << lowAm << endl
+ << " higP = " << highPro << endl
+ << " lowP = " << lowPro << ")" << endl;
+}
+
+PresetElement::~PresetElement()
+{
+ // nothing to do
+}
+
+}
diff --git a/src/gui/general/PresetElement.h b/src/gui/general/PresetElement.h
new file mode 100644
index 0000000..24d3ee4
--- /dev/null
+++ b/src/gui/general/PresetElement.h
@@ -0,0 +1,82 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ This file is Copyright 2006
+ D. Michael McIntyre <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_PRESETELEMENT_H_
+#define _RG_PRESETELEMENT_H_
+
+#include <qstring.h>
+
+#include <vector>
+
+
+
+namespace Rosegarden
+{
+
+/*
+ * A container class for storing a set of data describing a real world
+ * instrument for which one is writing musical notation
+ */
+class PresetElement
+{
+public:
+
+ PresetElement(QString name,
+ int clef,
+ int transpose,
+ int highAm,
+ int lowAm,
+ int highPro,
+ int lowPro);
+
+ ~PresetElement();
+
+ // accessors
+ QString getName() { return m_name; }
+ int getClef() { return m_clef; }
+ int getTranspose() { return m_transpose; }
+ int getHighAm() { return m_highAm; }
+ int getLowAm() { return m_lowAm; }
+ int getHighPro() { return m_highPro; }
+ int getLowPro() { return m_lowPro; }
+
+private:
+ QString m_name;
+ int m_clef;
+ int m_transpose;
+ int m_highAm;
+ int m_lowAm;
+ int m_highPro;
+ int m_lowPro;
+}; // PresetElement
+
+typedef std::vector<PresetElement> ElementContainer;
+
+}
+
+#endif
diff --git a/src/gui/general/PresetGroup.cpp b/src/gui/general/PresetGroup.cpp
new file mode 100644
index 0000000..4a457a9
--- /dev/null
+++ b/src/gui/general/PresetGroup.cpp
@@ -0,0 +1,269 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ This file is Copyright 2006
+ D. Michael McIntyre <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "PresetGroup.h"
+
+#include "misc/Debug.h"
+#include "misc/Strings.h"
+#include "gui/general/ClefIndex.h"
+#include "base/Exception.h"
+#include "CategoryElement.h"
+#include <klocale.h>
+#include <kstddirs.h>
+#include <kglobal.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qregexp.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+PresetGroup::PresetGroup() :
+ m_errorString(i18n("unknown error")),
+ m_elCategoryName(""),
+ m_elInstrumentName(""),
+ m_elClef(0),
+ m_elTranspose(0),
+ m_elLowAm(0),
+ m_elHighAm(0),
+ m_elLowPro(0),
+ m_elHighPro(0),
+ m_lastCategory( -1),
+ m_currentCategory( -1),
+ m_lastInstrument( -1),
+ m_currentInstrument( -1),
+ m_name(false),
+ m_clef(false),
+ m_transpose(false),
+ m_amateur(false),
+ m_pro(false)
+{
+ m_presetDirectory = KGlobal::dirs()->findResource("appdata", "presets/");
+
+ QString language = KGlobal::locale()->language();
+
+ QString presetFileName = QString("%1/presets-%2.xml")
+ .arg(m_presetDirectory).arg(language);
+
+ if (!QFileInfo(presetFileName).isReadable()) {
+
+ RG_DEBUG << "Failed to open " << presetFileName << endl;
+
+ language.replace(QRegExp("_.*$"), "");
+ presetFileName = QString("%1/presets-%2.xml")
+ .arg(m_presetDirectory).arg(language);
+
+ if (!QFileInfo(presetFileName).isReadable()) {
+
+ RG_DEBUG << "Failed to open " << presetFileName << endl;
+
+ presetFileName = QString("%1/presets.xml")
+ .arg(m_presetDirectory);
+
+ if (!QFileInfo(presetFileName).isReadable()) {
+
+ RG_DEBUG << "Failed to open " << presetFileName << endl;
+
+ throw PresetFileReadFailed
+ (qstrtostr(i18n("Can't open preset file %1").
+ arg(presetFileName)));
+ }
+ }
+ }
+
+ QFile presetFile(presetFileName);
+
+ QXmlInputSource source(presetFile);
+ QXmlSimpleReader reader;
+ reader.setContentHandler(this);
+ reader.setErrorHandler(this);
+ bool ok = reader.parse(source);
+ presetFile.close();
+
+ if (!ok) {
+ throw PresetFileReadFailed(qstrtostr(m_errorString));
+ }
+}
+
+PresetGroup::~PresetGroup()
+{
+ //!!! do I have anything to do here?
+}
+
+bool
+PresetGroup::startElement(const QString &, const QString &,
+ const QString &qName,
+ const QXmlAttributes &attributes)
+{
+ QString lcName = qName.lower();
+
+ // RG_DEBUG << "PresetGroup::startElement: processing starting element: " << lcName << endl;
+
+ if (lcName == "category") {
+
+ QString s = attributes.value("name");
+ if (s) {
+ m_elCategoryName = s;
+ // increment the current category number
+ m_lastCategory = m_currentCategory;
+ m_currentCategory++;
+
+ // reset the instrument counter going into the new category
+ m_lastInstrument = -1;
+ m_currentInstrument = -1;
+
+ RG_DEBUG << "PresetGroup::startElement: adding category " << m_elCategoryName << " last: "
+ << m_lastCategory << " curr: " << m_currentCategory << endl;
+
+ // add new CategoryElement to m_categories, in order to contain
+ // subsequent PresetElements
+ CategoryElement ce(m_elCategoryName);
+ m_categories.push_back(ce);
+ }
+
+ } else if (lcName == "instrument") {
+
+ QString s = attributes.value("name");
+ if (s) {
+ m_elInstrumentName = s;
+ m_name = true;
+
+ // increment the current instrument number
+ m_lastInstrument = m_currentInstrument;
+ m_currentInstrument++;
+ }
+
+ } else if (lcName == "clef") {
+ QString s = attributes.value("type");
+ if (s) {
+ m_elClef = clefNameToClefIndex(s);
+ m_clef = true;
+ }
+ } else if (lcName == "transpose") {
+ QString s = attributes.value("value");
+ if (s) {
+ m_elTranspose = s.toInt();
+ m_transpose = true;
+ }
+
+ } else if (lcName == "range") {
+ QString s = attributes.value("class");
+
+ if (s == "amateur") {
+ s = attributes.value("low");
+ if (s) {
+ m_elLowAm = s.toInt();
+ m_amateur = true;
+ }
+
+ s = attributes.value("high");
+ if (s && m_amateur) {
+ m_elHighAm = s.toInt();
+ } else {
+ return false;
+ }
+
+ } else if (s == "professional") {
+ s = attributes.value("low");
+ if (s) {
+ m_pro = true;
+ m_elLowPro = s.toInt();
+ }
+
+ s = attributes.value("high");
+ if (s && m_pro) {
+ m_elHighPro = s.toInt();
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ // RG_DEBUG << "PresetGroup::startElement(): accumulating flags:" << endl
+ // << " name: " << (m_name ? "true" : "false") << endl
+ // << " clef: " << (m_clef ? "true" : "false") << endl
+ // << "transpose: " << (m_transpose ? "true" : "false") << endl
+ // << " am. rng: " << (m_amateur ? "true" : "false") << endl
+ // << " pro rng: " << (m_pro ? "true" : "false") << endl;
+
+ // once we have assembled all the bits, create a new PresetElement
+ if (m_name && m_clef && m_transpose && m_amateur && m_pro) {
+ m_categories[m_currentCategory].addPreset(m_elInstrumentName,
+ m_elClef,
+ m_elTranspose,
+ m_elHighAm,
+ m_elLowAm,
+ m_elHighPro,
+ m_elLowPro);
+ // increment the current instrument
+ //!!! (is this ever going to be needed?)
+ m_lastInstrument = m_currentInstrument;
+ m_currentInstrument++;
+
+ // reset the "do we have a whole preset yet?" flags
+ m_name = false;
+ m_clef = false;
+ m_transpose = false;
+ m_amateur = false;
+ m_pro = false;
+ }
+
+ return true;
+
+} // startElement
+
+bool
+PresetGroup::error(const QXmlParseException& exception)
+{
+ RG_DEBUG << "PresetGroup::error(): jubilation and glee, we have an error, whee!" << endl;
+
+ m_errorString = QString("%1 at line %2, column %3: %4")
+ .arg(exception.message())
+ .arg(exception.lineNumber())
+ .arg(exception.columnNumber())
+ .arg(m_errorString);
+ return QXmlDefaultHandler::error(exception);
+}
+
+bool
+PresetGroup::fatalError(const QXmlParseException& exception)
+{
+ RG_DEBUG << "PresetGroup::fatalError(): double your jubilation, and triple your glee, a fatal error doth it be!" << endl;
+ m_errorString = QString("%1 at line %2, column %3: %4")
+ .arg(exception.message())
+ .arg(exception.lineNumber())
+ .arg(exception.columnNumber())
+ .arg(m_errorString);
+ return QXmlDefaultHandler::fatalError(exception);
+}
+
+}
diff --git a/src/gui/general/PresetGroup.h b/src/gui/general/PresetGroup.h
new file mode 100644
index 0000000..476a878
--- /dev/null
+++ b/src/gui/general/PresetGroup.h
@@ -0,0 +1,105 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ This file is Copyright 2006
+ D. Michael McIntyre <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_PRESETGROUP_H_
+#define _RG_PRESETGROUP_H_
+
+#include "base/Exception.h"
+#include "CategoryElement.h"
+#include <qstring.h>
+#include <qxml.h>
+
+
+class QXmlParseException;
+class QXmlAttributes;
+
+
+namespace Rosegarden
+{
+
+/*
+ * Read presets.xml from disk and store a collection of PresetElement objects
+ * which can then be used to populate and run the chooser GUI
+ */
+class PresetGroup : public QXmlDefaultHandler
+{
+public:
+ typedef Exception PresetFileReadFailed;
+
+ PresetGroup(); // load and parse the XML mapping file
+ ~PresetGroup();
+
+ CategoriesContainer getCategories() { return m_categories; }
+ //CategoryElement getCategoryByIndex(int index) { return m_categories [index]; }
+
+ // Xml handler methods:
+
+ virtual bool startElement (const QString& namespaceURI, const QString& localName,
+ const QString& qName, const QXmlAttributes& atts);
+
+ bool error(const QXmlParseException& exception);
+ bool fatalError(const QXmlParseException& exception);
+
+ // I don't think I have anything to do with this, but it must return true?
+// bool characters(const QString &) { return true; }
+
+private:
+
+ //--------------- Data members ---------------------------------
+ CategoriesContainer m_categories;
+
+ // For use when reading the XML file:
+ QString m_errorString;
+ QString m_presetDirectory;
+
+ QString m_elCategoryName;
+ QString m_elInstrumentName;
+ int m_elClef;
+ int m_elTranspose;
+ int m_elLowAm;
+ int m_elHighAm;
+ int m_elLowPro;
+ int m_elHighPro;
+
+ int m_lastCategory;
+ int m_currentCategory;
+ int m_lastInstrument;
+ int m_currentInstrument;
+
+ bool m_name;
+ bool m_clef;
+ bool m_transpose;
+ bool m_amateur;
+ bool m_pro;
+
+}; // PresetGroup
+
+
+}
+
+#endif
diff --git a/src/gui/general/PresetHandlerDialog.cpp b/src/gui/general/PresetHandlerDialog.cpp
new file mode 100644
index 0000000..6081f85
--- /dev/null
+++ b/src/gui/general/PresetHandlerDialog.cpp
@@ -0,0 +1,281 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ This file is Copyright 2006
+ D. Michael McIntyre <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "PresetHandlerDialog.h"
+#include <qlayout.h>
+#include <kapplication.h>
+
+#include <klocale.h>
+#include "misc/Debug.h"
+#include "document/ConfigGroups.h"
+#include "CategoryElement.h"
+#include "PresetElement.h"
+#include "PresetGroup.h"
+#include <kcombobox.h>
+#include <kconfig.h>
+#include <kdialogbase.h>
+#include <qbuttongroup.h>
+#include <qdialog.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qstring.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+PresetHandlerDialog::PresetHandlerDialog(QWidget *parent, bool fromNotation)
+ : KDialogBase(parent, "presethandlerdialog", true, i18n("Load track parameters preset"), Ok | Cancel, Ok),
+ m_config(kapp->config()),
+ m_fromNotation(fromNotation)
+{
+ m_presets = new PresetGroup();
+ m_categories = m_presets->getCategories();
+ if (m_fromNotation) setCaption(i18n("Convert notation for..."));
+
+ initDialog();
+}
+
+PresetHandlerDialog::~PresetHandlerDialog()
+{
+ // delete m_presets
+ if (m_presets != NULL) {
+ delete m_presets;
+ }
+}
+
+void
+PresetHandlerDialog::initDialog()
+{
+ RG_DEBUG << "PresetHandlerDialog::initDialog()" << endl;
+
+ QVBox *vBox = makeVBoxMainWidget();
+
+ QFrame *frame = new QFrame(vBox);
+
+ QGridLayout *layout = new QGridLayout(frame, 6, 5, 10, 5);
+
+ QLabel *title = new QLabel(i18n("Select preset track parameters for:"), frame);
+ if (m_fromNotation) title->setText(i18n("Create appropriate notation for:"));
+
+ QLabel *catlabel = new QLabel(i18n("Category"), frame);
+ m_categoryCombo = new KComboBox(frame);
+
+ QLabel *inslabel = new QLabel(i18n("Instrument"), frame);
+ m_instrumentCombo = new KComboBox(frame);
+
+ QLabel *plylabel = new QLabel(i18n("Player Ability"), frame);
+ m_playerCombo = new KComboBox(frame);
+ m_playerCombo->insertItem(i18n("Amateur"));
+ m_playerCombo->insertItem(i18n("Professional"));
+
+ QGroupBox *scopeBox = new QButtonGroup
+ (1, Horizontal, i18n("Scope"), frame);
+ if (m_fromNotation) {
+ QRadioButton *onlySelectedSegments = new
+ QRadioButton(i18n("Only selected segments"), scopeBox);
+ m_convertAllSegments = new
+ QRadioButton(i18n("All segments in this track"), scopeBox);
+ onlySelectedSegments->setChecked(true);
+ }
+ else {
+ QRadioButton *onlyNewSegments = new
+ QRadioButton(i18n("Only for new segments"), scopeBox);
+ m_convertSegments = new
+ QRadioButton(i18n("Convert existing segments"), scopeBox);
+ onlyNewSegments->setChecked(true);
+ }
+
+ layout->addMultiCellWidget(title, 0, 0, 0, 1, AlignLeft);
+ layout->addWidget(catlabel, 1, 0, AlignRight);
+ layout->addWidget(m_categoryCombo, 1, 1);
+ layout->addWidget(inslabel, 2, 0, AlignRight);
+ layout->addWidget(m_instrumentCombo, 2, 1);
+ layout->addWidget(plylabel, 3, 0, AlignRight);
+ layout->addWidget(m_playerCombo, 3, 1);
+ layout->addMultiCellWidget(scopeBox, 4, 4, 0, 1, AlignLeft);
+
+ populateCategoryCombo();
+ // try to set to same category used previously
+ m_config->setGroup(GeneralOptionsConfigGroup);
+ m_categoryCombo->setCurrentItem(m_config->readNumEntry("category_combo_index", 0));
+
+ // populate the instrument combo
+ slotCategoryIndexChanged(m_categoryCombo->currentItem());
+
+ // try to set to same instrument used previously
+ m_config->setGroup(GeneralOptionsConfigGroup);
+ m_instrumentCombo->setCurrentItem(m_config->readNumEntry("instrument_combo_index", 0));
+
+ // set to same player used previously (this one can't fail, unlike the
+ // others, because the contents of this combo are static)
+ m_playerCombo->setCurrentItem(m_config->readNumEntry("player_combo_index", 0));
+
+ if (m_fromNotation){
+ m_convertAllSegments->setChecked(m_config->readBoolEntry("convert_all_segments", 0));
+ }
+ else {
+ m_convertSegments->setChecked(m_config->readBoolEntry("convert_segments", 0));
+ }
+
+
+ connect(m_categoryCombo, SIGNAL(activated(int)),
+ SLOT(slotCategoryIndexChanged(int)));
+}
+
+QString
+PresetHandlerDialog::getName()
+{
+ return m_instrumentCombo->currentText();
+}
+
+int
+PresetHandlerDialog::getClef()
+{
+ PresetElement p = m_categories[m_categoryCombo->currentItem()].
+ getPresetByIndex(m_instrumentCombo->currentItem());
+
+ return p.getClef();
+}
+
+int
+PresetHandlerDialog::getTranspose()
+{
+ PresetElement p = m_categories[m_categoryCombo->currentItem()].
+ getPresetByIndex(m_instrumentCombo->currentItem());
+
+ return p.getTranspose();
+}
+
+int
+PresetHandlerDialog::getLowRange()
+{
+ PresetElement p = m_categories[m_categoryCombo->currentItem()].
+ getPresetByIndex(m_instrumentCombo->currentItem());
+ // 0 == amateur
+ // 1 == pro
+ if (m_playerCombo->currentItem() == 0) {
+ return p.getLowAm();
+ } else {
+ return p.getLowPro();
+ }
+}
+
+int
+PresetHandlerDialog::getHighRange()
+{
+ PresetElement p = m_categories[m_categoryCombo->currentItem()].
+ getPresetByIndex(m_instrumentCombo->currentItem());
+ // 0 == amateur
+ // 1 == pro
+ if (m_playerCombo->currentItem() == 0) {
+ return p.getHighAm();
+ } else {
+ return p.getHighPro();
+ }
+}
+
+bool
+PresetHandlerDialog::getConvertAllSegments()
+{
+ if (m_fromNotation) {
+ return m_convertAllSegments && m_convertAllSegments->isChecked();
+ }
+ else {
+ return m_convertSegments && m_convertSegments->isChecked();
+ }
+}
+
+bool
+PresetHandlerDialog::getConvertOnlySelectedSegments()
+{
+ if (m_fromNotation) {
+ return m_convertAllSegments && !m_convertAllSegments->isChecked();
+ }
+ else {
+ return false;
+ }
+}
+
+void
+PresetHandlerDialog::populateCategoryCombo()
+{
+ RG_DEBUG << "PresetHandlerDialog::populateCategoryCombo()" << endl;
+
+ for (CategoriesContainer::iterator i = m_categories.begin();
+ i != m_categories.end(); ++i) {
+
+ RG_DEBUG << " adding category: " << (*i).getName() << endl;
+
+ m_categoryCombo->insertItem((*i).getName());
+ }
+}
+
+void
+PresetHandlerDialog::slotCategoryIndexChanged(int index)
+{
+ RG_DEBUG << "PresetHandlerDialog::slotCategoryIndexChanged(" << index << ")" << endl;
+
+ CategoryElement e = m_categories[index];
+ ElementContainer c = e.getPresets();
+
+ m_instrumentCombo->clear();
+
+ for (ElementContainer::iterator i = c.begin();
+ i != c.end(); ++i) {
+
+ RG_DEBUG << " adding instrument: " << (*i).getName() << endl;
+
+ m_instrumentCombo->insertItem((*i).getName());
+ }
+
+}
+
+void
+PresetHandlerDialog::slotOk()
+{
+ m_config->setGroup(GeneralOptionsConfigGroup);
+ m_config->writeEntry("category_combo_index", m_categoryCombo->currentItem());
+ m_config->writeEntry("instrument_combo_index", m_instrumentCombo->currentItem());
+ m_config->writeEntry("player_combo_index", m_playerCombo->currentItem());
+
+ if (m_fromNotation) {
+ m_config->writeEntry("convert_all_segments", m_convertAllSegments->isChecked());
+ }
+ else {
+ m_config->writeEntry("convert_segments", m_convertSegments->isChecked());
+ }
+
+ QDialog::accept();
+}
+
+}
+#include "PresetHandlerDialog.moc"
diff --git a/src/gui/general/PresetHandlerDialog.h b/src/gui/general/PresetHandlerDialog.h
new file mode 100644
index 0000000..879ddca
--- /dev/null
+++ b/src/gui/general/PresetHandlerDialog.h
@@ -0,0 +1,107 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ This file is Copyright 2006
+ D. Michael McIntyre <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_PRESETHANDLERDIALOG_H_
+#define _RG_PRESETHANDLERDIALOG_H_
+
+#include <kdialogbase.h>
+#include <qradiobutton.h>
+#include <qstring.h>
+#include "CategoryElement.h"
+
+class QWidget;
+class KConfig;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class PresetGroup;
+
+
+class PresetHandlerDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+
+ PresetHandlerDialog(QWidget* parent, bool fromNotation = false);
+ ~PresetHandlerDialog();
+
+ PresetGroup *m_presets;
+ CategoriesContainer m_categories;
+
+ KConfig *m_config;
+ bool m_fromNotation;
+
+ //-------[ accessor functions ]------------------------
+
+ QString getName();
+
+ int getClef();
+ int getTranspose();
+ int getLowRange();
+ int getHighRange();
+ bool getConvertAllSegments();
+ bool getConvertOnlySelectedSegments();
+
+protected:
+
+ //--------[ member functions ]-------------------------
+
+ // initialize the dialog
+ void initDialog();
+
+ // populate the category combo
+ void populateCategoryCombo();
+
+
+ //---------[ data members ]-----------------------------
+
+ KComboBox *m_categoryCombo;
+ KComboBox *m_instrumentCombo;
+ KComboBox *m_playerCombo;
+ QRadioButton *m_convertSegments;
+ QRadioButton *m_convertAllSegments;
+
+protected slots:
+
+ // de-populate and re-populate the Instrument combo when the category
+ // changes.
+ void slotCategoryIndexChanged(int index);
+
+ // write out settings to kconfig data for next time and call accept()
+ void slotOk();
+
+}; // PresetHandlerDialog
+
+
+}
+
+#endif
diff --git a/src/gui/general/ProgressReporter.cpp b/src/gui/general/ProgressReporter.cpp
new file mode 100644
index 0000000..0d9e896
--- /dev/null
+++ b/src/gui/general/ProgressReporter.cpp
@@ -0,0 +1,53 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "ProgressReporter.h"
+
+#include <qobject.h>
+
+
+namespace Rosegarden
+{
+
+ProgressReporter::ProgressReporter(QObject* parent, const char* name)
+ : QObject(parent, name), m_isCancelled(false)
+{}
+
+
+void ProgressReporter::throwIfCancelled()
+{
+ if (m_isCancelled) {
+ m_isCancelled = false;
+ throw Cancelled();
+ }
+}
+
+void ProgressReporter::slotCancel()
+{
+ m_isCancelled = true;
+};
+
+}
+#include "ProgressReporter.moc"
diff --git a/src/gui/general/ProgressReporter.h b/src/gui/general/ProgressReporter.h
new file mode 100644
index 0000000..d8aa306
--- /dev/null
+++ b/src/gui/general/ProgressReporter.h
@@ -0,0 +1,80 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_PROGRESSREPORTER_H_
+#define _RG_PROGRESSREPORTER_H_
+
+#include <qobject.h>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+class ProgressReporter : public QObject
+{
+ Q_OBJECT
+public:
+ ProgressReporter(QObject* parent, const char* name = 0);
+
+ // exception class for cancellations
+ class Cancelled { };
+
+protected:
+ /**
+ * Call this at appropriate times if you know Qt isn't in the stack
+ */
+ void throwIfCancelled();
+
+ /*
+ We have to use these accessors rather than throwing directly
+ from slotCancel() because Qt is generally compiled without
+ exception support, so we can't throw from a slot.
+ */
+ bool isOperationCancelled() const { return m_isCancelled; }
+// void resetOperationCancelledState() { m_isCancelled = false; }
+
+protected slots:
+ virtual void slotCancel();
+
+signals:
+ /// Report progress
+ void setProgress(int);
+ void incrementProgress(int);
+ void setOperationName(QString);
+
+protected:
+ //--------------- Data members ---------------------------------
+ bool m_isCancelled;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/general/RosegardenCanvasView.cpp b/src/gui/general/RosegardenCanvasView.cpp
new file mode 100644
index 0000000..a829aac
--- /dev/null
+++ b/src/gui/general/RosegardenCanvasView.cpp
@@ -0,0 +1,485 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "RosegardenCanvasView.h"
+
+#include "misc/Debug.h"
+#include "gui/general/CanvasItemGC.h"
+#include <qcanvas.h>
+#include <qcursor.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qscrollbar.h>
+#include <qsize.h>
+#include <qsizepolicy.h>
+#include <qtimer.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+RosegardenCanvasView::RosegardenCanvasView(QCanvas* canvas,
+ QWidget* parent,
+ const char* name, WFlags f)
+ : QCanvasView(canvas, parent, name, f),
+ m_bottomWidget(0),
+ m_currentBottomWidgetHeight( -1),
+ m_leftWidget(0),
+ m_smoothScroll(true),
+ m_smoothScrollTimeInterval(DefaultSmoothScrollTimeInterval),
+ m_minDeltaScroll(DefaultMinDeltaScroll),
+ m_autoScrollTime(InitialScrollTime),
+ m_autoScrollAccel(InitialScrollAccel),
+ m_autoScrollXMargin(0),
+ m_autoScrollYMargin(0),
+ m_currentScrollDirection(None),
+ m_scrollDirectionConstraint(NoFollow),
+ m_autoScrolling(false)
+{
+ setDragAutoScroll(true);
+ connect( &m_autoScrollTimer, SIGNAL( timeout() ),
+ this, SLOT( doAutoScroll() ) );
+}
+
+void RosegardenCanvasView::fitWidthToContents()
+{
+ QRect allItemsBoundingRect;
+
+ QCanvasItemList items = canvas()->allItems();
+
+ QCanvasItemList::Iterator it;
+
+ for (it = items.begin(); it != items.end(); ++it) {
+ allItemsBoundingRect |= (*it)->boundingRect();
+ }
+
+ QSize currentSize = canvas()->size();
+ resizeContents(allItemsBoundingRect.width(), currentSize.height());
+}
+
+void RosegardenCanvasView::setBottomFixedWidget(QWidget* w)
+{
+ m_bottomWidget = w;
+ if (m_bottomWidget) {
+ int lww = m_leftWidget ? m_leftWidget->sizeHint().width() : 0;
+ m_bottomWidget->reparent(this, 0, QPoint(0, 0));
+ m_bottomWidget->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
+ setMargins(lww, 0, 0, m_bottomWidget->sizeHint().height());
+ }
+}
+
+void RosegardenCanvasView::slotUpdate()
+{
+ CanvasItemGC::gc();
+ canvas()->update();
+}
+
+// Smooth scroll checks
+//
+
+const int RosegardenCanvasView::AutoscrollMargin = 16;
+const int RosegardenCanvasView::InitialScrollTime = 30;
+const int RosegardenCanvasView::InitialScrollAccel = 5;
+const int RosegardenCanvasView::MaxScrollDelta = 100; // max a.scroll speed
+const double RosegardenCanvasView::ScrollAccelValue = 1.04;// acceleration rate
+
+const int RosegardenCanvasView::DefaultSmoothScrollTimeInterval = 10;
+const double RosegardenCanvasView::DefaultMinDeltaScroll = 1.2;
+
+void RosegardenCanvasView::startAutoScroll()
+{
+ // RG_DEBUG << "RosegardenCanvasView::startAutoScroll()\n";
+
+ if ( !m_autoScrollTimer.isActive() ) {
+ m_autoScrollTime = InitialScrollTime;
+ m_autoScrollAccel = InitialScrollAccel;
+ m_autoScrollTimer.start( m_autoScrollTime );
+ }
+
+ QPoint autoScrollStartPoint = viewport()->mapFromGlobal( QCursor::pos() );
+ m_autoScrollYMargin = autoScrollStartPoint.y() / 10;
+ m_autoScrollXMargin = autoScrollStartPoint.x() / 10;
+
+ m_autoScrolling = true;
+}
+
+void RosegardenCanvasView::startAutoScroll(int directionConstraint)
+{
+ setScrollDirectionConstraint(directionConstraint);
+ startAutoScroll();
+}
+
+void RosegardenCanvasView::stopAutoScroll()
+{
+ // RG_DEBUG << "RosegardenCanvasView::stopAutoScroll()\n";
+
+ m_autoScrollTimer.stop();
+ m_minDeltaScroll = DefaultMinDeltaScroll;
+ m_currentScrollDirection = None;
+
+ m_autoScrolling = false;
+}
+
+void RosegardenCanvasView::doAutoScroll()
+{
+ // RG_DEBUG << "RosegardenCanvasView::doAutoScroll()\n";
+
+ QPoint p = viewport()->mapFromGlobal( QCursor::pos() );
+ QPoint dp = p - m_previousP;
+ m_previousP = p;
+
+ m_autoScrollTimer.start( m_autoScrollTime );
+ ScrollDirection scrollDirection = None;
+
+ int dx = 0, dy = 0;
+ if (m_scrollDirectionConstraint & FollowVertical) {
+ if ( p.y() < m_autoScrollYMargin ) {
+ dy = -(int(m_minDeltaScroll));
+ scrollDirection = Top;
+ } else if ( p.y() > visibleHeight() - m_autoScrollYMargin ) {
+ dy = + (int(m_minDeltaScroll));
+ scrollDirection = Bottom;
+ }
+ }
+ bool startDecelerating = false;
+ if (m_scrollDirectionConstraint & FollowHorizontal) {
+
+ // RG_DEBUG << "p.x() : " << p.x() << " - visibleWidth : " << visibleWidth() << " - autoScrollXMargin : " << m_autoScrollXMargin << endl;
+
+ if ( p.x() < m_autoScrollXMargin ) {
+ if ( dp.x() > 0 ) {
+ startDecelerating = true;
+ m_minDeltaScroll /= ScrollAccelValue;
+ }
+ dx = -(int(m_minDeltaScroll));
+ scrollDirection = Left;
+ } else if ( p.x() > visibleWidth() - m_autoScrollXMargin ) {
+ if ( dp.x() < 0 ) {
+ startDecelerating = true;
+ m_minDeltaScroll /= ScrollAccelValue;
+ }
+ dx = + (int(m_minDeltaScroll));
+ scrollDirection = Right;
+ }
+ }
+
+ // RG_DEBUG << "dx: " << dx << ", dy: " << dy << endl;
+
+ if ( (dx || dy) &&
+ ((scrollDirection == m_currentScrollDirection) || (m_currentScrollDirection == None)) ) {
+ scrollBy(dx, dy);
+ if ( startDecelerating )
+ m_minDeltaScroll /= ScrollAccelValue;
+ else
+ m_minDeltaScroll *= ScrollAccelValue;
+ if (m_minDeltaScroll > MaxScrollDelta )
+ m_minDeltaScroll = MaxScrollDelta;
+ m_currentScrollDirection = scrollDirection;
+
+ } else {
+ // Don't automatically stopAutoScroll() here, the mouse button
+ // is presumably still pressed.
+ m_minDeltaScroll = DefaultMinDeltaScroll;
+ m_currentScrollDirection = None;
+ }
+
+}
+
+bool RosegardenCanvasView::isTimeForSmoothScroll()
+{
+ if (m_smoothScroll) {
+ int ta = m_scrollAccelerationTimer.elapsed();
+ int t = m_scrollTimer.elapsed();
+
+ // RG_DEBUG << "t = " << t << ", ta = " << ta << ", int " << m_smoothScrollTimeInterval << ", delta " << m_minDeltaScroll << endl;
+
+ if (t < m_smoothScrollTimeInterval) {
+
+ return false;
+
+ } else {
+
+ if (ta > 300) {
+ // reset smoothScrollTimeInterval
+ m_smoothScrollTimeInterval = DefaultSmoothScrollTimeInterval;
+ m_minDeltaScroll = DefaultMinDeltaScroll;
+ m_scrollAccelerationTimer.restart();
+ } else if (ta > 50) {
+ // m_smoothScrollTimeInterval /= 2;
+ m_minDeltaScroll *= 1.08;
+ m_scrollAccelerationTimer.restart();
+ }
+
+ m_scrollTimer.restart();
+ return true;
+ }
+ }
+
+ return true;
+}
+
+void RosegardenCanvasView::slotScrollHoriz(int hpos)
+{
+ QScrollBar* hbar = getMainHorizontalScrollBar();
+ int currentContentYPos = contentsY();
+
+ /* Lots of performance hitting debug
+ RG_DEBUG << "RosegardenCanvasView::slotScrollHoriz: hpos is " << hpos
+ << ", contentsX is " << contentsX() << ", visibleWidth is "
+ << visibleWidth() << endl;
+ */
+
+ if (hpos == 0) {
+
+ // returning to zero
+ // hbar->setValue(0);
+ setContentsPos(0, currentContentYPos);
+
+ } else if (hpos > (contentsX() +
+ visibleWidth() * 1.6) ||
+ hpos < (contentsX() -
+ visibleWidth() * 0.7)) {
+
+ // miles off one side or the other
+ // hbar->setValue(hpos - int(visibleWidth() * 0.4));
+ setContentsPos(hpos - int(visibleWidth() * 0.4), currentContentYPos);
+
+ } else if (hpos > (contentsX() +
+ visibleWidth() * 0.9)) {
+
+ // moving off the right hand side of the view
+ // hbar->setValue(hbar->value() + int(visibleWidth() * 0.6));
+ setContentsPos(hbar->value() + int(visibleWidth() * 0.6), currentContentYPos);
+
+ } else if (hpos < (contentsX() +
+ visibleWidth() * 0.1)) {
+
+ // moving off the left
+ // hbar->setValue(hbar->value() - int(visibleWidth() * 0.6));
+ setContentsPos(hbar->value() - int(visibleWidth() * 0.6), currentContentYPos);
+ }
+}
+
+void RosegardenCanvasView::slotScrollHorizSmallSteps(int hpos)
+{
+ QScrollBar* hbar = getMainHorizontalScrollBar();
+ int currentContentYPos = contentsY();
+
+ int diff = 0;
+
+ if (hpos == 0) {
+
+ // returning to zero
+ // hbar->setValue(0);
+ setContentsPos(0, currentContentYPos);
+
+ } else if ((diff = int(hpos - (contentsX() +
+ visibleWidth() * 0.90))) > 0) {
+
+ // moving off the right hand side of the view
+
+ int delta = diff / 6;
+ int diff10 = std::min(diff, (int)m_minDeltaScroll);
+ delta = std::max(delta, diff10);
+
+ // hbar->setValue(hbar->value() + delta);
+ setContentsPos(hbar->value() + delta, currentContentYPos);
+
+ } else if ((diff = int(hpos - (contentsX() +
+ visibleWidth() * 0.10))) < 0) {
+ // moving off the left
+
+ int delta = -diff / 6;
+ int diff10 = std::min( -diff, (int)m_minDeltaScroll);
+ delta = std::max(delta, diff10);
+
+ // hbar->setValue(hbar->value() - delta);
+ setContentsPos(hbar->value() - delta, currentContentYPos);
+
+ }
+}
+
+void RosegardenCanvasView::slotScrollVertSmallSteps(int vpos)
+{
+ QScrollBar* vbar = verticalScrollBar();
+
+ // RG_DEBUG << "RosegardenCanvasView::slotScrollVertSmallSteps: vpos is " << vpos << ", contentsY is " << contentsY() << ", visibleHeight is " << visibleHeight() << endl;
+
+ // As a special case (or hack), ignore any request made before we've
+ // actually been rendered and sized
+ if (visibleHeight() <= 1)
+ return ;
+
+ int diff = 0;
+
+ if (vpos == 0) {
+
+ // returning to zero
+ vbar->setValue(0);
+
+ } else if ((diff = int(vpos - (contentsY() +
+ visibleHeight() * 0.90))) > 0) {
+
+ // moving off up
+
+ int delta = diff / 6;
+ int diff10 = std::min(diff, (int)m_minDeltaScroll);
+ delta = std::max(delta, diff10);
+
+ vbar->setValue(vbar->value() + diff);
+
+ } else if ((diff = int(vpos - (contentsY() +
+ visibleHeight() * 0.10))) < 0) {
+
+ // moving off down
+
+ int delta = -diff / 6;
+ int diff10 = std::min( -diff, (int)m_minDeltaScroll);
+ delta = std::max(delta, diff10);
+
+ vbar->setValue(vbar->value() - delta);
+
+ }
+}
+
+void RosegardenCanvasView::slotScrollVertToTop(int vpos)
+{
+ QScrollBar* vbar = verticalScrollBar();
+ if (vpos < visibleHeight() / 3)
+ vbar->setValue(0);
+ else
+ vbar->setValue(vpos - visibleHeight() / 5);
+}
+
+void RosegardenCanvasView::slotSetScrollPos(const QPoint &pos)
+{
+ getMainHorizontalScrollBar()->setValue(pos.x());
+ verticalScrollBar()->setValue(pos.y());
+}
+
+void RosegardenCanvasView::resizeEvent(QResizeEvent* e)
+{
+ QCanvasView::resizeEvent(e);
+ if (!horizontalScrollBar()->isVisible())
+ updateBottomWidgetGeometry();
+ updateLeftWidgetGeometry();
+}
+
+void RosegardenCanvasView::setHBarGeometry(QScrollBar &hbar, int x, int y, int w, int h)
+{
+ QCanvasView::setHBarGeometry(hbar, x, y, w, h);
+ updateBottomWidgetGeometry();
+}
+
+void RosegardenCanvasView::updateBottomWidgetGeometry()
+{
+ if (!m_bottomWidget)
+ return ;
+
+ int bottomWidgetHeight = m_bottomWidget->sizeHint().height();
+
+ int leftWidgetWidth = 0;
+ if (m_leftWidget && m_leftWidget->isVisible()) {
+ QScrollView * qsv = dynamic_cast<QScrollView *>(m_leftWidget);
+ leftWidgetWidth = qsv->contentsWidth()+2;
+ qsv->setFixedWidth(leftWidgetWidth);
+ }
+
+ setMargins(leftWidgetWidth, 0, 0, bottomWidgetHeight);
+
+ QRect r = frameRect();
+ int hScrollBarHeight = 0;
+ if (horizontalScrollBar()->isVisible())
+ hScrollBarHeight = horizontalScrollBar()->height() + 2;
+ // + 2 offset : needed to preserve border shadow
+
+ int vScrollBarWidth = 0;
+ if (verticalScrollBar()->isVisible())
+ vScrollBarWidth = verticalScrollBar()->width();
+
+ m_bottomWidget->setGeometry(r.x() + leftWidgetWidth,
+ r.y() + r.height() - bottomWidgetHeight - hScrollBarHeight,
+ r.width() - vScrollBarWidth - leftWidgetWidth,
+ bottomWidgetHeight);
+
+ if (bottomWidgetHeight != m_currentBottomWidgetHeight) {
+ emit bottomWidgetHeightChanged(bottomWidgetHeight);
+ m_currentBottomWidgetHeight = bottomWidgetHeight;
+ }
+}
+
+void RosegardenCanvasView::wheelEvent(QWheelEvent *e)
+{
+ if (e->state() & ControlButton) {
+ if (e->delta() > 0)
+ emit zoomIn();
+ else if (e->delta() < 0)
+ emit zoomOut();
+ return ;
+ }
+ QCanvasView::wheelEvent(e);
+}
+
+void RosegardenCanvasView::setLeftFixedWidget(QWidget* w)
+{
+ m_leftWidget = w;
+ if (m_leftWidget) {
+ int bwh = m_bottomWidget ? m_bottomWidget->sizeHint().height() : 0;
+ m_leftWidget->reparent(this, 0, QPoint(0, 0));
+ m_leftWidget->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
+ setMargins(m_leftWidget->sizeHint().width(), 0, 0, bwh);
+ }
+}
+
+void RosegardenCanvasView::updateLeftWidgetGeometry()
+{
+ if (!m_leftWidget)
+ return ;
+
+ int leftWidgetWidth = 0;
+ if (m_leftWidget->isVisible()) {
+ QScrollView * qsv = dynamic_cast<QScrollView *>(m_leftWidget);
+ leftWidgetWidth = qsv->contentsWidth() + 2;
+ }
+ m_leftWidget->setFixedWidth(leftWidgetWidth);
+
+ int bottomWidgetHeight = m_bottomWidget ?
+ m_bottomWidget->sizeHint().height() : 0;
+
+ setMargins(leftWidgetWidth, 0, 0, bottomWidgetHeight);
+
+ QRect r = frameRect();
+ int hScrollBarHeight = 0;
+ if (horizontalScrollBar()->isVisible())
+ hScrollBarHeight = horizontalScrollBar()->height() + 2;
+ // + 2 offset : needed to preserve border shadow
+
+ m_leftWidget->setFixedHeight(r.height() - bottomWidgetHeight - hScrollBarHeight);
+}
+
+
+}
+#include "RosegardenCanvasView.moc"
diff --git a/src/gui/general/RosegardenCanvasView.h b/src/gui/general/RosegardenCanvasView.h
new file mode 100644
index 0000000..509c1aa
--- /dev/null
+++ b/src/gui/general/RosegardenCanvasView.h
@@ -0,0 +1,197 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_ROSEGARDENCANVASVIEW_H_
+#define _RG_ROSEGARDENCANVASVIEW_H_
+
+#include <qpoint.h>
+#include <qtimer.h>
+#include <qcanvas.h>
+#include <qdatetime.h>
+#include <qwmatrix.h>
+
+class QWidget;
+class QWheelEvent;
+class QScrollBar;
+class QResizeEvent;
+
+
+namespace Rosegarden
+{
+
+/**
+ * A QCanvasView with an auxiliary horiz. scrollbar
+ * That scrollbar should be provided by the parent widget
+ * (typically an EditView). The RosegardenCanvasView keeps
+ * the auxilliary horiz. scrollbar range in sync with the
+ * one of its own scrollbar with slotUpdate().
+ */
+
+class RosegardenCanvasView : public QCanvasView
+{
+ Q_OBJECT
+public:
+ RosegardenCanvasView(QCanvas*,
+ QWidget* parent=0, const char* name=0, WFlags f=0);
+
+ /**
+ * EditTool::handleMouseMove() returns a OR-ed combination of these
+ * to indicate which direction to scroll to
+ */
+ enum {
+ NoFollow = 0x0,
+ FollowHorizontal = 0x1,
+ FollowVertical = 0x2
+ };
+
+ /**
+ * Sets the canvas width to be exactly the width needed to show
+ * all the items
+ */
+ void fitWidthToContents();
+
+ /**
+ * Sets the widget which will be between the scrollable part of the view
+ * and the horizontal scrollbar
+ */
+ void setBottomFixedWidget(QWidget*);
+
+ void updateBottomWidgetGeometry();
+
+ /**
+ * Sets the widget which will be between the scrollable part of the view
+ * and the left edge of the view.
+ */
+ void setLeftFixedWidget(QWidget*);
+
+ void updateLeftWidgetGeometry();
+
+ /// Map a point with the inverse world matrix
+ QPoint inverseMapPoint(const QPoint& p) { return inverseWorldMatrix().map(p); }
+
+ void setSmoothScroll(bool s) { m_smoothScroll = s; }
+
+ bool isTimeForSmoothScroll();
+
+ void setScrollDirectionConstraint(int d) { m_scrollDirectionConstraint = d; }
+
+ bool isAutoScrolling() const { return m_autoScrolling; }
+
+ virtual void wheelEvent(QWheelEvent *);
+
+public slots:
+ /// Update the RosegardenCanvasView after a change of content
+ virtual void slotUpdate();
+
+ /**
+ * Scroll horizontally to make the given position visible,
+ * paging to as to get some visibility of the next screenful
+ * (for playback etc)
+ */
+ void slotScrollHoriz(int hpos);
+
+ /**
+ * Scroll horizontally to make the given position somewhat
+ * nearer to visible, scrolling by only "a small distance"
+ * at a time
+ */
+ void slotScrollHorizSmallSteps(int hpos);
+
+ /**
+ * Scroll vertically to make the given position somewhat
+ * nearer to visible, scrolling by only "a small distance"
+ * at a time
+ */
+ void slotScrollVertSmallSteps(int vpos);
+
+ /**
+ * Scroll vertically so as to place the given position
+ * somewhere near the top of the viewport.
+ */
+ void slotScrollVertToTop(int vpos);
+
+ /**
+ * Set the x and y scrollbars to a particular position
+ */
+ void slotSetScrollPos(const QPoint &);
+
+ void startAutoScroll();
+ void startAutoScroll(int directionConstraint);
+ void stopAutoScroll();
+ void doAutoScroll();
+
+signals:
+ void bottomWidgetHeightChanged(int);
+
+ void zoomIn();
+ void zoomOut();
+
+protected:
+
+ virtual void resizeEvent(QResizeEvent*);
+ virtual void setHBarGeometry(QScrollBar &hbar, int x, int y, int w, int h);
+
+ virtual QScrollBar* getMainHorizontalScrollBar() { return horizontalScrollBar(); }
+
+ //--------------- Data members ---------------------------------
+ enum ScrollDirection { None, Top, Bottom, Left, Right };
+
+
+ QWidget* m_bottomWidget;
+ int m_currentBottomWidgetHeight;
+
+ QWidget* m_leftWidget;
+
+ bool m_smoothScroll;
+ int m_smoothScrollTimeInterval;
+ float m_minDeltaScroll;
+ QTime m_scrollTimer;
+ QTime m_scrollAccelerationTimer;
+
+ QTimer m_autoScrollTimer;
+ int m_autoScrollTime;
+ int m_autoScrollAccel;
+ QPoint m_previousP;
+ int m_autoScrollXMargin;
+ int m_autoScrollYMargin;
+ ScrollDirection m_currentScrollDirection;
+ int m_scrollDirectionConstraint;
+ bool m_autoScrolling;
+
+ static const int DefaultSmoothScrollTimeInterval;
+ static const double DefaultMinDeltaScroll;
+
+ static const int AutoscrollMargin;
+ static const int InitialScrollTime;
+ static const int InitialScrollAccel;
+ static const int MaxScrollDelta;
+ static const double ScrollAccelValue;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/general/RosegardenScrollView.cpp b/src/gui/general/RosegardenScrollView.cpp
new file mode 100644
index 0000000..fbcaf79
--- /dev/null
+++ b/src/gui/general/RosegardenScrollView.cpp
@@ -0,0 +1,416 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "RosegardenScrollView.h"
+
+#include "misc/Debug.h"
+#include <qapplication.h>
+#include <qcursor.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qscrollbar.h>
+#include <qscrollview.h>
+#include <qsizepolicy.h>
+#include <qtimer.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+// Smooth scroll checks
+//
+
+const int RosegardenScrollView::AutoscrollMargin = 16;
+const int RosegardenScrollView::InitialScrollTime = 30;
+const int RosegardenScrollView::InitialScrollAccel = 5;
+const int RosegardenScrollView::MaxScrollDelta = 100; // max a.scroll speed
+const double RosegardenScrollView::ScrollAccelValue = 1.04;// acceleration rate
+
+RosegardenScrollView::RosegardenScrollView(QWidget* parent,
+ const char* name, WFlags f)
+ : QScrollView(parent, name, f),
+ m_bottomWidget(0),
+ m_currentBottomWidgetHeight( -1),
+ m_smoothScroll(true),
+ m_smoothScrollTimeInterval(DefaultSmoothScrollTimeInterval),
+ m_minDeltaScroll(DefaultMinDeltaScroll),
+ m_autoScrollTime(InitialScrollTime),
+ m_autoScrollAccel(InitialScrollAccel),
+ m_autoScrollXMargin(0),
+ m_autoScrollYMargin(0),
+ m_currentScrollDirection(None),
+ m_scrollDirectionConstraint(NoFollow),
+ m_autoScrolling(false)
+{
+ setDragAutoScroll(true);
+ connect( &m_autoScrollTimer, SIGNAL( timeout() ),
+ this, SLOT( doAutoScroll() ) );
+}
+
+void RosegardenScrollView::setBottomFixedWidget(QWidget* w)
+{
+ m_bottomWidget = w;
+ if (m_bottomWidget) {
+ m_bottomWidget->reparent(this, 0, QPoint(0, 0));
+ m_bottomWidget->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed));
+ setMargins(0, 0, 0, m_bottomWidget->sizeHint().height());
+ }
+}
+
+void RosegardenScrollView::startAutoScroll()
+{
+ // RG_DEBUG << "RosegardenScrollView::startAutoScroll()\n";
+
+ if ( !m_autoScrollTimer.isActive() ) {
+ m_autoScrollTime = InitialScrollTime;
+ m_autoScrollAccel = InitialScrollAccel;
+ m_autoScrollTimer.start( m_autoScrollTime );
+ }
+
+ QPoint autoScrollStartPoint = viewport()->mapFromGlobal( QCursor::pos() );
+ m_autoScrollYMargin = autoScrollStartPoint.y() / 10;
+ m_autoScrollXMargin = autoScrollStartPoint.x() / 10;
+
+ m_autoScrolling = true;
+}
+
+void RosegardenScrollView::startAutoScroll(int directionConstraint)
+{
+ setScrollDirectionConstraint(directionConstraint);
+ startAutoScroll();
+}
+
+void RosegardenScrollView::stopAutoScroll()
+{
+ // RG_DEBUG << "RosegardenScrollView::stopAutoScroll()\n";
+
+ m_autoScrollTimer.stop();
+ m_minDeltaScroll = DefaultMinDeltaScroll;
+ m_currentScrollDirection = None;
+
+ m_autoScrolling = false;
+}
+
+void RosegardenScrollView::doAutoScroll()
+{
+ // RG_DEBUG << "RosegardenScrollView::doAutoScroll()\n";
+
+ QPoint p = viewport()->mapFromGlobal( QCursor::pos() );
+ QPoint dp = p - m_previousP;
+ m_previousP = p;
+
+ m_autoScrollTimer.start( m_autoScrollTime );
+ ScrollDirection scrollDirection = None;
+
+ int dx = 0, dy = 0;
+ if (m_scrollDirectionConstraint & FollowVertical) {
+ if ( p.y() < m_autoScrollYMargin ) {
+ dy = -(int(m_minDeltaScroll));
+ scrollDirection = Top;
+ } else if ( p.y() > visibleHeight() - m_autoScrollYMargin ) {
+ dy = + (int(m_minDeltaScroll));
+ scrollDirection = Bottom;
+ }
+ }
+ bool startDecelerating = false;
+ if (m_scrollDirectionConstraint & FollowHorizontal) {
+
+ // RG_DEBUG << "p.x() : " << p.x() << " - visibleWidth : " << visibleWidth() << " - autoScrollXMargin : " << m_autoScrollXMargin << endl;
+
+ if ( p.x() < m_autoScrollXMargin ) {
+ if ( dp.x() > 0 ) {
+ startDecelerating = true;
+ m_minDeltaScroll /= ScrollAccelValue;
+ }
+ dx = -(int(m_minDeltaScroll));
+ scrollDirection = Left;
+ } else if ( p.x() > visibleWidth() - m_autoScrollXMargin ) {
+ if ( dp.x() < 0 ) {
+ startDecelerating = true;
+ m_minDeltaScroll /= ScrollAccelValue;
+ }
+ dx = + (int(m_minDeltaScroll));
+ scrollDirection = Right;
+ }
+ }
+
+ // RG_DEBUG << "dx: " << dx << ", dy: " << dy << endl;
+
+ if ( (dx || dy) &&
+ ((scrollDirection == m_currentScrollDirection) || (m_currentScrollDirection == None)) ) {
+ scrollBy(dx, dy);
+ if ( startDecelerating )
+ m_minDeltaScroll /= ScrollAccelValue;
+ else
+ m_minDeltaScroll *= ScrollAccelValue;
+ if (m_minDeltaScroll > MaxScrollDelta )
+ m_minDeltaScroll = MaxScrollDelta;
+ m_currentScrollDirection = scrollDirection;
+
+ } else {
+ // Don't automatically stopAutoScroll() here, the mouse button
+ // is presumably still pressed.
+ m_minDeltaScroll = DefaultMinDeltaScroll;
+ m_currentScrollDirection = None;
+ }
+
+}
+
+const int RosegardenScrollView::DefaultSmoothScrollTimeInterval = 10;
+const double RosegardenScrollView::DefaultMinDeltaScroll = 1.2;
+
+bool RosegardenScrollView::isTimeForSmoothScroll()
+{
+ static int desktopWidth = QApplication::desktop()->width(),
+ desktopHeight = QApplication::desktop()->height();
+
+ if (m_smoothScroll) {
+ int ta = m_scrollAccelerationTimer.elapsed();
+ int t = m_scrollTimer.elapsed();
+
+ RG_DEBUG << "t = " << t << ", ta = " << ta << ", int " << m_smoothScrollTimeInterval << ", delta " << m_minDeltaScroll << endl;
+
+ if (t < m_smoothScrollTimeInterval) {
+
+ return false;
+
+ } else {
+
+ if (ta > 300) {
+ // reset smoothScrollTimeInterval
+ m_smoothScrollTimeInterval = DefaultSmoothScrollTimeInterval;
+ m_minDeltaScroll = DefaultMinDeltaScroll;
+ m_scrollAccelerationTimer.restart();
+ } else if (ta > 50) {
+ // m_smoothScrollTimeInterval /= 2;
+ m_minDeltaScroll *= 1.08;
+ m_scrollAccelerationTimer.restart();
+ }
+
+ m_scrollTimer.restart();
+ return true;
+ }
+ }
+
+ return true;
+}
+
+void RosegardenScrollView::slotScrollHoriz(int hpos)
+{
+ QScrollBar* hbar = getMainHorizontalScrollBar();
+ int currentContentYPos = contentsY();
+
+ /* Lots of performance hitting debug
+ RG_DEBUG << "RosegardenCanvasView::slotScrollHoriz: hpos is " << hpos
+ << ", contentsX is " << contentsX() << ", visibleWidth is "
+ << visibleWidth() << endl;
+ */
+
+ if (hpos == 0) {
+
+ // returning to zero
+ // hbar->setValue(0);
+ setContentsPos(0, currentContentYPos);
+
+ } else if (hpos > (contentsX() +
+ visibleWidth() * 1.6) ||
+ hpos < (contentsX() -
+ visibleWidth() * 0.7)) {
+
+ // miles off one side or the other
+ // hbar->setValue(hpos - int(visibleWidth() * 0.4));
+ setContentsPos(hpos - int(visibleWidth() * 0.4), currentContentYPos);
+
+ } else if (hpos > (contentsX() +
+ visibleWidth() * 0.9)) {
+
+ // moving off the right hand side of the view
+ // hbar->setValue(hbar->value() + int(visibleWidth() * 0.6));
+ setContentsPos(hbar->value() + int(visibleWidth() * 0.6), currentContentYPos);
+
+ } else if (hpos < (contentsX() +
+ visibleWidth() * 0.1)) {
+
+ // moving off the left
+ // hbar->setValue(hbar->value() - int(visibleWidth() * 0.6));
+ setContentsPos(hbar->value() - int(visibleWidth() * 0.6), currentContentYPos);
+ }
+}
+
+void RosegardenScrollView::slotScrollHorizSmallSteps(int hpos)
+{
+ QScrollBar* hbar = getMainHorizontalScrollBar();
+ int currentContentYPos = contentsY();
+
+ int diff = 0;
+
+ if (hpos == 0) {
+
+ // returning to zero
+ // hbar->setValue(0);
+ setContentsPos(0, currentContentYPos);
+
+ } else if ((diff = int(hpos - (contentsX() +
+ visibleWidth() * 0.90))) > 0) {
+
+ // moving off the right hand side of the view
+
+ int delta = diff / 6;
+ int diff10 = std::min(diff, (int)m_minDeltaScroll);
+ delta = std::max(delta, diff10);
+
+ // hbar->setValue(hbar->value() + delta);
+ setContentsPos(hbar->value() + delta, currentContentYPos);
+
+ } else if ((diff = int(hpos - (contentsX() +
+ visibleWidth() * 0.10))) < 0) {
+ // moving off the left
+
+ int delta = -diff / 6;
+ int diff10 = std::min( -diff, (int)m_minDeltaScroll);
+ delta = std::max(delta, diff10);
+
+ // hbar->setValue(hbar->value() - delta);
+ setContentsPos(hbar->value() - delta, currentContentYPos);
+
+ }
+}
+
+void RosegardenScrollView::slotScrollVertSmallSteps(int vpos)
+{
+ QScrollBar* vbar = verticalScrollBar();
+
+ // RG_DEBUG << "RosegardenCanvasView::slotScrollVertSmallSteps: vpos is " << vpos << ", contentsY is " << contentsY() << ", visibleHeight is " << visibleHeight() << endl;
+
+ // As a special case (or hack), ignore any request made before we've
+ // actually been rendered and sized
+ if (visibleHeight() <= 1)
+ return ;
+
+ int diff = 0;
+
+ if (vpos == 0) {
+
+ // returning to zero
+ vbar->setValue(0);
+
+ } else if ((diff = int(vpos - (contentsY() +
+ visibleHeight() * 0.90))) > 0) {
+
+ // moving off up
+
+ int delta = diff / 6;
+ int diff10 = std::min(diff, (int)m_minDeltaScroll);
+ delta = std::max(delta, diff10);
+
+ vbar->setValue(vbar->value() + diff);
+
+ } else if ((diff = int(vpos - (contentsY() +
+ visibleHeight() * 0.10))) < 0) {
+
+ // moving off down
+
+ int delta = -diff / 6;
+ int diff10 = std::min( -diff, (int)m_minDeltaScroll);
+ delta = std::max(delta, diff10);
+
+ vbar->setValue(vbar->value() - delta);
+
+ }
+}
+
+void RosegardenScrollView::slotScrollVertToTop(int vpos)
+{
+ QScrollBar* vbar = verticalScrollBar();
+ if (vpos < visibleHeight() / 3)
+ vbar->setValue(0);
+ else
+ vbar->setValue(vpos - visibleHeight() / 5);
+}
+
+void RosegardenScrollView::slotSetScrollPos(const QPoint &pos)
+{
+ horizontalScrollBar()->setValue(pos.x());
+ verticalScrollBar()->setValue(pos.y());
+}
+
+void RosegardenScrollView::resizeEvent(QResizeEvent* e)
+{
+ QScrollView::resizeEvent(e);
+ if (!horizontalScrollBar()->isVisible())
+ updateBottomWidgetGeometry();
+
+}
+
+void RosegardenScrollView::setHBarGeometry(QScrollBar &hbar, int x, int y, int w, int h)
+{
+ QScrollView::setHBarGeometry(hbar, x, y, w, h);
+ updateBottomWidgetGeometry();
+}
+
+void RosegardenScrollView::updateBottomWidgetGeometry()
+{
+ if (!m_bottomWidget)
+ return ;
+
+ int bottomWidgetHeight = m_bottomWidget->sizeHint().height();
+
+ setMargins(0, 0, 0, bottomWidgetHeight);
+ QRect r = frameRect();
+ int hScrollBarHeight = 0;
+ if (horizontalScrollBar()->isVisible())
+ hScrollBarHeight = horizontalScrollBar()->height() + 2; // + 2 offset needed to preserve border shadow
+
+ int vScrollBarWidth = 0;
+ if (verticalScrollBar()->isVisible())
+ vScrollBarWidth = verticalScrollBar()->width();
+
+ m_bottomWidget->setGeometry(r.x(),
+ r.y() + r.height() - bottomWidgetHeight - hScrollBarHeight,
+ r.width() - vScrollBarWidth,
+ bottomWidgetHeight);
+
+ if (bottomWidgetHeight != m_currentBottomWidgetHeight) {
+ emit bottomWidgetHeightChanged(bottomWidgetHeight);
+ m_currentBottomWidgetHeight = bottomWidgetHeight;
+ }
+
+}
+
+void RosegardenScrollView::wheelEvent(QWheelEvent *e)
+{
+ if (e->state() & ControlButton) {
+ if (e->delta() > 0)
+ emit zoomIn();
+ else if (e->delta() < 0)
+ emit zoomOut();
+ return ;
+ }
+ QScrollView::wheelEvent(e);
+}
+
+}
+#include "RosegardenScrollView.moc"
diff --git a/src/gui/general/RosegardenScrollView.h b/src/gui/general/RosegardenScrollView.h
new file mode 100644
index 0000000..6a0dab7
--- /dev/null
+++ b/src/gui/general/RosegardenScrollView.h
@@ -0,0 +1,183 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_ROSEGARDENSCROLLVIEW_H_
+#define _RG_ROSEGARDENSCROLLVIEW_H_
+
+#include <qpoint.h>
+#include <qscrollview.h>
+#include <qdatetime.h>
+#include <qtimer.h>
+
+
+class QWidget;
+class QWheelEvent;
+class QScrollBar;
+class QResizeEvent;
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * A QScrollView with more elaborate auto-scrolling capabilities
+ * and the ability to have a "fixed" (non-scrolling) widget at its bottom,
+ * just above the bottom scrollbar.
+ */
+class RosegardenScrollView : public QScrollView
+{
+ Q_OBJECT
+public:
+ RosegardenScrollView(QWidget* parent=0, const char* name=0, WFlags f=0);
+
+ /**
+ * EditTool::handleMouseMove() returns a OR-ed combination of these
+ * to indicate which direction to scroll to
+ */
+ enum {
+ NoFollow = 0x0,
+ FollowHorizontal = 0x1,
+ FollowVertical = 0x2
+ };
+
+ /**
+ * Sets the canvas width to be exactly the width needed to show
+ * all the items
+ */
+// void fitWidthToContents();
+
+ /**
+ * Sets the widget which will be between the scrollable part of the view
+ * and the horizontal scrollbar
+ */
+ void setBottomFixedWidget(QWidget*);
+
+ void updateBottomWidgetGeometry();
+
+ /// Map a point with the inverse world matrix
+// QPoint inverseMapPoint(const QPoint& p) { return inverseWorldMatrix().map(p); }
+
+ void setSmoothScroll(bool s) { m_smoothScroll = s; }
+
+ bool isTimeForSmoothScroll();
+
+ void setScrollDirectionConstraint(int d) { m_scrollDirectionConstraint = d; }
+
+ int getDeltaScroll() { return m_minDeltaScroll; }
+
+ virtual void wheelEvent(QWheelEvent *);
+
+public slots:
+ /**
+ * Scroll horizontally to make the given position visible,
+ * paging to as to get some visibility of the next screenful
+ * (for playback etc)
+ */
+ void slotScrollHoriz(int hpos);
+
+ /**
+ * Scroll horizontally to make the given position somewhat
+ * nearer to visible, scrolling by only "a small distance"
+ * at a time
+ */
+ void slotScrollHorizSmallSteps(int hpos);
+
+ /**
+ * Scroll vertically to make the given position somewhat
+ * nearer to visible, scrolling by only "a small distance"
+ * at a time
+ */
+ void slotScrollVertSmallSteps(int vpos);
+
+ /**
+ * Scroll vertically so as to place the given position
+ * somewhere near the top of the viewport.
+ */
+ void slotScrollVertToTop(int vpos);
+
+ /**
+ * Set the x and y scrollbars to a particular position
+ */
+ void slotSetScrollPos(const QPoint &);
+
+ void startAutoScroll();
+ void startAutoScroll(int directionConstraint);
+ void stopAutoScroll();
+ void doAutoScroll();
+
+ bool isAutoScrolling() const { return m_autoScrolling; }
+
+signals:
+ void bottomWidgetHeightChanged(int);
+
+ void zoomIn();
+ void zoomOut();
+
+protected:
+
+ virtual void resizeEvent(QResizeEvent*);
+ virtual void setHBarGeometry(QScrollBar &hbar, int x, int y, int w, int h);
+
+ virtual QScrollBar* getMainHorizontalScrollBar() { return horizontalScrollBar(); }
+
+ //--------------- Data members ---------------------------------
+ enum ScrollDirection { None, Top, Bottom, Left, Right };
+
+ QWidget* m_bottomWidget;
+ int m_currentBottomWidgetHeight;
+
+ bool m_smoothScroll;
+ int m_smoothScrollTimeInterval;
+ float m_minDeltaScroll;
+ QTime m_scrollTimer;
+ QTime m_scrollAccelerationTimer;
+
+ QTimer m_autoScrollTimer;
+ int m_autoScrollTime;
+ int m_autoScrollAccel;
+ QPoint m_previousP;
+ int m_autoScrollXMargin;
+ int m_autoScrollYMargin;
+ ScrollDirection m_currentScrollDirection;
+ int m_scrollDirectionConstraint;
+ bool m_autoScrolling;
+
+ static const int DefaultSmoothScrollTimeInterval;
+ static const double DefaultMinDeltaScroll;
+
+ static const int AutoscrollMargin;
+ static const int InitialScrollTime;
+ static const int InitialScrollAccel;
+ static const int MaxScrollDelta;
+ static const double ScrollAccelValue;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/general/Spline.cpp b/src/gui/general/Spline.cpp
new file mode 100644
index 0000000..455cca5
--- /dev/null
+++ b/src/gui/general/Spline.cpp
@@ -0,0 +1,130 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "Spline.h"
+
+#include <qpoint.h>
+
+
+namespace Rosegarden
+{
+
+Spline::PointList *
+
+Spline::calculate(const QPoint &s, const QPoint &f, const PointList &cp,
+ QPoint &topLeft, QPoint &bottomRight)
+{
+ if (cp.size() < 2)
+ return 0;
+
+ int i;
+ PointList *acc = new PointList();
+ QPoint p(s);
+
+ topLeft = bottomRight = QPoint(0, 0);
+
+ for (i = 1; i < cp.size(); ++i) {
+
+ QPoint c(cp[i - 1]);
+
+ int x = (c.x() + cp[i].x()) / 2;
+ int y = (c.y() + cp[i].y()) / 2;
+ QPoint n(x, y);
+
+ calculateSegment(acc, p, n, c, topLeft, bottomRight);
+
+ p = n;
+ }
+
+ calculateSegment(acc, p, f, cp[i - 1], topLeft, bottomRight);
+
+ return acc;
+}
+
+void
+Spline::calculateSegment(PointList *acc,
+ const QPoint &s, const QPoint &f, const QPoint &c,
+ QPoint &topLeft, QPoint &bottomRight)
+{
+ int x, y, n;
+
+ x = c.x() - s.x();
+ y = c.y() - s.y();
+
+ if (x < 0)
+ x = -x;
+ if (y < 0)
+ y = -y;
+ if (x > y)
+ n = x;
+ else
+ n = y;
+
+ x = f.x() - c.x();
+ y = f.y() - c.y();
+
+ if (x < 0)
+ x = -x;
+ if (y < 0)
+ y = -y;
+ if (x > y)
+ n += x;
+ else
+ n += y;
+
+ calculateSegmentSub(acc, s, f, c, n, topLeft, bottomRight);
+}
+
+void
+Spline::calculateSegmentSub(PointList *acc,
+ const QPoint &s, const QPoint &f, const QPoint &c,
+ int n, QPoint &topLeft, QPoint &bottomRight)
+{
+ double ax = (double)(f.x() + s.x() - 2 * c.x()) / (double)n;
+ double ay = (double)(f.y() + s.y() - 2 * c.y()) / (double)n;
+
+ double bx = 2.0 * (double)(c.x() - s.x());
+ double by = 2.0 * (double)(c.y() - s.y());
+
+ for (int m = 0; m <= n; ++m) {
+
+ int x = s.x() + (int)((m * ((double)m * ax + bx)) / n);
+ int y = s.y() + (int)((m * ((double)m * ay + by)) / n);
+
+ if (x < topLeft.x())
+ topLeft.setX(x);
+ if (y < topLeft.y())
+ topLeft.setY(y);
+
+ if (x > bottomRight.x())
+ bottomRight.setX(x);
+ if (y > bottomRight.y())
+ bottomRight.setY(y);
+
+ acc->push_back(QPoint(x, y));
+ }
+}
+
+}
diff --git a/src/gui/general/Spline.h b/src/gui/general/Spline.h
new file mode 100644
index 0000000..63946a5
--- /dev/null
+++ b/src/gui/general/Spline.h
@@ -0,0 +1,71 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_SPLINE_H_
+#define _RG_SPLINE_H_
+
+#include "base/FastVector.h"
+
+
+class QPoint;
+class PointList;
+
+
+namespace Rosegarden
+{
+
+
+
+class Spline
+{
+public:
+ typedef FastVector<QPoint> PointList;
+
+ /**
+ * Calculate a set of polyline points to approximate
+ * a Bezier spline. Caller takes ownership of returned
+ * heap-allocated container.
+ */
+ static PointList *calculate(const QPoint &start, const QPoint &finish,
+ const PointList &controlPoints,
+ QPoint &topLeft, QPoint &bottomRight);
+
+private:
+ static void calculateSegment
+ (PointList *acc,
+ const QPoint &start, const QPoint &finish, const QPoint &control,
+ QPoint &topLeft, QPoint &bottomRight);
+
+ static void calculateSegmentSub
+ (PointList *acc,
+ const QPoint &start, const QPoint &finish, const QPoint &control, int n,
+ QPoint &topLeft, QPoint &bottomRight);
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/general/StaffLine.cpp b/src/gui/general/StaffLine.cpp
new file mode 100644
index 0000000..ab5d5ff
--- /dev/null
+++ b/src/gui/general/StaffLine.cpp
@@ -0,0 +1,64 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+
+#include "StaffLine.h"
+
+#include "misc/Debug.h"
+#include <qcanvas.h>
+#include <qpen.h>
+
+
+namespace Rosegarden
+{
+
+StaffLine::StaffLine(QCanvas *c, QCanvasItemGroup *g, int height) :
+ QCanvasLineGroupable(c, g),
+ m_height(height),
+ m_significant(true)
+{
+ setZ(1);
+}
+
+void
+StaffLine::setHighlighted(bool highlighted)
+{
+ // RG_DEBUG << "StaffLine::setHighlighted("
+ // << highlighted << ")\n";
+
+ if (highlighted) {
+
+ m_normalPen = pen();
+ QPen newPen = m_normalPen;
+ newPen.setColor(red);
+ setPen(newPen);
+
+ } else {
+
+ setPen(m_normalPen);
+
+ }
+}
+
+}
diff --git a/src/gui/general/StaffLine.h b/src/gui/general/StaffLine.h
new file mode 100644
index 0000000..7d01ff4
--- /dev/null
+++ b/src/gui/general/StaffLine.h
@@ -0,0 +1,78 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_STAFFLINE_H_
+#define _RG_STAFFLINE_H_
+
+#include "gui/kdeext/QCanvasGroupableItem.h"
+#include <qpen.h>
+
+
+class QCanvasItemGroup;
+class QCanvas;
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * A staff line
+ *
+ * A groupable line which can be "highlighted"
+ * (drawn with a different color)
+ */
+class StaffLine : public QCanvasLineGroupable
+{
+public:
+ StaffLine(QCanvas *c, QCanvasItemGroup *g, int height);
+
+ enum { NoHeight = -150 };
+
+ void setHeight(int h) { m_height = h; }
+ int getHeight() const { return m_height; }
+
+ void setSignificant(bool s) { m_significant = s; }
+ bool isSignificant() const { return m_significant; }
+
+ /**
+ * "highlight" the line (set its pen to red)
+ */
+ void setHighlighted(bool);
+
+protected:
+ //--------------- Data members ---------------------------------
+
+ int m_height;
+ bool m_significant;
+
+ QPen m_normalPen;
+};
+
+
+}
+
+#endif