diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 18:37:05 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 18:37:05 +0000 |
commit | 145364a8af6a1fec06556221e66d4b724a62fc9a (patch) | |
tree | 53bd71a544008c518034f208d64c932dc2883f50 /src/gui/general | |
download | rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.tar.gz rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.zip |
Added old abandoned KDE3 version of the RoseGarden MIDI tool
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/rosegarden@1097595 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/gui/general')
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 |