summaryrefslogtreecommitdiffstats
path: root/src/gui/editors/notation
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/editors/notation')
-rw-r--r--src/gui/editors/notation/ClefInserter.cpp132
-rw-r--r--src/gui/editors/notation/ClefInserter.h83
-rw-r--r--src/gui/editors/notation/FontViewFrame.cpp252
-rw-r--r--src/gui/editors/notation/FontViewFrame.h77
-rw-r--r--src/gui/editors/notation/GuitarChordInserter.cpp185
-rw-r--r--src/gui/editors/notation/GuitarChordInserter.h96
-rw-r--r--src/gui/editors/notation/HeadersGroup.cpp160
-rw-r--r--src/gui/editors/notation/HeadersGroup.h144
-rw-r--r--src/gui/editors/notation/NotationCanvasView.cpp485
-rw-r--r--src/gui/editors/notation/NotationCanvasView.h218
-rw-r--r--src/gui/editors/notation/NotationChord.cpp335
-rw-r--r--src/gui/editors/notation/NotationChord.h90
-rw-r--r--src/gui/editors/notation/NotationElement.cpp198
-rw-r--r--src/gui/editors/notation/NotationElement.h176
-rw-r--r--src/gui/editors/notation/NotationEraser.cpp115
-rw-r--r--src/gui/editors/notation/NotationEraser.h81
-rw-r--r--src/gui/editors/notation/NotationGroup.cpp979
-rw-r--r--src/gui/editors/notation/NotationGroup.h133
-rw-r--r--src/gui/editors/notation/NotationHLayout.cpp2110
-rw-r--r--src/gui/editors/notation/NotationHLayout.h446
-rw-r--r--src/gui/editors/notation/NotationProperties.cpp85
-rw-r--r--src/gui/editors/notation/NotationProperties.h108
-rw-r--r--src/gui/editors/notation/NotationSelectionPaster.cpp89
-rw-r--r--src/gui/editors/notation/NotationSelectionPaster.h72
-rw-r--r--src/gui/editors/notation/NotationSelector.cpp957
-rw-r--r--src/gui/editors/notation/NotationSelector.h197
-rw-r--r--src/gui/editors/notation/NotationStaff.cpp2300
-rw-r--r--src/gui/editors/notation/NotationStaff.h488
-rw-r--r--src/gui/editors/notation/NotationStrings.cpp301
-rw-r--r--src/gui/editors/notation/NotationStrings.h121
-rw-r--r--src/gui/editors/notation/NotationTool.cpp57
-rw-r--r--src/gui/editors/notation/NotationTool.h93
-rw-r--r--src/gui/editors/notation/NotationToolBox.cpp102
-rw-r--r--src/gui/editors/notation/NotationToolBox.h65
-rw-r--r--src/gui/editors/notation/NotationVLayout.cpp731
-rw-r--r--src/gui/editors/notation/NotationVLayout.h122
-rw-r--r--src/gui/editors/notation/NotationView.cpp7552
-rw-r--r--src/gui/editors/notation/NotationView.h1131
-rw-r--r--src/gui/editors/notation/NoteCharacter.cpp133
-rw-r--r--src/gui/editors/notation/NoteCharacter.h93
-rw-r--r--src/gui/editors/notation/NoteCharacterNames.cpp123
-rw-r--r--src/gui/editors/notation/NoteCharacterNames.h120
-rw-r--r--src/gui/editors/notation/NoteFont.cpp650
-rw-r--r--src/gui/editors/notation/NoteFont.h184
-rw-r--r--src/gui/editors/notation/NoteFontFactory.cpp236
-rw-r--r--src/gui/editors/notation/NoteFontFactory.h71
-rw-r--r--src/gui/editors/notation/NoteFontMap.cpp1088
-rw-r--r--src/gui/editors/notation/NoteFontMap.h333
-rw-r--r--src/gui/editors/notation/NoteFontViewer.cpp125
-rw-r--r--src/gui/editors/notation/NoteFontViewer.h68
-rw-r--r--src/gui/editors/notation/NoteInserter.cpp722
-rw-r--r--src/gui/editors/notation/NoteInserter.h166
-rw-r--r--src/gui/editors/notation/NotePixmapFactory.cpp3689
-rw-r--r--src/gui/editors/notation/NotePixmapFactory.h358
-rw-r--r--src/gui/editors/notation/NotePixmapPainter.h148
-rw-r--r--src/gui/editors/notation/NotePixmapParameters.cpp151
-rw-r--r--src/gui/editors/notation/NotePixmapParameters.h161
-rw-r--r--src/gui/editors/notation/NoteStyle.cpp485
-rw-r--r--src/gui/editors/notation/NoteStyle.h142
-rw-r--r--src/gui/editors/notation/NoteStyleFactory.cpp124
-rw-r--r--src/gui/editors/notation/NoteStyleFactory.h61
-rw-r--r--src/gui/editors/notation/NoteStyleFileReader.cpp193
-rw-r--r--src/gui/editors/notation/NoteStyleFileReader.h59
-rw-r--r--src/gui/editors/notation/RestInserter.cpp150
-rw-r--r--src/gui/editors/notation/RestInserter.h76
-rw-r--r--src/gui/editors/notation/SystemFont.cpp165
-rw-r--r--src/gui/editors/notation/SystemFont.h63
-rw-r--r--src/gui/editors/notation/SystemFontQt.cpp78
-rw-r--r--src/gui/editors/notation/SystemFontQt.h49
-rw-r--r--src/gui/editors/notation/SystemFontXft.cpp193
-rw-r--r--src/gui/editors/notation/SystemFontXft.h58
-rw-r--r--src/gui/editors/notation/TextInserter.cpp169
-rw-r--r--src/gui/editors/notation/TextInserter.h78
-rw-r--r--src/gui/editors/notation/TrackHeader.cpp450
-rw-r--r--src/gui/editors/notation/TrackHeader.h219
75 files changed, 32497 insertions, 0 deletions
diff --git a/src/gui/editors/notation/ClefInserter.cpp b/src/gui/editors/notation/ClefInserter.cpp
new file mode 100644
index 0000000..f39327e
--- /dev/null
+++ b/src/gui/editors/notation/ClefInserter.cpp
@@ -0,0 +1,132 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "ClefInserter.h"
+
+#include <klocale.h>
+#include "base/Event.h"
+#include "base/NotationTypes.h"
+#include "base/ViewElement.h"
+#include "commands/notation/ClefInsertionCommand.h"
+#include "gui/general/EditTool.h"
+#include "gui/general/LinedStaff.h"
+#include "NotationElement.h"
+#include "NotationTool.h"
+#include "NotationView.h"
+#include "NotePixmapFactory.h"
+#include <kaction.h>
+#include <qiconset.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+ClefInserter::ClefInserter(NotationView* view)
+ : NotationTool("ClefInserter", view),
+ m_clef(Clef::Treble)
+{
+ QIconSet icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("select")));
+ new KAction(i18n("Switch to Select Tool"), icon, 0, this,
+ SLOT(slotSelectSelected()), actionCollection(),
+ "select");
+
+ new KAction(i18n("Switch to Erase Tool"), "eraser", 0, this,
+ SLOT(slotEraseSelected()), actionCollection(),
+ "erase");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("crotchet")));
+ new KAction(i18n("Switch to Inserting Notes"), icon, 0, this,
+ SLOT(slotNotesSelected()), actionCollection(),
+ "notes");
+
+ createMenu("clefinserter.rc");
+}
+
+void ClefInserter::slotNotesSelected()
+{
+ m_nParentView->slotLastNoteAction();
+}
+
+void ClefInserter::slotEraseSelected()
+{
+ m_parentView->actionCollection()->action("erase")->activate();
+}
+
+void ClefInserter::slotSelectSelected()
+{
+ m_parentView->actionCollection()->action("select")->activate();
+}
+
+void ClefInserter::ready()
+{
+ m_nParentView->setCanvasCursor(Qt::crossCursor);
+ m_nParentView->setHeightTracking(false);
+}
+
+void ClefInserter::setClef(std::string clefType)
+{
+ m_clef = clefType;
+}
+
+void ClefInserter::handleLeftButtonPress(timeT,
+ int,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement*)
+{
+ if (staffNo < 0)
+ return ;
+ Event *clef = 0, *key = 0;
+
+ LinedStaff *staff = m_nParentView->getLinedStaff(staffNo);
+
+ NotationElementList::iterator closestElement =
+ staff->getClosestElementToCanvasCoords(e->x(), (int)e->y(),
+ clef, key, false, -1);
+
+ if (closestElement == staff->getViewElementList()->end())
+ return ;
+
+ timeT time = (*closestElement)->event()->getAbsoluteTime(); // not getViewAbsoluteTime()
+
+
+ ClefInsertionCommand *command =
+ new ClefInsertionCommand(staff->getSegment(), time, m_clef);
+
+ m_nParentView->addCommandToHistory(command);
+
+ Event *event = command->getLastInsertedEvent();
+ if (event)
+ m_nParentView->setSingleSelectedEvent(staffNo, event);
+}
+
+const QString ClefInserter::ToolName = "clefinserter";
+
+}
+#include "ClefInserter.moc"
diff --git a/src/gui/editors/notation/ClefInserter.h b/src/gui/editors/notation/ClefInserter.h
new file mode 100644
index 0000000..460bfa5
--- /dev/null
+++ b/src/gui/editors/notation/ClefInserter.h
@@ -0,0 +1,83 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_CLEFINSERTER_H_
+#define _RG_CLEFINSERTER_H_
+
+#include "base/NotationTypes.h"
+#include "NotationTool.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class NotationView;
+
+
+/**
+ * This tool will insert clefs on mouse click events
+ */
+class ClefInserter : public NotationTool
+{
+ Q_OBJECT
+
+ friend class NotationToolBox;
+
+public:
+ void setClef(std::string clefType);
+
+ virtual void ready();
+
+ virtual void handleLeftButtonPress(timeT,
+ int height,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement* el);
+ static const QString ToolName;
+
+protected slots:
+ void slotNotesSelected();
+ void slotEraseSelected();
+ void slotSelectSelected();
+
+protected:
+ ClefInserter(NotationView*);
+
+ //--------------- Data members ---------------------------------
+
+ Clef m_clef;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/FontViewFrame.cpp b/src/gui/editors/notation/FontViewFrame.cpp
new file mode 100644
index 0000000..ab0498f
--- /dev/null
+++ b/src/gui/editors/notation/FontViewFrame.cpp
@@ -0,0 +1,252 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "FontViewFrame.h"
+#include <kapplication.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <qfontmetrics.h>
+#include <qframe.h>
+#include <qsize.h>
+#include <qstring.h>
+#include <qwidget.h>
+#include <qpainter.h>
+
+#ifdef HAVE_XFT
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+#include FT_GLYPH_H
+#include <X11/Xft/Xft.h>
+#endif
+
+namespace Rosegarden
+{
+
+FontViewFrame::FontViewFrame( int pixelSize, QWidget* parent, const char* name ) :
+ QFrame(parent, name),
+ m_fontSize(pixelSize),
+ m_tableFont(0)
+{
+ setBackgroundMode(PaletteBase);
+ setFrameStyle(Panel | Sunken);
+ setMargin(8);
+ setRow(0);
+}
+
+FontViewFrame::~FontViewFrame()
+{
+ // empty
+}
+
+void
+FontViewFrame::setFont(QString font)
+{
+ m_fontName = font;
+ loadFont();
+ update();
+}
+
+void
+FontViewFrame::loadFont()
+{
+#ifdef HAVE_XFT
+ if (m_tableFont) {
+ XftFontClose(x11AppDisplay(), (XftFont *)m_tableFont);
+ }
+ m_tableFont = 0;
+
+ static bool haveDir = false;
+ if (!haveDir) {
+ FcConfigAppFontAddDir(FcConfigGetCurrent(),
+ (const FcChar8 *)"/opt/kde3/share/apps/rosegarden/fonts");
+ haveDir = true;
+ }
+
+ FcPattern *pattern = FcPatternCreate();
+ FcPatternAddString(pattern, FC_FAMILY, (FcChar8 *)m_fontName.latin1());
+ FcPatternAddInteger(pattern, FC_PIXEL_SIZE, m_fontSize);
+
+ FcConfigSubstitute(FcConfigGetCurrent(), pattern, FcMatchPattern);
+
+ FcResult result = FcResultMatch;
+ FcPattern *match = FcFontMatch(FcConfigGetCurrent(), pattern, &result);
+ FcPatternDestroy(pattern);
+
+ if (!match || result != FcResultMatch) {
+ KMessageBox::error(this, i18n("Error: Unable to match font name %1").arg(m_fontName));
+ return ;
+ }
+
+ FcChar8 *matchFamily;
+ FcPatternGetString(match, FC_FAMILY, 0, &matchFamily);
+
+ if (QString((const char *)matchFamily).lower() != m_fontName.lower()) {
+ KMessageBox::sorry(this, i18n("Warning: No good match for font name %1 (best is %2)").
+ arg(m_fontName).arg(QString((const char *)matchFamily)));
+ m_fontName = (const char *)matchFamily;
+ }
+
+ m_tableFont = XftFontOpenPattern(x11AppDisplay(), match);
+
+ if (!m_tableFont) {
+ KMessageBox::error(this, i18n("Error: Unable to open best-match font %1").
+ arg(QString((const char *)matchFamily)));
+ }
+#endif
+}
+
+void FontViewFrame::setGlyphs(bool glyphs)
+{
+ m_glyphs = glyphs;
+ update();
+}
+
+QSize FontViewFrame::sizeHint() const
+{
+ return QSize(16 * m_fontSize * 3 / 2 + margin() + 2 * frameWidth(),
+ 16 * m_fontSize * 3 / 2 + margin() + 2 * frameWidth());
+}
+
+QSize FontViewFrame::cellSize() const
+{
+ QFontMetrics fm = fontMetrics();
+ return QSize( fm.maxWidth(), fm.lineSpacing() + 1 );
+}
+
+void FontViewFrame::paintEvent( QPaintEvent* e )
+{
+#ifdef HAVE_XFT
+ if (!m_tableFont)
+ return ;
+
+ QFrame::paintEvent(e);
+ QPainter p(this);
+
+ int ll = 25;
+ int ml = frameWidth() + margin() + ll + 1;
+ int mt = frameWidth() + margin();
+ QSize cell((width() - 16 - ml) / 17, (height() - 16 - mt) / 17);
+
+ if ( !cell.width() || !cell.height() )
+ return ;
+
+ QColor body(255, 255, 192);
+ QColor negative(255, 192, 192);
+ QColor positive(192, 192, 255);
+ QColor rnegative(255, 128, 128);
+ QColor rpositive(128, 128, 255);
+
+ Drawable drawable = (Drawable)handle();
+ XftDraw *draw = XftDrawCreate(x11AppDisplay(), drawable,
+ (Visual *)x11Visual(), x11Colormap());
+
+ QColor pen(Qt::black);
+ XftColor col;
+ col.color.red = pen.red () | pen.red() << 8;
+ col.color.green = pen.green () | pen.green() << 8;
+ col.color.blue = pen.blue () | pen.blue() << 8;
+ col.color.alpha = 0xffff;
+ col.pixel = pen.pixel();
+
+ for (int j = 0; j <= 16; j++) {
+ for (int i = 0; i <= 16; i++) {
+
+ int x = i * cell.width();
+ int y = j * cell.height();
+
+ x += ml;
+ y += mt; // plus ascent
+
+ if (i == 0) {
+ if (j == 0)
+ continue;
+ p.setFont(kapp->font());
+ QFontMetrics afm(kapp->font());
+ QString s = QString("%1").arg(m_row * 256 + (j - 1) * 16);
+ p.drawText(x - afm.width(s), y, s);
+ p.setPen(QColor(190, 190, 255));
+ p.drawLine(0, y, width(), y);
+ p.setPen(Qt::black);
+ continue;
+ } else if (j == 0) {
+ p.setFont(kapp->font());
+ QString s = QString("%1").arg(i - 1);
+ p.drawText(x, y, s);
+ p.setPen(QColor(190, 190, 255));
+ p.drawLine(x, 0, x, height());
+ p.setPen(Qt::black);
+ continue;
+ }
+
+ p.save();
+
+ if (m_glyphs) {
+ FT_UInt ui = m_row * 256 + (j - 1) * 16 + i - 1;
+ XftDrawGlyphs(draw, &col, (XftFont *)m_tableFont, x, y, &ui, 1);
+ } else {
+ FcChar32 ch = m_row * 256 + (j - 1) * 16 + i - 1;
+ if (XftCharExists(x11AppDisplay(), (XftFont *)m_tableFont, ch)) {
+ XftDrawString32(draw, &col, (XftFont *)m_tableFont, x, y, &ch, 1);
+ }
+ }
+
+ p.restore();
+ }
+ }
+#endif
+}
+
+bool
+FontViewFrame::hasRow(int r) const
+{
+#ifdef HAVE_XFT
+ if (m_glyphs) {
+
+ if (r < 256)
+ return true;
+
+ } else {
+
+ for (int c = 0; c < 256; ++c) {
+ FcChar32 ch = r * 256 + c;
+ if (XftCharExists(x11AppDisplay(), (XftFont *)m_tableFont, ch)) {
+ return true;
+ }
+ }
+ }
+#endif
+ return false;
+}
+
+void FontViewFrame::setRow(int row)
+{
+ m_row = row;
+ update();
+}
+
+}
+#include "FontViewFrame.moc"
diff --git a/src/gui/editors/notation/FontViewFrame.h b/src/gui/editors/notation/FontViewFrame.h
new file mode 100644
index 0000000..8a1a946
--- /dev/null
+++ b/src/gui/editors/notation/FontViewFrame.h
@@ -0,0 +1,77 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_FONTVIEWFRAME_H_
+#define _RG_FONTVIEWFRAME_H_
+
+#include <qframe.h>
+#include <qsize.h>
+#include <qstring.h>
+
+
+class QWidget;
+class QPaintEvent;
+
+
+namespace Rosegarden
+{
+
+
+
+class FontViewFrame : public QFrame
+{
+ Q_OBJECT
+
+public:
+ FontViewFrame(int pixelSize, QWidget *parent = 0, const char *name = 0);
+ virtual ~FontViewFrame();
+
+ QSize sizeHint() const;
+ bool hasRow(int row) const;
+
+public slots:
+ void setFont(QString name);
+ void setRow(int);
+ void setGlyphs(bool glyphs);
+
+protected:
+ QSize cellSize() const;
+ void paintEvent( QPaintEvent* );
+ void loadFont();
+
+private:
+ QString m_fontName;
+ int m_fontSize;
+ void *m_tableFont;
+ int m_row;
+ bool m_glyphs;
+};
+
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/GuitarChordInserter.cpp b/src/gui/editors/notation/GuitarChordInserter.cpp
new file mode 100644
index 0000000..2482b87
--- /dev/null
+++ b/src/gui/editors/notation/GuitarChordInserter.cpp
@@ -0,0 +1,185 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "GuitarChordInserter.h"
+
+#include <klocale.h>
+#include "base/Event.h"
+#include "base/Exception.h"
+#include "base/Staff.h"
+#include "base/ViewElement.h"
+#include "commands/notation/EraseEventCommand.h"
+#include "commands/notation/GuitarChordInsertionCommand.h"
+#include "gui/general/EditTool.h"
+#include "gui/general/LinedStaff.h"
+#include "gui/editors/guitar/GuitarChordSelectorDialog.h"
+#include "misc/Debug.h"
+#include "NotationElement.h"
+#include "NotationTool.h"
+#include "NotationView.h"
+#include "NotePixmapFactory.h"
+#include <kaction.h>
+#include <qdialog.h>
+#include <qiconset.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+GuitarChordInserter::GuitarChordInserter(NotationView* view)
+ : NotationTool("GuitarChordInserter", view),
+ m_guitarChordSelector(0)
+{
+ QIconSet icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("select")));
+
+ new KAction(i18n("Switch to Select Tool"), icon, 0, this,
+ SLOT(slotSelectSelected()), actionCollection(),
+ "select");
+
+ new KAction(i18n("Switch to Erase Tool"), "eraser", 0, this,
+ SLOT(slotEraseSelected()), actionCollection(),
+ "erase");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("crotchet")));
+
+ new KAction(i18n("Switch to Inserting Notes"), icon, 0, this,
+ SLOT(slotNoteSelected()), actionCollection(),
+ "notes");
+
+ m_guitarChordSelector = new GuitarChordSelectorDialog(m_nParentView);
+ m_guitarChordSelector->init();
+ createMenu("guitarchordinserter.rc");
+}
+
+void GuitarChordInserter::slotGuitarChordSelected()
+{
+ // Switch to last selected Guitar Chord
+ // m_nParentView->slotLastGuitarChordAction();
+}
+
+void GuitarChordInserter::slotEraseSelected()
+{
+ m_parentView->actionCollection()->action("erase")->activate();
+}
+
+void GuitarChordInserter::slotSelectSelected()
+{
+ m_parentView->actionCollection()->action("select")->activate();
+}
+
+void GuitarChordInserter::handleLeftButtonPress(timeT,
+ int,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement *element)
+{
+ NOTATION_DEBUG << "GuitarChordInserter::handleLeftButtonPress" << endl;
+
+ if (staffNo < 0) {
+ return ;
+ }
+
+ Staff *staff = m_nParentView->getStaff(staffNo);
+
+ if (element && element->event()->isa(Guitar::Chord::EventType)) {
+ handleSelectedGuitarChord (element, staff);
+ } else {
+ createNewGuitarChord (element, staff, e);
+ }
+}
+
+bool GuitarChordInserter::processDialog( Staff* staff,
+ timeT& insertionTime)
+{
+ bool result = false;
+
+ if (m_guitarChordSelector->exec() == QDialog::Accepted) {
+ Guitar::Chord chord = m_guitarChordSelector->getChord();
+
+ GuitarChordInsertionCommand *command =
+ new GuitarChordInsertionCommand
+ (staff->getSegment(), insertionTime, chord);
+
+ m_nParentView->addCommandToHistory(command);
+ result = true;
+ }
+
+ return result;
+}
+
+void GuitarChordInserter::handleSelectedGuitarChord (ViewElement* element, Staff *staff)
+{
+ NOTATION_DEBUG << "GuitarChordInserter::handleSelectedGuitarChord" << endl;
+
+
+ // Get time of where guitar chord is inserted
+ timeT insertionTime = element->event()->getAbsoluteTime(); // not getViewAbsoluteTime()
+
+ // edit an existing guitar chord, if that's what we clicked on
+ try {
+ Guitar::Chord chord(*(element->event()));
+
+ m_guitarChordSelector->setChord(chord);
+
+ if ( processDialog( staff, insertionTime ) ) {
+ // Erase old guitar chord
+ EraseEventCommand *command =
+ new EraseEventCommand(staff->getSegment(),
+ element->event(),
+ false);
+
+ m_nParentView->addCommandToHistory(command);
+ }
+ } catch (Exception e) {}
+}
+
+void GuitarChordInserter::createNewGuitarChord (ViewElement* element, Staff *staff, QMouseEvent* e)
+{
+ NOTATION_DEBUG << "GuitarChordInserter::createNewGuitarChord" << endl;
+ Event *clef = 0, *key = 0;
+
+ LinedStaff *s = dynamic_cast<LinedStaff *>(staff);
+
+ NotationElementList::iterator closestElement =
+ s->getClosestElementToCanvasCoords(e->x(), (int)e->y(),
+ clef, key, false, -1);
+
+ if (closestElement == staff->getViewElementList()->end()) {
+ return ;
+ }
+
+ timeT insertionTime = (*closestElement)->event()->getAbsoluteTime(); // not getViewAbsoluteTime()
+
+ processDialog( staff, insertionTime );
+}
+
+const QString GuitarChordInserter::ToolName = "guitarchordinserter";
+
+}
+#include "GuitarChordInserter.moc"
diff --git a/src/gui/editors/notation/GuitarChordInserter.h b/src/gui/editors/notation/GuitarChordInserter.h
new file mode 100644
index 0000000..3bd5660
--- /dev/null
+++ b/src/gui/editors/notation/GuitarChordInserter.h
@@ -0,0 +1,96 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_GUITAR_CHORD_INSERTER_H_
+#define _RG_GUITAR_CHORD_INSERTER_H_
+
+#include "NotationTool.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class Staff;
+class NotationView;
+class GuitarChordSelectorDialog;
+
+/**
+ * This tool will insert guitar chord on mouse click events
+*/
+class GuitarChordInserter : public NotationTool
+{
+ Q_OBJECT
+
+ friend class NotationToolBox;
+
+public:
+
+ virtual void handleLeftButtonPress(timeT t,
+ int height,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement *element);
+
+/*
+ virtual void handleMouseDoubleClick(timeT,
+ int height, int staffNo,
+ QMouseEvent*,
+ ViewElement* el);
+*/
+
+ static const QString ToolName;
+
+protected slots:
+ void slotGuitarChordSelected();
+ void slotEraseSelected();
+ void slotSelectSelected();
+
+protected:
+ GuitarChordSelectorDialog* m_guitarChordSelector;
+
+ GuitarChordInserter(NotationView*);
+
+private:
+ void handleSelectedGuitarChord (ViewElement* element,
+ Staff *staff);
+
+ void createNewGuitarChord (ViewElement* element,
+ Staff *staff,
+ QMouseEvent* e);
+
+ bool processDialog (Staff *staff,
+ timeT& insertionTime);
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/HeadersGroup.cpp b/src/gui/editors/notation/HeadersGroup.cpp
new file mode 100644
index 0000000..c0a2de0
--- /dev/null
+++ b/src/gui/editors/notation/HeadersGroup.cpp
@@ -0,0 +1,160 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ This file is Copyright 2007-2008
+ Yves Guillemot <[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 <limits>
+#include <qsize.h>
+#include <qwidget.h>
+#include <qvbox.h>
+#include <qlabel.h>
+
+#include "HeadersGroup.h"
+#include "TrackHeader.h"
+#include "NotationView.h"
+#include "NotePixmapFactory.h"
+
+
+namespace Rosegarden
+{
+
+
+HeadersGroup::
+HeadersGroup(QWidget *parent, NotationView * nv, Composition * comp) :
+ QVBox(parent),
+ m_notationView(nv),
+ m_composition(comp),
+ m_usedHeight(0),
+ m_filler(0),
+ m_lastX(INT_MIN),
+ m_lastWidth(-1)
+{
+}
+
+void
+HeadersGroup::removeAllHeaders()
+{
+ TrackHeaderVector::iterator i;
+ for (i=m_headers.begin(); i!=m_headers.end(); i++) {
+ delete *i;
+ }
+ m_headers.erase(m_headers.begin(), m_headers.end());
+
+ if (m_filler) {
+ delete m_filler;
+ m_filler = 0;
+ }
+ m_usedHeight = 0;
+ m_lastWidth = -1;
+}
+
+void
+HeadersGroup::addHeader(int trackId, int height, int ypos, double xcur)
+{
+ TrackHeader * sh = new TrackHeader(this, trackId, height, ypos);
+ m_headers.push_back(sh);
+ m_usedHeight += height;
+}
+
+void
+HeadersGroup::completeToHeight(int height)
+{
+ if (height > m_usedHeight) {
+ if (!m_filler) m_filler = new QLabel(this);
+ m_filler->setFixedHeight(height - m_usedHeight);
+ }
+}
+
+void
+HeadersGroup::slotUpdateAllHeaders(int x, int y, bool force)
+{
+ // Minimum header width
+ int headerMinWidth = m_notationView->getHeadersTopFrameMinWidth();
+
+ // Maximum header width (may be overriden by clef and key width)
+ int headerMaxWidth = (m_notationView->getCanvasVisibleWidth() * 10) / 100;
+
+ if ((x != m_lastX) || force) {
+ m_lastX = x;
+ TrackHeaderVector::iterator i;
+ int neededWidth = 0;
+
+ // Pass 1 : get the max width needed
+ for (i=m_headers.begin(); i!=m_headers.end(); i++) {
+ int w = (*i)->lookAtStaff(x, headerMaxWidth);
+ if (w > neededWidth) neededWidth = w;
+ }
+
+ if (neededWidth < headerMinWidth) neededWidth = headerMinWidth;
+
+ // Only when m_lastWidth is valid (the first time, m_lastWidth = -1)
+ if (m_lastWidth > 0) {
+ // Don't redraw the headers when change of width is very small
+ const int treshold = 10; // Treshold value should be refined ...
+ int deltaWidth = m_lastWidth - neededWidth;
+ if ((deltaWidth < treshold) && (deltaWidth > -treshold))
+ neededWidth = m_lastWidth;
+ }
+
+ // Pass 2 : redraw the headers when necessary
+ for (i=m_headers.begin(); i!=m_headers.end(); i++) {
+ (*i)->updateHeader(neededWidth);
+ }
+
+ if (neededWidth != m_lastWidth) {
+ setFixedWidth(neededWidth);
+ m_lastWidth = neededWidth;
+
+ // Suppress vertical white stripes on canvas when headers
+ // width changes while scrolling
+ /// TODO : Limit "setChanged()" to the useful part of canvas
+ m_notationView->canvas()->setAllChanged();
+ m_notationView->canvas()->update();
+ }
+ }
+}
+
+
+
+
+void
+HeadersGroup::setCurrent(TrackId trackId)
+{
+ TrackHeaderVector::iterator i;
+ for (i=m_headers.begin(); i!=m_headers.end(); i++)
+ (*i)->setCurrent((*i)->getId() == trackId);
+}
+
+void
+HeadersGroup::resizeEvent(QResizeEvent * ev)
+{
+ // Needed to avoid gray zone at the right of headers
+ // when width is decreasing
+ emit headersResized(ev->size().width());
+}
+
+}
+#include "HeadersGroup.moc"
diff --git a/src/gui/editors/notation/HeadersGroup.h b/src/gui/editors/notation/HeadersGroup.h
new file mode 100644
index 0000000..22d25da
--- /dev/null
+++ b/src/gui/editors/notation/HeadersGroup.h
@@ -0,0 +1,144 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ This file is Copyright 2007-2008
+ Yves Guillemot <[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_HEADERSGROUP_H_
+#define _RG_HEADERSGROUP_H_
+
+#include "base/Track.h"
+
+#include <vector>
+#include <qsize.h>
+#include <qwidget.h>
+#include <qvbox.h>
+
+
+class QLabel;
+class QResizeEvent;
+
+
+namespace Rosegarden
+{
+
+
+class NotationView;
+class Composition;
+class TrackHeader;
+
+
+class HeadersGroup : public QVBox
+{
+ Q_OBJECT
+public:
+ /**
+ * Create an empty headers group
+ */
+ HeadersGroup(QWidget *parent, NotationView * nv, Composition * comp);
+
+ void removeAllHeaders();
+
+ void addHeader(int trackId, int height, int ypos, double xcur);
+
+ /**
+ * Resize a filler at bottom of group to set the headersGroup height
+ * to the value specified in parameter.
+ * (Used to give to the headers group exactly the same height as the
+ * canvas. Necessary to get synchronous vertical scroll.)
+ */
+ void completeToHeight(int height);
+
+ NotationView * getNotationView()
+ { return m_notationView;
+ }
+
+ Composition * getComposition()
+ { return m_composition;
+ }
+
+ /**
+ * Return the total height of all the headers (without the filler).
+ */
+ int getUsedHeight()
+ { return m_usedHeight;
+ }
+
+ /**
+ * Highlight as "current" the header of the specified track.
+ */
+ void setCurrent(TrackId trackId);
+
+ /**
+ * Highlight as "current" the header of the specified track.
+ */
+ int getWidth()
+ {
+ return m_lastWidth;
+ }
+
+ typedef enum { ShowNever, ShowWhenNeeded, ShowAlways } ShowHeadersModeType;
+
+ // Used to ensure to have one default value and only one.
+ static const ShowHeadersModeType DefaultShowMode = ShowAlways;
+
+ // Useful in configuration dialog.
+ static bool isValidShowMode(int mode)
+ {
+ return ((mode >= ShowNever) && (mode <= ShowAlways));
+ }
+
+public slots :
+ /**
+ * Called when notation canvas moves.
+ * Setting force to true forces the headers to be redrawn even
+ * if x has not changed since the last call.
+ */
+ void slotUpdateAllHeaders(int x, int y, bool force = false);
+
+signals :
+ void headersResized(int newWidth);
+
+private:
+ void resizeEvent(QResizeEvent * ev);
+
+ NotationView * m_notationView;
+ Composition * m_composition;
+
+ typedef std::vector<TrackHeader *> TrackHeaderVector;
+ TrackHeaderVector m_headers;
+
+ int m_usedHeight;
+ QLabel * m_filler;
+ int m_lastX;
+ int m_lastWidth;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationCanvasView.cpp b/src/gui/editors/notation/NotationCanvasView.cpp
new file mode 100644
index 0000000..55e63ac
--- /dev/null
+++ b/src/gui/editors/notation/NotationCanvasView.cpp
@@ -0,0 +1,485 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NotationCanvasView.h"
+#include "misc/Debug.h"
+
+#include "misc/Strings.h"
+#include "gui/general/LinedStaffManager.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "gui/kdeext/QCanvasGroupableItem.h"
+#include "gui/kdeext/QCanvasSimpleSprite.h"
+#include "NotationElement.h"
+#include "NotationProperties.h"
+#include "NotationStaff.h"
+#include <qcanvas.h>
+#include <qcolor.h>
+#include <qpainter.h>
+#include <qpen.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qsize.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+NotationCanvasView::NotationCanvasView(const LinedStaffManager &staffmgr,
+ QCanvas *viewing, QWidget *parent,
+ const char *name, WFlags f) :
+ RosegardenCanvasView(viewing, parent, name, f),
+ m_linedStaffManager(staffmgr),
+ m_lastYPosNearStaff(0),
+ m_currentStaff(0),
+ m_currentHeight( -1000),
+ m_legerLineOffset(false),
+ m_heightTracking(false)
+{
+ // -- switching mandolin-sonatina first staff to page mode:
+ // default params (I think 16,100): render 1000ms position 1070ms
+ // 64,100: 1000ms 980ms
+ // 8, 100: 1140ms 1140ms
+ // 128, 100: 1060ms 980ms
+ // 256, 100: 1060ms 980ms / 930ms 920ms
+
+ // canvas()->retune(256, 100);
+
+ viewport()->setMouseTracking(true);
+
+ m_heightMarker = new QCanvasItemGroup(viewing);
+
+ m_vert1 = new QCanvasLineGroupable(viewing, m_heightMarker);
+ m_vert1->setPoints(0, 0, 0, 8);
+ m_vert1->setPen(QPen(QColor(64, 64, 64), 1));
+
+ m_vert2 = new QCanvasLineGroupable(viewing, m_heightMarker);
+ m_vert2->setPoints(17, 0, 17, 8);
+ m_vert2->setPen(QPen(QColor(64, 64, 64), 1));
+
+ m_heightMarker->hide();
+}
+
+NotationCanvasView::~NotationCanvasView()
+{
+ // All canvas items are deleted in ~NotationView()
+}
+
+void
+NotationCanvasView::setHeightTracking(bool t)
+{
+ m_heightTracking = t;
+ if (!t) {
+ m_heightMarker->hide();
+ canvas()->update();
+ }
+}
+
+void
+NotationCanvasView::contentsMouseReleaseEvent(QMouseEvent *e)
+{
+ emit mouseReleased(e);
+}
+
+void
+NotationCanvasView::contentsMouseMoveEvent(QMouseEvent *e)
+{
+ NotationStaff *prevStaff = m_currentStaff;
+ int prevHeight = m_currentHeight;
+
+ m_currentStaff = dynamic_cast<NotationStaff *>
+ (m_linedStaffManager.getStaffForCanvasCoords(e->x(), e->y()));
+
+ if (!m_currentStaff) {
+
+ emit hoveredOverNoteChanged(QString::null);
+ if (prevStaff) {
+ m_heightMarker->hide();
+ canvas()->update();
+ }
+
+ } else {
+
+ m_currentHeight = m_currentStaff->getHeightAtCanvasCoords(e->x(), e->y());
+
+ int x = e->x() - 8; // magic based on mouse cursor size
+ bool needUpdate = (m_heightTracking && (m_heightMarker->x() != x));
+ m_heightMarker->setX(x);
+
+ if (prevStaff != m_currentStaff ||
+ prevHeight != m_currentHeight) {
+
+ if (m_heightTracking) {
+ setHeightMarkerHeight(e);
+ m_heightMarker->show();
+ needUpdate = true;
+ }
+
+ emit hoveredOverNoteChanged
+ (strtoqstr
+ (m_currentStaff->getNoteNameAtCanvasCoords(e->x(), e->y())));
+ }
+
+ if (needUpdate)
+ canvas()->update();
+ }
+
+ NotationElement *elt = getElementAtXCoord(e);
+ if (elt) {
+ emit hoveredOverAbsoluteTimeChanged(elt->getViewAbsoluteTime());
+ }
+
+ // if(tracking) ??
+ emit mouseMoved(e);
+}
+
+void NotationCanvasView::contentsMousePressEvent(QMouseEvent *e)
+{
+ NOTATION_DEBUG << "NotationCanvasView::contentsMousePressEvent() - btn : "
+ << e->button() << " - state : " << e->state()
+ << endl;
+
+ QCanvasItemList itemList = canvas()->collisions(e->pos());
+
+ // We don't want to use m_currentStaff/Height, because we want
+ // to make sure the event happens at the point we clicked at
+ // rather than the last point for which contentsMouseMoveEvent
+ // happened to be called
+
+ NotationStaff *staff = dynamic_cast<NotationStaff *>
+ (m_linedStaffManager.getStaffForCanvasCoords(e->x(), e->y()));
+
+ QCanvasItemList::Iterator it;
+ NotationElement *clickedNote = 0;
+ NotationElement *clickedVagueNote = 0;
+ NotationElement *clickedNonNote = 0;
+
+ bool haveClickHeight = false;
+ int clickHeight = 0;
+ if (staff) {
+ clickHeight = staff->getHeightAtCanvasCoords(e->x(), e->y());
+ haveClickHeight = true;
+ }
+
+ for (it = itemList.begin(); it != itemList.end(); ++it) {
+
+ if ((*it)->active()) {
+ emit activeItemPressed(e, *it);
+ return ;
+ }
+
+ QCanvasNotationSprite *sprite =
+ dynamic_cast<QCanvasNotationSprite*>(*it);
+ if (!sprite) {
+ if (dynamic_cast<QCanvasNonElementSprite *>(*it)) {
+ emit nonNotationItemPressed(e, *it);
+ return ;
+ } else if (dynamic_cast<QCanvasText *>(*it)) {
+ emit textItemPressed(e, *it);
+ return ;
+ }
+ continue;
+ }
+
+ NotationElement &el = sprite->getNotationElement();
+
+ // #957364 (Notation: Hard to select upper note in chords of
+ // seconds) -- adjust x-coord for shifted note head
+
+ double cx = el.getCanvasX();
+ int nbw = 10;
+
+ if (staff) {
+ nbw = staff->getNotePixmapFactory(false).getNoteBodyWidth();
+ bool shifted = false;
+
+ if (el.event()->get
+ <Bool>
+ (staff->getProperties().NOTE_HEAD_SHIFTED, shifted) && shifted) {
+ cx += nbw;
+ }
+ }
+
+ if (el.isNote() && haveClickHeight) {
+ long eventHeight = 0;
+ if (el.event()->get
+ <Int>
+ (NotationProperties::HEIGHT_ON_STAFF, eventHeight)) {
+
+ if (eventHeight == clickHeight) {
+
+ if (!clickedNote &&
+ e->x() >= cx &&
+ e->x() <= cx + nbw) {
+ clickedNote = &el;
+ } else if (!clickedVagueNote &&
+ e->x() >= cx - 2 &&
+ e->x() <= cx + nbw + 2) {
+ clickedVagueNote = &el;
+ }
+
+ } else if (eventHeight - 1 == clickHeight ||
+ eventHeight + 1 == clickHeight) {
+ if (!clickedVagueNote)
+ clickedVagueNote = &el;
+ }
+ }
+ } else if (!el.isNote()) {
+ if (!clickedNonNote)
+ clickedNonNote = &el;
+ }
+ }
+
+ int staffNo = -1;
+ if (staff)
+ staffNo = staff->getId();
+
+ if (clickedNote)
+ handleMousePress(clickHeight, staffNo, e, clickedNote);
+ else if (clickedNonNote)
+ handleMousePress(clickHeight, staffNo, e, clickedNonNote);
+ else if (clickedVagueNote)
+ handleMousePress(clickHeight, staffNo, e, clickedVagueNote);
+ else
+ handleMousePress(clickHeight, staffNo, e);
+}
+
+void NotationCanvasView::contentsMouseDoubleClickEvent(QMouseEvent* e)
+{
+ NOTATION_DEBUG << "NotationCanvasView::contentsMouseDoubleClickEvent()\n";
+
+ contentsMousePressEvent(e);
+}
+
+void
+NotationCanvasView::processActiveItems(QMouseEvent* e,
+ QCanvasItemList itemList)
+{
+ QCanvasItem* pressedItem = 0;
+ QCanvasItemList::Iterator it;
+
+ for (it = itemList.begin(); it != itemList.end(); ++it) {
+
+ QCanvasItem *item = *it;
+ if (item->active() && !pressedItem) {
+ NOTATION_DEBUG << "mousepress : got active item\n";
+ pressedItem = item;
+ }
+ }
+
+ if (pressedItem)
+ emit activeItemPressed(e, pressedItem);
+
+}
+
+void
+NotationCanvasView::handleMousePress(int height,
+ int staffNo,
+ QMouseEvent *e,
+ NotationElement *el)
+{
+ NOTATION_DEBUG << "NotationCanvasView::handleMousePress() at height "
+ << height << endl;
+
+ emit itemPressed(height, staffNo, e, el);
+}
+
+bool
+NotationCanvasView::posIsTooFarFromStaff(const QPoint &pos)
+{
+ // return true if pos.y is more than m_staffLineThreshold away from
+ // the last pos for which a collision was detected
+ //
+ return (pos.y() > m_lastYPosNearStaff) ?
+ (pos.y() - m_lastYPosNearStaff) > (int)m_staffLineThreshold :
+ (m_lastYPosNearStaff - pos.y()) > (int)m_staffLineThreshold;
+
+}
+
+int
+NotationCanvasView::getLegerLineCount(int height, bool &offset)
+{
+ //!!! This is far too specifically notation-related to be here, really
+
+ if (height < 0) {
+
+ offset = (( -height % 2) == 1);
+ return height / 2;
+
+ } else if (height > 8) {
+
+ offset = ((height % 2) == 1);
+ return (height - 8) / 2;
+ }
+
+ return 0;
+}
+
+void
+NotationCanvasView::setHeightMarkerHeight(QMouseEvent *e)
+{
+ NotationStaff *staff = dynamic_cast<NotationStaff *>
+ (m_linedStaffManager.getStaffForCanvasCoords(e->x(), e->y()));
+
+ int height = staff->getHeightAtCanvasCoords(e->x(), e->y());
+ int lineY = staff->getCanvasYForHeight(height, e->x(), e->y());
+
+ // NOTATION_DEBUG << "NotationCanvasView::setHeightMarkerHeight: "
+ // << e->y() << " snapped to line -> " << lineY
+ // << " (height " << height << ")" << endl;
+
+ int spacing = staff->getLineSpacing() - 1;
+
+ m_staffLineThreshold = spacing;
+ m_vert1->setPoints(0, -spacing / 2, 0, spacing / 2);
+ m_vert2->setPoints(17, -spacing / 2, 17, spacing / 2); // magic based on mouse cursor size
+ m_heightMarker->setY(lineY);
+
+ bool legerLineOffset = false;
+ int legerLineCount = getLegerLineCount(height, legerLineOffset);
+
+ if (legerLineCount != (int)m_legerLines.size() ||
+ legerLineOffset != m_legerLineOffset) {
+
+ bool above = false;
+ if (legerLineCount < 0) {
+ above = true;
+ legerLineCount = -legerLineCount;
+ }
+
+ int i;
+ for (i = 0; i < (int)m_legerLines.size(); ++i) {
+ delete m_legerLines[i];
+ }
+ m_legerLines.clear();
+
+ for (i = 0; i < legerLineCount; ++i) {
+
+ QCanvasLineGroupable *line =
+ new QCanvasLineGroupable(canvas(), m_heightMarker);
+
+ line->setPen(QPen(QColor(64, 64, 64), 1));
+
+ int y = (int)m_heightMarker->y() +
+ (above ? -1 : 1) * (i * (spacing + 1));
+ int x = (int)m_heightMarker->x() + 1;
+
+ if (legerLineOffset) {
+ if (above)
+ y -= spacing / 2 + 1;
+ else
+ y += spacing / 2;
+ }
+
+ line->setPoints(x, y, x + 15, y); // magic based on mouse cursor size
+ m_legerLines.push_back(line);
+ }
+
+ m_legerLineOffset = legerLineOffset;
+ }
+}
+
+NotationElement *
+NotationCanvasView::getElementAtXCoord(QMouseEvent *e) // any old element
+{
+ QRect threshold(e->pos(), QSize(4, 100)); //!!!
+ threshold.moveCenter(e->pos());
+
+ QCanvasItemList itemList = canvas()->collisions(threshold);
+
+ QCanvasItemList::Iterator it;
+ QCanvasNotationSprite* sprite = 0;
+
+ for (it = itemList.begin(); it != itemList.end(); ++it)
+ {
+
+ QCanvasItem *item = *it;
+
+ if ((sprite = dynamic_cast<QCanvasNotationSprite*>(item))) {
+ return & (sprite->getNotationElement());
+ }
+ }
+
+ return 0;
+}
+
+void
+NotationCanvasView::viewportPaintEvent(QPaintEvent *e)
+{
+ int cx(e->rect().x()),
+ cy(e->rect().y()),
+ cw(e->rect().width()) /*,
+ ch(e->rect().height())*/;
+ // NOTATION_DEBUG << "NotationCanvasView::viewportPaintEvent: (" << cx << ","
+ // << cy << ") size (" << cw << "x" << ch << ")" << endl;
+ QCanvasView::viewportPaintEvent(e);
+
+ cx += contentsX();
+ cy += contentsY();
+ m_lastRender = e->rect();
+ emit renderRequired(std::min(contentsX(), cx),
+ std::max(contentsX() + visibleWidth(), cx + cw));
+}
+
+void
+NotationCanvasView::drawContents(QPainter *p, int cx, int cy, int cw, int ch)
+{
+ /*
+ m_lastRender = QRect(cx, cy, cw, ch);
+ NOTATION_DEBUG << "NotationCanvasView::drawContents: (" << cx << ","
+ << cy << ") size (" << cw << "x" << ch << ")" << endl;
+ */
+ QCanvasView::drawContents(p, cx, cy, cw, ch);
+ /*
+ emit renderRequired(std::min(contentsX(), cx),
+ std::max(contentsX() + visibleWidth(), cx + cw));
+ */
+}
+
+void
+NotationCanvasView::slotRenderComplete()
+{
+ /* QPainter painter(viewport());
+ int cx(m_lastRender.x()),
+ cy(m_lastRender.y()),
+ cw(m_lastRender.width()),
+ ch(m_lastRender.height());
+ NOTATION_DEBUG << "NotationCanvasView::slotRenderComplete: (" << cx << ","
+ << cy << ") size (" << cw << "x" << ch << ")" << endl;
+ QCanvasView::drawContents(&painter, cx, cy, cw, ch);
+ */
+ QPaintEvent ev(m_lastRender);
+ QCanvasView::viewportPaintEvent(&ev);
+}
+
+void
+NotationCanvasView::slotExternalWheelEvent(QWheelEvent* e)
+{
+ wheelEvent(e);
+}
+
+}
+#include "NotationCanvasView.moc"
diff --git a/src/gui/editors/notation/NotationCanvasView.h b/src/gui/editors/notation/NotationCanvasView.h
new file mode 100644
index 0000000..5c88fb0
--- /dev/null
+++ b/src/gui/editors/notation/NotationCanvasView.h
@@ -0,0 +1,218 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTATIONCANVASVIEW_H_
+#define _RG_NOTATIONCANVASVIEW_H_
+
+#include "gui/general/RosegardenCanvasView.h"
+#include <qrect.h>
+#include <vector>
+
+
+class QWidget;
+class QString;
+class QPoint;
+class QPaintEvent;
+class QPainter;
+class QMouseEvent;
+class QCanvasLineGroupable;
+class QCanvasItemGroup;
+class QCanvasItem;
+class QCanvas;
+
+
+namespace Rosegarden
+{
+
+class NotationStaff;
+class NotationElement;
+class LinedStaffManager;
+
+
+/**
+ * Central widget for the NotationView window
+ *
+ * This class only takes care of the event handling
+ * (see the various signals).
+ *
+ * It does not deal with any canvas element. All elements are added by
+ * the NotationView.
+ *
+ *@see NotationView
+ */
+
+class NotationCanvasView : public RosegardenCanvasView
+{
+ Q_OBJECT
+
+public:
+ NotationCanvasView(const LinedStaffManager &staffmgr,
+ QCanvas *viewing, QWidget *parent=0,
+ const char *name=0, WFlags f=0);
+
+ ~NotationCanvasView();
+
+ void setHeightTracking(bool t);
+
+signals:
+
+ /**
+ * Emitted when the user clicks on a staff (e.g. mouse button press)
+ * \a pitch is set to the MIDI pitch on which the click occurred
+ * \a staffNo is set to the staff on which the click occurred
+ * \a point is set to the coordinates of the click event
+ * \a el points to the NotationElement which was clicked on, if any
+ */
+ void itemPressed(int pitch, int staffNo,
+ QMouseEvent*,
+ NotationElement* el);
+
+ /**
+ * Emitted when the user clicks on a QCanvasItem which is active
+ *
+ * @see QCanvasItem#setActive
+ */
+ void activeItemPressed(QMouseEvent*,
+ QCanvasItem* item);
+
+ /**
+ * Emitted when the user clicks on a QCanvasItem which is neither
+ * active nor a notation element
+ */
+ void nonNotationItemPressed(QMouseEvent *,
+ QCanvasItem *);
+
+ /**
+ * Emitted when the user clicks on a QCanvasItem which is a
+ * plain QCanvasText
+ */
+ void textItemPressed(QMouseEvent *,
+ QCanvasItem *);
+
+ /**
+ * Emitted when the mouse cursor moves to a different height
+ * on the staff
+ *
+ * \a noteName contains the MIDI name of the corresponding note
+ */
+ void hoveredOverNoteChanged(const QString &noteName);
+
+ /**
+ * Emitted when the mouse cursor moves to a note which is at a
+ * different time
+ *
+ * \a time is set to the absolute time of the note the cursor is
+ * hovering on
+ */
+ void hoveredOverAbsoluteTimeChanged(unsigned int time);
+
+ /**
+ * Emitted when the mouse cursor moves (used by the selection tool)
+ */
+ void mouseMoved(QMouseEvent*);
+
+ /**
+ * Emitted when the mouse button is released
+ */
+ void mouseReleased(QMouseEvent*);
+
+ /**
+ * Emitted when a region is about to be drawn by the canvas view.
+ * Indicates that any on-demand rendering for that region should
+ * be carried out.
+ */
+ void renderRequired(double cx0, double cx1);
+
+public slots:
+ void slotRenderComplete();
+
+ void slotExternalWheelEvent(QWheelEvent* e);
+
+protected:
+
+ virtual void viewportPaintEvent(QPaintEvent *e);
+ virtual void drawContents(QPainter *p, int cx, int cy, int cw, int ch);
+
+ const LinedStaffManager &m_linedStaffManager;
+
+ /**
+ * Callback for a mouse button press event in the canvas
+ */
+ virtual void contentsMousePressEvent(QMouseEvent*);
+
+ /**
+ * Callback for a mouse button release event in the canvas
+ */
+ virtual void contentsMouseReleaseEvent(QMouseEvent*);
+
+ /**
+ * Callback for a mouse move event in the canvas
+ */
+ virtual void contentsMouseMoveEvent(QMouseEvent*);
+
+ /**
+ * Callback for a mouse double click event in the canvas
+ */
+ virtual void contentsMouseDoubleClickEvent(QMouseEvent*);
+
+ void processActiveItems(QMouseEvent*, QCanvasItemList);
+
+ void handleMousePress(int height, int staffNo,
+ QMouseEvent*,
+ NotationElement* pressedNotationElement = 0);
+
+ bool posIsTooFarFromStaff(const QPoint &pos);
+
+ int getLegerLineCount(int height, bool &offset);
+
+ void setHeightMarkerHeight(QMouseEvent *e);
+
+ NotationElement *getElementAtXCoord(QMouseEvent *e);
+
+ //--------------- Data members ---------------------------------
+
+ int m_lastYPosNearStaff;
+
+ unsigned int m_staffLineThreshold;
+
+ NotationStaff *m_currentStaff;
+ int m_currentHeight;
+
+ QCanvasItemGroup *m_heightMarker;
+ QCanvasLineGroupable *m_vert1;
+ QCanvasLineGroupable *m_vert2;
+ std::vector<QCanvasLineGroupable *> m_legerLines;
+ bool m_legerLineOffset;
+
+ bool m_heightTracking;
+
+ QRect m_lastRender;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationChord.cpp b/src/gui/editors/notation/NotationChord.cpp
new file mode 100644
index 0000000..7b0a263
--- /dev/null
+++ b/src/gui/editors/notation/NotationChord.cpp
@@ -0,0 +1,335 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NotationChord.h"
+
+#include "base/Sets.h"
+#include "base/Event.h"
+#include "base/NotationRules.h"
+#include "base/NotationTypes.h"
+#include "base/Quantizer.h"
+#include "NotationProperties.h"
+#include "NoteStyleFactory.h"
+
+namespace Rosegarden
+{
+
+template <>
+Event *
+AbstractSet<NotationElement, NotationElementList>::getAsEvent(const NotationElementList::iterator &i)
+{
+ return (*i)->event();
+}
+
+NotationChord::NotationChord(NotationElementList &c,
+ NotationElementList::iterator i,
+ const Quantizer *quantizer,
+ const NotationProperties &properties,
+ const Clef &clef,
+ const ::Rosegarden::Key &key) :
+ GenericChord < NotationElement,
+ NotationElementList, true > (c, i, quantizer,
+ NotationProperties::STEM_UP),
+ m_properties(properties),
+ m_clef(clef),
+ m_key(key)
+{
+ // nothing else
+}
+
+int
+NotationChord::getHeight(const Iterator &i) const
+{
+ //!!! We use HEIGHT_ON_STAFF in preference to the passed clef/key,
+ //but what if the clef/key changed since HEIGHT_ON_STAFF was
+ //written? Who updates the properties then? Check this.
+
+ long h = 0;
+ if (getAsEvent(i)->get
+ <Int>(NotationProperties::HEIGHT_ON_STAFF, h)) {
+ return h;
+ }
+
+ try {
+ Pitch pitch(*getAsEvent(i));
+ h = pitch.getHeightOnStaff(m_clef, m_key);
+ } catch (...) {
+ // no pitch!
+ }
+
+ // set non-persistent, not setMaybe, as we know the property is absent:
+ getAsEvent(i)->set
+ <Int>(NotationProperties::HEIGHT_ON_STAFF, h, false);
+ return h;
+}
+
+bool
+NotationChord::hasStem() const
+{
+ // true if any of the notes is stemmed
+
+ Iterator i(getInitialNote());
+ for (;;) {
+ long note;
+ if (!getAsEvent(i)->get
+ <Int>(BaseProperties::NOTE_TYPE, note)) return true;
+ if (NoteStyleFactory::getStyleForEvent(getAsEvent(i))->hasStem(note))
+ return true;
+ if (i == getFinalNote())
+ return false;
+ ++i;
+ }
+ return false;
+}
+
+bool
+NotationChord::hasStemUp() const
+{
+ NotationRules rules;
+
+ // believe anything found in any of the notes, if in a persistent
+ // property or a property apparently set by the beaming algorithm
+
+ Iterator i(getInitialNote());
+
+ for (;;) {
+ Event *e = getAsEvent(i);
+ /*!!!
+ if (e->has(m_properties.VIEW_LOCAL_STEM_UP)) {
+ return e->get<Bool>(m_properties.VIEW_LOCAL_STEM_UP);
+ }
+ */
+ if (e->has(NotationProperties::STEM_UP)) {
+ return e->get
+ <Bool>(NotationProperties::STEM_UP);
+ }
+
+ if (e->has(NotationProperties::BEAM_ABOVE)) {
+ if (e->has(NotationProperties::BEAMED) &&
+ e->get
+ <Bool>(NotationProperties::BEAMED)) {
+ return e->get
+ <Bool>(NotationProperties::BEAM_ABOVE);
+ }
+ else {
+ return !e->get
+ <Bool>(NotationProperties::BEAM_ABOVE);
+ }
+ }
+
+ if (i == getFinalNote())
+ break;
+ ++i;
+ }
+
+ return rules.isStemUp(getHighestNoteHeight(),getLowestNoteHeight());
+}
+
+bool
+NotationChord::hasNoteHeadShifted() const
+{
+ int ph = 10000;
+
+ for (unsigned int i = 0; i < size(); ++i) {
+ int h = getHeight((*this)[i]);
+ if (h == ph + 1)
+ return true;
+ ph = h;
+ }
+
+ return false;
+}
+
+bool
+NotationChord::isNoteHeadShifted(const Iterator &itr) const
+{
+ unsigned int i;
+ for (i = 0; i < size(); ++i) {
+ if ((*this)[i] == itr)
+ break;
+ }
+
+ if (i == size()) {
+ std::cerr << "NotationChord::isNoteHeadShifted: Warning: Unable to find note head " << getAsEvent(itr) << std::endl;
+ return false;
+ }
+
+ int h = getHeight((*this)[i]);
+
+ if (hasStemUp()) {
+ if ((i > 0) && (h == getHeight((*this)[i - 1]) + 1)) {
+ return (!isNoteHeadShifted((*this)[i - 1]));
+ }
+ } else {
+ if ((i < size() - 1) && (h == getHeight((*this)[i + 1]) - 1)) {
+ return (!isNoteHeadShifted((*this)[i + 1]));
+ }
+ }
+
+ return false;
+}
+
+void
+NotationChord::applyAccidentalShiftProperties()
+{
+ // Some rules:
+ //
+ // The top accidental always gets the minimum shift (i.e. normally
+ // right next to the note head or stem).
+ //
+ // The bottom accidental gets the next least: the same, if the
+ // interval is more than a sixth, or the next shift out otherwise.
+ //
+ // We then progress up from the bottom accidental upwards.
+ //
+ // These rules aren't really enough, but they might do for now!
+
+ //!!! Uh-oh... we have a catch-22 here. We can't determine the
+ // proper minimum shift until we know which way the stem goes,
+ // because if we have a shifted note head and the stem goes down,
+ // we need to shift one place further than otherwise. But we
+ // don't know for sure which way the stem goes until we've
+ // calculated the beam, and we don't do that until after we've
+ // worked out the x-coordinates based on (among other things) the
+ // accidental shift.
+
+ int minShift = 0;
+ bool extra = false;
+
+ if (!hasStemUp() && hasNoteHeadShifted()) {
+ minShift = 1; // lazy
+ extra = true;
+ }
+
+ int lastShift = minShift;
+ int lastHeight = 0, maxHeight = 999;
+ int lastWidth = 1;
+
+ for (iterator i = end(); i != begin(); ) {
+
+ --i;
+ Event *e = getAsEvent(*i);
+
+ Accidental acc;
+ if (e->get
+ <String>(m_properties.DISPLAY_ACCIDENTAL, acc) &&
+ acc != Accidentals::NoAccidental) {
+ e->setMaybe<Int>(m_properties.ACCIDENTAL_SHIFT, minShift);
+ e->setMaybe<Bool>(m_properties.ACCIDENTAL_EXTRA_SHIFT, extra);
+ maxHeight = lastHeight = getHeight(*i);
+ break;
+ }
+ }
+
+ if (maxHeight == 999) {
+ return ;
+ }
+
+ for (iterator i = begin(); i != end(); ++i) {
+
+ Event *e = getAsEvent(*i);
+ int height = getHeight(*i);
+
+ if (height == maxHeight && e->has(m_properties.ACCIDENTAL_SHIFT)) {
+ // finished -- we've come around to the highest one again
+ break;
+ }
+
+ Accidental acc;
+
+ if (e->get
+ <String>(m_properties.DISPLAY_ACCIDENTAL, acc) &&
+ acc != Accidentals::NoAccidental) {
+
+ int shift = lastShift;
+
+ if (height < lastHeight) { // lastHeight was the first, up top
+ if (lastHeight - height < 6) {
+ shift = lastShift + lastWidth;
+ }
+ } else {
+ if (height - lastHeight >= 6) {
+ if (maxHeight - height >= 6) {
+ shift = minShift;
+ } else {
+ shift = minShift + 1;
+ }
+ } else {
+ shift = lastShift + lastWidth;
+ }
+ }
+
+ e->setMaybe<Int>(m_properties.ACCIDENTAL_SHIFT, shift);
+
+ lastHeight = height;
+ lastShift = shift;
+
+ lastWidth = 1;
+ bool c = false;
+ if (e->get
+ <Bool>(m_properties.DISPLAY_ACCIDENTAL_IS_CAUTIONARY, c)
+ && c) {
+ lastWidth = 2;
+ }
+ }
+ }
+}
+
+int
+NotationChord::getMaxAccidentalShift(bool &extra) const
+{
+ int maxShift = 0;
+
+ for (const_iterator i = begin(); i != end(); ++i) {
+ Event *e = getAsEvent(*i);
+ if (e->has(m_properties.ACCIDENTAL_SHIFT)) {
+ int shift = e->get
+ <Int>(m_properties.ACCIDENTAL_SHIFT);
+ if (shift > maxShift) {
+ maxShift = shift;
+ e->get
+ <Bool>(m_properties.ACCIDENTAL_EXTRA_SHIFT, extra);
+ }
+ }
+ }
+
+ return maxShift;
+}
+
+int
+NotationChord::getAccidentalShift(const Iterator &i, bool &extra) const
+{
+ if (getAsEvent(i)->has(m_properties.ACCIDENTAL_SHIFT)) {
+ int shift = getAsEvent(i)->get
+ <Int>(m_properties.ACCIDENTAL_SHIFT);
+ getAsEvent(i)->get
+ <Bool>(m_properties.ACCIDENTAL_EXTRA_SHIFT, extra);
+ return shift;
+ } else {
+ return 0;
+ }
+}
+
+}
diff --git a/src/gui/editors/notation/NotationChord.h b/src/gui/editors/notation/NotationChord.h
new file mode 100644
index 0000000..7ce12fd
--- /dev/null
+++ b/src/gui/editors/notation/NotationChord.h
@@ -0,0 +1,90 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTATIONCHORD_H_
+#define _RG_NOTATIONCHORD_H_
+
+#include "base/NotationTypes.h"
+#include "base/Sets.h"
+#include "NotationElement.h"
+
+class Iterator;
+
+
+namespace Rosegarden
+{
+
+class Quantizer;
+class NotationProperties;
+
+
+class NotationChord : public GenericChord<NotationElement,
+ NotationElementList,
+ true>
+{
+public:
+ NotationChord(NotationElementList &c,
+ NotationElementList::iterator i,
+ const Quantizer *quantizer,
+ const NotationProperties &properties,
+ const Clef &clef = Clef::DefaultClef,
+ const Key &key = Key::DefaultKey);
+
+ virtual ~NotationChord() { }
+
+ virtual int getHighestNoteHeight() const {
+ return getHeight(getHighestNote());
+ }
+ virtual int getLowestNoteHeight() const {
+ return getHeight(getLowestNote());
+ }
+
+ virtual bool hasStem() const;
+ virtual bool hasStemUp() const;
+
+ virtual bool hasNoteHeadShifted() const;
+ virtual bool isNoteHeadShifted(const NotationElementList::iterator &itr)
+ const;
+
+ void applyAccidentalShiftProperties();
+
+ virtual int getMaxAccidentalShift(bool &extra) const;
+ virtual int getAccidentalShift(const NotationElementList::iterator &itr,
+ bool &extra) const;
+
+protected:
+ const NotationProperties &m_properties;
+ Clef m_clef;
+ Key m_key;
+
+
+ int getHeight(const Iterator&) const;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationElement.cpp b/src/gui/editors/notation/NotationElement.cpp
new file mode 100644
index 0000000..7df1cd5
--- /dev/null
+++ b/src/gui/editors/notation/NotationElement.cpp
@@ -0,0 +1,198 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NotationElement.h"
+#include "misc/Debug.h"
+
+#include "base/BaseProperties.h"
+#include "base/Event.h"
+#include "base/Exception.h"
+#include "base/NotationTypes.h"
+#include "base/ViewElement.h"
+
+#include <qcanvas.h>
+
+namespace Rosegarden
+{
+
+NotationElement::NotationElement(Event *event)
+ : ViewElement(event),
+ m_recentlyRegenerated(false),
+ m_isColliding(false),
+ m_canvasItem(0),
+ m_extraItems(0)
+{
+ // NOTATION_DEBUG << "new NotationElement "
+ // << this << " wrapping " << event << endl;
+}
+
+NotationElement::~NotationElement()
+{
+ removeCanvasItem();
+}
+
+timeT
+NotationElement::getViewAbsoluteTime() const
+{
+ return event()->getNotationAbsoluteTime();
+}
+
+timeT
+NotationElement::getViewDuration() const
+{
+ return event()->getNotationDuration();
+}
+
+double
+NotationElement::getCanvasX()
+{
+ if (m_canvasItem)
+ return m_canvasItem->x();
+ else {
+ std::cerr << "ERROR: No canvas item for this notation element:";
+ event()->dump(std::cerr);
+ throw NoCanvasItem("No canvas item for notation element of type " +
+ event()->getType(), __FILE__, __LINE__);
+ }
+}
+
+double
+NotationElement::getCanvasY()
+{
+ if (m_canvasItem)
+ return m_canvasItem->y();
+ else {
+ std::cerr << "ERROR: No canvas item for this notation element:";
+ event()->dump(std::cerr);
+ throw NoCanvasItem("No canvas item for notation element of type " +
+ event()->getType(), __FILE__, __LINE__);
+ }
+}
+
+bool
+NotationElement::isRest() const
+{
+ return event()->isa(Note::EventRestType);
+}
+
+bool
+NotationElement::isNote() const
+{
+ return event()->isa(Note::EventType);
+}
+
+bool
+NotationElement::isTuplet() const
+{
+ return event()->has(BaseProperties::BEAMED_GROUP_TUPLET_BASE);
+}
+
+bool
+NotationElement::isGrace() const
+{
+ return event()->has(BaseProperties::IS_GRACE_NOTE) &&
+ event()->get
+ <Bool>(BaseProperties::IS_GRACE_NOTE);
+}
+
+void
+NotationElement::setCanvasItem(QCanvasItem *e, double canvasX, double canvasY)
+{
+ removeCanvasItem();
+ m_recentlyRegenerated = true;
+ m_canvasItem = e;
+ e->move(canvasX, canvasY);
+}
+
+void
+NotationElement::addCanvasItem(QCanvasItem *e, double canvasX, double canvasY)
+{
+ if (!m_canvasItem) {
+ std::cerr << "ERROR: Attempt to add extra canvas item to element without main canvas item:";
+ event()->dump(std::cerr);
+ throw NoCanvasItem("No canvas item for notation element of type " +
+ event()->getType(), __FILE__, __LINE__);
+ }
+ if (!m_extraItems) {
+ m_extraItems = new ItemList;
+ }
+ e->move(canvasX, canvasY);
+ m_extraItems->push_back(e);
+}
+
+void
+NotationElement::removeCanvasItem()
+{
+ m_recentlyRegenerated = false;
+
+ delete m_canvasItem;
+ m_canvasItem = 0;
+
+ if (m_extraItems) {
+
+ for (ItemList::iterator i = m_extraItems->begin();
+ i != m_extraItems->end(); ++i)
+ delete *i;
+ m_extraItems->clear();
+
+ delete m_extraItems;
+ m_extraItems = 0;
+ }
+}
+
+void
+NotationElement::reposition(double canvasX, double canvasY)
+{
+ m_recentlyRegenerated = false;
+ if (!m_canvasItem)
+ return ;
+
+ double dx = canvasX - m_canvasItem->x();
+ double dy = canvasY - m_canvasItem->y();
+ m_canvasItem->move(canvasX, canvasY);
+
+ if (m_extraItems) {
+ for (ItemList::iterator i = m_extraItems->begin();
+ i != m_extraItems->end(); ++i) {
+ (*i)->moveBy(dx, dy);
+ }
+ }
+}
+
+bool
+NotationElement::isSelected()
+{
+ return m_canvasItem ? m_canvasItem->selected() : false;
+}
+
+void
+NotationElement::setSelected(bool selected)
+{
+ m_recentlyRegenerated = false;
+ if (m_canvasItem)
+ m_canvasItem->setSelected(selected);
+}
+
+}
diff --git a/src/gui/editors/notation/NotationElement.h b/src/gui/editors/notation/NotationElement.h
new file mode 100644
index 0000000..c756641
--- /dev/null
+++ b/src/gui/editors/notation/NotationElement.h
@@ -0,0 +1,176 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTATIONELEMENT_H_
+#define _RG_NOTATIONELEMENT_H_
+
+#include "base/Exception.h"
+#include "base/ViewElement.h"
+#include <vector>
+#include "base/Event.h"
+
+
+class QCanvasItem;
+class ItemList;
+
+
+namespace Rosegarden
+{
+
+class Event;
+
+
+/**
+ * The Notation H and V layout is performed on a
+ * NotationElementList. Once this is done, each NotationElement is
+ * affected a QCanvasItem which is set at these coords.
+ *
+ * @see NotationView#showElements()
+ */
+
+class NotationElement : public ViewElement
+{
+public:
+ typedef Exception NoCanvasItem;
+
+ NotationElement(Event *event);
+
+ ~NotationElement();
+
+ virtual timeT getViewAbsoluteTime() const;
+ virtual timeT getViewDuration() const;
+
+ void getLayoutAirspace(double &x, double &width) {
+ x = m_airX;
+ width = m_airWidth;
+ }
+
+ void getCanvasAirspace(double &x, double &width) {
+ x = m_airX - getLayoutX() + getCanvasX();
+ width = m_airWidth;
+ }
+
+ /// returns the x pos of the associated canvas item
+ double getCanvasX();
+
+ /// returns the y pos of the associated canvas item
+ double getCanvasY();
+
+ /**
+ * Sets the X coordinate and width of the space "underneath"
+ * this element, i.e. the extents within which a mouse click
+ * or some such might be considered to be interested in this
+ * element as opposed to any other. These are layout coords
+ */
+ void setLayoutAirspace(double x, double width) {
+ m_airX = x; m_airWidth = width;
+ }
+
+ /// Returns true if the wrapped event is a rest
+ bool isRest() const;
+
+ /// Returns true if the wrapped event is a note
+ bool isNote() const;
+
+ /// Returns true if the wrapped event is a tuplet
+ bool isTuplet() const;
+
+ /// Returns true if the wrapped event is a grace note
+ bool isGrace() const;
+
+ /**
+ * Sets the canvas item representing this notation element on screen.
+ *
+ * NOTE: The object takes ownership of its canvas item.
+ */
+ void setCanvasItem(QCanvasItem *e, double canvasX, double canvasY);
+
+ /**
+ * Add an extra canvas item associated with this element, for
+ * example where an element has been split across two or more
+ * staff rows.
+ *
+ * The element will take ownership of these canvas items and
+ * delete them when it deletes the main canvas item.
+ */
+ void addCanvasItem(QCanvasItem *e, double canvasX, double canvasY);
+
+ /**
+ * Remove the main canvas item and any additional ones.
+ */
+ void removeCanvasItem();
+
+ /**
+ * Reset the position of the canvas item (which is assumed to
+ * exist already).
+ */
+ void reposition(double canvasX, double canvasY);
+
+ /**
+ * Return true if setCanvasItem has been called more recently
+ * than reposition. If true, any code that positions this
+ * element will probably not need to regenerate its sprite as
+ * well, even if other indications suggest otherwise.
+ */
+ bool isRecentlyRegenerated() { return m_recentlyRegenerated; }
+
+ bool isSelected();
+ void setSelected(bool selected);
+
+ /**
+ * Return true if the element is a note which lies at the exactly
+ * same place than another note.
+ * Only valid after NotationVLayout::scanStaff() call.
+ * Only a returned true is meaningful (when 2 notes are colliding, the
+ * first element returns false and the second one returns true).
+ */
+ bool isColliding() { return m_isColliding; }
+
+ void setIsColliding(bool value) { m_isColliding = value; }
+
+ /// Returns the associated canvas item
+ QCanvasItem* getCanvasItem() { return m_canvasItem; }
+
+protected:
+ //--------------- Data members ---------------------------------
+
+ double m_airX;
+ double m_airWidth;
+ bool m_recentlyRegenerated;
+ bool m_isColliding;
+
+ QCanvasItem *m_canvasItem;
+
+ typedef std::vector<QCanvasItem *> ItemList;
+ ItemList *m_extraItems;
+};
+
+typedef ViewElementList NotationElementList;
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationEraser.cpp b/src/gui/editors/notation/NotationEraser.cpp
new file mode 100644
index 0000000..4124e44
--- /dev/null
+++ b/src/gui/editors/notation/NotationEraser.cpp
@@ -0,0 +1,115 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NotationEraser.h"
+#include <kapplication.h>
+
+#include <klocale.h>
+#include "document/ConfigGroups.h"
+#include "base/ViewElement.h"
+#include "commands/notation/EraseEventCommand.h"
+#include "gui/general/EditTool.h"
+#include "NotationTool.h"
+#include "NotationView.h"
+#include "NotePixmapFactory.h"
+#include <kaction.h>
+#include <kconfig.h>
+#include <qiconset.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+NotationEraser::NotationEraser(NotationView* view)
+ : NotationTool("NotationEraser", view),
+ m_collapseRest(false)
+{
+ KConfig *config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+ m_collapseRest = config->readBoolEntry("collapse", false);
+
+ new KToggleAction(i18n("Collapse rests after erase"), 0, this,
+ SLOT(slotToggleRestCollapse()), actionCollection(),
+ "toggle_rest_collapse");
+
+ QIconSet icon
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("crotchet")));
+ new KAction(i18n("Switch to Insert Tool"), icon, 0, this,
+ SLOT(slotInsertSelected()), actionCollection(),
+ "insert");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("select")));
+ new KAction(i18n("Switch to Select Tool"), icon, 0, this,
+ SLOT(slotSelectSelected()), actionCollection(),
+ "select");
+
+ createMenu("notationeraser.rc");
+}
+
+void NotationEraser::ready()
+{
+ m_nParentView->setCanvasCursor(Qt::pointingHandCursor);
+ m_nParentView->setHeightTracking(false);
+}
+
+void NotationEraser::handleLeftButtonPress(timeT,
+ int,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement* element)
+{
+ if (!element || staffNo < 0)
+ return ;
+
+ EraseEventCommand *command =
+ new EraseEventCommand(m_nParentView->getStaff(staffNo)->getSegment(),
+ element->event(),
+ m_collapseRest);
+
+ m_nParentView->addCommandToHistory(command);
+}
+
+void NotationEraser::slotToggleRestCollapse()
+{
+ m_collapseRest = !m_collapseRest;
+}
+
+void NotationEraser::slotInsertSelected()
+{
+ m_nParentView->slotLastNoteAction();
+}
+
+void NotationEraser::slotSelectSelected()
+{
+ m_parentView->actionCollection()->action("select")->activate();
+}
+
+const QString NotationEraser::ToolName = "notationeraser";
+
+}
+#include "NotationEraser.moc"
diff --git a/src/gui/editors/notation/NotationEraser.h b/src/gui/editors/notation/NotationEraser.h
new file mode 100644
index 0000000..9efdd13
--- /dev/null
+++ b/src/gui/editors/notation/NotationEraser.h
@@ -0,0 +1,81 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTATIONERASER_H_
+#define _RG_NOTATIONERASER_H_
+
+#include "NotationTool.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class NotationView;
+
+
+/**
+ * This tool will erase a note on mouse click events
+ */
+class NotationEraser : public NotationTool
+{
+ Q_OBJECT
+
+ friend class NotationToolBox;
+
+public:
+
+ virtual void ready();
+
+ virtual void handleLeftButtonPress(timeT,
+ int height,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement* el);
+ static const QString ToolName;
+
+public slots:
+ void slotToggleRestCollapse();
+
+ void slotInsertSelected();
+ void slotSelectSelected();
+
+protected:
+ NotationEraser(NotationView*);
+
+ //--------------- Data members ---------------------------------
+
+ bool m_collapseRest;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationGroup.cpp b/src/gui/editors/notation/NotationGroup.cpp
new file mode 100644
index 0000000..78525d9
--- /dev/null
+++ b/src/gui/editors/notation/NotationGroup.cpp
@@ -0,0 +1,979 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NotationGroup.h"
+#include "misc/Debug.h"
+
+#include "base/Equation.h"
+#include "base/Event.h"
+#include "base/NotationRules.h"
+#include "base/NotationTypes.h"
+#include "base/Quantizer.h"
+#include "NotationChord.h"
+#include "NotationElement.h"
+#include "NotationProperties.h"
+#include "NotationStaff.h"
+#include "NoteStyleFactory.h"
+#include "NotePixmapFactory.h"
+
+
+namespace Rosegarden
+{
+
+NotationGroup::NotationGroup(NotationElementList &nel,
+ NELIterator i, const Quantizer *q,
+ std::pair<timeT, timeT> barRange,
+ const NotationProperties &p,
+ const Clef &clef, const Key &key) :
+ AbstractSet<NotationElement, NotationElementList>(nel, i, q),
+ m_barRange(barRange),
+ //!!! What if the clef and/or key change in the course of the group?
+ m_clef(clef),
+ m_key(key),
+ m_weightAbove(0),
+ m_weightBelow(0),
+ m_userSamples(false),
+ m_type(Beamed),
+ m_properties(p)
+{
+ if (!(*i)->event()->get<Int>
+ (BaseProperties::BEAMED_GROUP_ID, m_groupNo)) m_groupNo = -1;
+
+ initialise();
+
+ /*
+ NOTATION_DEBUG << "NotationGroup::NotationGroup: id is " << m_groupNo << endl;
+ i = getInitialElement();
+ while (i != getContainer().end()) {
+ long gid = -1;
+ (*i)->event()->get<Int>(BEAMED_GROUP_ID, gid);
+ NOTATION_DEBUG << "Found element with group id "
+ << gid << endl;
+ if (i == getFinalElement()) break;
+ ++i;
+ }
+ */
+}
+
+NotationGroup::NotationGroup(NotationElementList &nel,
+ const Quantizer *q,
+ const NotationProperties &p,
+ const Clef &clef, const Key &key) :
+ AbstractSet<NotationElement, NotationElementList>(nel, nel.end(), q),
+ m_barRange(0, 0),
+ //!!! What if the clef and/or key change in the course of the group?
+ m_clef(clef),
+ m_key(key),
+ m_weightAbove(0),
+ m_weightBelow(0),
+ m_userSamples(true),
+ m_groupNo( -1),
+ m_type(Beamed),
+ m_properties(p)
+{
+ //...
+}
+
+NotationGroup::~NotationGroup()
+{}
+
+bool NotationGroup::test(const NELIterator &i)
+{
+ // An event is a candidate for being within the bounds of the
+ // set if it's simply within the same bar as the original event.
+ // (Groups may contain other groups, so our bounds may enclose
+ // events that aren't members of the group: we reject those in
+ // sample rather than test, so as to avoid erroneously ending
+ // the group too soon.)
+
+ return ((*i)->getViewAbsoluteTime() >= m_barRange.first &&
+ (*i)->getViewAbsoluteTime() < m_barRange.second);
+}
+
+bool
+NotationGroup::sample(const NELIterator &i, bool goingForwards)
+{
+ if (m_baseIterator == getContainer().end()) {
+ m_baseIterator = i;
+ if (m_userSamples)
+ m_initial = i;
+ }
+ if (m_userSamples)
+ m_final = i;
+
+ std::string t;
+ if (!(*i)->event()->get<String>(BaseProperties::BEAMED_GROUP_TYPE, t)) {
+ NOTATION_DEBUG << "NotationGroup::NotationGroup: Rejecting sample() for non-beamed element" << endl;
+ return false;
+ }
+
+ long n;
+ if (!(*i)->event()->get<Int>(BaseProperties::BEAMED_GROUP_ID, n)) return false;
+ if (m_groupNo == -1) {
+ m_groupNo = n;
+ } else if (n != m_groupNo) {
+ NOTATION_DEBUG << "NotationGroup::NotationGroup: Rejecting sample() for event with group id " << n << " (mine is " << m_groupNo << ")" << endl;
+ return false;
+ }
+
+ if (t == BaseProperties::GROUP_TYPE_BEAMED) {
+ m_type = Beamed;
+ } else if (t == BaseProperties::GROUP_TYPE_TUPLED) {
+ m_type = Tupled;
+ } else if (t == BaseProperties::GROUP_TYPE_GRACE) {
+ std::cerr << "NotationGroup::NotationGroup: WARNING: Obsolete group type Grace found" << std::endl;
+ return false;
+ } else {
+ NOTATION_DEBUG << "NotationGroup::NotationGroup: Warning: Rejecting sample() for unknown GroupType \"" << t << "\"" << endl;
+ return false;
+ }
+
+ NOTATION_DEBUG << "NotationGroup::sample: group id is " << m_groupNo << endl;
+
+ AbstractSet<NotationElement, NotationElementList>::sample
+ (i, goingForwards);
+
+ // If the sum of the distances from the middle line to the notes
+ // above the middle line exceeds the sum of the distances from the
+ // middle line to the notes below, then the beam goes below. We
+ // can calculate the weightings here, as we construct the group.
+
+ if (!static_cast<NotationElement*>(*i)->isNote())
+ return true;
+ if (m_userSamples) {
+ if (m_initialNote == getContainer().end()) m_initialNote = i;
+ m_finalNote = i;
+ }
+
+ // The code that uses the Group should not rely on the presence of
+ // e.g. BEAM_GRADIENT to indicate that a beam should be drawn;
+ // it's possible the gradient might be left over from a previous
+ // calculation and the group might have changed since. Instead it
+ // should test BEAMED, which may be false even if there is a
+ // gradient present.
+ (*i)->event()->setMaybe<Bool>(NotationProperties::BEAMED, false);
+
+ int h = height(i);
+ if (h > 4)
+ m_weightAbove += h - 4;
+ if (h < 4)
+ m_weightBelow += 4 - h;
+
+ return true;
+}
+
+bool
+NotationGroup::contains(const NELIterator &i) const
+{
+ NELIterator j(getInitialElement()),
+ k( getFinalElement());
+
+ for (;;) {
+ if (j == i)
+ return true;
+ if (j == k)
+ return false;
+ ++j;
+ }
+}
+
+int
+NotationGroup::height(const NELIterator &i) const
+{
+ long h = 0;
+ if ((*i)->event()->get<Int>(NotationProperties::HEIGHT_ON_STAFF, h)) {
+ return h;
+ }
+
+ //!!! int pitch = (*i)->event()->get<Int>(PITCH);
+ // NotationDisplayPitch p(pitch, m_clef, m_key);
+ // h = p.getHeightOnStaff();
+
+ try {
+ Pitch pitch(*getAsEvent(i));
+ h = pitch.getHeightOnStaff(m_clef, m_key);
+ } catch (...) {
+ // no pitch!
+ }
+
+ // not setMaybe, as we know the property is absent:
+ (*i)->event()->set<Int>(NotationProperties::HEIGHT_ON_STAFF, h, false);
+ return h;
+}
+
+void
+NotationGroup::applyStemProperties()
+{
+ NotationRules rules;
+
+ NELIterator
+ initialNote(getInitialNote()),
+ finalNote(getFinalNote());
+
+ if (initialNote == getContainer().end() ||
+ initialNote == finalNote) {
+ //!!! This is not true -- if initialNote == finalNote there is
+ // one note in the group, not none. But we still won't have a
+ // beam.
+ NOTATION_DEBUG << "NotationGroup::applyStemProperties: no notes in group"
+ << endl;
+ return; // no notes, no case to answer
+ }
+
+ if (getHighestNote() == getContainer().end()) {
+ std::cerr << "ERROR: NotationGroup::applyStemProperties: no highest note!" << std::endl;
+ abort();
+ }
+
+ if (getLowestNote() == getContainer().end()) {
+ std::cerr << "ERROR: NotationGroup::applyStemProperties: no lowest note!" << std::endl;
+ abort();
+ }
+
+ int up = 0, down = 0;
+
+ for (NELIterator i = initialNote; i != getContainer().end(); ++i) {
+ NotationElement* el = static_cast<NotationElement*>(*i);
+ if (el->isNote()) {
+ if (el->event()->has(NotationProperties::STEM_UP)) {
+ if (el->event()->get<Bool>(NotationProperties::STEM_UP)) ++up;
+ else ++down;
+ }
+ }
+
+ if (i == finalNote) break;
+ }
+
+ NOTATION_DEBUG << "NotationGroup::applyStemProperties: weightAbove "
+ << m_weightAbove << ", weightBelow " << m_weightBelow
+ << ", up " << up << ", down " << down << endl;
+
+ bool aboveNotes = rules.isBeamAbove(height(getHighestNote()),
+ height(getLowestNote()),
+ m_weightAbove,
+ m_weightBelow);
+ if (up != down) {
+ if (up > down) aboveNotes = true;
+ else aboveNotes = false;
+ }
+
+ NOTATION_DEBUG << "NotationGroup::applyStemProperties: hence aboveNotes "
+ << aboveNotes << endl;
+
+ /*!!!
+ if ((*initialNote)->event()->has(STEM_UP) &&
+ (*initialNote)->event()->isPersistent<Bool>(STEM_UP)) {
+ aboveNotes = (*initialNote)->event()->get<Bool>(STEM_UP);
+ }
+
+ if ((*initialNote)->event()->has(NotationProperties::BEAM_ABOVE) &&
+ (*initialNote)->event()->isPersistent<Bool>(NotationProperties::BEAM_ABOVE)) {
+ aboveNotes = (*initialNote)->event()->get<Bool>
+ (NotationProperties::BEAM_ABOVE);
+ }
+ */
+ for (NELIterator i = initialNote; i != getContainer().end(); ++i) {
+
+ NotationElement* el = static_cast<NotationElement*>(*i);
+
+ el->event()->setMaybe<Bool>(NotationProperties::BEAM_ABOVE, aboveNotes);
+
+ if (el->isNote() &&
+ el->event()->has(BaseProperties::NOTE_TYPE) &&
+ el->event()->get<Int>(BaseProperties::NOTE_TYPE) < Note::Crotchet &&
+ el->event()->has(BaseProperties::BEAMED_GROUP_ID) &&
+ el->event()->get<Int>(BaseProperties::BEAMED_GROUP_ID) == m_groupNo) {
+
+ el->event()->setMaybe<Bool>(NotationProperties::BEAMED, true);
+ // el->event()->setMaybe<Bool>(m_properties.VIEW_LOCAL_STEM_UP, aboveNotes);
+
+ } else if (el->isNote()) {
+
+ if (i == initialNote || i == finalNote) {
+ (*i)->event()->setMaybe<Bool>
+ (m_properties.VIEW_LOCAL_STEM_UP, aboveNotes);
+ } else {
+ (*i)->event()->setMaybe<Bool>
+ (m_properties.VIEW_LOCAL_STEM_UP, !aboveNotes);
+ }
+ }
+
+ if (i == finalNote) break;
+ }
+}
+
+bool
+NotationGroup::haveInternalRest()
+const
+{
+ bool inside = false;
+ bool found = false;
+
+ for (NELIterator i = getInitialNote(); i != getContainer().end(); ++i) {
+ NotationElement* el = static_cast<NotationElement*>(*i);
+
+ if (el->isNote() &&
+ el->event()->has(BaseProperties::NOTE_TYPE) &&
+ el->event()->get<Int>(BaseProperties::NOTE_TYPE) < Note::Crotchet &&
+ el->event()->has(BaseProperties::BEAMED_GROUP_ID) &&
+ el->event()->get<Int>(BaseProperties::BEAMED_GROUP_ID) == m_groupNo) {
+ if (found) return true; // a rest is wholly enclosed by beamed notes
+ inside = true;
+ }
+
+ if (el->isRest() && inside) found = true;
+
+ if (i == getFinalNote()) break;
+ }
+
+ return false;
+}
+
+NotationGroup::Beam
+NotationGroup::calculateBeam(NotationStaff &staff)
+{
+ NotationRules rules;
+
+ Beam beam;
+ beam.aboveNotes = true;
+ beam.startY = 0;
+ beam.gradient = 0;
+ beam.necessary = false;
+
+ NELIterator
+ initialNote(getInitialNote()),
+ finalNote(getFinalNote());
+
+ if (initialNote == getContainer().end() ||
+ initialNote == finalNote) {
+ return beam; // no notes, or at most one: no case to answer
+ }
+
+ beam.aboveNotes = rules.isBeamAbove(height(getHighestNote()),
+ height(getLowestNote()),
+ m_weightAbove,
+ m_weightBelow);
+
+ if ((*initialNote)->event()->has(NotationProperties::BEAM_ABOVE)) {
+ beam.aboveNotes = (*initialNote)->event()->get
+ <Bool>
+ (NotationProperties::BEAM_ABOVE);
+ }
+
+ timeT crotchet = Note(Note::Crotchet).getDuration();
+
+ beam.necessary =
+ (*initialNote)->getViewDuration() < crotchet &&
+ (*finalNote)->getViewDuration() < crotchet;
+
+ beam.necessary = beam.necessary &&
+ (((*finalNote)->getViewAbsoluteTime() >
+ (*initialNote)->getViewAbsoluteTime()) ||
+ (((*finalNote)->getViewAbsoluteTime() ==
+ (*initialNote)->getViewAbsoluteTime()) &&
+ ((*finalNote)->event()->getSubOrdering() >
+ (*initialNote)->event()->getSubOrdering())));
+
+ // We continue even if the beam is not necessary, because the
+ // same data is used to generate the tupling line in tupled
+ // groups that do not have beams
+
+ // if (!beam.necessary) return beam;
+
+ NOTATION_DEBUG << "NotationGroup::calculateBeam: beam necessariness: " << beam.necessary << endl;
+
+ NotationChord initialChord(getContainer(), initialNote, &getQuantizer(),
+ m_properties, m_clef, m_key),
+ finalChord(getContainer(), finalNote, &getQuantizer(),
+ m_properties, m_clef, m_key);
+
+ if (initialChord.getInitialElement() == finalChord.getInitialElement()) {
+ return beam;
+ }
+
+ bool isGrace =
+ (*initialNote)->event()->has(BaseProperties::IS_GRACE_NOTE) &&
+ (*initialNote)->event()->get<Bool>(BaseProperties::IS_GRACE_NOTE);
+
+ int initialHeight, finalHeight, extremeHeight;
+ NELIterator extremeNote;
+
+ if (beam.aboveNotes) {
+
+ initialHeight = height(initialChord.getHighestNote());
+ finalHeight = height( finalChord.getHighestNote());
+ extremeHeight = height( getHighestNote());
+ extremeNote = getHighestNote();
+
+ } else {
+ initialHeight = height(initialChord.getLowestNote());
+ finalHeight = height( finalChord.getLowestNote());
+ extremeHeight = height( getLowestNote());
+ extremeNote = getLowestNote();
+ }
+
+ int diff = initialHeight - finalHeight;
+ if (diff < 0) diff = -diff;
+
+ bool linear =
+ (beam.aboveNotes ?
+ (extremeHeight <= std::max(initialHeight, finalHeight)) :
+ (extremeHeight >= std::min(initialHeight, finalHeight)));
+
+ if (!linear) {
+ if (diff > 2)
+ diff = 1;
+ else
+ diff = 0;
+ }
+
+ // Now, we need to judge the height of the beam such that the
+ // nearest note of the whole group, the nearest note of the first
+ // chord and the nearest note of the final chord are all at least
+ // two note-body-heights away from it, and at least one of the
+ // start and end points is at least the usual note stem-length
+ // away from it. This is a straight-line equation y = mx + c,
+ // where we have m and two x,y pairs and need to find c.
+
+ //!!! If we find that making one extreme a sensible distance from
+ //the note head makes the other extreme way too far away from it
+ //in the direction of the gradient, then we should flatten the
+ //gradient. There may be a better heuristic for this.
+
+ int initialX = (int)(*initialNote)->getLayoutX();
+ int finalDX = (int) (*finalNote)->getLayoutX() - initialX;
+ int extremeDX = (int)(*extremeNote)->getLayoutX() - initialX;
+
+ int spacing = staff.getNotePixmapFactory(isGrace).getLineSpacing();
+
+ beam.gradient = 0;
+ if (finalDX > 0) {
+ do {
+ if (diff == 0)
+ break;
+ else if (diff > 3)
+ diff = 3;
+ else
+ --diff;
+ beam.gradient = (diff * spacing * 100) / (finalDX * 2);
+ } while (beam.gradient > 18);
+ } else {
+ beam.gradient = 0;
+ }
+ if (initialHeight < finalHeight)
+ beam.gradient = -beam.gradient;
+
+ int finalY = staff.getLayoutYForHeight(finalHeight);
+ int extremeY = staff.getLayoutYForHeight(extremeHeight);
+
+ int c0 = staff.getLayoutYForHeight(initialHeight), c1, c2;
+ double dgrad = (double)beam.gradient / 100.0;
+
+ Equation::solve(Equation::C, extremeY, dgrad, extremeDX, c1);
+ Equation::solve(Equation::C, finalY, dgrad, finalDX, c2);
+
+ using std::max;
+ using std::min;
+ long shortestNoteType = Note::Quaver;
+ if (!(*getShortestElement())->event()->get
+ <Int>(BaseProperties::NOTE_TYPE,
+ shortestNoteType)) {
+ NOTATION_DEBUG << "NotationGroup::calculateBeam: WARNING: Shortest element has no note-type; should this be possible?" << endl;
+ NOTATION_DEBUG << "(Event dump follows)" << endl;
+ (*getShortestElement())->event()->dump(std::cerr);
+ }
+
+ // minimal stem lengths at start, middle-extreme and end of beam
+ int sl = staff.getNotePixmapFactory(isGrace).getStemLength();
+ int ml = spacing * 2;
+ int el = sl;
+
+ NOTATION_DEBUG << "c0: " << c0 << ", c1: " << c1 << ", c2: " << c2 << endl;
+ NOTATION_DEBUG << "sl: " << sl << ", ml: " << ml << ", el: " << el << endl;
+
+ // If the stems are down, we will need to ensure they end at
+ // heights lower than 0 if there's an internal rest -- likewise
+ // with stems up and an internal rest we need to ensure they end
+ // at higher than 8.
+ // [Avoid doing expensive haveInternalRest() test where possible]
+
+ if (beam.aboveNotes) {
+
+ int topY = staff.getLayoutYForHeight(8);
+
+ if ((c0 - sl > topY) || (c1 - ml > topY) || (c2 - el > topY)) {
+ if (haveInternalRest()) {
+ if (c0 - sl > topY) sl = c0 - topY;
+ if (c1 - ml > topY) ml = c1 - topY;
+ if (c2 - el > topY) el = c2 - topY;
+ NOTATION_DEBUG << "made internal rest adjustment for above notes" << endl;
+ NOTATION_DEBUG << "sl: " << sl << ", ml: " << ml << ", el: " << el << endl;
+ }
+ }
+ } else {
+ int bottomY = staff.getLayoutYForHeight(0);
+
+ if ((c0 + sl < bottomY) || (c1 + ml < bottomY) || (c2 + el < bottomY)) {
+ if (haveInternalRest()) {
+ if (c0 + sl < bottomY) sl = bottomY - c0;
+ if (c1 + ml < bottomY) ml = bottomY - c1;
+ if (c2 + el < bottomY) el = bottomY - c2;
+ NOTATION_DEBUG << "made internal rest adjustment for below notes" << endl;
+ NOTATION_DEBUG << "sl: " << sl << ", ml: " << ml << ", el: " << el << endl;
+ }
+ }
+ }
+
+
+ if (shortestNoteType < Note::Semiquaver) {
+ int off = spacing * (Note::Semiquaver - shortestNoteType);
+ sl += off;
+ el += off;
+ }
+
+ if (shortestNoteType < Note::Quaver) {
+ int off = spacing * (Note::Quaver - shortestNoteType);
+ ml += off;
+ }
+
+
+ int midY = staff.getLayoutYForHeight(4);
+
+ // ensure extended to middle line if necessary, and assign suitable stem length
+ if (beam.aboveNotes) {
+ if (c0 - sl > midY) sl = c0 - midY;
+ if (c1 - ml > midY) ml = c1 - midY;
+ if (c2 - el > midY) el = c2 - midY;
+ if (extremeDX > 1.0 || extremeDX < -1.0) {
+ // beam.gradient = int(100 * double(c2 - c0) / double(extremeDX));
+ }
+ beam.startY = min(min(c0 - sl, c1 - ml), c2 - el);
+ } else {
+ if (c0 + sl < midY) sl = midY - c0;
+ if (c1 + ml < midY) ml = midY - c1;
+ if (c2 + el < midY) el = midY - c2;
+ if (extremeDX > 1.0 || extremeDX < -1.0) {
+ // beam.gradient = int(100 * double(c2 - c0) / double(extremeDX));
+ }
+ beam.startY = max(max(c0 + sl, c1 + ml), c2 + el);
+ }
+ /*
+ NOTATION_DEBUG << "NotationGroup::calculateBeam: beam data:" << endl
+ << "gradient: " << beam.gradient << endl
+ << "(c0 " << c0 << ", c2 " << c2 << ", extremeDX " << extremeDX << ")" << endl
+ << "startY: " << beam.startY << endl
+ << "aboveNotes: " << beam.aboveNotes << endl
+ << "shortestNoteType: " << shortestNoteType << endl
+ << "necessary: " << beam.necessary << endl;
+ */
+ return beam;
+}
+
+void
+NotationGroup::applyBeam(NotationStaff &staff)
+{
+ // NOTATION_DEBUG << "NotationGroup::applyBeam, group no is " << m_groupNo << endl;
+ /*
+ NOTATION_DEBUG << "\nNotationGroup::applyBeam" << endl;
+ NOTATION_DEBUG << "Group id: " << m_groupNo << ", type " << m_type << endl;
+ NOTATION_DEBUG << "Coverage:" << endl;
+ int i = 0;
+ for (NELIterator i = getInitialElement(); i != getContainer().end(); ++i) {
+ (*i)->event()->dump(cerr);
+ if (i == getFinalElement()) break;
+ }
+ {
+ NELIterator i(getInitialNote());
+ NOTATION_DEBUG << "Initial note: " << (i == getContainer().end() ? -1 : (*i)->event()->getAbsoluteTime()) << endl;
+ }
+ {
+ NELIterator i(getFinalNote());
+ NOTATION_DEBUG << "Final note: " << (i == getContainer().end() ? -1 : (*i)->event()->getAbsoluteTime()) << endl;
+ }
+ {
+ NELIterator i(getHighestNote());
+ NOTATION_DEBUG << "Highest note: " << (i == getContainer().end() ? -1 : (*i)->event()->getAbsoluteTime()) << endl;
+ }
+ {
+ NELIterator i(getLowestNote());
+ NOTATION_DEBUG << "Lowest note: " << (i == getContainer().end() ? -1 : (*i)->event()->getAbsoluteTime()) << endl;
+ }
+ */
+ Beam beam(calculateBeam(staff));
+ if (!beam.necessary) {
+ for (NELIterator i = getInitialNote(); i != getContainer().end(); ++i) {
+ (*i)->event()->unset(NotationProperties::BEAMED);
+ (*i)->event()->unset(m_properties.TUPLING_LINE_MY_Y);
+ if (i == getFinalNote())
+ break;
+ }
+ return ;
+ }
+
+ // NOTATION_DEBUG << "NotationGroup::applyBeam: Beam is necessary" << endl;
+
+ NELIterator initialNote(getInitialNote()),
+ finalNote( getFinalNote());
+ int initialX = (int)(*initialNote)->getLayoutX();
+ timeT finalTime = (*finalNote)->getViewAbsoluteTime();
+
+ // For each chord in the group, we nominate the note head furthest
+ // from the beam as the primary note, the one that "owns" the stem
+ // and the section of beam up to the following chord. For this
+ // note, we need to:
+ //
+ // * Set the start height, start x-coord and gradient of the beam
+ // (we can't set the stem length for this note directly, because
+ // we don't know its y-coordinate yet)
+ //
+ // * Set width of this section of beam
+ //
+ // * Set the number of beams required for the following note (one
+ // slight complication here: a beamed group in which the very
+ // first chord is shorter than the following one. Here the first
+ // chord needs to know it's the first, or else it can't draw the
+ // part-beams immediately to its right correctly.)
+ //
+ // For the rest of the notes in the chord, we just need to
+ // indicate that they aren't part of the beam-drawing process and
+ // don't need to draw a stem.
+
+ NELIterator prev = getContainer().end(), prevprev = getContainer().end();
+ double gradient = (double)beam.gradient / 100.0;
+
+ // NOTATION_DEBUG << "NotationGroup::applyBeam starting for group "<< this << endl;
+
+ for (NELIterator i = getInitialNote(); i != getContainer().end(); ++i) {
+ NotationElement* el = static_cast<NotationElement*>(*i);
+
+ // Clear tuplingness for all events in the group, to be
+ // reinstated by any subsequent call to applyTuplingLine. We
+ // do this because applyTuplingLine doesn't clear these
+ // properties from notes that don't need them; it only applies
+ // them to notes that do.
+ el->event()->unset(m_properties.TUPLING_LINE_MY_Y);
+
+ if (el->isNote() &&
+ el->event()->has(BaseProperties::NOTE_TYPE) &&
+ el->event()->get
+ <Int>(BaseProperties::NOTE_TYPE) < Note::Crotchet &&
+ el->event()->has(BaseProperties::BEAMED_GROUP_ID) &&
+ el->event()->get<Int>(BaseProperties::BEAMED_GROUP_ID) == m_groupNo) {
+
+ NotationChord chord(getContainer(), i, &getQuantizer(),
+ m_properties, m_clef, m_key);
+ unsigned int j;
+
+ // NOTATION_DEBUG << "NotationGroup::applyBeam: Found chord" << endl;
+
+ bool hasShifted = chord.hasNoteHeadShifted();
+
+ for (j = 0; j < chord.size(); ++j) {
+ NotationElement *el = static_cast<NotationElement*>(*chord[j]);
+
+ el->event()->setMaybe<Bool>
+ (m_properties.CHORD_PRIMARY_NOTE, false);
+
+ el->event()->setMaybe<Bool>
+ (m_properties.DRAW_FLAG, false);
+
+ el->event()->setMaybe<Bool>
+ (NotationProperties::BEAMED, true);
+
+ el->event()->setMaybe<Bool>
+ (NotationProperties::BEAM_ABOVE, beam.aboveNotes);
+
+ el->event()->setMaybe<Bool>
+ (m_properties.VIEW_LOCAL_STEM_UP, beam.aboveNotes);
+
+ bool shifted = chord.isNoteHeadShifted(chord[j]);
+ el->event()->setMaybe<Bool>
+ (m_properties.NOTE_HEAD_SHIFTED, shifted);
+
+ long dots = 0;
+ (void)el->event()->get
+ <Int>(BaseProperties::NOTE_DOTS, dots);
+
+ el->event()->setMaybe<Bool>
+ (m_properties.NOTE_DOT_SHIFTED, false);
+ if (hasShifted && beam.aboveNotes) {
+ long dots = 0;
+ (void)el->event()->get
+ <Int>(BaseProperties::NOTE_DOTS, dots);
+ if (dots > 0) {
+ el->event()->setMaybe<Bool>
+ (m_properties.NOTE_DOT_SHIFTED, true);
+ }
+ }
+
+ el->event()->setMaybe<Bool>
+ (m_properties.NEEDS_EXTRA_SHIFT_SPACE,
+ chord.hasNoteHeadShifted() && !beam.aboveNotes);
+ }
+
+ if (beam.aboveNotes)
+ j = 0;
+ else
+ j = chord.size() - 1;
+
+ NotationElement *el = static_cast<NotationElement*>(*chord[j]);
+ el->event()->setMaybe<Bool>(NotationProperties::BEAMED, false); // set later
+ el->event()->setMaybe<Bool>(m_properties.DRAW_FLAG, true); // set later
+
+ int x = (int)el->getLayoutX();
+ int myY = (int)(gradient * (x - initialX)) + beam.startY;
+
+ int beamCount =
+ NoteStyleFactory::getStyleForEvent(el->event())->
+ getFlagCount(el->event()->get
+ <Int>(BaseProperties::NOTE_TYPE));
+
+ // If THIS_PART_BEAMS is true, then when drawing the
+ // chord, if it requires more beams than the following
+ // chord then they should be added as partial beams to the
+ // right of the stem.
+
+ // If NEXT_PART_BEAMS is true, then when drawing the
+ // chord, if it requires fewer beams than the following
+ // chord then the difference should be added as partial
+ // beams to the left of the following chord's stem.
+
+ // Procedure for setting these: If we have more beams than
+ // the preceding chord, then the preceding chord should
+ // have NEXT_PART_BEAMS set, until possibly unset again on
+ // the next iteration. If we have at least as many beams
+ // as the preceding chord, then the preceding chord should
+ // have THIS_PART_BEAMS unset and the one before it should
+ // have NEXT_PART_BEAMS unset. The first chord should
+ // have THIS_PART_BEAMS set, until possibly unset again on
+ // the next iteration.
+
+ if (prev != getContainer().end()) {
+
+ NotationElement *prevEl = static_cast<NotationElement*>(*prev);
+ int secWidth = x - (int)prevEl->getLayoutX();
+
+ // prevEl->event()->setMaybe<Int>(BEAM_NEXT_Y, myY);
+
+ prevEl->event()->setMaybe<Int>
+ (m_properties.BEAM_SECTION_WIDTH, secWidth);
+ prevEl->event()->setMaybe<Int>
+ (m_properties.BEAM_NEXT_BEAM_COUNT, beamCount);
+
+ int prevBeamCount =
+ NoteStyleFactory::getStyleForEvent(prevEl->event())->
+ getFlagCount(prevEl->event()->get
+ <Int>(BaseProperties::NOTE_TYPE));
+
+ if ((beamCount > 0) && (prevBeamCount > 0)) {
+ el->event()->setMaybe<Bool>(m_properties.BEAMED, true);
+ el->event()->setMaybe<Bool>(m_properties.DRAW_FLAG, false);
+ prevEl->event()->setMaybe<Bool>(m_properties.BEAMED, true);
+ prevEl->event()->setMaybe<Bool>(m_properties.DRAW_FLAG, false);
+ }
+
+ if (beamCount >= prevBeamCount) {
+ prevEl->event()->setMaybe<Bool>
+ (m_properties.BEAM_THIS_PART_BEAMS, false);
+ if (prevprev != getContainer().end()) {
+ (*prevprev)->event()->setMaybe<Bool>
+ (m_properties.BEAM_NEXT_PART_BEAMS, false);
+ }
+ }
+
+ if (beamCount > prevBeamCount) {
+ prevEl->event()->setMaybe<Bool>
+ (m_properties.BEAM_NEXT_PART_BEAMS, true);
+ }
+
+ } else {
+ el->event()->setMaybe<Bool>(m_properties.BEAM_THIS_PART_BEAMS, true);
+ }
+
+ el->event()->setMaybe<Bool>(m_properties.CHORD_PRIMARY_NOTE, true);
+
+ el->event()->setMaybe<Int>(m_properties.BEAM_MY_Y, myY);
+ el->event()->setMaybe<Int>(m_properties.BEAM_GRADIENT, beam.gradient);
+
+ // until they're set next time around the loop, as (*prev)->...
+ // el->event()->setMaybe<Int>(m_properties.BEAM_NEXT_Y, myY);
+ el->event()->setMaybe<Int>(m_properties.BEAM_SECTION_WIDTH, 0);
+ el->event()->setMaybe<Int>(m_properties.BEAM_NEXT_BEAM_COUNT, 1);
+
+ prevprev = prev;
+ prev = chord[j];
+ i = chord.getFinalElement();
+
+ }
+ else if (el->isNote()) {
+
+ //!!! should we really be setting these here as well as in
+ // applyStemProperties?
+ /*
+ if (i == initialNote || i == finalNote) {
+ (*i)->event()->setMaybe<Bool>(m_properties.VIEW_LOCAL_STEM_UP, beam.aboveNotes);
+ } else {
+ (*i)->event()->setMaybe<Bool>(m_properties.VIEW_LOCAL_STEM_UP, !beam.aboveNotes);
+ }
+ */
+ }
+
+ if (i == finalNote || el->getViewAbsoluteTime() > finalTime) break;
+ }
+}
+
+void
+NotationGroup::applyTuplingLine(NotationStaff &staff)
+{
+ // NOTATION_DEBUG << "NotationGroup::applyTuplingLine, group no is " << m_groupNo << ", group type is " << m_type << endl;
+
+ if (m_type != Tupled)
+ return ;
+
+ // NOTATION_DEBUG << "NotationGroup::applyTuplingLine: line is necessary" << endl;
+
+ Beam beam(calculateBeam(staff));
+
+ NELIterator
+ initialNote(getInitialNote()),
+ finalNote(getFinalNote()),
+ initialElement(getInitialElement()),
+ finalElement(getFinalElement());
+
+ NELIterator initialNoteOrRest(initialElement);
+ NotationElement* initialNoteOrRestEl = static_cast<NotationElement*>(*initialNoteOrRest);
+
+ while (initialNoteOrRest != finalElement &&
+ !(initialNoteOrRestEl->isNote() ||
+ initialNoteOrRestEl->isRest())) {
+ ++initialNoteOrRest;
+ initialNoteOrRestEl = static_cast<NotationElement*>(*initialNoteOrRest);
+ }
+
+ if (!initialNoteOrRestEl->isRest()) {
+ initialNoteOrRest = initialNote;
+ initialNoteOrRestEl = static_cast<NotationElement*>(*initialNoteOrRest);
+ }
+
+ if (initialNoteOrRest == staff.getViewElementList()->end()) return;
+
+ bool isGrace = false;
+ if (initialNote != staff.getViewElementList()->end()) {
+ isGrace =
+ (*initialNote)->event()->has(BaseProperties::IS_GRACE_NOTE) &&
+ (*initialNote)->event()->get<Bool>(BaseProperties::IS_GRACE_NOTE);
+ }
+
+ // NOTATION_DEBUG << "NotationGroup::applyTuplingLine: first element is " << (initialNoteOrRestEl->isNote() ? "Note" : "Non-Note") << ", last is " << (static_cast<NotationElement*>(*finalElement)->isNote() ? "Note" : "Non-Note") << endl;
+
+ int initialX = (int)(*initialNoteOrRest)->getLayoutX();
+ int finalX = (int)(*finalElement)->getLayoutX();
+
+ if (initialNote == staff.getViewElementList()->end() &&
+ finalNote == staff.getViewElementList()->end()) {
+
+ Event *e = (*initialNoteOrRest)->event();
+ e->setMaybe<Int>(m_properties.TUPLING_LINE_MY_Y,
+ staff.getLayoutYForHeight(12));
+ e->setMaybe<Int>(m_properties.TUPLING_LINE_WIDTH, finalX - initialX);
+ e->setMaybe<Int>(m_properties.TUPLING_LINE_GRADIENT, 0);
+
+ } else {
+
+ // only notes have height
+ int initialY = staff.getLayoutYForHeight(height(initialNote));
+ int finalY = staff.getLayoutYForHeight(height( finalNote));
+
+ // if we have a beam and both end-points of it are notes,
+ // place the tupling number over it (that is, make the tupling
+ // line follow the beam and say so); otherwise make the line
+ // follow the gradient a beam would have, but on the other
+ // side of the notes
+ bool followBeam =
+ (beam.necessary &&
+ (*initialNoteOrRest)->event()->isa(Note::EventType) &&
+ (finalNote == finalElement));
+
+ int startY = (followBeam ? beam.startY :
+ initialY - (beam.startY - initialY));
+
+ int endY = startY + (int)((finalX - initialX) *
+ ((double)beam.gradient / 100.0));
+
+ // NOTATION_DEBUG << "applyTuplingLine: beam.startY is " << beam.startY << ", initialY is " << initialY << " so my startY is " << startY << ", endY " << endY << ", beam.gradient " << beam.gradient << endl;
+
+ int nh = staff.getNotePixmapFactory(isGrace).getNoteBodyHeight();
+
+ if (followBeam) { // adjust to move text slightly away from beam
+
+ int maxEndBeamCount = 1;
+ long bc;
+ if ((*initialNoteOrRest)->event()->get<Int>
+ (m_properties.BEAM_NEXT_BEAM_COUNT, bc)) {
+ if (bc > maxEndBeamCount)
+ maxEndBeamCount = bc;
+ }
+ if ((*finalNote)->event()->get<Int>
+ (m_properties.BEAM_NEXT_BEAM_COUNT, bc)) {
+ if (bc > maxEndBeamCount)
+ maxEndBeamCount = bc;
+ }
+
+ int extraBeamSpace = maxEndBeamCount * nh + nh / 2;
+
+ if (beam.aboveNotes) {
+ startY -= extraBeamSpace;
+ endY -= extraBeamSpace;
+ finalX += nh;
+ } else {
+ startY += extraBeamSpace;
+ endY += extraBeamSpace;
+ finalX -= nh;
+ }
+
+ } else { // adjust to place close to note heads
+
+ if (startY < initialY) {
+ if (initialY - startY > nh * 3)
+ startY = initialY - nh * 3;
+ if ( finalY - endY < nh * 2)
+ startY -= nh * 2 - (finalY - endY);
+ } else {
+ if (startY - initialY > nh * 3)
+ startY = initialY + nh * 3;
+ if ( endY - finalY < nh * 2)
+ startY += nh * 2 - (endY - finalY);
+ }
+ }
+
+ Event *e = (*initialNoteOrRest)->event();
+ e->setMaybe<Int>(m_properties.TUPLING_LINE_MY_Y, startY);
+ e->setMaybe<Int>(m_properties.TUPLING_LINE_WIDTH, finalX - initialX);
+ e->setMaybe<Int>(m_properties.TUPLING_LINE_GRADIENT, beam.gradient);
+ e->setMaybe<Bool>(m_properties.TUPLING_LINE_FOLLOWS_BEAM, followBeam);
+ }
+}
+
+}
diff --git a/src/gui/editors/notation/NotationGroup.h b/src/gui/editors/notation/NotationGroup.h
new file mode 100644
index 0000000..c7b1134
--- /dev/null
+++ b/src/gui/editors/notation/NotationGroup.h
@@ -0,0 +1,133 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTATIONGROUP_H_
+#define _RG_NOTATIONGROUP_H_
+
+#include "base/Sets.h"
+#include <utility>
+#include "base/Event.h"
+#include "NotationElement.h"
+
+
+
+
+namespace Rosegarden
+{
+
+class Quantizer;
+class NotationStaff;
+class NotationProperties;
+class Key;
+class Clef;
+
+
+/// Several sorts of "Beamed Group"
+
+class NotationGroup : public AbstractSet<NotationElement,
+ NotationElementList>
+{
+public:
+ typedef NotationElementList::iterator NELIterator;
+
+ enum Type { Beamed, Tupled };
+
+ /// Group contents will be sampled from elements surrounding elementInGroup
+ NotationGroup(NotationElementList &nel, NELIterator elementInGroup,
+ const Quantizer *,
+ std::pair<timeT, timeT> barRange,
+ const NotationProperties &properties,
+ const Clef &clef, const Key &key);
+
+ /// Caller intends to call sample() for each item in the group, _in order_
+ NotationGroup(NotationElementList &nel,
+ const Quantizer *,
+ const NotationProperties &properties,
+ const Clef &clef, const Key &key);
+
+ virtual ~NotationGroup();
+
+ Type getGroupType() const { return m_type; }
+
+ /**
+ * Writes the BEAMED, BEAM_ABOVE, and STEM_UP properties into the
+ * notes in the group, as appropriate. Does not require layout x
+ * coordinates to have been set.
+ */
+ void applyStemProperties();
+
+ /**
+ * Writes beam data into each note in the group. Notes' layout x
+ * coordinates must already have been set. Does not require
+ * applyStemProperties to have already been called.
+ */
+ void applyBeam(NotationStaff &);
+
+ /**
+ * Writes tupling line data into each note in the group. Notes'
+ * layout x coordinates must already have been set. Does nothing
+ * if this is not a tupled group.
+ */
+ void applyTuplingLine(NotationStaff &);
+
+ virtual bool contains(const NELIterator &) const;
+
+ virtual bool sample(const NELIterator &i, bool goingForwards);
+
+protected:
+ virtual bool test(const NELIterator &i);
+
+private:
+ struct Beam
+ { // if a beam has a line equation y = mx + c,
+ int gradient; // -- then this is m*100 (i.e. a percentage)
+ int startY; // -- and this is c
+ bool aboveNotes;
+ bool necessary;
+ };
+
+ Beam calculateBeam(NotationStaff &);
+
+ int height(const NELIterator&) const;
+
+ bool haveInternalRest() const;
+
+ //--------------- Data members ---------------------------------
+
+ std::pair<timeT, timeT> m_barRange;
+ const Clef &m_clef;
+ const Key &m_key;
+ int m_weightAbove, m_weightBelow;
+ bool m_userSamples;
+ long m_groupNo;
+ Type m_type;
+
+ const NotationProperties &m_properties;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationHLayout.cpp b/src/gui/editors/notation/NotationHLayout.cpp
new file mode 100644
index 0000000..1b13765
--- /dev/null
+++ b/src/gui/editors/notation/NotationHLayout.cpp
@@ -0,0 +1,2110 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NotationHLayout.h"
+#include "misc/Debug.h"
+#include <kapplication.h>
+
+#include "base/Composition.h"
+#include "base/LayoutEngine.h"
+#include "base/NotationTypes.h"
+#include "base/Profiler.h"
+#include "base/NotationQuantizer.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/SegmentNotationHelper.h"
+#include "base/Staff.h"
+#include "base/ViewElement.h"
+#include "gui/editors/guitar/Chord.h"
+#include "gui/general/ProgressReporter.h"
+#include "gui/widgets/ProgressDialog.h"
+#include "NotationChord.h"
+#include "NotationElement.h"
+#include "NotationGroup.h"
+#include "NotationProperties.h"
+#include "NotationStaff.h"
+#include "NotePixmapFactory.h"
+#include <kconfig.h>
+#include <qobject.h>
+#include <cmath>
+
+namespace Rosegarden
+{
+
+using namespace BaseProperties;
+
+
+NotationHLayout::NotationHLayout(Composition *c, NotePixmapFactory *npf,
+ const NotationProperties &properties,
+ QObject* parent, const char* name) :
+ ProgressReporter(parent, name),
+ HorizontalLayoutEngine(c),
+ m_totalWidth(0.),
+ m_pageMode(false),
+ m_pageWidth(0.),
+ m_spacing(100),
+ m_proportion(60),
+ m_keySigCancelMode(1),
+ m_npf(npf),
+ m_notationQuantizer(c->getNotationQuantizer()),
+ m_properties(properties),
+ m_timePerProgressIncrement(0),
+ m_staffCount(0)
+{
+ // NOTATION_DEBUG << "NotationHLayout::NotationHLayout()" << endl;
+
+ KConfig *config = kapp->config();
+ config->setGroup("Notation Options");
+ m_keySigCancelMode = config->readNumEntry("keysigcancelmode", 1);
+}
+
+NotationHLayout::~NotationHLayout()
+{
+ // empty
+}
+
+std::vector<int>
+NotationHLayout::getAvailableSpacings()
+{
+ if (m_availableSpacings.size() == 0) {
+ m_availableSpacings.push_back(30);
+ m_availableSpacings.push_back(60);
+ m_availableSpacings.push_back(85);
+ m_availableSpacings.push_back(100);
+ m_availableSpacings.push_back(130);
+ m_availableSpacings.push_back(170);
+ m_availableSpacings.push_back(220);
+ }
+ return m_availableSpacings;
+}
+
+std::vector<int>
+NotationHLayout::getAvailableProportions()
+{
+ if (m_availableProportions.size() == 0) {
+ m_availableProportions.push_back(0);
+ m_availableProportions.push_back(20);
+ m_availableProportions.push_back(40);
+ m_availableProportions.push_back(60);
+ m_availableProportions.push_back(80);
+ m_availableProportions.push_back(100);
+ }
+ return m_availableProportions;
+}
+
+NotationHLayout::BarDataList &
+
+NotationHLayout::getBarData(Staff &staff)
+{
+ BarDataMap::iterator i = m_barData.find(&staff);
+ if (i == m_barData.end()) {
+ m_barData[&staff] = BarDataList();
+ }
+
+ return m_barData[&staff];
+}
+
+const NotationHLayout::BarDataList &
+
+NotationHLayout::getBarData(Staff &staff) const
+{
+ return ((NotationHLayout *)this)->getBarData(staff);
+}
+
+NotationElementList::iterator
+NotationHLayout::getStartOfQuantizedSlice(NotationElementList *notes,
+ timeT t)
+const
+{
+ NotationElementList::iterator i = notes->findTime(t);
+ NotationElementList::iterator j(i);
+
+ while (true) {
+ if (i == notes->begin())
+ return i;
+ --j;
+ if ((*j)->getViewAbsoluteTime() < t)
+ return i;
+ i = j;
+ }
+}
+
+NotePixmapFactory *
+NotationHLayout::getNotePixmapFactory(Staff &staff)
+{
+ NotationStaff *ns = dynamic_cast<NotationStaff *>(&staff);
+ if (ns) return &ns->getNotePixmapFactory(false);
+ else return 0;
+}
+
+NotePixmapFactory *
+NotationHLayout::getGraceNotePixmapFactory(Staff &staff)
+{
+ NotationStaff *ns = dynamic_cast<NotationStaff *>(&staff);
+ if (ns) return &ns->getNotePixmapFactory(true);
+ else return 0;
+}
+
+void
+NotationHLayout::scanStaff(Staff &staff, timeT startTime, timeT endTime)
+{
+ throwIfCancelled();
+ Profiler profiler("NotationHLayout::scanStaff");
+
+ Segment &segment(staff.getSegment());
+ bool isFullScan = (startTime == endTime);
+ int startBarOfStaff = getComposition()->getBarNumber(segment.getStartTime());
+
+ if (isFullScan) {
+ clearBarList(staff);
+ startTime = segment.getStartTime();
+ endTime = segment.getEndMarkerTime();
+ } else {
+ startTime = getComposition()->getBarStartForTime(startTime);
+ endTime = getComposition()->getBarEndForTime(endTime);
+ }
+
+ NotationElementList *notes = staff.getViewElementList();
+ BarDataList &barList(getBarData(staff));
+
+ NotePixmapFactory *npf = getNotePixmapFactory(staff);
+
+ int startBarNo = getComposition()->getBarNumber(startTime);
+ int endBarNo = getComposition()->getBarNumber(endTime);
+ /*
+ if (endBarNo > startBarNo &&
+ getComposition()->getBarStart(endBarNo) == segment.getEndMarkerTime()) {
+ --endBarNo;
+ }
+ */
+ std::string name =
+ segment.getComposition()->
+ getTrackById(segment.getTrack())->getLabel();
+ m_staffNameWidths[&staff] =
+ npf->getNoteBodyWidth() * 2 +
+ npf->getTextWidth(Text(name, Text::StaffName));
+
+ NOTATION_DEBUG << "NotationHLayout::scanStaff: full scan " << isFullScan << ", times " << startTime << "->" << endTime << ", bars " << startBarNo << "->" << endBarNo << ", staff name \"" << segment.getLabel() << "\", width " << m_staffNameWidths[&staff] << endl;
+
+ SegmentNotationHelper helper(segment);
+ if (isFullScan) {
+ helper.setNotationProperties();
+ } else {
+ helper.setNotationProperties(startTime, endTime);
+ }
+
+ ::Rosegarden::Key key = segment.getKeyAtTime(startTime);
+ Clef clef = segment.getClefAtTime(startTime);
+ TimeSignature timeSignature =
+ segment.getComposition()->getTimeSignatureAt(startTime);
+ bool barCorrect = true;
+
+ int ottavaShift = 0;
+ timeT ottavaEnd = segment.getEndMarkerTime();
+
+ if (isFullScan) {
+
+ NOTATION_DEBUG << "full scan: setting haveOttava false" << endl;
+
+ m_haveOttavaSomewhere[&staff] = false;
+
+ } else if (m_haveOttavaSomewhere[&staff]) {
+
+ NOTATION_DEBUG << "not full scan but ottava is listed" << endl;
+
+ Segment::iterator i = segment.findTime(startTime);
+ while (1) {
+ if ((*i)->isa(Indication::EventType)) {
+ try {
+ Indication indication(**i);
+ if (indication.isOttavaType()) {
+ ottavaShift = indication.getOttavaShift();
+ ottavaEnd = (*i)->getAbsoluteTime() +
+ indication.getIndicationDuration();
+ break;
+ }
+ } catch (...) { }
+ }
+ if (i == segment.begin())
+ break;
+ --i;
+ }
+ }
+
+ NOTATION_DEBUG << "ottava shift at start:" << ottavaShift << ", ottavaEnd " << ottavaEnd << endl;
+
+ KConfig *config = kapp->config();
+ config->setGroup("Notation Options");
+
+ int accOctaveMode = config->readNumEntry("accidentaloctavemode", 1);
+ AccidentalTable::OctaveType octaveType =
+ (accOctaveMode == 0 ? AccidentalTable::OctavesIndependent :
+ accOctaveMode == 1 ? AccidentalTable::OctavesCautionary :
+ AccidentalTable::OctavesEquivalent);
+
+ int accBarMode = config->readNumEntry("accidentalbarmode", 0);
+ AccidentalTable::BarResetType barResetType =
+ (accBarMode == 0 ? AccidentalTable::BarResetNone :
+ accBarMode == 1 ? AccidentalTable::BarResetCautionary :
+ AccidentalTable::BarResetExplicit);
+
+ bool showInvisibles = config->readBoolEntry("showinvisibles", true);
+
+ if (barResetType != AccidentalTable::BarResetNone) {
+ //!!! very crude and expensive way of making sure we see the
+ // accidentals from previous bar:
+ if (startBarNo > segment.getComposition()->getBarNumber(segment.getStartTime())) {
+ --startBarNo;
+ }
+ }
+
+ AccidentalTable accTable(key, clef, octaveType, barResetType);
+
+ for (int barNo = startBarNo; barNo <= endBarNo; ++barNo) {
+
+ std::pair<timeT, timeT> barTimes =
+ getComposition()->getBarRange(barNo);
+
+ if (barTimes.first >= segment.getEndMarkerTime()) {
+ // clear data if we have any old stuff
+ BarDataList::iterator i(barList.find(barNo));
+ if (i != barList.end()) {
+ barList.erase(i);
+ }
+ continue; // so as to erase any further bars next time around
+ }
+
+ NotationElementList::iterator from =
+ getStartOfQuantizedSlice(notes, barTimes.first);
+
+ NOTATION_DEBUG << "getStartOfQuantizedSlice returned " <<
+ (from != notes->end() ? (*from)->getViewAbsoluteTime() : -1)
+ << " from " << barTimes.first << endl;
+
+ NotationElementList::iterator to =
+ getStartOfQuantizedSlice(notes, barTimes.second);
+
+ if (barTimes.second >= segment.getEndMarkerTime()) {
+ to = notes->end();
+ }
+
+ bool newTimeSig = false;
+ timeSignature = getComposition()->getTimeSignatureInBar
+ (barNo, newTimeSig);
+ NOTATION_DEBUG << "bar " << barNo << ", startBarOfStaff " << startBarOfStaff
+ << ", newTimeSig " << newTimeSig << endl;
+
+ float fixedWidth = 0.0;
+ if (newTimeSig && !timeSignature.isHidden()) {
+ fixedWidth += npf->getNoteBodyWidth() +
+ npf->getTimeSigWidth(timeSignature);
+ }
+
+ setBarBasicData(staff, barNo, from, barCorrect, timeSignature, newTimeSig);
+ BarDataList::iterator bdli(barList.find(barNo));
+ bdli->second.layoutData.needsLayout = true;
+
+ ChunkList &chunks = bdli->second.chunks;
+ chunks.clear();
+
+ float lyricWidth = 0;
+ int graceCount = 0;
+
+ typedef std::set
+ <long> GroupIdSet;
+ GroupIdSet groupIds;
+
+ NOTATION_DEBUG << "NotationHLayout::scanStaff: bar " << barNo << ", from " << barTimes.first << ", to " << barTimes.second << " (end " << segment.getEndMarkerTime() << "); from is at " << (from == notes->end() ? -1 : (*from)->getViewAbsoluteTime()) << ", to is at " << (to == notes->end() ? -1 : (*to)->getViewAbsoluteTime()) << endl;
+
+ timeT actualBarEnd = barTimes.first;
+
+ accTable.newBar();
+
+ for (NotationElementList::iterator itr = from; itr != to; ++itr) {
+
+ NotationElement *el = static_cast<NotationElement*>((*itr));
+ NOTATION_DEBUG << "element is a " << el->event()->getType() << endl;
+
+ if (ottavaShift != 0) {
+ if (el->event()->getAbsoluteTime() >= ottavaEnd) {
+ NOTATION_DEBUG << "reached end of ottava" << endl;
+ ottavaShift = 0;
+ }
+ }
+
+ bool invisible = false;
+ if (el->event()->get<Bool>(INVISIBLE, invisible) && invisible) {
+ if (!showInvisibles)
+ continue;
+ }
+
+ if (el->event()->has(BEAMED_GROUP_ID)) {
+ NOTATION_DEBUG << "element is beamed" << endl;
+ long groupId = el->event()->get<Int>(BEAMED_GROUP_ID);
+ if (groupIds.find(groupId) == groupIds.end()) {
+ NOTATION_DEBUG << "it's a new beamed group, applying stem properties" << endl;
+ NotationGroup group(*staff.getViewElementList(),
+ itr,
+ m_notationQuantizer,
+ barTimes,
+ m_properties,
+ clef, key);
+ group.applyStemProperties();
+ groupIds.insert(groupId);
+ }
+ }
+
+ if (el->event()->isa(Clef::EventType)) {
+
+ // NOTATION_DEBUG << "Found clef" << endl;
+ chunks.push_back(Chunk(el->event()->getSubOrdering(),
+ getLayoutWidth(*el, npf, key)));
+
+ clef = Clef(*el->event());
+ accTable.newClef(clef);
+
+ } else if (el->event()->isa(::Rosegarden::Key::EventType)) {
+
+ // NOTATION_DEBUG << "Found key" << endl;
+ chunks.push_back(Chunk(el->event()->getSubOrdering(),
+ getLayoutWidth(*el, npf, key)));
+
+ key = ::Rosegarden::Key(*el->event());
+
+ accTable = AccidentalTable
+ (key, clef, octaveType, barResetType);
+
+ } else if (el->event()->isa(Text::EventType)) {
+
+ // the only text events of interest are lyrics, which
+ // contribute to a fixed area following the next chord
+
+ if (el->event()->has(Text::TextTypePropertyName) &&
+ el->event()->get<String>(Text::TextTypePropertyName) ==
+ Text::Lyric) {
+ lyricWidth = std::max
+ (lyricWidth, float(npf->getTextWidth(Text(*el->event()))));
+ NOTATION_DEBUG << "Setting lyric width to " << lyricWidth
+ << " for text " << el->event()->get<String>(Text::TextPropertyName) << endl;
+ }
+ chunks.push_back(Chunk(el->event()->getSubOrdering(), 0));
+
+ } else if (el->isNote()) {
+
+ NotePixmapFactory *cnpf = npf;
+ if (el->isGrace()) cnpf = getGraceNotePixmapFactory(staff);
+
+ scanChord(notes, itr, clef, key, accTable,
+ lyricWidth, chunks, cnpf, ottavaShift, to);
+
+ } else if (el->isRest()) {
+
+ chunks.push_back(Chunk(el->getViewDuration(),
+ el->event()->getSubOrdering(),
+ 0,
+ getLayoutWidth(*el, npf, key)));
+
+ } else if (el->event()->isa(Indication::EventType)) {
+
+ // NOTATION_DEBUG << "Found indication" << endl;
+
+ chunks.push_back(Chunk(el->event()->getSubOrdering(), 0));
+
+ try {
+ Indication indication(*el->event());
+ if (indication.isOttavaType()) {
+ ottavaShift = indication.getOttavaShift();
+ ottavaEnd = el->event()->getAbsoluteTime() +
+ indication.getIndicationDuration();
+ m_haveOttavaSomewhere[&staff] = true;
+ }
+ } catch (...) {
+ NOTATION_DEBUG << "Bad indication!" << endl;
+ }
+
+ } else {
+
+// NOTATION_DEBUG << "Found something I don't know about (type is " << el->event()->getType() << ")" << endl;
+ chunks.push_back(Chunk(el->event()->getSubOrdering(),
+ getLayoutWidth(*el, npf, key)));
+ }
+
+ actualBarEnd = el->getViewAbsoluteTime() + el->getViewDuration();
+ }
+
+ if (actualBarEnd == barTimes.first)
+ actualBarEnd = barTimes.second;
+ barCorrect = (actualBarEnd == barTimes.second);
+
+ setBarSizeData(staff, barNo, fixedWidth,
+ actualBarEnd - barTimes.first);
+
+ if ((endTime > startTime) && (barNo % 20 == 0)) {
+ emit setProgress((barTimes.second - startTime) * 95 /
+ (endTime - startTime));
+ ProgressDialog::processEvents();
+ }
+
+ throwIfCancelled();
+ }
+ /*
+ BarDataList::iterator ei(barList.end());
+ while (ei != barList.begin() && (--ei)->first > endBarNo) {
+ barList.erase(ei);
+ ei = barList.end();
+ }
+ */
+}
+
+void
+NotationHLayout::clearBarList(Staff &staff)
+{
+ BarDataList &bdl = m_barData[&staff];
+ bdl.clear();
+}
+
+void
+NotationHLayout::setBarBasicData(Staff &staff,
+ int barNo,
+ NotationElementList::iterator start,
+ bool correct,
+ TimeSignature timeSig,
+ bool newTimeSig)
+{
+ // NOTATION_DEBUG << "setBarBasicData for " << barNo << endl;
+
+ BarDataList &bdl(m_barData[&staff]);
+
+ BarDataList::iterator i(bdl.find(barNo));
+ if (i == bdl.end()) {
+ NotationElementList::iterator endi = staff.getViewElementList()->end();
+ bdl.insert(BarDataPair(barNo, BarData(endi, true,
+ TimeSignature(), false)));
+ i = bdl.find(barNo);
+ }
+
+ i->second.basicData.start = start;
+ i->second.basicData.correct = correct;
+ i->second.basicData.timeSignature = timeSig;
+ i->second.basicData.newTimeSig = newTimeSig;
+}
+
+void
+NotationHLayout::setBarSizeData(Staff &staff,
+ int barNo,
+ float fixedWidth,
+ timeT actualDuration)
+{
+ // NOTATION_DEBUG << "setBarSizeData for " << barNo << endl;
+
+ BarDataList &bdl(m_barData[&staff]);
+
+ BarDataList::iterator i(bdl.find(barNo));
+ if (i == bdl.end()) {
+ NotationElementList::iterator endi = staff.getViewElementList()->end();
+ bdl.insert(BarDataPair(barNo, BarData(endi, true,
+ TimeSignature(), false)));
+ i = bdl.find(barNo);
+ }
+
+ i->second.sizeData.actualDuration = actualDuration;
+ i->second.sizeData.idealWidth = 0.0;
+ i->second.sizeData.reconciledWidth = 0.0;
+ i->second.sizeData.clefKeyWidth = 0;
+ i->second.sizeData.fixedWidth = fixedWidth;
+}
+
+void
+NotationHLayout::scanChord(NotationElementList *notes,
+ NotationElementList::iterator &itr,
+ const Clef &clef,
+ const ::Rosegarden::Key &key,
+ AccidentalTable &accTable,
+ float &lyricWidth,
+ ChunkList &chunks,
+ NotePixmapFactory *npf,
+ int ottavaShift,
+ NotationElementList::iterator &to)
+{
+ NotationChord chord(*notes, itr, m_notationQuantizer, m_properties);
+ Accidental someAccidental = Accidentals::NoAccidental;
+ bool someCautionary = false;
+ bool barEndsInChord = false;
+ bool grace = false;
+
+// std::cerr << "NotationHLayout::scanChord: "
+// << chord.size() << "-voice chord at "
+// << (*itr)->event()->getAbsoluteTime()
+// << " unquantized, "
+// << (*itr)->getViewAbsoluteTime()
+// << " quantized" << std::endl;
+
+// NOTATION_DEBUG << "Contents:" << endl;
+
+ /*
+ for (NotationElementList::iterator i = chord.getInitialElement();
+ i != notes->end(); ++i) {
+ (*i)->event()->dump(std::cerr);
+ if (i == chord.getFinalElement()) break;
+ }
+ */
+ // We don't need to get the chord's notes in pitch order here,
+ // but we do need to ensure we see any random non-note events
+ // that may crop up in the middle of it.
+
+ for (NotationElementList::iterator i = chord.getInitialElement();
+ i != notes->end(); ++i) {
+
+ NotationElement *el = static_cast<NotationElement*>(*i);
+ if (el->isRest()) {
+ el->event()->setMaybe<Bool>(m_properties.REST_TOO_SHORT, true);
+ if (i == chord.getFinalElement())
+ break;
+ continue;
+ }
+
+ if (el->isGrace()) {
+ grace = true;
+ }
+
+ long pitch = 64;
+ if (!el->event()->get<Int>(PITCH, pitch)) {
+ NOTATION_DEBUG <<
+ "WARNING: NotationHLayout::scanChord: couldn't get pitch for element, using default pitch of " << pitch << endl;
+ }
+
+ Accidental explicitAccidental = Accidentals::NoAccidental;
+ (void)el->event()->get<String>(ACCIDENTAL, explicitAccidental);
+
+ Pitch p(pitch, explicitAccidental);
+ int h = p.getHeightOnStaff(clef, key);
+ Accidental acc = p.getDisplayAccidental(key);
+
+ h -= 7 * ottavaShift;
+
+ el->event()->setMaybe<Int>(NotationProperties::OTTAVA_SHIFT, ottavaShift);
+ el->event()->setMaybe<Int>(NotationProperties::HEIGHT_ON_STAFF, h);
+ el->event()->setMaybe<String>(m_properties.CALCULATED_ACCIDENTAL, acc);
+
+ // update display acc for note according to the accTable
+ // (accidentals in force when the last chord ended) and tell
+ // accTable about accidentals from this note.
+
+ bool cautionary = false;
+ if (el->event()->has(m_properties.USE_CAUTIONARY_ACCIDENTAL)) {
+ cautionary = el->event()->get<Bool>(m_properties.USE_CAUTIONARY_ACCIDENTAL);
+ }
+ Accidental dacc = accTable.processDisplayAccidental(acc, h, cautionary);
+ el->event()->setMaybe<String>(m_properties.DISPLAY_ACCIDENTAL, dacc);
+ el->event()->setMaybe<Bool>(m_properties.DISPLAY_ACCIDENTAL_IS_CAUTIONARY,
+ cautionary);
+ if (cautionary) {
+ someCautionary = true;
+ }
+
+ if (someAccidental == Accidentals::NoAccidental)
+ someAccidental = dacc;
+
+ if (i == to)
+ barEndsInChord = true;
+
+ if (i == chord.getFinalElement())
+ break;
+ }
+
+ // tell accTable the chord has ended, so to bring its accidentals
+ // into force for future chords
+ accTable.update();
+
+ chord.applyAccidentalShiftProperties();
+
+ float extraWidth = 0;
+
+ if (someAccidental != Accidentals::NoAccidental) {
+ bool extraShift = false;
+ int shift = chord.getMaxAccidentalShift(extraShift);
+ int e = npf->getAccidentalWidth(someAccidental, shift, extraShift);
+ if (someAccidental != Accidentals::Sharp) {
+ e = std::max(e, npf->getAccidentalWidth(Accidentals::Sharp, shift, extraShift));
+ }
+ if (someCautionary) {
+ e += npf->getNoteBodyWidth();
+ }
+ extraWidth += e;
+ }
+
+ float layoutExtra = 0;
+ if (chord.hasNoteHeadShifted()) {
+ if (chord.hasStemUp()) {
+ layoutExtra += npf->getNoteBodyWidth();
+ } else {
+ extraWidth = std::max(extraWidth, float(npf->getNoteBodyWidth()));
+ }
+ }
+/*!!!
+ if (grace) {
+ std::cerr << "Grace note: subordering " << chord.getSubOrdering() << std::endl;
+ chunks.push_back(Chunk(-10 + graceCount,
+ extraWidth + npf->getNoteBodyWidth()));
+ if (graceCount < 9) ++graceCount;
+ return;
+ } else {
+ std::cerr << "Non-grace note (grace count was " << graceCount << ")" << std::endl;
+ graceCount = 0;
+ }
+*/
+ NotationElementList::iterator myLongest = chord.getLongestElement();
+ if (myLongest == notes->end()) {
+ NOTATION_DEBUG << "WARNING: NotationHLayout::scanChord: No longest element in chord!" << endl;
+ }
+
+ timeT d = (*myLongest)->getViewDuration();
+
+ NOTATION_DEBUG << "Lyric width is " << lyricWidth << endl;
+
+ if (grace) {
+ chunks.push_back(Chunk(d, chord.getSubOrdering(),
+ extraWidth + layoutExtra
+ + getLayoutWidth(**myLongest, npf, key)
+ - npf->getNoteBodyWidth(), // tighten up
+ 0));
+ } else {
+ chunks.push_back(Chunk(d, 0, extraWidth,
+ std::max(layoutExtra +
+ getLayoutWidth(**myLongest, npf, key),
+ lyricWidth)));
+ lyricWidth = 0;
+ }
+
+ itr = chord.getFinalElement();
+ if (barEndsInChord) {
+ to = itr;
+ ++to;
+ }
+}
+
+struct ChunkLocation {
+ timeT time;
+ short subordering;
+ ChunkLocation(timeT t, short s) : time(t), subordering(s) { }
+};
+
+bool operator<(const ChunkLocation &l0, const ChunkLocation &l1) {
+ return ((l0.time < l1.time) ||
+ ((l0.time == l1.time) && (l0.subordering < l1.subordering)));
+}
+
+
+
+void
+NotationHLayout::preSquishBar(int barNo)
+{
+ typedef std::vector<Chunk *> ChunkRefList;
+ typedef std::map<ChunkLocation, ChunkRefList> ColumnMap;
+ static ColumnMap columns;
+ bool haveSomething = false;
+
+ columns.clear();
+
+ for (BarDataMap::iterator mi = m_barData.begin();
+ mi != m_barData.end(); ++mi) {
+
+ BarDataList &bdl = mi->second;
+ BarDataList::iterator bdli = bdl.find(barNo);
+
+ if (bdli != bdl.end()) {
+
+ haveSomething = true;
+ ChunkList &cl(bdli->second.chunks);
+ timeT aggregateTime = 0;
+
+ for (ChunkList::iterator cli = cl.begin(); cli != cl.end(); ++cli) {
+
+ // Subordering is typically zero for notes, positive
+ // for rests and negative for other stuff. We want to
+ // handle notes and rests together, but not the others.
+
+ int subordering = cli->subordering;
+ if (subordering > 0)
+ subordering = 0;
+
+ columns[ChunkLocation(aggregateTime, subordering)].push_back(&(*cli));
+
+ aggregateTime += cli->duration;
+ }
+ }
+ }
+
+ if (!haveSomething)
+ return ;
+
+ // now modify chunks in-place
+
+ // What we want to do here is idle along the whole set of chunk
+ // lists, inspecting all the chunks that occur at each moment in
+ // turn and choosing a "rate" from the "slowest" of these
+ // (i.e. most space per time)
+
+ float x = 0.0;
+ timeT prevTime = 0;
+ double prevRate = 0.0;
+ float maxStretchy = 0.0;
+
+ NOTATION_DEBUG << "NotationHLayout::preSquishBar(" << barNo << "): have "
+ << columns.size() << " columns" << endl;
+
+ for (ColumnMap::iterator i = columns.begin(); i != columns.end(); ++i) {
+
+ timeT time = i->first.time;
+ ChunkRefList &list = i->second;
+
+ NOTATION_DEBUG << "NotationHLayout::preSquishBar: "
+ << "column at " << time << " : " << i->first.subordering << endl;
+
+
+ double minRate = -1.0;
+ float totalFixed = 0.0;
+ maxStretchy = 0.0;
+
+ for (ChunkRefList::iterator j = list.begin(); j != list.end(); ++j) {
+ if ((*j)->stretchy > 0.0) {
+ double rate = (*j)->duration / (*j)->stretchy; // time per px
+ NOTATION_DEBUG << "NotationHLayout::preSquishBar: rate " << rate << endl;
+ if (minRate < 0.0 || rate < minRate)
+ minRate = rate;
+ } else {
+ NOTATION_DEBUG << "NotationHLayout::preSquishBar: not stretchy" << endl;
+ }
+
+ maxStretchy = std::max(maxStretchy, (*j)->stretchy);
+ totalFixed = std::max(totalFixed, (*j)->fixed);
+ }
+
+ NOTATION_DEBUG << "NotationHLayout::preSquishBar: minRate " << minRate << ", maxStretchy " << maxStretchy << ", totalFixed " << totalFixed << endl;
+
+ // we have the rate from this point, but we want to assign
+ // these elements an x coord based on the rate and distance
+ // from the previous point, plus fixed space for this point
+ // if it's a note (otherwise fixed space goes afterwards)
+
+ if (i->first.subordering == 0)
+ x += totalFixed;
+ if (prevRate > 0.0)
+ x += (time - prevTime) / prevRate;
+
+ for (ChunkRefList::iterator j = list.begin(); j != list.end(); ++j) {
+ NOTATION_DEBUG << "Setting x for time " << time << " to " << x << " in chunk at " << *j << endl;
+ (*j)->x = x;
+ }
+
+ if (i->first.subordering != 0)
+ x += totalFixed;
+
+ prevTime = time;
+ prevRate = minRate;
+ }
+
+ x += maxStretchy;
+
+ for (BarDataMap::iterator mi = m_barData.begin();
+ mi != m_barData.end(); ++mi) {
+
+ BarDataList &bdl = mi->second;
+ BarDataList::iterator bdli = bdl.find(barNo);
+ if (bdli != bdl.end()) {
+
+ bdli->second.sizeData.idealWidth =
+ bdli->second.sizeData.fixedWidth + x;
+
+ if (!bdli->second.basicData.timeSignature.hasHiddenBars()) {
+ bdli->second.sizeData.idealWidth += getBarMargin();
+ } else if (bdli->second.basicData.newTimeSig) {
+ bdli->second.sizeData.idealWidth += getPostBarMargin();
+ }
+
+ bdli->second.sizeData.reconciledWidth =
+ bdli->second.sizeData.idealWidth;
+
+ bdli->second.layoutData.needsLayout = true;
+ }
+ }
+}
+
+Staff *
+NotationHLayout::getStaffWithWidestBar(int barNo)
+{
+ float maxWidth = -1;
+ Staff *widest = 0;
+
+ for (BarDataMap::iterator mi = m_barData.begin();
+ mi != m_barData.end(); ++mi) {
+
+ BarDataList &list = mi->second;
+ BarDataList::iterator li = list.find(barNo);
+ if (li != list.end()) {
+
+ NOTATION_DEBUG << "getStaffWithWidestBar: idealWidth is " << li->second.sizeData.idealWidth << endl;
+
+ if (li->second.sizeData.idealWidth == 0.0) {
+ NOTATION_DEBUG << "getStaffWithWidestBar(" << barNo << "): found idealWidth of zero, presquishing" << endl;
+ preSquishBar(barNo);
+ }
+
+ if (li->second.sizeData.idealWidth > maxWidth) {
+ maxWidth = li->second.sizeData.idealWidth;
+ widest = mi->first;
+ }
+ }
+ }
+
+ return widest;
+}
+
+int
+NotationHLayout::getMaxRepeatedClefAndKeyWidth(int barNo)
+{
+ int max = 0;
+
+ timeT barStart = 0;
+
+ for (BarDataMap::iterator mi = m_barData.begin();
+ mi != m_barData.end(); ++mi) {
+
+ Staff *staff = mi->first;
+ if (mi == m_barData.begin()) {
+ barStart = staff->getSegment().getComposition()->getBarStart(barNo);
+ }
+
+ timeT t;
+ int w = 0;
+
+ Clef clef = staff->getSegment().getClefAtTime(barStart, t);
+ if (t < barStart)
+ w += m_npf->getClefWidth(clef);
+
+ ::Rosegarden::Key key = staff->getSegment().getKeyAtTime(barStart, t);
+ if (t < barStart)
+ w += m_npf->getKeyWidth(key);
+
+ if (w > max)
+ max = w;
+ }
+
+ NOTATION_DEBUG << "getMaxRepeatedClefAndKeyWidth(" << barNo << "): " << max
+ << endl;
+
+ if (max > 0)
+ return max + getFixedItemSpacing() * 2;
+ else
+ return 0;
+}
+
+void
+NotationHLayout::reconcileBarsLinear()
+{
+ Profiler profiler("NotationHLayout::reconcileBarsLinear");
+
+ // Ensure that concurrent bars on all staffs have the same width,
+ // which for now we make the maximum width required for this bar
+ // on any staff. These days getStaffWithWidestBar actually does
+ // most of the work in its call to preSquishBar, but this function
+ // still sets the bar line positions etc.
+
+ int barNo = getFirstVisibleBar();
+
+ m_totalWidth = 0.0;
+ for (StaffIntMap::iterator i = m_staffNameWidths.begin();
+ i != m_staffNameWidths.end(); ++i) {
+ if (i->second > m_totalWidth)
+ m_totalWidth = double(i->second);
+ }
+
+ for (;;) {
+
+ Staff *widest = getStaffWithWidestBar(barNo);
+
+ if (!widest) {
+ // have we reached the end of the piece?
+ if (barNo >= getLastVisibleBar()) { // yes
+ break;
+ } else {
+ m_totalWidth += m_spacing / 3;
+ NOTATION_DEBUG << "Setting bar position for degenerate bar "
+ << barNo << " to " << m_totalWidth << endl;
+
+ m_barPositions[barNo] = m_totalWidth;
+ ++barNo;
+ continue;
+ }
+ }
+
+ float maxWidth = m_barData[widest].find(barNo)->second.sizeData.idealWidth;
+ if (m_pageWidth > 0.1 && maxWidth > m_pageWidth) {
+ maxWidth = m_pageWidth;
+ }
+
+ NOTATION_DEBUG << "Setting bar position for bar " << barNo
+ << " to " << m_totalWidth << endl;
+
+ m_barPositions[barNo] = m_totalWidth;
+ m_totalWidth += maxWidth;
+
+ // Now apply width to this bar on all staffs
+
+ for (BarDataMap::iterator i = m_barData.begin();
+ i != m_barData.end(); ++i) {
+
+ BarDataList &list = i->second;
+ BarDataList::iterator bdli = list.find(barNo);
+ if (bdli != list.end()) {
+
+ BarData::SizeData &bd(bdli->second.sizeData);
+
+ NOTATION_DEBUG << "Changing width from " << bd.reconciledWidth << " to " << maxWidth << endl;
+
+ double diff = maxWidth - bd.reconciledWidth;
+ if (diff < -0.1 || diff > 0.1) {
+ NOTATION_DEBUG << "(So needsLayout becomes true)" << endl;
+ bdli->second.layoutData.needsLayout = true;
+ }
+ bd.reconciledWidth = maxWidth;
+ }
+ }
+
+ ++barNo;
+ }
+
+ NOTATION_DEBUG << "Setting bar position for bar " << barNo
+ << " to " << m_totalWidth << endl;
+
+ m_barPositions[barNo] = m_totalWidth;
+}
+
+void
+NotationHLayout::reconcileBarsPage()
+{
+ Profiler profiler("NotationHLayout::reconcileBarsPage");
+
+ int barNo = getFirstVisibleBar();
+ int barNoThisRow = 0;
+
+ // pair of the recommended number of bars with those bars'
+ // original total width, for each row
+ std::vector<std::pair<int, double> > rowData;
+
+ double stretchFactor = 10.0;
+ double maxStaffNameWidth = 0.0;
+
+ for (StaffIntMap::iterator i = m_staffNameWidths.begin();
+ i != m_staffNameWidths.end(); ++i) {
+ if (i->second > maxStaffNameWidth) {
+ maxStaffNameWidth = double(i->second);
+ }
+ }
+
+ double pageWidthSoFar = maxStaffNameWidth;
+ m_totalWidth = maxStaffNameWidth + getPreBarMargin();
+
+ NOTATION_DEBUG << "NotationHLayout::reconcileBarsPage: pageWidthSoFar is " << pageWidthSoFar << endl;
+
+ for (;;) {
+
+ Staff *widest = getStaffWithWidestBar(barNo);
+ double maxWidth = m_spacing / 3;
+
+ if (!widest) {
+ // have we reached the end of the piece?
+ if (barNo >= getLastVisibleBar())
+ break; // yes
+ } else {
+ maxWidth =
+ m_barData[widest].find(barNo)->second.sizeData.idealWidth;
+ }
+
+ // Work on the assumption that this bar is the last in the
+ // row. How would that make things look?
+
+ double nextPageWidth = pageWidthSoFar + maxWidth;
+ double nextStretchFactor = m_pageWidth / nextPageWidth;
+
+ NOTATION_DEBUG << "barNo is " << barNo << ", maxWidth " << maxWidth << ", nextPageWidth " << nextPageWidth << ", nextStretchFactor " << nextStretchFactor << ", m_pageWidth " << m_pageWidth << endl;
+
+ // We have to have at least one bar per row
+
+ bool tooFar = false;
+
+ if (barNoThisRow >= 1) {
+
+ // If this stretch factor is "worse" than the previous
+ // one, we've come too far and have too many bars
+
+ if (fabs(1.0 - nextStretchFactor) > fabs(1.0 - stretchFactor)) {
+ tooFar = true;
+ }
+
+ // If the next stretch factor is less than 1 and would
+ // make this bar on any of the staffs narrower than it can
+ // afford to be, then we've got too many bars
+ //!!! rework this -- we have no concept of "too narrow"
+ // any more but we can declare we don't want it any
+ // narrower than e.g. 90% or something based on the spacing
+ /*!!!
+ if (!tooFar && (nextStretchFactor < 1.0)) {
+
+ for (BarDataMap::iterator i = m_barData.begin();
+ i != m_barData.end(); ++i) {
+
+ BarDataList &list = i->second;
+ BarDataList::iterator bdli = list.find(barNo);
+ if (bdli != list.end()) {
+ BarData::SizeData &bd(bdli->second.sizeData);
+ if ((nextStretchFactor * bd.idealWidth) <
+ (double)(bd.fixedWidth + bd.baseWidth)) {
+ tooFar = true;
+ break;
+ }
+ }
+ }
+ }
+ */
+ }
+
+ if (tooFar) {
+ rowData.push_back(std::pair<int, double>(barNoThisRow,
+ pageWidthSoFar));
+ barNoThisRow = 1;
+
+ // When we start a new row, we always need to allow for the
+ // repeated clef and key at the start of it.
+ int maxClefKeyWidth = getMaxRepeatedClefAndKeyWidth(barNo);
+
+ for (BarDataMap::iterator i = m_barData.begin();
+ i != m_barData.end(); ++i) {
+
+ BarDataList &list = i->second;
+ BarDataList::iterator bdli = list.find(barNo);
+
+ if (bdli != list.end()) {
+ bdli->second.sizeData.clefKeyWidth = maxClefKeyWidth;
+ }
+ }
+
+ pageWidthSoFar = maxWidth + maxClefKeyWidth;
+ stretchFactor = m_pageWidth / pageWidthSoFar;
+ } else {
+ ++barNoThisRow;
+ pageWidthSoFar = nextPageWidth;
+ stretchFactor = nextStretchFactor;
+ }
+
+ ++barNo;
+ }
+
+ if (barNoThisRow > 0) {
+ rowData.push_back(std::pair<int, double>(barNoThisRow,
+ pageWidthSoFar));
+ }
+
+ // Now we need to actually apply the widths
+
+ barNo = getFirstVisibleBar();
+
+ for (unsigned int row = 0; row < rowData.size(); ++row) {
+
+ barNoThisRow = barNo;
+ int finalBarThisRow = barNo + rowData[row].first - 1;
+
+ pageWidthSoFar = (row > 0 ? 0 : maxStaffNameWidth + getPreBarMargin());
+ stretchFactor = m_pageWidth / rowData[row].second;
+
+ for (; barNoThisRow <= finalBarThisRow; ++barNoThisRow, ++barNo) {
+
+ bool finalRow = (row == rowData.size() - 1);
+
+ Staff *widest = getStaffWithWidestBar(barNo);
+ if (finalRow && (stretchFactor > 1.0))
+ stretchFactor = 1.0;
+ double maxWidth = 0.0;
+
+ if (!widest) {
+ // have we reached the end of the piece? (shouldn't happen)
+ if (barNo >= getLastVisibleBar())
+ break; // yes
+ else
+ maxWidth = stretchFactor * (m_spacing / 3);
+ } else {
+ BarData &bd = m_barData[widest].find(barNo)->second;
+ maxWidth = (stretchFactor * bd.sizeData.idealWidth) +
+ bd.sizeData.clefKeyWidth;
+ NOTATION_DEBUG << "setting maxWidth to " << (stretchFactor * bd.sizeData.idealWidth) << " + " << bd.sizeData.clefKeyWidth << " = " << maxWidth << endl;
+ }
+
+ if (barNoThisRow == finalBarThisRow) {
+ if (!finalRow ||
+ (maxWidth > (m_pageWidth - pageWidthSoFar))) {
+ maxWidth = m_pageWidth - pageWidthSoFar;
+ NOTATION_DEBUG << "reset maxWidth to " << m_pageWidth << " - " << pageWidthSoFar << " = " << maxWidth << endl;
+ }
+ }
+
+ m_barPositions[barNo] = m_totalWidth;
+ m_totalWidth += maxWidth;
+
+ for (BarDataMap::iterator i = m_barData.begin();
+ i != m_barData.end(); ++i) {
+
+ BarDataList &list = i->second;
+ BarDataList::iterator bdli = list.find(barNo);
+ if (bdli != list.end()) {
+ BarData::SizeData &bd(bdli->second.sizeData);
+ double diff = maxWidth - bd.reconciledWidth;
+ if (diff < -0.1 || diff > 0.1) {
+ bdli->second.layoutData.needsLayout = true;
+ }
+ bd.reconciledWidth = maxWidth;
+ }
+ }
+
+ pageWidthSoFar += maxWidth;
+ }
+ }
+
+ m_barPositions[barNo] = m_totalWidth;
+}
+
+void
+NotationHLayout::finishLayout(timeT startTime, timeT endTime)
+{
+ Profiler profiler("NotationHLayout::finishLayout");
+ m_barPositions.clear();
+
+ bool isFullLayout = (startTime == endTime);
+ if (m_pageMode && (m_pageWidth > 0.1))
+ reconcileBarsPage();
+ else
+ reconcileBarsLinear();
+
+ int staffNo = 0;
+
+ for (BarDataMap::iterator i(m_barData.begin());
+ i != m_barData.end(); ++i) {
+
+ emit setProgress(100 * staffNo / m_barData.size());
+ ProgressDialog::processEvents();
+
+ throwIfCancelled();
+
+ timeT timeCovered = endTime - startTime;
+
+ if (isFullLayout) {
+ NotationElementList *notes = i->first->getViewElementList();
+ if (notes->begin() != notes->end()) {
+ NotationElementList::iterator j(notes->end());
+ timeCovered =
+ (*--j)->getViewAbsoluteTime() -
+ (*notes->begin())->getViewAbsoluteTime();
+ }
+ }
+
+ m_timePerProgressIncrement = timeCovered / (100 / m_barData.size());
+
+ layout(i, startTime, endTime);
+ ++staffNo;
+ }
+}
+
+void
+NotationHLayout::layout(BarDataMap::iterator i, timeT startTime, timeT endTime)
+{
+ Profiler profiler("NotationHLayout::layout");
+
+ Staff &staff = *(i->first);
+ NotationElementList *notes = staff.getViewElementList();
+ BarDataList &barList(getBarData(staff));
+ NotationStaff &notationStaff = dynamic_cast<NotationStaff &>(staff);
+
+ bool isFullLayout = (startTime == endTime);
+
+ // these two are for partial layouts:
+ // bool haveSimpleOffset = false;
+ // double simpleOffset = 0;
+
+ NOTATION_DEBUG << "NotationHLayout::layout: full layout " << isFullLayout << ", times " << startTime << "->" << endTime << endl;
+
+ double x = 0, barX = 0;
+ TieMap tieMap;
+
+ timeT lastIncrement =
+ (isFullLayout && (notes->begin() != notes->end())) ?
+ (*notes->begin())->getViewAbsoluteTime() : startTime;
+
+ ::Rosegarden::Key key = notationStaff.getSegment().getKeyAtTime(lastIncrement);
+ Clef clef = notationStaff.getSegment().getClefAtTime(lastIncrement);
+ TimeSignature timeSignature;
+
+ int startBar = getComposition()->getBarNumber(startTime);
+
+ KConfig *config = kapp->config();
+ config->setGroup("Notation Options");
+ bool showInvisibles = config->readBoolEntry("showinvisibles", true);
+
+ for (BarPositionList::iterator bpi = m_barPositions.begin();
+ bpi != m_barPositions.end(); ++bpi) {
+
+ int barNo = bpi->first;
+ if (!isFullLayout && barNo < startBar)
+ continue;
+
+ NOTATION_DEBUG << "NotationHLayout::looking for bar "
+ << bpi->first << endl;
+ BarDataList::iterator bdi = barList.find(barNo);
+ if (bdi == barList.end())
+ continue;
+ barX = bpi->second;
+
+ NotationElementList::iterator from = bdi->second.basicData.start;
+ NotationElementList::iterator to;
+
+ NOTATION_DEBUG << "NotationHLayout::layout(): starting bar " << barNo << ", x = " << barX << ", width = " << bdi->second.sizeData.idealWidth << ", time = " << (from == notes->end() ? -1 : (*from)->getViewAbsoluteTime()) << endl;
+
+ BarDataList::iterator nbdi(bdi);
+ if (++nbdi == barList.end()) {
+ to = notes->end();
+ } else {
+ to = nbdi->second.basicData.start;
+ }
+
+ if (from == notes->end()) {
+ NOTATION_DEBUG << "Start is end" << endl;
+ }
+ if (from == to) {
+ NOTATION_DEBUG << "Start is to" << endl;
+ }
+
+ if (!bdi->second.layoutData.needsLayout) {
+
+ double offset = barX - bdi->second.layoutData.x;
+
+ NOTATION_DEBUG << "NotationHLayout::layout(): bar " << barNo << " has needsLayout false and offset of " << offset << endl;
+
+ if (offset > -0.1 && offset < 0.1) {
+ NOTATION_DEBUG << "NotationHLayout::layout(): no offset, ignoring" << endl;
+ continue;
+ }
+
+ bdi->second.layoutData.x += offset;
+
+ if (bdi->second.basicData.newTimeSig)
+ bdi->second.layoutData.timeSigX += (int)offset;
+
+ for (NotationElementList::iterator it = from;
+ it != to && it != notes->end(); ++it) {
+
+ NotationElement* nel = static_cast<NotationElement*>(*it);
+ NOTATION_DEBUG << "NotationHLayout::layout(): shifting element's x to " << ((*it)->getLayoutX() + offset) << " (was " << (*it)->getLayoutX() << ")" << endl;
+ nel->setLayoutX((*it)->getLayoutX() + offset);
+ double airX, airWidth;
+ nel->getLayoutAirspace(airX, airWidth);
+ nel->setLayoutAirspace(airX + offset, airWidth);
+ }
+
+ continue;
+ }
+
+ bdi->second.layoutData.x = barX;
+ // x = barX + getPostBarMargin();
+
+ bool timeSigToPlace = false;
+ if (bdi->second.basicData.newTimeSig) {
+ timeSignature = bdi->second.basicData.timeSignature;
+ timeSigToPlace = !bdi->second.basicData.timeSignature.isHidden();
+ }
+ if (timeSigToPlace) {
+ NOTATION_DEBUG << "NotationHLayout::layout(): there's a time sig in this bar" << endl;
+ }
+
+ bool repeatClefAndKey = false;
+ if (bdi->second.sizeData.clefKeyWidth > 0) {
+ repeatClefAndKey = true;
+ }
+ if (repeatClefAndKey) {
+ NOTATION_DEBUG << "NotationHLayout::layout(): need to repeat clef & key in this bar" << endl;
+ }
+
+ double barInset = notationStaff.getBarInset(barNo, repeatClefAndKey);
+
+ NotationElement *lastDynamicText = 0;
+ int fretboardCount = 0;
+ int count = 0;
+
+ double offset = 0.0;
+ double reconciledWidth = bdi->second.sizeData.reconciledWidth;
+
+ if (repeatClefAndKey) {
+ offset = bdi->second.sizeData.clefKeyWidth;
+ reconciledWidth -= offset;
+ }
+
+ if (bdi->second.basicData.newTimeSig ||
+ !bdi->second.basicData.timeSignature.hasHiddenBars()) {
+ offset += getPostBarMargin();
+ }
+
+ ChunkList &chunks = bdi->second.chunks;
+ ChunkList::iterator chunkitr = chunks.begin();
+ double reconcileRatio = 1.0;
+ if (bdi->second.sizeData.idealWidth > 0.0) {
+ reconcileRatio = reconciledWidth / bdi->second.sizeData.idealWidth;
+ }
+
+ NOTATION_DEBUG << "have " << chunks.size() << " chunks, reconciledWidth " << bdi->second.sizeData.reconciledWidth << ", idealWidth " << bdi->second.sizeData.idealWidth << ", ratio " << reconcileRatio << endl;
+
+ double delta = 0;
+ float sigx = 0.f;
+
+ for (NotationElementList::iterator it = from; it != to; ++it) {
+
+ NotationElement *el = static_cast<NotationElement*>(*it);
+ delta = 0;
+ float fixed = 0;
+
+ if (el->event()->isa(Note::EventType)) {
+ long pitch = 0;
+ el->event()->get<Int>(PITCH, pitch);
+ NOTATION_DEBUG << "element is a " << el->event()->getType() << " (pitch " << pitch << ")" << endl;
+ } else {
+ NOTATION_DEBUG << "element is a " << el->event()->getType() << endl;
+ }
+
+ bool invisible = false;
+ if (el->event()->get<Bool>(INVISIBLE, invisible) && invisible) {
+ if (!showInvisibles)
+ continue;
+ }
+
+// float sigx = 0;
+
+ if (chunkitr != chunks.end()) {
+ NOTATION_DEBUG << "new chunk: addr " << &(*chunkitr) << " duration=" << (*chunkitr).duration << " subordering=" << (*chunkitr).subordering << " fixed=" << (*chunkitr).fixed << " stretchy=" << (*chunkitr).stretchy << " x=" << (*chunkitr).x << endl;
+ x = barX + offset + reconcileRatio * (*chunkitr).x;
+ fixed = (*chunkitr).fixed;
+// sigx = barX + offset - fixed;
+// sigx = x - fixed;
+ NOTATION_DEBUG << "adjusted x is " << x << ", fixed is " << fixed << endl;
+
+ if (timeSigToPlace) {
+ if (el->event()->isa(Clef::EventType) ||
+ el->event()->isa(Rosegarden::Key::EventType)) {
+ sigx = x + (*chunkitr).fixed + (*chunkitr).stretchy;
+ }
+ }
+
+ ChunkList::iterator chunkscooter(chunkitr);
+ if (++chunkscooter != chunks.end()) {
+ delta = (*chunkscooter).x - (*chunkitr).x;
+ } else {
+ delta = reconciledWidth -
+ bdi->second.sizeData.fixedWidth - (*chunkitr).x;
+ }
+ delta *= reconcileRatio;
+
+ ++chunkitr;
+ } else {
+ x = barX + reconciledWidth - getPreBarMargin();
+// sigx = x;
+ delta = 0;
+ }
+
+ if (timeSigToPlace &&
+ !el->event()->isa(Clef::EventType) &&
+ !el->event()->isa(::Rosegarden::Key::EventType)) {
+
+ if (sigx == 0.f) {
+ sigx = barX + offset;
+ }
+
+// NOTATION_DEBUG << "Placing timesig at " << (x - fixed) << endl;
+// bdi->second.layoutData.timeSigX = (int)(x - fixed);
+ NOTATION_DEBUG << "Placing timesig at " << sigx << " (would previously have been " << int(x-fixed) << "?)" << endl;
+ bdi->second.layoutData.timeSigX = (int)sigx;
+ double shift = getFixedItemSpacing() +
+ m_npf->getTimeSigWidth(timeSignature);
+ offset += shift;
+ x += shift;
+ NOTATION_DEBUG << "and moving next elt to " << x << endl;
+ timeSigToPlace = false;
+ }
+
+ if (barInset >= 1.0) {
+ if (el->event()->isa(Clef::EventType) ||
+ el->event()->isa(::Rosegarden::Key::EventType)) {
+ NOTATION_DEBUG << "Pulling clef/key back by " << getPreBarMargin() << endl;
+ x -= getPostBarMargin() * 2 / 3;
+ } else {
+ barInset = 0.0;
+ }
+ }
+
+ NOTATION_DEBUG << "NotationHLayout::layout(): setting element's x to " << x << " (was " << el->getLayoutX() << ")" << endl;
+
+ double displacedX = 0.0;
+ long dxRaw = 0;
+ el->event()->get<Int>(DISPLACED_X, dxRaw);
+ displacedX = double(dxRaw * m_npf->getNoteBodyWidth()) / 1000.0;
+
+ el->setLayoutX(x + displacedX);
+ el->setLayoutAirspace(x, int(delta));
+
+ // #704958 (multiple tuplet spanners created when entering
+ // triplet chord) -- only do this here for non-notes,
+ // notes get it from positionChord
+ if (!el->isNote()) {
+ sampleGroupElement(staff, clef, key, it);
+ }
+
+ if (el->isNote()) {
+
+ // This modifies "it" and "tieMap"
+ positionChord(staff, it, clef, key, tieMap, to);
+
+ } else if (el->isRest()) {
+
+ // nothing to do
+
+ } else if (el->event()->isa(Clef::EventType)) {
+
+ clef = Clef(*el->event());
+
+ } else if (el->event()->isa(::Rosegarden::Key::EventType)) {
+
+ key = ::Rosegarden::Key(*el->event());
+
+ } else if (el->event()->isa(Text::EventType)) {
+
+ // if it's a dynamic, make a note of it in case a
+ // hairpin immediately follows it
+
+ if (el->event()->has(Text::TextTypePropertyName) &&
+ el->event()->get<String>(Text::TextTypePropertyName) ==
+ Text::Dynamic) {
+ lastDynamicText = el;
+ }
+
+ } else if (el->event()->isa(Indication::EventType)) {
+
+ std::string type;
+ double ix = x;
+
+ // Check for a dynamic text at the same time as the
+ // indication and if found, move the indication to the
+ // right to make room. We know the text should have
+ // preceded the indication in the staff because it has
+ // a smaller subordering
+
+ if (el->event()->get<String>
+ (Indication::IndicationTypePropertyName, type) &&
+ (type == Indication::Crescendo ||
+ type == Indication::Decrescendo) &&
+ lastDynamicText &&
+ lastDynamicText->getViewAbsoluteTime() ==
+ el->getViewAbsoluteTime()) {
+
+ ix = x + m_npf->getTextWidth
+ (Text(*lastDynamicText->event())) +
+ m_npf->getNoteBodyWidth() / 4;
+ }
+
+ el->setLayoutX(ix + displacedX);
+ el->setLayoutAirspace(ix, delta - (ix - x));
+
+ } else if (el->event()->isa(Guitar::Chord::EventType)) {
+
+ int guitarChordWidth = m_npf->getLineSpacing() * 6;
+ el->setLayoutX(x - (guitarChordWidth / 2)
+ + fretboardCount * (guitarChordWidth +
+ m_npf->getNoteBodyWidth()/2)
+ + displacedX);
+ ++fretboardCount;
+
+ } else {
+
+ // nothing else
+ }
+
+ if (m_timePerProgressIncrement > 0 && (++count == 100)) {
+ count = 0;
+ timeT sinceIncrement = el->getViewAbsoluteTime() - lastIncrement;
+ if (sinceIncrement > m_timePerProgressIncrement) {
+ emit incrementProgress
+ (sinceIncrement / m_timePerProgressIncrement);
+ lastIncrement +=
+ (sinceIncrement / m_timePerProgressIncrement)
+ * m_timePerProgressIncrement;
+ throwIfCancelled();
+ }
+ }
+ }
+
+ if (timeSigToPlace) {
+ // no other events in this bar, so we never managed to place it
+ x = barX + offset;
+ NOTATION_DEBUG << "Placing timesig reluctantly at " << x << endl;
+ bdi->second.layoutData.timeSigX = (int)(x);
+ timeSigToPlace = false;
+ }
+
+ for (NotationGroupMap::iterator mi = m_groupsExtant.begin();
+ mi != m_groupsExtant.end(); ++mi) {
+ mi->second->applyBeam(notationStaff);
+ mi->second->applyTuplingLine(notationStaff);
+ delete mi->second;
+ }
+ m_groupsExtant.clear();
+
+ bdi->second.layoutData.needsLayout = false;
+ }
+}
+
+void
+NotationHLayout::sampleGroupElement(Staff &staff,
+ const Clef &clef,
+ const ::Rosegarden::Key &key,
+ const NotationElementList::iterator &itr)
+{
+ NotationElement *el = static_cast<NotationElement *>(*itr);
+
+ if (el->event()->has(BEAMED_GROUP_ID)) {
+
+ //!!! Gosh. We need some clever logic to establish whether
+ // one group is happening while another has not yet ended --
+ // perhaps we decide one has ended if we see another, and then
+ // re-open the case of the first if we meet another note that
+ // claims to be in it. Then we need to hint to both of the
+ // groups that they should choose appropriate stem directions
+ // -- we could just use HEIGHT_ON_STAFF of their first notes
+ // to determine this, as if that doesn't work, nothing will
+
+ long groupId = el->event()->get<Int>(BEAMED_GROUP_ID);
+ NOTATION_DEBUG << "group id: " << groupId << endl;
+ if (m_groupsExtant.find(groupId) == m_groupsExtant.end()) {
+ NOTATION_DEBUG << "(new group)" << endl;
+ m_groupsExtant[groupId] =
+ new NotationGroup(*staff.getViewElementList(),
+ m_notationQuantizer,
+ m_properties, clef, key);
+ }
+ m_groupsExtant[groupId]->sample(itr, true);
+ }
+}
+
+timeT
+NotationHLayout::getSpacingDuration(Staff &staff,
+ const NotationElementList::iterator &i)
+{
+ SegmentNotationHelper helper(staff.getSegment());
+ timeT t((*i)->getViewAbsoluteTime());
+ timeT d((*i)->getViewDuration());
+
+ if (d > 0 && (*i)->event()->getDuration() == 0) return d; // grace note
+
+ NotationElementList::iterator j(i), e(staff.getViewElementList()->end());
+ while (j != e && ((*j)->getViewAbsoluteTime() == t ||
+ (*j)->getViewDuration() == 0)) {
+ ++j;
+ }
+ if (j == e) {
+ return d;
+ } else {
+ return (*j)->getViewAbsoluteTime() - (*i)->getViewAbsoluteTime();
+ }
+}
+
+timeT
+NotationHLayout::getSpacingDuration(Staff &staff,
+ const NotationChord &chord)
+{
+ SegmentNotationHelper helper(staff.getSegment());
+
+ NotationElementList::iterator i = chord.getShortestElement();
+ timeT d((*i)->getViewDuration());
+
+ if (d > 0 && (*i)->event()->getDuration() == 0) return d; // grace note
+
+ NotationElementList::iterator j(i), e(staff.getViewElementList()->end());
+ while (j != e && (chord.contains(j) || (*j)->getViewDuration() == 0))
+ ++j;
+
+ if (j != e) {
+ d = (*j)->getViewAbsoluteTime() - (*i)->getViewAbsoluteTime();
+ }
+
+ return d;
+}
+
+void
+NotationHLayout::positionChord(Staff &staff,
+ NotationElementList::iterator &itr,
+ const Clef &clef, const ::Rosegarden::Key &key,
+ TieMap &tieMap,
+ NotationElementList::iterator &to)
+{
+ NotationChord chord(*staff.getViewElementList(), itr, m_notationQuantizer,
+ m_properties, clef, key);
+ double baseX, delta;
+ (static_cast<NotationElement *>(*itr))->getLayoutAirspace(baseX, delta);
+
+ bool barEndsInChord = false;
+
+ NOTATION_DEBUG << "NotationHLayout::positionChord: x = " << baseX << endl;
+
+ // #938545 (Broken notation: Duplicated note can float outside
+ // stave) -- We need to iterate over all elements in the chord
+ // range here, not just the ordered set of notes actually in the
+ // chord. They all have the same x-coord, so there's no
+ // particular complication here.
+
+ for (NotationElementList::iterator citr = chord.getInitialElement();
+ citr != staff.getViewElementList()->end(); ++citr) {
+
+ if (citr == to)
+ barEndsInChord = true;
+
+ // #704958 (multiple tuplet spanners created when entering
+ // triplet chord) -- layout() updates the beamed group data
+ // for non-notes, but we have to do it for notes so as to
+ // ensure every note in the chord is accounted for
+ sampleGroupElement(staff, clef, key, citr);
+
+ NotationElement *elt = static_cast<NotationElement*>(*citr);
+
+ double displacedX = 0.0;
+ long dxRaw = 0;
+ elt->event()->get<Int>(DISPLACED_X, dxRaw);
+ displacedX = double(dxRaw * m_npf->getNoteBodyWidth()) / 1000.0;
+
+ elt->setLayoutX(baseX + displacedX);
+ elt->setLayoutAirspace(baseX, delta);
+
+ NOTATION_DEBUG << "NotationHLayout::positionChord: assigned x to elt at " << elt->getViewAbsoluteTime() << endl;
+
+ if (citr == chord.getFinalElement())
+ break;
+ }
+
+ // Check for any ties going back, and if so work out how long they
+ // must have been and assign accordingly.
+
+ for (NotationElementList::iterator citr = chord.getInitialElement();
+ citr != staff.getViewElementList()->end(); ++citr) {
+
+ NotationElement *note = static_cast<NotationElement*>(*citr);
+ if (!note->isNote()) {
+ if (citr == chord.getFinalElement())
+ break;
+ continue;
+ }
+
+ bool tiedForwards = false;
+ bool tiedBack = false;
+
+ note->event()->get<Bool>(TIED_FORWARD, tiedForwards);
+ note->event()->get<Bool>(TIED_BACKWARD, tiedBack);
+
+ if (!note->event()->has(PITCH))
+ continue;
+ int pitch = note->event()->get<Int>(PITCH);
+
+ if (tiedBack) {
+ TieMap::iterator ti(tieMap.find(pitch));
+
+ if (ti != tieMap.end()) {
+ NotationElementList::iterator otherItr(ti->second);
+
+ if ((*otherItr)->getViewAbsoluteTime() +
+ (*otherItr)->getViewDuration() ==
+ note->getViewAbsoluteTime()) {
+
+ NOTATION_DEBUG << "Second note in tie at " << note->getViewAbsoluteTime() << ": found first note, it matches" << endl;
+
+ (*otherItr)->event()->setMaybe<Int>
+ (m_properties.TIE_LENGTH,
+ (int)(baseX - (*otherItr)->getLayoutX()));
+
+ } else {
+ NOTATION_DEBUG << "Second note in tie at " << note->getViewAbsoluteTime() << ": found first note but it ends at " << ((*otherItr)->getViewAbsoluteTime() + (*otherItr)->getViewDuration()) << endl;
+
+ tieMap.erase(pitch);
+ }
+ }
+ }
+
+ if (tiedForwards) {
+ note->event()->setMaybe<Int>(m_properties.TIE_LENGTH, 0);
+ tieMap[pitch] = citr;
+ } else {
+ note->event()->unset(m_properties.TIE_LENGTH);
+ }
+
+ if (citr == chord.getFinalElement())
+ break;
+ }
+
+ itr = chord.getFinalElement();
+ if (barEndsInChord) {
+ to = itr;
+ ++to;
+ }
+}
+
+float
+NotationHLayout::getLayoutWidth(ViewElement &ve,
+ NotePixmapFactory *npf,
+ const ::Rosegarden::Key &previousKey) const
+{
+ NotationElement& e = static_cast<NotationElement&>(ve);
+
+ if ((e.isNote() || e.isRest()) && e.event()->has(NOTE_TYPE)) {
+
+ long noteType = e.event()->get<Int>(NOTE_TYPE);
+ long dots = 0;
+ (void)e.event()->get<Int>(NOTE_DOTS, dots);
+
+ double bw = 0;
+
+ if (e.isNote()) {
+ bw = m_npf->getNoteBodyWidth(noteType)
+ + m_npf->getDotWidth() * dots;
+ } else {
+ bw = m_npf->getRestWidth(Note(noteType, dots));
+ }
+
+ double multiplier = double(Note(noteType, dots).getDuration()) /
+ double(Note(Note::Quaver).getDuration());
+ multiplier -= 1.0;
+ multiplier *= m_proportion / 100.0;
+ multiplier += 1.0;
+
+ double gap = m_npf->getNoteBodyWidth(noteType) * multiplier;
+
+ NOTATION_DEBUG << "note type " << noteType << ", isNote " << e.isNote() << ", dots " << dots << ", multiplier " << multiplier << ", gap " << gap << ", result " << (bw + gap * m_spacing / 100.0) << endl;
+
+ gap = gap * m_spacing / 100.0;
+ return bw + gap;
+
+ } else {
+
+ double w = getFixedItemSpacing();
+
+ if (e.event()->isa(Clef::EventType)) {
+
+ w += m_npf->getClefWidth(Clef(*e.event()));
+
+ } else if (e.event()->isa(::Rosegarden::Key::EventType)) {
+
+ ::Rosegarden::Key key(*e.event());
+
+ ::Rosegarden::Key cancelKey = previousKey;
+
+ if (m_keySigCancelMode == 0) { // only when entering C maj / A min
+
+ if (key.getAccidentalCount() != 0)
+ cancelKey = ::Rosegarden::Key();
+
+ } else if (m_keySigCancelMode == 1) { // only when reducing acc count
+
+ if (!(key.isSharp() == cancelKey.isSharp() &&
+ key.getAccidentalCount() < cancelKey.getAccidentalCount())) {
+ cancelKey = ::Rosegarden::Key();
+ }
+ }
+
+ w += m_npf->getKeyWidth(key, cancelKey);
+
+ } else if (e.event()->isa(Indication::EventType) ||
+ e.event()->isa(Text::EventType)) {
+
+ w = 0;
+
+ } else {
+ // NOTATION_DEBUG << "NotationHLayout::getLayoutWidth(): no case for event type " << e.event()->getType() << endl;
+ // w += 24;
+ w = 0;
+ }
+
+ return w;
+ }
+}
+
+int NotationHLayout::getBarMargin() const
+{
+ return (int)(m_npf->getBarMargin() * m_spacing / 100.0);
+}
+
+int NotationHLayout::getPreBarMargin() const
+{
+ return getBarMargin() / 3;
+}
+
+int NotationHLayout::getPostBarMargin() const
+{
+ return getBarMargin() - getPreBarMargin();
+}
+
+int NotationHLayout::getFixedItemSpacing() const
+{
+ return (int)((m_npf->getNoteBodyWidth() * 2.0 / 3.0) * m_spacing / 100.0);
+}
+
+void
+NotationHLayout::reset()
+{
+ for (BarDataMap::iterator i = m_barData.begin();
+ i != m_barData.end(); ++i) {
+ clearBarList(*i->first);
+ }
+
+ m_barData.clear();
+ m_barPositions.clear();
+ m_totalWidth = 0;
+}
+
+void
+NotationHLayout::resetStaff(Staff &staff, timeT startTime, timeT endTime)
+{
+ if (startTime == endTime) {
+ getBarData(staff).clear();
+ m_totalWidth = 0;
+ }
+}
+
+int
+NotationHLayout::getFirstVisibleBar() const
+{
+ int bar = 0;
+ bool haveBar = false;
+ for (BarDataMap::const_iterator i = m_barData.begin(); i != m_barData.end(); ++i) {
+ if (i->second.begin() == i->second.end())
+ continue;
+ int barHere = i->second.begin()->first;
+ if (barHere < bar || !haveBar) {
+ bar = barHere;
+ haveBar = true;
+ }
+ }
+
+ // NOTATION_DEBUG << "NotationHLayout::getFirstVisibleBar: returning " << bar << endl;
+
+ return bar;
+}
+
+int
+NotationHLayout::getFirstVisibleBarOnStaff(Staff &staff)
+{
+ BarDataList &bdl(getBarData(staff));
+
+ int bar = 0;
+ if (bdl.begin() != bdl.end())
+ bar = bdl.begin()->first;
+
+ // NOTATION_DEBUG << "NotationHLayout::getFirstVisibleBarOnStaff: returning " << bar << endl;
+
+ return bar;
+}
+
+int
+NotationHLayout::getLastVisibleBar() const
+{
+ int bar = 0;
+ bool haveBar = false;
+ for (BarDataMap::const_iterator i = m_barData.begin();
+ i != m_barData.end(); ++i) {
+ if (i->second.begin() == i->second.end())
+ continue;
+ int barHere = getLastVisibleBarOnStaff(*i->first);
+ if (barHere > bar || !haveBar) {
+ bar = barHere;
+ haveBar = true;
+ }
+ }
+
+ // NOTATION_DEBUG << "NotationHLayout::getLastVisibleBar: returning " << bar << endl;
+
+ return bar;
+}
+
+int
+NotationHLayout::getLastVisibleBarOnStaff(Staff &staff) const
+{
+ const BarDataList &bdl(getBarData(staff));
+ int bar = 0;
+
+ if (bdl.begin() != bdl.end()) {
+ BarDataList::const_iterator i = bdl.end();
+ bar = ((--i)->first) + 1; // last visible bar_line_
+ }
+
+ // NOTATION_DEBUG << "NotationHLayout::getLastVisibleBarOnStaff: returning " << bar << endl;
+
+ return bar;
+}
+
+double
+NotationHLayout::getBarPosition(int bar) const
+{
+ double position = 0.0;
+
+ BarPositionList::const_iterator i = m_barPositions.find(bar);
+
+ if (i != m_barPositions.end()) {
+
+ position = i->second;
+
+ } else {
+
+ i = m_barPositions.begin();
+ if (i != m_barPositions.end()) {
+ if (bar < i->first)
+ position = i->second;
+ else {
+ i = m_barPositions.end();
+ --i;
+ if (bar > i->first)
+ position = i->second;
+ }
+ }
+ }
+
+ // NOTATION_DEBUG << "NotationHLayout::getBarPosition: returning " << position << " for bar " << bar << endl;
+
+ return position;
+}
+
+bool
+NotationHLayout::isBarCorrectOnStaff(Staff &staff, int i)
+{
+ BarDataList &bdl(getBarData(staff));
+ ++i;
+
+ BarDataList::iterator bdli(bdl.find(i));
+ if (bdli != bdl.end())
+ return bdli->second.basicData.correct;
+ else
+ return true;
+}
+
+bool NotationHLayout::getTimeSignaturePosition(Staff &staff,
+ int i,
+ TimeSignature &timeSig,
+ double &timeSigX)
+{
+ BarDataList &bdl(getBarData(staff));
+
+ BarDataList::iterator bdli(bdl.find(i));
+ if (bdli != bdl.end()) {
+ timeSig = bdli->second.basicData.timeSignature;
+ timeSigX = (double)(bdli->second.layoutData.timeSigX);
+ return bdli->second.basicData.newTimeSig;
+ } else
+ return 0;
+}
+
+timeT
+NotationHLayout::getTimeForX(double x) const
+{
+ return RulerScale::getTimeForX(x);
+}
+
+double
+NotationHLayout::getXForTime(timeT t) const
+{
+ return RulerScale::getXForTime(t);
+}
+
+double
+NotationHLayout::getXForTimeByEvent(timeT time) const
+{
+ // NOTATION_DEBUG << "NotationHLayout::getXForTime(" << time << ")" << endl;
+
+ for (BarDataMap::const_iterator i = m_barData.begin(); i != m_barData.end(); ++i) {
+
+ Staff *staff = i->first;
+
+ if (staff->getSegment().getStartTime() <= time &&
+ staff->getSegment().getEndMarkerTime() > time) {
+
+ ViewElementList::iterator vli =
+ staff->getViewElementList()->findNearestTime(time);
+
+ bool found = false;
+ double x = 0.0, dx = 0.0;
+ timeT t = 0, dt = 0;
+
+ while (!found) {
+ if (vli == staff->getViewElementList()->end())
+ break;
+ NotationElement *element = static_cast<NotationElement *>(*vli);
+ if (element->getCanvasItem()) {
+ x = element->getLayoutX();
+ double temp;
+ element->getLayoutAirspace(temp, dx);
+ t = element->event()->getNotationAbsoluteTime();
+ dt = element->event()->getNotationDuration();
+ found = true;
+ break;
+ }
+ ++vli;
+ }
+
+ if (found) {
+ if (time > t) {
+
+ while (vli != staff->getViewElementList()->end() &&
+ ((*vli)->event()->getNotationAbsoluteTime() < time ||
+ !((static_cast<NotationElement *>(*vli))->getCanvasItem())))
+ ++vli;
+
+ if (vli != staff->getViewElementList()->end()) {
+ NotationElement *element = static_cast<NotationElement *>(*vli);
+ dx = element->getLayoutX() - x;
+ dt = element->event()->getNotationAbsoluteTime() - t;
+ }
+
+ if (dt > 0 && dx > 0) {
+ return x + dx * (time - t) / dt;
+ }
+ }
+
+ return x - 3;
+ }
+ }
+ }
+
+ return RulerScale::getXForTime(time);
+}
+
+std::vector<int> NotationHLayout::m_availableSpacings;
+std::vector<int> NotationHLayout::m_availableProportions;
+
+}
diff --git a/src/gui/editors/notation/NotationHLayout.h b/src/gui/editors/notation/NotationHLayout.h
new file mode 100644
index 0000000..9d7366b
--- /dev/null
+++ b/src/gui/editors/notation/NotationHLayout.h
@@ -0,0 +1,446 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTATIONHLAYOUT_H_
+#define _RG_NOTATIONHLAYOUT_H_
+
+#include "base/LayoutEngine.h"
+#include "base/NotationTypes.h"
+#include "NotationElement.h"
+#include "gui/general/ProgressReporter.h"
+#include <map>
+#include <vector>
+#include "base/Event.h"
+
+
+class TieMap;
+class QObject;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class Staff;
+class Quantizer;
+class NotePixmapFactory;
+class NotationProperties;
+class NotationGroup;
+class NotationChord;
+class Key;
+class Composition;
+class Clef;
+class AccidentalTable;
+
+
+/**
+ * Horizontal notation layout
+ *
+ * computes the X coordinates of notation elements
+ */
+
+class NotationHLayout : public ProgressReporter,
+ public HorizontalLayoutEngine
+{
+public:
+ NotationHLayout(Composition *c,
+ NotePixmapFactory *npf,
+ const NotationProperties &properties,
+ QObject* parent, const char* name = 0);
+
+ virtual ~NotationHLayout();
+
+ void setNotePixmapFactory(NotePixmapFactory *npf) {
+ m_npf = npf;
+ }
+
+ /**
+ * Precomputes layout data for a single staff. The resulting data
+ * is stored in the BarDataMap, keyed from the staff reference;
+ * the entire map is then used by reconcileBars() and layout().
+ * The map should be cleared (by calling reset()) before a full
+ * set of staffs is preparsed.
+ */
+ virtual void scanStaff(Staff &staff,
+ timeT startTime = 0,
+ timeT endTime = 0);
+
+ /**
+ * Resets internal data stores, notably the BarDataMap that is
+ * used to retain the data computed by scanStaff().
+ */
+ virtual void reset();
+
+ /**
+ * Resets internal data stores, notably the given staff's entry
+ * in the BarDataMap used to retain the data computed by scanStaff().
+ */
+ virtual void resetStaff(Staff &staff,
+ timeT startTime = 0,
+ timeT endTime = 0);
+
+ /**
+ * Lays out all staffs that have been scanned
+ */
+ virtual void finishLayout(timeT startTime = 0,
+ timeT endTime = 0);
+
+ /**
+ * Set page mode
+ */
+ virtual void setPageMode(bool pageMode) { m_pageMode = pageMode; }
+
+ /**
+ * Get the page mode setting
+ */
+ virtual bool isPageMode() { return m_pageMode; }
+
+ /**
+ * Set a page width
+ */
+ virtual void setPageWidth(double pageWidth) { m_pageWidth = pageWidth; }
+
+ /**
+ * Get the page width
+ */
+ virtual double getPageWidth() { return m_pageWidth; }
+
+ /**
+ * Gets the current spacing factor (100 == "normal" spacing)
+ */
+ int getSpacing() const { return m_spacing; }
+
+ /**
+ * Sets the current spacing factor (100 == "normal" spacing)
+ */
+ void setSpacing(int spacing) { m_spacing = spacing; }
+
+ /**
+ * Gets the range of "standard" spacing factors (you can
+ * setSpacing() to anything you want, but it makes sense to
+ * have a standard list for GUI use). The only guaranteed
+ * property of the returned list is that 100 will be in it.
+ */
+ static std::vector<int> getAvailableSpacings();
+
+ /**
+ * Gets the current proportion (100 == spaces proportional to
+ * durations, 0 == equal spacings)
+ */
+ int getProportion() const { return m_proportion; }
+
+ /**
+ * Sets the current proportion (100 == spaces proportional to
+ * durations, 0 == equal spacings)
+ */
+ void setProportion(int proportion) { m_proportion = proportion; }
+
+ /**
+ * Gets the range of "standard" proportion factors (you can
+ * setProportion() to anything you want, but it makes sense to
+ * have a standard list for GUI use). The only guaranteed
+ * property of the returned list is that 0, 100, and whatever the
+ * default proportion is will be in it.
+ */
+ static std::vector<int> getAvailableProportions();
+
+ /**
+ * Returns the total length of all elements once layout is done
+ * This is the x-coord of the end of the last element on the longest
+ * staff, plus the space allocated to that element
+ */
+ virtual double getTotalWidth() const { return m_totalWidth; }
+
+ /**
+ * Returns the number of the first visible bar line on the given
+ * staff
+ */
+ virtual int getFirstVisibleBarOnStaff(Staff &staff);
+
+ /**
+ * Returns the number of the first visible bar line on any
+ * staff
+ */
+ virtual int getFirstVisibleBar() const;
+
+ /**
+ * Returns the number of the last visible bar line on the given
+ * staff
+ */
+ virtual int getLastVisibleBarOnStaff(Staff &staff) const;
+
+ /**
+ * Returns the number of the first visible bar line on any
+ * staff
+ */
+ virtual int getLastVisibleBar() const;
+
+ /**
+ * Returns the x-coordinate of the given bar number
+ */
+ virtual double getBarPosition(int barNo) const;
+
+ /**
+ * Returns the nearest time value to the given X coord.
+ */
+ virtual timeT getTimeForX(double x) const;
+
+ /**
+ * Returns the X coord corresponding to the given time value.
+ * This RulerScale method works by interpolating between bar lines
+ * (the inverse of the way getTimeForX works), and should be used
+ * for any rulers associated with the layout.
+ */
+ virtual double getXForTime(timeT time) const;
+
+ /**
+ * Returns the X coord corresponding to the given time value.
+ * This method works by interpolating between event positions, and
+ * should be used for position pointer tracking during playback.
+ */
+ virtual double getXForTimeByEvent(timeT time) const;
+
+ /**
+ * Returns true if the specified bar has the correct length
+ */
+ virtual bool isBarCorrectOnStaff(Staff &staff, int barNo);
+
+ /**
+ * Returns true if there is a new time signature in the given bar,
+ * setting timeSignature appropriately and setting timeSigX to its
+ * x-coord
+ */
+ virtual bool getTimeSignaturePosition
+ (Staff &staff, int barNo,
+ TimeSignature &timeSig, double &timeSigX);
+
+ /// purely optional, used only for progress reporting
+ void setStaffCount(int staffCount) {
+ m_staffCount = staffCount;
+ }
+
+protected:
+
+ struct Chunk {
+ timeT duration;
+ short subordering;
+ float fixed;
+ float stretchy;
+ float x;
+
+ Chunk(timeT d, short sub, float f, float s) :
+ duration(d), subordering(sub), fixed(f), stretchy(s), x(0) { }
+ Chunk(short sub, float f) :
+ duration(0), subordering(sub), fixed(f), stretchy(0), x(0) { }
+ };
+ typedef std::vector<Chunk> ChunkList;
+
+ /**
+ * Inner class for bar data, used by scanStaff()
+ */
+ struct BarData
+ {
+ ChunkList chunks;
+
+ struct BasicData
+ { // slots that can be filled at construction time
+
+ NotationElementList::iterator start; // i.e. event following barline
+ bool correct; // bar preceding barline has correct duration
+ TimeSignature timeSignature;
+ bool newTimeSig;
+
+ } basicData;
+
+ struct SizeData
+ { // slots that can be filled when the following bar has been scanned
+
+ float idealWidth; // theoretical width of bar following barline
+ float reconciledWidth;
+ float fixedWidth; // width of non-chunk items in bar
+ int clefKeyWidth;
+ timeT actualDuration; // may exceed nominal duration
+
+ } sizeData;
+
+ struct LayoutData
+ { // slots either assumed, or only known at layout time
+ bool needsLayout;
+ double x; // coordinate for display of barline
+ int timeSigX;
+
+ } layoutData;
+
+ BarData(NotationElementList::iterator i,
+ bool correct, TimeSignature timeSig, bool newTimeSig) {
+ basicData.start = i;
+ basicData.correct = correct;
+ basicData.timeSignature = timeSig;
+ basicData.newTimeSig = newTimeSig;
+ sizeData.idealWidth = 0;
+ sizeData.reconciledWidth = 0;
+ sizeData.fixedWidth = 0;
+ sizeData.clefKeyWidth = 0;
+ sizeData.actualDuration = 0;
+ layoutData.needsLayout = true;
+ layoutData.x = -1;
+ layoutData.timeSigX = -1;
+ }
+ };
+
+ typedef std::map<int, BarData> BarDataList;
+ typedef BarDataList::value_type BarDataPair;
+ typedef std::map<Staff *, BarDataList> BarDataMap;
+ typedef std::map<int, double> BarPositionList;
+
+ typedef std::map<Staff *, int> StaffIntMap;
+ typedef std::map<long, NotationGroup *> NotationGroupMap;
+
+ void clearBarList(Staff &);
+
+
+ /**
+ * Set the basic data for the given barNo. If barNo is
+ * beyond the end of the existing bar data list, create new
+ * records and/or fill with empty ones as appropriate.
+ */
+ void setBarBasicData(Staff &staff, int barNo,
+ NotationElementList::iterator start, bool correct,
+ TimeSignature timeSig, bool newTimeSig);
+
+ /**
+ * Set the size data for the given barNo. If barNo is
+ * beyond the end of the existing bar data list, create new
+ * records and/or fill with empty ones as appropriate.
+ */
+ void setBarSizeData(Staff &staff, int barNo,
+ float fixedWidth, timeT actualDuration);
+
+ /**
+ * Returns the bar positions for a given staff, provided that
+ * staff has been preparsed since the last reset
+ */
+ BarDataList& getBarData(Staff &staff);
+ const BarDataList& getBarData(Staff &staff) const;
+
+ /// Find the staff in which bar "barNo" is widest
+ Staff *getStaffWithWidestBar(int barNo);
+
+ /// Find width of clef+key in the staff in which they're widest in this bar
+ int getMaxRepeatedClefAndKeyWidth(int barNo);
+
+ /// For a single bar, makes sure synchronisation points align in all staves
+ void preSquishBar(int barNo);
+
+ /// Tries to harmonize the bar positions for all the staves (linear mode)
+ void reconcileBarsLinear();
+
+ /// Tries to harmonize the bar positions for all the staves (page mode)
+ void reconcileBarsPage();
+
+ void layout(BarDataMap::iterator,
+ timeT startTime,
+ timeT endTime);
+
+ /// Find earliest element with quantized time of t or greater
+ NotationElementList::iterator getStartOfQuantizedSlice
+ (NotationElementList *, timeT t) const;
+
+ void scanChord
+ (NotationElementList *notes, NotationElementList::iterator &i,
+ const Clef &, const ::Rosegarden::Key &,
+ AccidentalTable &, float &lyricWidth,
+ ChunkList &chunks, NotePixmapFactory *, int ottavaShift,
+ NotationElementList::iterator &to);
+
+ typedef std::map<int, NotationElementList::iterator> TieMap;
+
+ // This modifies the NotationElementList::iterator passed to it,
+ // moving it on to the last note in the chord; updates the TieMap;
+ // and may modify the to-iterator if it turns out to point at a
+ // note within the chord
+ void positionChord
+ (Staff &staff,
+ NotationElementList::iterator &, const Clef &clef,
+ const ::Rosegarden::Key &key, TieMap &, NotationElementList::iterator &to);
+
+ void sampleGroupElement
+ (Staff &staff, const Clef &clef,
+ const ::Rosegarden::Key &key, const NotationElementList::iterator &);
+
+ /// Difference between absolute time of next event and of this
+ timeT getSpacingDuration
+ (Staff &staff, const NotationElementList::iterator &);
+
+ /// Difference between absolute time of chord and of first event not in it
+ timeT getSpacingDuration
+ (Staff &staff, const NotationChord &);
+
+ float getLayoutWidth(ViewElement &,
+ NotePixmapFactory *,
+ const ::Rosegarden::Key &) const;
+
+ int getBarMargin() const;
+ int getPreBarMargin() const;
+ int getPostBarMargin() const;
+ int getFixedItemSpacing() const;
+
+ NotePixmapFactory *getNotePixmapFactory(Staff &);
+ NotePixmapFactory *getGraceNotePixmapFactory(Staff &);
+
+ //--------------- Data members ---------------------------------
+
+ BarDataMap m_barData;
+ StaffIntMap m_staffNameWidths;
+ BarPositionList m_barPositions;
+ NotationGroupMap m_groupsExtant;
+
+ double m_totalWidth;
+ bool m_pageMode;
+ double m_pageWidth;
+ int m_spacing;
+ int m_proportion;
+ int m_keySigCancelMode;
+
+ //!!! This should not be here -- different staffs may have
+ //different sizes in principle, so we should always be referring
+ //to the npf of a particular staff
+ NotePixmapFactory *m_npf;
+
+ static std::vector<int> m_availableSpacings;
+ static std::vector<int> m_availableProportions;
+
+ const Quantizer *m_notationQuantizer;
+ const NotationProperties &m_properties;
+
+ int m_timePerProgressIncrement;
+ std::map<Staff *, bool> m_haveOttavaSomewhere;
+ int m_staffCount; // purely for progress reporting
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationProperties.cpp b/src/gui/editors/notation/NotationProperties.cpp
new file mode 100644
index 0000000..8c87cc3
--- /dev/null
+++ b/src/gui/editors/notation/NotationProperties.cpp
@@ -0,0 +1,85 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NotationProperties.h"
+
+#include "base/PropertyName.h"
+
+
+namespace Rosegarden
+{
+
+const PropertyName NotationProperties::NOTE_STYLE = "NoteStyle";
+const PropertyName NotationProperties::HEIGHT_ON_STAFF = "HeightOnStaff";
+const PropertyName NotationProperties::BEAMED = "Beamed";
+const PropertyName NotationProperties::BEAM_ABOVE = "BeamAbove";
+const PropertyName NotationProperties::SLASHES = "Slashes";
+const PropertyName NotationProperties::STEM_UP = "NoteStemUp";
+const PropertyName NotationProperties::USE_CAUTIONARY_ACCIDENTAL = "UseCautionaryAccidental";
+const PropertyName NotationProperties::OTTAVA_SHIFT = "OttavaShift";
+const PropertyName NotationProperties::SLUR_ABOVE = "SlurAbove";
+
+NotationProperties::NotationProperties(const std::string &prefix) :
+
+ VIEW_LOCAL_STEM_UP (prefix + "StemUp"),
+
+ MIN_WIDTH (prefix + "MinWidth"),
+
+ CALCULATED_ACCIDENTAL (prefix + "NoteCalculatedAccidental"),
+ DISPLAY_ACCIDENTAL (prefix + "NoteDisplayAccidental"),
+ DISPLAY_ACCIDENTAL_IS_CAUTIONARY(prefix + "NoteDisplayAccidentalIsCautionary"),
+ ACCIDENTAL_SHIFT (prefix + "NoteAccidentalShift"),
+ ACCIDENTAL_EXTRA_SHIFT (prefix + "NoteAccidentalExtraShift"),
+ UNBEAMED_STEM_LENGTH (prefix + "UnbeamedStemLength"),
+ DRAW_FLAG (prefix + "NoteDrawFlag"),
+ NOTE_HEAD_SHIFTED (prefix + "NoteHeadShifted"),
+ NEEDS_EXTRA_SHIFT_SPACE (prefix + "NeedsExtraShiftSpace"),
+ NOTE_DOT_SHIFTED (prefix + "NoteDotShifted"),
+ CHORD_PRIMARY_NOTE (prefix + "ChordPrimaryNote"),
+ CHORD_MARK_COUNT (prefix + "ChordMarkCount"),
+ TIE_LENGTH (prefix + "TieLength"),
+ SLUR_Y_DELTA (prefix + "SlurYDelta"),
+ SLUR_LENGTH (prefix + "SlurLength"),
+ LYRIC_EXTRA_WIDTH (prefix + "LyricExtraWidth"),
+ REST_TOO_SHORT (prefix + "RestTooShort"),
+ REST_OUTSIDE_STAVE (prefix + "RestOutsideStave"),
+
+ BEAM_GRADIENT (prefix + "BeamGradient"),
+ BEAM_SECTION_WIDTH (prefix + "BeamSectionWidth"),
+ BEAM_NEXT_BEAM_COUNT (prefix + "BeamNextBeamCount"),
+ BEAM_NEXT_PART_BEAMS (prefix + "BeamNextPartBeams"),
+ BEAM_THIS_PART_BEAMS (prefix + "BeamThisPartBeams"),
+ BEAM_MY_Y (prefix + "BeamMyY"),
+
+ TUPLING_LINE_MY_Y (prefix + "TuplingLineMyY"),
+ TUPLING_LINE_WIDTH (prefix + "TuplingLineWidth"),
+ TUPLING_LINE_GRADIENT (prefix + "TuplingLineGradient"),
+ TUPLING_LINE_FOLLOWS_BEAM (prefix + "TuplingLineFollowsBeam")
+
+{
+ // nothing else
+}
+
+}
diff --git a/src/gui/editors/notation/NotationProperties.h b/src/gui/editors/notation/NotationProperties.h
new file mode 100644
index 0000000..69a26cf
--- /dev/null
+++ b/src/gui/editors/notation/NotationProperties.h
@@ -0,0 +1,108 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTATIONPROPERTIES_H_
+#define _RG_NOTATIONPROPERTIES_H_
+
+#include "base/PropertyName.h"
+#include <string>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * Property names for properties that are computed and cached within
+ * the notation module, but that need not necessarily be saved with
+ * the file.
+ *
+ * If you add something here, remember to add the definition to
+ * notationproperties.cpp as well...
+ */
+
+class NotationProperties
+{
+public:
+ NotationProperties(const std::string &prefix);
+
+ // These are only of interest to notation views, but are the
+ // same across all notation views.
+
+ static const PropertyName HEIGHT_ON_STAFF;
+ static const PropertyName NOTE_STYLE;
+ static const PropertyName BEAMED;
+ static const PropertyName BEAM_ABOVE;
+ static const PropertyName SLASHES;
+ static const PropertyName STEM_UP;
+ static const PropertyName USE_CAUTIONARY_ACCIDENTAL;
+ static const PropertyName OTTAVA_SHIFT;
+ static const PropertyName SLUR_ABOVE;
+
+ // The rest are, or may be, view-local
+
+ const PropertyName VIEW_LOCAL_STEM_UP;
+ const PropertyName MIN_WIDTH;
+ const PropertyName CALCULATED_ACCIDENTAL;
+ const PropertyName DISPLAY_ACCIDENTAL;
+ const PropertyName DISPLAY_ACCIDENTAL_IS_CAUTIONARY;
+ const PropertyName ACCIDENTAL_SHIFT;
+ const PropertyName ACCIDENTAL_EXTRA_SHIFT;
+ const PropertyName UNBEAMED_STEM_LENGTH;
+ const PropertyName DRAW_FLAG;
+ const PropertyName NOTE_HEAD_SHIFTED;
+ const PropertyName NEEDS_EXTRA_SHIFT_SPACE;
+ const PropertyName NOTE_DOT_SHIFTED;
+ const PropertyName CHORD_PRIMARY_NOTE;
+ const PropertyName CHORD_MARK_COUNT;
+ const PropertyName TIE_LENGTH;
+ const PropertyName SLUR_Y_DELTA;
+ const PropertyName SLUR_LENGTH;
+ const PropertyName LYRIC_EXTRA_WIDTH;
+ const PropertyName REST_TOO_SHORT;
+ const PropertyName REST_OUTSIDE_STAVE;
+
+ // Set in applyBeam in notationsets.cpp:
+
+ const PropertyName BEAM_GRADIENT;
+ const PropertyName BEAM_SECTION_WIDTH;
+ const PropertyName BEAM_NEXT_BEAM_COUNT;
+ const PropertyName BEAM_NEXT_PART_BEAMS;
+ const PropertyName BEAM_THIS_PART_BEAMS;
+ const PropertyName BEAM_MY_Y;
+ const PropertyName TUPLING_LINE_MY_Y;
+ const PropertyName TUPLING_LINE_WIDTH;
+ const PropertyName TUPLING_LINE_GRADIENT;
+ const PropertyName TUPLING_LINE_FOLLOWS_BEAM;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationSelectionPaster.cpp b/src/gui/editors/notation/NotationSelectionPaster.cpp
new file mode 100644
index 0000000..3b008f2
--- /dev/null
+++ b/src/gui/editors/notation/NotationSelectionPaster.cpp
@@ -0,0 +1,89 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NotationSelectionPaster.h"
+
+#include <klocale.h>
+#include "base/Event.h"
+#include "base/Selection.h"
+#include "base/ViewElement.h"
+#include "commands/edit/PasteEventsCommand.h"
+#include "gui/general/EditTool.h"
+#include "gui/general/LinedStaff.h"
+#include "document/RosegardenGUIDoc.h"
+#include "NotationTool.h"
+#include "NotationView.h"
+#include "NotationElement.h"
+
+
+namespace Rosegarden
+{
+
+NotationSelectionPaster::NotationSelectionPaster(EventSelection& es,
+ NotationView* view)
+ : NotationTool("NotationPaster", view),
+ m_selection(es)
+{
+ m_nParentView->setCanvasCursor(Qt::crossCursor);
+}
+
+NotationSelectionPaster::~NotationSelectionPaster()
+{}
+
+void NotationSelectionPaster::handleLeftButtonPress(timeT,
+ int,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement*)
+{
+ if (staffNo < 0)
+ return ;
+ Event *clef = 0, *key = 0;
+
+ LinedStaff *staff = m_nParentView->getLinedStaff(staffNo);
+
+ NotationElementList::iterator closestElement =
+ staff->getClosestElementToCanvasCoords(e->x(), (int)e->y(),
+ clef, key, false, -1);
+
+ if (closestElement == staff->getViewElementList()->end())
+ return ;
+
+ timeT time = (*closestElement)->getViewAbsoluteTime();
+
+ Segment& segment = staff->getSegment();
+ PasteEventsCommand *command = new PasteEventsCommand
+ (segment, m_parentView->getDocument()->getClipboard(), time,
+ PasteEventsCommand::Restricted);
+
+ if (!command->isPossible()) {
+ m_parentView->slotStatusHelpMsg(i18n("Couldn't paste at this point"));
+ } else {
+ m_parentView->addCommandToHistory(command);
+ m_parentView->slotStatusHelpMsg(i18n("Ready."));
+ }
+}
+
+}
diff --git a/src/gui/editors/notation/NotationSelectionPaster.h b/src/gui/editors/notation/NotationSelectionPaster.h
new file mode 100644
index 0000000..e6a80dd
--- /dev/null
+++ b/src/gui/editors/notation/NotationSelectionPaster.h
@@ -0,0 +1,72 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTATIONSELECTIONPASTER_H_
+#define _RG_NOTATIONSELECTIONPASTER_H_
+
+#include "NotationTool.h"
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class NotationView;
+class EventSelection;
+
+
+/**
+ * Selection pasting - unused at the moment
+ */
+class NotationSelectionPaster : public NotationTool
+{
+public:
+
+ ~NotationSelectionPaster();
+
+ virtual void handleLeftButtonPress(timeT,
+ int height, int staffNo,
+ QMouseEvent*,
+ ViewElement* el);
+
+protected:
+ NotationSelectionPaster(EventSelection&,
+ NotationView*);
+
+ //--------------- Data members ---------------------------------
+
+ EventSelection& m_selection;
+
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationSelector.cpp b/src/gui/editors/notation/NotationSelector.cpp
new file mode 100644
index 0000000..221fbe3
--- /dev/null
+++ b/src/gui/editors/notation/NotationSelector.cpp
@@ -0,0 +1,957 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NotationSelector.h"
+#include "misc/Debug.h"
+
+#include <klocale.h>
+#include "base/Event.h"
+#include "base/NotationTypes.h"
+#include "base/PropertyName.h"
+#include "base/Selection.h"
+#include "base/ViewElement.h"
+#include "base/BaseProperties.h"
+#include "commands/edit/MoveAcrossSegmentsCommand.h"
+#include "commands/edit/MoveCommand.h"
+#include "commands/edit/TransposeCommand.h"
+#include "commands/notation/IncrementDisplacementsCommand.h"
+#include "gui/general/EditTool.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/LinedStaff.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "gui/kdeext/QCanvasSimpleSprite.h"
+#include "NotationElement.h"
+#include "NotationProperties.h"
+#include "NotationStaff.h"
+#include "NotationTool.h"
+#include "NotationView.h"
+#include "NotePixmapFactory.h"
+#include <kaction.h>
+#include <qapplication.h>
+#include <qiconset.h>
+#include <qrect.h>
+#include <qstring.h>
+#include <qtimer.h>
+
+
+namespace Rosegarden
+{
+
+using namespace BaseProperties;
+
+NotationSelector::NotationSelector(NotationView* view)
+ : NotationTool("NotationSelector", view),
+ m_selectionRect(0),
+ m_updateRect(false),
+ m_selectedStaff(0),
+ m_clickedElement(0),
+ m_selectionToMerge(0),
+ m_justSelectedBar(false),
+ m_wholeStaffSelectionComplete(false)
+{
+ connect(m_parentView, SIGNAL(usedSelection()),
+ this, SLOT(slotHideSelection()));
+
+ connect(this, SIGNAL(editElement(NotationStaff *, NotationElement *, bool)),
+ m_parentView, SLOT(slotEditElement(NotationStaff *, NotationElement *, bool)));
+
+ QIconSet icon
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("crotchet")));
+ new KToggleAction(i18n("Switch to Insert Tool"), icon, 0, this,
+ SLOT(slotInsertSelected()), actionCollection(),
+ "insert");
+
+ new KAction(i18n("Switch to Erase Tool"), "eraser", 0, this,
+ SLOT(slotEraseSelected()), actionCollection(),
+ "erase");
+
+ // (this crashed, and it might be superfluous with ^N anyway, so I'm
+ // commenting it out, but leaving it here in case I change my mind about
+ // fooling with it.) (DMM)
+ // new KAction(i18n("Normalize Rests"), 0, 0, this,
+ // SLOT(slotCollapseRests()), actionCollection(),
+ // "collapse_rests");
+
+ new KAction(i18n("Collapse Rests"), 0, 0, this,
+ SLOT(slotCollapseRestsHard()), actionCollection(),
+ "collapse_rests_aggressively");
+
+ new KAction(i18n("Respell as Flat"), 0, 0, this,
+ SLOT(slotRespellFlat()), actionCollection(),
+ "respell_flat");
+
+ new KAction(i18n("Respell as Sharp"), 0, 0, this,
+ SLOT(slotRespellSharp()), actionCollection(),
+ "respell_sharp");
+
+ new KAction(i18n("Respell as Natural"), 0, 0, this,
+ SLOT(slotRespellNatural()), actionCollection(),
+ "respell_natural");
+
+ new KAction(i18n("Collapse Notes"), 0, 0, this,
+ SLOT(slotCollapseNotes()), actionCollection(),
+ "collapse_notes");
+
+ new KAction(i18n("Interpret"), 0, 0, this,
+ SLOT(slotInterpret()), actionCollection(),
+ "interpret");
+
+ new KAction(i18n("Move to Staff Above"), 0, 0, this,
+ SLOT(slotStaffAbove()), actionCollection(),
+ "move_events_up_staff");
+
+ new KAction(i18n("Move to Staff Below"), 0, 0, this,
+ SLOT(slotStaffBelow()), actionCollection(),
+ "move_events_down_staff");
+
+ new KAction(i18n("Make Invisible"), 0, 0, this,
+ SLOT(slotMakeInvisible()), actionCollection(),
+ "make_invisible");
+
+ new KAction(i18n("Make Visible"), 0, 0, this,
+ SLOT(slotMakeVisible()), actionCollection(),
+ "make_visible");
+
+ createMenu("notationselector.rc");
+}
+
+NotationSelector::~NotationSelector()
+{
+ delete m_selectionToMerge;
+}
+
+void NotationSelector::handleLeftButtonPress(timeT t,
+ int height,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement *element)
+{
+ std::cerr << "NotationSelector::handleMousePress: time is " << t << ", staffNo is "
+ << staffNo << ", e and element are " << e << " and " << element << std::endl;
+
+ if (m_justSelectedBar) {
+ handleMouseTripleClick(t, height, staffNo, e, element);
+ m_justSelectedBar = false;
+ return ;
+ }
+
+ m_wholeStaffSelectionComplete = false;
+
+ delete m_selectionToMerge;
+ const EventSelection *selectionToMerge = 0;
+ if (e->state() & ShiftButton) {
+ m_clickedShift = true;
+ selectionToMerge = m_nParentView->getCurrentSelection();
+ } else {
+ m_clickedShift = false;
+ }
+ m_selectionToMerge =
+ (selectionToMerge ? new EventSelection(*selectionToMerge) : 0);
+
+ m_clickedElement = dynamic_cast<NotationElement*>(element);
+ if (m_clickedElement) {
+ m_selectedStaff = getStaffForElement(m_clickedElement);
+ m_lastDragPitch = -400;
+ m_lastDragTime = m_clickedElement->event()->getNotationAbsoluteTime();
+ } else {
+ m_selectedStaff = 0; // don't know yet; wait until we have an element
+ }
+
+ m_selectionRect->setX(e->x());
+ m_selectionRect->setY(e->y());
+ m_selectionRect->setSize(0, 0);
+
+ m_selectionRect->show();
+ m_updateRect = true;
+ m_startedFineDrag = false;
+
+ //m_parentView->setCursorPosition(p.x());
+}
+
+void NotationSelector::handleRightButtonPress(timeT t,
+ int height,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement *element)
+{
+ std::cerr << "NotationSelector::handleRightButtonPress" << std::endl;
+
+ const EventSelection *sel = m_nParentView->getCurrentSelection();
+
+ if (!sel || sel->getSegmentEvents().empty()) {
+
+ // if nothing selected, permit the possibility of selecting
+ // something before showing the menu
+
+ if (element) {
+ m_clickedElement = dynamic_cast<NotationElement*>(element);
+ m_selectedStaff = getStaffForElement(m_clickedElement);
+ m_nParentView->setSingleSelectedEvent
+ (m_selectedStaff->getId(), m_clickedElement->event(),
+ true, true);
+ }
+
+ handleLeftButtonPress(t, height, staffNo, e, element);
+ }
+
+ EditTool::handleRightButtonPress(t, height, staffNo, e, element);
+}
+
+void NotationSelector::slotClickTimeout()
+{
+ m_justSelectedBar = false;
+}
+
+void NotationSelector::handleMouseDoubleClick(timeT,
+ int,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement *element)
+{
+ NOTATION_DEBUG << "NotationSelector::handleMouseDoubleClick" << endl;
+ m_clickedElement = dynamic_cast<NotationElement*>(element);
+
+ NotationStaff *staff = m_nParentView->getNotationStaff(staffNo);
+ if (!staff)
+ return ;
+ m_selectedStaff = staff;
+
+ bool advanced = (e->state() & ShiftButton);
+
+ if (m_clickedElement) {
+
+ emit editElement(staff, m_clickedElement, advanced);
+
+ } else {
+
+ QRect rect = staff->getBarExtents(e->x(), e->y());
+
+ m_selectionRect->setX(rect.x() + 1);
+ m_selectionRect->setY(rect.y());
+ m_selectionRect->setSize(rect.width() - 1, rect.height());
+
+ m_selectionRect->show();
+ m_updateRect = false;
+
+ m_justSelectedBar = true;
+ QTimer::singleShot(QApplication::doubleClickInterval(), this,
+ SLOT(slotClickTimeout()));
+ }
+
+ return ;
+}
+
+void NotationSelector::handleMouseTripleClick(timeT t,
+ int height,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement *element)
+{
+ if (!m_justSelectedBar)
+ return ;
+ m_justSelectedBar = false;
+
+ NOTATION_DEBUG << "NotationSelector::handleMouseTripleClick" << endl;
+ m_clickedElement = dynamic_cast<NotationElement*>(element);
+
+ NotationStaff *staff = m_nParentView->getNotationStaff(staffNo);
+ if (!staff)
+ return ;
+ m_selectedStaff = staff;
+
+ if (m_clickedElement) {
+
+ // should be safe, as we've already set m_justSelectedBar false
+ handleLeftButtonPress(t, height, staffNo, e, element);
+ return ;
+
+ } else {
+
+ m_selectionRect->setX(staff->getX());
+ m_selectionRect->setY(staff->getY());
+ m_selectionRect->setSize(int(staff->getTotalWidth()) - 1,
+ staff->getTotalHeight() - 1);
+
+ m_selectionRect->show();
+ m_updateRect = false;
+ }
+
+ m_wholeStaffSelectionComplete = true;
+
+ return ;
+}
+
+int NotationSelector::handleMouseMove(timeT, int,
+ QMouseEvent* e)
+{
+ if (!m_updateRect)
+ return RosegardenCanvasView::NoFollow;
+
+ int w = int(e->x() - m_selectionRect->x());
+ int h = int(e->y() - m_selectionRect->y());
+
+ if (m_clickedElement /* && !m_clickedElement->isRest() */) {
+
+ if (m_startedFineDrag) {
+ dragFine(e->x(), e->y(), false);
+ } else if (m_clickedShift) {
+ if (w > 2 || w < -2 || h > 2 || h < -2) {
+ dragFine(e->x(), e->y(), false);
+ }
+ } else if (w > 3 || w < -3 || h > 3 || h < -3) {
+ drag(e->x(), e->y(), false);
+ }
+
+ } else {
+
+ // Qt rectangle dimensions appear to be 1-based
+ if (w > 0)
+ ++w;
+ else
+ --w;
+ if (h > 0)
+ ++h;
+ else
+ --h;
+
+ m_selectionRect->setSize(w, h);
+ setViewCurrentSelection(true);
+ m_nParentView->canvas()->update();
+ }
+
+ return RosegardenCanvasView::FollowHorizontal | RosegardenCanvasView::FollowVertical;
+}
+
+void NotationSelector::handleMouseRelease(timeT, int, QMouseEvent *e)
+{
+ NOTATION_DEBUG << "NotationSelector::handleMouseRelease" << endl;
+ m_updateRect = false;
+
+ NOTATION_DEBUG << "selectionRect width, height: " << m_selectionRect->width()
+ << ", " << m_selectionRect->height() << endl;
+
+ // Test how far we've moved from the original click position -- not
+ // how big the rectangle is (if we were dragging an event, the
+ // rectangle size will still be zero).
+
+ if (((e->x() - m_selectionRect->x()) > -3 &&
+ (e->x() - m_selectionRect->x()) < 3 &&
+ (e->y() - m_selectionRect->y()) > -3 &&
+ (e->y() - m_selectionRect->y()) < 3) &&
+ !m_startedFineDrag) {
+
+ if (m_clickedElement != 0 && m_selectedStaff) {
+
+ // If we didn't drag out a meaningful area, but _did_
+ // click on an individual event, then select just that
+ // event
+
+ if (m_selectionToMerge &&
+ m_selectionToMerge->getSegment() ==
+ m_selectedStaff->getSegment()) {
+
+ // if the event was already part of the selection, we want to
+ // remove it
+ if (m_selectionToMerge->contains(m_clickedElement->event())) {
+ m_selectionToMerge->removeEvent(m_clickedElement->event());
+ } else {
+ m_selectionToMerge->addEvent(m_clickedElement->event());
+ }
+
+ m_nParentView->setCurrentSelection(m_selectionToMerge,
+ true, true);
+ m_selectionToMerge = 0;
+
+ } else {
+
+ m_nParentView->setSingleSelectedEvent
+ (m_selectedStaff->getId(), m_clickedElement->event(),
+ true, true);
+ }
+ /*
+ } else if (m_selectedStaff) {
+
+ // If we clicked on no event but on a staff, move the
+ // insertion cursor to the point where we clicked.
+ // Actually we only really want this to happen if
+ // we aren't double-clicking -- consider using a timer
+ // to establish whether a double-click is going to happen
+
+ m_nParentView->slotSetInsertCursorPosition(e->x(), (int)e->y());
+ */
+ } else {
+ setViewCurrentSelection(false);
+ }
+
+ } else {
+
+ if (m_startedFineDrag) {
+ dragFine(e->x(), e->y(), true);
+ } else if (m_clickedElement /* && !m_clickedElement->isRest() */) {
+ drag(e->x(), e->y(), true);
+ } else {
+ setViewCurrentSelection(false);
+ }
+ }
+
+ m_clickedElement = 0;
+ m_selectionRect->hide();
+ m_wholeStaffSelectionComplete = false;
+ m_nParentView->canvas()->update();
+}
+
+void NotationSelector::drag(int x, int y, bool final)
+{
+ NOTATION_DEBUG << "NotationSelector::drag " << x << ", " << y << endl;
+
+ if (!m_clickedElement || !m_selectedStaff)
+ return ;
+
+ EventSelection *selection = m_nParentView->getCurrentSelection();
+ if (!selection || !selection->contains(m_clickedElement->event())) {
+ selection = new EventSelection(m_selectedStaff->getSegment());
+ selection->addEvent(m_clickedElement->event());
+ }
+ m_nParentView->setCurrentSelection(selection);
+
+ LinedStaff *targetStaff = m_nParentView->getStaffForCanvasCoords(x, y);
+ if (!targetStaff)
+ targetStaff = m_selectedStaff;
+
+ // Calculate time and height
+
+ timeT clickedTime = m_clickedElement->event()->getNotationAbsoluteTime();
+
+ Accidental clickedAccidental = Accidentals::NoAccidental;
+ (void)m_clickedElement->event()->get<String>(ACCIDENTAL, clickedAccidental);
+
+ long clickedPitch = 0;
+ (void)m_clickedElement->event()->get<Int>(PITCH, clickedPitch);
+
+ long clickedHeight = 0;
+ (void)m_clickedElement->event()->get<Int>
+ (NotationProperties::HEIGHT_ON_STAFF, clickedHeight);
+
+ Event *clefEvt = 0, *keyEvt = 0;
+ Clef clef;
+ ::Rosegarden::Key key;
+
+ timeT dragTime = clickedTime;
+ double layoutX = m_clickedElement->getLayoutX();
+ timeT duration = m_clickedElement->getViewDuration();
+
+ NotationElementList::iterator itr =
+ targetStaff->getElementUnderCanvasCoords(x, y, clefEvt, keyEvt);
+
+ if (itr != targetStaff->getViewElementList()->end()) {
+
+ NotationElement *elt = dynamic_cast<NotationElement *>(*itr);
+ dragTime = elt->getViewAbsoluteTime();
+ layoutX = elt->getLayoutX();
+
+ if (elt->isRest() && duration > 0 && elt->getCanvasItem()) {
+
+ double restX = 0, restWidth = 0;
+ elt->getCanvasAirspace(restX, restWidth);
+
+ timeT restDuration = elt->getViewDuration();
+
+ if (restWidth > 0 &&
+ restDuration >= duration * 2) {
+
+ int parts = restDuration / duration;
+ double encroachment = x - restX;
+ NOTATION_DEBUG << "encroachment is " << encroachment << ", restWidth is " << restWidth << endl;
+ int part = (int)((encroachment / restWidth) * parts);
+ if (part >= parts)
+ part = parts - 1;
+
+ dragTime += part * restDuration / parts;
+ layoutX += part * restWidth / parts +
+ (restX - elt->getCanvasX());
+ }
+ }
+ }
+
+ if (clefEvt)
+ clef = Clef(*clefEvt);
+ if (keyEvt)
+ key = ::Rosegarden::Key(*keyEvt);
+
+ int height = targetStaff->getHeightAtCanvasCoords(x, y);
+ int pitch = clickedPitch;
+
+ if (height != clickedHeight)
+ pitch =
+ Pitch
+ (height, clef, key, clickedAccidental).getPerformancePitch();
+
+ if (pitch < clickedPitch) {
+ if (height < -10) {
+ height = -10;
+ pitch = Pitch
+ (height, clef, key, clickedAccidental).getPerformancePitch();
+ }
+ } else if (pitch > clickedPitch) {
+ if (height > 18) {
+ height = 18;
+ pitch = Pitch
+ (height, clef, key, clickedAccidental).getPerformancePitch();
+ }
+ }
+
+ bool singleNonNotePreview = !m_clickedElement->isNote() &&
+ selection->getSegmentEvents().size() == 1;
+
+ if (!final && !singleNonNotePreview) {
+
+ if ((pitch != m_lastDragPitch || dragTime != m_lastDragTime) &&
+ m_clickedElement->isNote()) {
+
+ m_nParentView->showPreviewNote(targetStaff->getId(),
+ layoutX, pitch, height,
+ Note::getNearestNote(duration),
+ m_clickedElement->isGrace());
+ m_lastDragPitch = pitch;
+ m_lastDragTime = dragTime;
+ }
+
+ } else {
+
+ m_nParentView->clearPreviewNote();
+
+ KMacroCommand *command = new KMacroCommand(MoveCommand::getGlobalName());
+ bool haveSomething = false;
+
+ MoveCommand *mc = 0;
+ Event *lastInsertedEvent = 0;
+
+ if (pitch != clickedPitch && m_clickedElement->isNote()) {
+ command->addCommand(new TransposeCommand(pitch - clickedPitch,
+ *selection));
+ haveSomething = true;
+ }
+
+ if (targetStaff != m_selectedStaff) {
+ command->addCommand(new MoveAcrossSegmentsCommand
+ (m_selectedStaff->getSegment(),
+ targetStaff->getSegment(),
+ dragTime - clickedTime + selection->getStartTime(),
+ true,
+ *selection));
+ haveSomething = true;
+ } else {
+ if (dragTime != clickedTime) {
+ mc = new MoveCommand
+ (m_selectedStaff->getSegment(), //!!!sort
+ dragTime - clickedTime, true, *selection);
+ command->addCommand(mc);
+ haveSomething = true;
+ }
+ }
+
+ if (haveSomething) {
+
+ m_nParentView->addCommandToHistory(command);
+
+ if (mc && singleNonNotePreview) {
+
+ lastInsertedEvent = mc->getLastInsertedEvent();
+
+ if (lastInsertedEvent) {
+ m_nParentView->setSingleSelectedEvent(targetStaff->getId(),
+ lastInsertedEvent);
+
+ ViewElementList::iterator vli =
+ targetStaff->findEvent(lastInsertedEvent);
+
+ if (vli != targetStaff->getViewElementList()->end()) {
+ m_clickedElement = dynamic_cast<NotationElement *>(*vli);
+ } else {
+ m_clickedElement = 0;
+ }
+
+ m_selectionRect->setX(x);
+ m_selectionRect->setY(y);
+ }
+ }
+ } else {
+ delete command;
+ }
+ }
+}
+
+void NotationSelector::dragFine(int x, int y, bool final)
+{
+ NOTATION_DEBUG << "NotationSelector::drag " << x << ", " << y << endl;
+
+ if (!m_clickedElement || !m_selectedStaff)
+ return ;
+
+ EventSelection *selection = m_nParentView->getCurrentSelection();
+ if (!selection)
+ selection = new EventSelection(m_selectedStaff->getSegment());
+ if (!selection->contains(m_clickedElement->event()))
+ selection->addEvent(m_clickedElement->event());
+ m_nParentView->setCurrentSelection(selection);
+
+ // Fine drag modifies the DISPLACED_X and DISPLACED_Y properties on
+ // each event. The modifications have to be relative to the previous
+ // values of these properties, not to zero, so for each event we need
+ // to store the previous value at the time the drag starts.
+
+ static PropertyName xProperty("temporary-displaced-x");
+ static PropertyName yProperty("temporary-displaced-y");
+
+ if (!m_startedFineDrag) {
+ // back up original properties
+
+ for (EventSelection::eventcontainer::iterator i =
+ selection->getSegmentEvents().begin();
+ i != selection->getSegmentEvents().end(); ++i) {
+ long prevX = 0, prevY = 0;
+ (*i)->get
+ <Int>(DISPLACED_X, prevX);
+ (*i)->get
+ <Int>(DISPLACED_Y, prevY);
+ (*i)->setMaybe<Int>(xProperty, prevX);
+ (*i)->setMaybe<Int>(yProperty, prevY);
+ }
+
+ m_startedFineDrag = true;
+ }
+
+ // We want the displacements in 1/1000ths of a staff space
+
+ double dx = x - m_selectionRect->x();
+ double dy = y - m_selectionRect->y();
+
+ double noteBodyWidth = m_nParentView->getNotePixmapFactory()->getNoteBodyWidth();
+ double lineSpacing = m_nParentView->getNotePixmapFactory()->getLineSpacing();
+ dx = (1000.0 * dx) / noteBodyWidth;
+ dy = (1000.0 * dy) / lineSpacing;
+
+ if (final) {
+
+ // reset original values (and remove backup values) before
+ // applying command
+
+ for (EventSelection::eventcontainer::iterator i =
+ selection->getSegmentEvents().begin();
+ i != selection->getSegmentEvents().end(); ++i) {
+ long prevX = 0, prevY = 0;
+ (*i)->get
+ <Int>(xProperty, prevX);
+ (*i)->get
+ <Int>(yProperty, prevY);
+ (*i)->setMaybe<Int>(DISPLACED_X, prevX);
+ (*i)->setMaybe<Int>(DISPLACED_Y, prevY);
+ (*i)->unset(xProperty);
+ (*i)->unset(yProperty);
+ }
+
+ IncrementDisplacementsCommand *command = new IncrementDisplacementsCommand
+ (*selection, long(dx), long(dy));
+ m_nParentView->addCommandToHistory(command);
+
+ } else {
+
+ timeT startTime = 0, endTime = 0;
+
+ for (EventSelection::eventcontainer::iterator i =
+ selection->getSegmentEvents().begin();
+ i != selection->getSegmentEvents().end(); ++i) {
+ long prevX = 0, prevY = 0;
+ (*i)->get
+ <Int>(xProperty, prevX);
+ (*i)->get
+ <Int>(yProperty, prevY);
+ (*i)->setMaybe<Int>(DISPLACED_X, prevX + long(dx));
+ (*i)->setMaybe<Int>(DISPLACED_Y, prevY + long(dy));
+ if (i == selection->getSegmentEvents().begin()) {
+ startTime = (*i)->getAbsoluteTime();
+ }
+ endTime = (*i)->getAbsoluteTime() + (*i)->getDuration();
+ }
+
+ if (startTime == endTime)
+ ++endTime;
+ selection->getSegment().updateRefreshStatuses(startTime, endTime);
+ m_nParentView->update();
+ }
+}
+
+void NotationSelector::ready()
+{
+ m_selectionRect = new QCanvasRectangle(m_nParentView->canvas());
+
+ m_selectionRect->hide();
+ m_selectionRect->setPen(GUIPalette::getColour(GUIPalette::SelectionRectangle));
+
+ m_nParentView->setCanvasCursor(Qt::arrowCursor);
+ m_nParentView->setHeightTracking(false);
+}
+
+void NotationSelector::stow()
+{
+ delete m_selectionRect;
+ m_selectionRect = 0;
+ m_nParentView->canvas()->update();
+}
+
+void NotationSelector::slotHideSelection()
+{
+ if (!m_selectionRect)
+ return ;
+ m_selectionRect->hide();
+ m_selectionRect->setSize(0, 0);
+ m_nParentView->canvas()->update();
+}
+
+void NotationSelector::slotInsertSelected()
+{
+ m_nParentView->slotLastNoteAction();
+}
+
+void NotationSelector::slotEraseSelected()
+{
+ m_parentView->actionCollection()->action("erase")->activate();
+}
+
+void NotationSelector::slotCollapseRestsHard()
+{
+ m_parentView->actionCollection()->action("collapse_rests_aggressively")->activate();
+}
+
+void NotationSelector::slotRespellFlat()
+{
+ m_parentView->actionCollection()->action("respell_flat")->activate();
+}
+
+void NotationSelector::slotRespellSharp()
+{
+ m_parentView->actionCollection()->action("respell_sharp")->activate();
+}
+
+void NotationSelector::slotRespellNatural()
+{
+ m_parentView->actionCollection()->action("respell_natural")->activate();
+}
+
+void NotationSelector::slotCollapseNotes()
+{
+ m_parentView->actionCollection()->action("collapse_notes")->activate();
+}
+
+void NotationSelector::slotInterpret()
+{
+ m_parentView->actionCollection()->action("interpret")->activate();
+}
+
+void NotationSelector::slotStaffAbove()
+{
+ m_parentView->actionCollection()->action("move_events_up_staff")->activate();
+}
+
+void NotationSelector::slotStaffBelow()
+{
+ m_parentView->actionCollection()->action("move_events_down_staff")->activate();
+}
+
+void NotationSelector::slotMakeInvisible()
+{
+ m_parentView->actionCollection()->action("make_invisible")->activate();
+}
+
+void NotationSelector::slotMakeVisible()
+{
+ m_parentView->actionCollection()->action("make_visible")->activate();
+}
+
+void NotationSelector::setViewCurrentSelection(bool preview)
+{
+ EventSelection *selection = getSelection();
+
+ if (m_selectionToMerge) {
+ if (selection &&
+ m_selectionToMerge->getSegment() == selection->getSegment()) {
+ selection->addFromSelection(m_selectionToMerge);
+ } else {
+ return ;
+ }
+ }
+
+ m_nParentView->setCurrentSelection(selection, preview, true);
+}
+
+NotationStaff *
+NotationSelector::getStaffForElement(NotationElement *elt)
+{
+ for (int i = 0; i < m_nParentView->getStaffCount(); ++i) {
+ NotationStaff *staff = m_nParentView->getNotationStaff(i);
+ if (staff->getSegment().findSingle(elt->event()) !=
+ staff->getSegment().end())
+ return staff;
+ }
+ return 0;
+}
+
+EventSelection* NotationSelector::getSelection()
+{
+ // If selection rect is not visible or too small,
+ // return 0
+ //
+ if (!m_selectionRect->visible()) return 0;
+
+ // NOTATION_DEBUG << "Selection x,y: " << m_selectionRect->x() << ","
+ // << m_selectionRect->y() << "; w,h: " << m_selectionRect->width() << "," << m_selectionRect->height() << endl;
+
+ if (m_selectionRect->width() > -3 &&
+ m_selectionRect->width() < 3 &&
+ m_selectionRect->height() > -3 &&
+ m_selectionRect->height() < 3) return 0;
+
+ QCanvasItemList itemList = m_selectionRect->collisions(false);
+ QCanvasItemList::Iterator it;
+
+ QRect rect = m_selectionRect->rect().normalize();
+ QCanvasNotationSprite *sprite = 0;
+
+ if (!m_selectedStaff) {
+
+ // Scan the list of collisions, looking for a valid notation
+ // element; if we find one, initialise m_selectedStaff from it.
+ // If we don't find one, we have no selection. This is a little
+ // inefficient but we only do it for the first event in the
+ // selection.
+
+ for (it = itemList.begin(); it != itemList.end(); ++it) {
+
+ if ((sprite = dynamic_cast<QCanvasNotationSprite*>(*it))) {
+
+ NotationElement &el = sprite->getNotationElement();
+
+ NotationStaff *staff = getStaffForElement(&el);
+ if (!staff) continue;
+
+ int x = (int)(*it)->x();
+ bool shifted = false;
+ int nbw = staff->getNotePixmapFactory(false).getNoteBodyWidth();
+
+
+ // #957364 (Notation: Hard to select upper note in
+ // chords of seconds) -- adjust x-coord for shifted
+ // note head
+ if (el.event()->get<Rosegarden::Bool>
+ (staff->getProperties().NOTE_HEAD_SHIFTED, shifted) && shifted) {
+ x += nbw;
+ }
+
+ if (!rect.contains(x, int((*it)->y()), true)) {
+ // #988217 (Notation: Special column of pixels
+ // prevents sweep selection) -- for notes, test
+ // again with centred x-coord
+ if (!el.isNote() || !rect.contains(x + nbw/2, int((*it)->y()), true)) {
+ continue;
+ }
+ }
+
+ m_selectedStaff = staff;
+ break;
+ }
+ }
+ }
+
+ if (!m_selectedStaff) return 0;
+ Segment& originalSegment = m_selectedStaff->getSegment();
+
+ // If we selected the whole staff, force that to happen explicitly
+ // rather than relying on collisions with the rectangle -- because
+ // events way off the currently visible area might not even have
+ // been drawn yet, and so will not appear in the collision list.
+ // (We did still need the collision list to determine which staff
+ // to use though.)
+
+ if (m_wholeStaffSelectionComplete) {
+ EventSelection *selection = new EventSelection(originalSegment,
+ originalSegment.getStartTime(),
+ originalSegment.getEndMarkerTime());
+ return selection;
+ }
+
+ EventSelection* selection = new EventSelection(originalSegment);
+
+ for (it = itemList.begin(); it != itemList.end(); ++it) {
+
+ if ((sprite = dynamic_cast<QCanvasNotationSprite*>(*it))) {
+
+ NotationElement &el = sprite->getNotationElement();
+
+ int x = (int)(*it)->x();
+ bool shifted = false;
+ int nbw = m_selectedStaff->getNotePixmapFactory(false).getNoteBodyWidth();
+
+ // #957364 (Notation: Hard to select upper note in chords
+ // of seconds) -- adjust x-coord for shifted note head
+ if (el.event()->get<Rosegarden::Bool>
+ (m_selectedStaff->getProperties().NOTE_HEAD_SHIFTED, shifted)
+ && shifted) {
+ x += nbw;
+ }
+
+ // check if the element's rect
+ // is actually included in the selection rect.
+ //
+ if (!rect.contains(x, int((*it)->y()), true)) {
+ // #988217 (Notation: Special column of pixels
+ // prevents sweep selection) -- for notes, test again
+ // with centred x-coord
+ if (!el.isNote() || !rect.contains(x + nbw/2, int((*it)->y()), true)) {
+ continue;
+ }
+ }
+
+ // must be in the same segment as we first started on,
+ // we can't select events across multiple segments
+ if (selection->getSegment().findSingle(el.event()) !=
+ selection->getSegment().end()) {
+ selection->addEvent(el.event());
+ }
+ }
+ }
+
+ if (selection->getAddedEvents() > 0) {
+ return selection;
+ } else {
+ delete selection;
+ return 0;
+ }
+}
+
+const QString NotationSelector::ToolName = "notationselector";
+
+}
+#include "NotationSelector.moc"
diff --git a/src/gui/editors/notation/NotationSelector.h b/src/gui/editors/notation/NotationSelector.h
new file mode 100644
index 0000000..7266fd5
--- /dev/null
+++ b/src/gui/editors/notation/NotationSelector.h
@@ -0,0 +1,197 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTATIONSELECTOR_H_
+#define _RG_NOTATIONSELECTOR_H_
+
+#include "NotationTool.h"
+#include "NotationElement.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+class QCanvasRectangle;
+class m_clickedElement;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class NotationView;
+class NotationStaff;
+class NotationElement;
+class EventSelection;
+class Event;
+
+
+/**
+ * Rectangular note selection
+ */
+class NotationSelector : public NotationTool
+{
+ Q_OBJECT
+
+ friend class NotationToolBox;
+
+public:
+
+ ~NotationSelector();
+
+ virtual void handleLeftButtonPress(timeT,
+ int height,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement* el);
+
+ virtual void handleRightButtonPress(timeT time,
+ int height,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement*);
+
+ virtual int handleMouseMove(timeT,
+ int height,
+ QMouseEvent*);
+
+ virtual void handleMouseRelease(timeT time,
+ int height,
+ QMouseEvent*);
+
+ virtual void handleMouseDoubleClick(timeT,
+ int height,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement*);
+
+ virtual void handleMouseTripleClick(timeT,
+ int height,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement*);
+
+ /**
+ * Create the selection rect
+ *
+ * We need this because NotationView deletes all QCanvasItems
+ * along with it. This happens before the NotationSelector is
+ * deleted, so we can't delete the selection rect in
+ * ~NotationSelector because that leads to double deletion.
+ */
+ virtual void ready();
+
+ /**
+ * Delete the selection rect.
+ */
+ virtual void stow();
+
+ /**
+ * Returns the currently selected events
+ *
+ * The returned result is owned by the caller
+ */
+ EventSelection* getSelection();
+
+ /**
+ * Respond to an event being deleted -- it may be the one the tool
+ * is remembering as the current event.
+ */
+ virtual void handleEventRemoved(Event *event) {
+ if (m_clickedElement && m_clickedElement->event() == event) {
+ m_clickedElement = 0;
+ }
+ }
+
+ static const QString ToolName;
+
+signals:
+ void editElement(NotationStaff *, NotationElement *, bool advanced);
+
+public slots:
+ /**
+ * Hide the selection rectangle
+ *
+ * Should be called after a cut or a copy has been
+ * performed
+ */
+ void slotHideSelection();
+
+ void slotInsertSelected();
+ void slotEraseSelected();
+// void slotCollapseRests();
+ void slotCollapseRestsHard();
+ void slotRespellFlat();
+ void slotRespellSharp();
+ void slotRespellNatural();
+ void slotCollapseNotes();
+ void slotInterpret();
+ void slotStaffAbove();
+ void slotStaffBelow();
+ void slotMakeInvisible();
+ void slotMakeVisible();
+
+ void slotClickTimeout();
+
+protected:
+ NotationSelector(NotationView*);
+
+ /**
+ * Set the current selection on the parent NotationView
+ */
+ void setViewCurrentSelection(bool preview);
+
+ /**
+ * Look up the staff containing the given notation element
+ */
+ NotationStaff *getStaffForElement(NotationElement *elt);
+
+ void drag(int x, int y, bool final);
+ void dragFine(int x, int y, bool final);
+
+ //--------------- Data members ---------------------------------
+
+ QCanvasRectangle* m_selectionRect;
+ bool m_updateRect;
+
+ NotationStaff *m_selectedStaff;
+ NotationElement *m_clickedElement;
+ bool m_clickedShift;
+ bool m_startedFineDrag;
+
+ EventSelection *m_selectionToMerge;
+
+ long m_lastDragPitch;
+ timeT m_lastDragTime;
+
+ bool m_justSelectedBar;
+ bool m_wholeStaffSelectionComplete;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationStaff.cpp b/src/gui/editors/notation/NotationStaff.cpp
new file mode 100644
index 0000000..c5219b4
--- /dev/null
+++ b/src/gui/editors/notation/NotationStaff.cpp
@@ -0,0 +1,2300 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NotationStaff.h"
+#include "misc/Debug.h"
+#include <kapplication.h>
+
+#include <klocale.h>
+#include "misc/Strings.h"
+#include "document/ConfigGroups.h"
+#include "base/Composition.h"
+#include "base/Device.h"
+#include "base/Event.h"
+#include "base/Exception.h"
+#include "base/Instrument.h"
+#include "base/MidiDevice.h"
+#include "base/MidiTypes.h"
+#include "base/NotationQuantizer.h"
+#include "base/NotationRules.h"
+#include "base/NotationTypes.h"
+#include "base/Profiler.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "base/SnapGrid.h"
+#include "base/Staff.h"
+#include "base/Studio.h"
+#include "base/Track.h"
+#include "base/ViewElement.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/editors/guitar/Chord.h"
+#include "gui/general/LinedStaff.h"
+#include "gui/general/PixmapFunctions.h"
+#include "gui/general/ProgressReporter.h"
+#include "gui/kdeext/QCanvasSimpleSprite.h"
+#include "NotationChord.h"
+#include "NotationElement.h"
+#include "NotationProperties.h"
+#include "NotationView.h"
+#include "NoteFontFactory.h"
+#include "NotePixmapFactory.h"
+#include "NotePixmapParameters.h"
+#include "NoteStyleFactory.h"
+#include <kconfig.h>
+#include <kmessagebox.h>
+#include <qcanvas.h>
+#include <qpainter.h>
+#include <qpoint.h>
+#include <qrect.h>
+
+
+namespace Rosegarden
+{
+
+NotationStaff::NotationStaff(QCanvas *canvas, Segment *segment,
+ SnapGrid *snapGrid, int id,
+ NotationView *view,
+ std::string fontName, int resolution) :
+ ProgressReporter(0),
+ LinedStaff(canvas, segment, snapGrid, id, resolution,
+ resolution / 16 + 1, // line thickness
+ LinearMode, 0, 0, // pageMode, pageWidth and pageHeight set later
+ 0 // row spacing
+ ),
+ m_notePixmapFactory(0),
+ m_graceNotePixmapFactory(0),
+ m_previewSprite(0),
+ m_staffName(0),
+ m_notationView(view),
+ m_legerLineCount(8),
+ m_barNumbersEvery(0),
+ m_colourQuantize(true),
+ m_showUnknowns(true),
+ m_showRanges(true),
+ m_showCollisions(true),
+ m_printPainter(0),
+ m_ready(false),
+ m_lastRenderedBar(0)
+{
+ KConfig *config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+ m_colourQuantize = config->readBoolEntry("colourquantize", false);
+
+ // Shouldn't change these during the lifetime of the staff, really:
+ m_showUnknowns = config->readBoolEntry("showunknowns", false);
+ m_showRanges = config->readBoolEntry("showranges", true);
+ m_showCollisions = config->readBoolEntry("showcollisions", true);
+
+ m_keySigCancelMode = config->readNumEntry("keysigcancelmode", 1);
+
+ changeFont(fontName, resolution);
+}
+
+NotationStaff::~NotationStaff()
+{
+ deleteTimeSignatures();
+ delete m_notePixmapFactory;
+ delete m_graceNotePixmapFactory;
+}
+
+void
+NotationStaff::changeFont(std::string fontName, int size)
+{
+ setResolution(size);
+
+ delete m_notePixmapFactory;
+ m_notePixmapFactory = new NotePixmapFactory(fontName, size);
+
+ std::vector<int> sizes = NoteFontFactory::getScreenSizes(fontName);
+ int graceSize = size;
+ for (unsigned int i = 0; i < sizes.size(); ++i) {
+ if (sizes[i] == size || sizes[i] > size*3 / 4)
+ break;
+ graceSize = sizes[i];
+ }
+ delete m_graceNotePixmapFactory;
+ m_graceNotePixmapFactory = new NotePixmapFactory(fontName, graceSize);
+
+ setLineThickness(m_notePixmapFactory->getStaffLineThickness());
+}
+
+void
+NotationStaff::insertTimeSignature(double layoutX,
+ const TimeSignature &timeSig)
+{
+ if (timeSig.isHidden())
+ return ;
+
+ m_notePixmapFactory->setSelected(false);
+ QCanvasPixmap *pixmap = m_notePixmapFactory->makeTimeSigPixmap(timeSig);
+ QCanvasTimeSigSprite *sprite =
+ new QCanvasTimeSigSprite(layoutX, pixmap, m_canvas);
+
+ LinedStaffCoords sigCoords =
+ getCanvasCoordsForLayoutCoords(layoutX, getLayoutYForHeight(4));
+
+ sprite->move(sigCoords.first, (double)sigCoords.second);
+ sprite->show();
+ m_timeSigs.insert(sprite);
+}
+
+void
+NotationStaff::deleteTimeSignatures()
+{
+ // NOTATION_DEBUG << "NotationStaff::deleteTimeSignatures()" << endl;
+
+ for (SpriteSet::iterator i = m_timeSigs.begin();
+ i != m_timeSigs.end(); ++i) {
+ delete *i;
+ }
+
+ m_timeSigs.clear();
+}
+
+void
+NotationStaff::insertRepeatedClefAndKey(double layoutX, int barNo)
+{
+ bool needClef = false, needKey = false;
+ timeT t;
+
+ timeT barStart = getSegment().getComposition()->getBarStart(barNo);
+
+ Clef clef = getSegment().getClefAtTime(barStart, t);
+ if (t < barStart)
+ needClef = true;
+
+ ::Rosegarden::Key key = getSegment().getKeyAtTime(barStart, t);
+ if (t < barStart)
+ needKey = true;
+
+ double dx = m_notePixmapFactory->getBarMargin() / 2;
+
+ if (!m_notationView->isInPrintMode())
+ m_notePixmapFactory->setShaded(true);
+
+ if (needClef) {
+
+ int layoutY = getLayoutYForHeight(clef.getAxisHeight());
+
+ LinedStaffCoords coords =
+ getCanvasCoordsForLayoutCoords(layoutX + dx, layoutY);
+
+ QCanvasPixmap *pixmap = m_notePixmapFactory->makeClefPixmap(clef);
+
+ QCanvasNonElementSprite *sprite =
+ new QCanvasNonElementSprite(pixmap, m_canvas);
+
+ sprite->move(coords.first, coords.second);
+ sprite->show();
+ m_repeatedClefsAndKeys.insert(sprite);
+
+ dx += pixmap->width() + m_notePixmapFactory->getNoteBodyWidth() * 2 / 3;
+ }
+
+ if (needKey) {
+
+ int layoutY = getLayoutYForHeight(12);
+
+ LinedStaffCoords coords =
+ getCanvasCoordsForLayoutCoords(layoutX + dx, layoutY);
+
+ QCanvasPixmap *pixmap = m_notePixmapFactory->makeKeyPixmap(key, clef);
+
+ QCanvasNonElementSprite *sprite =
+ new QCanvasNonElementSprite(pixmap, m_canvas);
+
+ sprite->move(coords.first, coords.second);
+ sprite->show();
+ m_repeatedClefsAndKeys.insert(sprite);
+
+ dx += pixmap->width();
+ }
+
+ /* attempt to blot out things like slurs & ties that overrun this area: doesn't work
+
+ if (m_notationView->isInPrintMode() && (needClef || needKey)) {
+
+ int layoutY = getLayoutYForHeight(14);
+ int h = getLayoutYForHeight(-8) - layoutY;
+
+ LinedStaffCoords coords =
+ getCanvasCoordsForLayoutCoords(layoutX, layoutY);
+
+ QCanvasRectangle *rect = new QCanvasRectangle(coords.first, coords.second,
+ dx, h, m_canvas);
+ rect->setPen(Qt::black);
+ rect->setBrush(Qt::white);
+ rect->setZ(1);
+ rect->show();
+
+ m_repeatedClefsAndKeys.insert(rect);
+ }
+ */
+
+ m_notePixmapFactory->setShaded(false);
+}
+
+void
+NotationStaff::deleteRepeatedClefsAndKeys()
+{
+ for (ItemSet::iterator i = m_repeatedClefsAndKeys.begin();
+ i != m_repeatedClefsAndKeys.end(); ++i) {
+ delete *i;
+ }
+
+ m_repeatedClefsAndKeys.clear();
+}
+
+void
+NotationStaff::drawStaffName()
+{
+ delete m_staffName;
+
+ m_staffNameText =
+ getSegment().getComposition()->
+ getTrackById(getSegment().getTrack())->getLabel();
+
+ QCanvasPixmap *map =
+ m_notePixmapFactory->makeTextPixmap
+ (Text(m_staffNameText, Text::StaffName));
+
+ m_staffName = new QCanvasStaffNameSprite(map, m_canvas);
+
+ int layoutY = getLayoutYForHeight(3);
+ LinedStaffCoords coords = getCanvasCoordsForLayoutCoords(0, layoutY);
+ m_staffName->move(getX() + getMargin() + m_notePixmapFactory->getNoteBodyWidth(),
+ coords.second - map->height() / 2);
+ m_staffName->show();
+}
+
+bool
+NotationStaff::isStaffNameUpToDate()
+{
+ return (m_staffNameText ==
+ getSegment().getComposition()->
+ getTrackById(getSegment().getTrack())->getLabel());
+}
+
+timeT
+NotationStaff::getTimeAtCanvasCoords(double cx, int cy) const
+{
+ LinedStaffCoords layoutCoords = getLayoutCoordsForCanvasCoords(cx, cy);
+ RulerScale * rs = m_notationView->getHLayout();
+ return rs->getTimeForX(layoutCoords.first);
+}
+
+void
+NotationStaff::getClefAndKeyAtCanvasCoords(double cx, int cy,
+ Clef &clef,
+ ::Rosegarden::Key &key) const
+{
+ LinedStaffCoords layoutCoords = getLayoutCoordsForCanvasCoords(cx, cy);
+ int i;
+
+ for (i = 0; i < m_clefChanges.size(); ++i) {
+ if (m_clefChanges[i].first > layoutCoords.first)
+ break;
+ clef = m_clefChanges[i].second;
+ }
+
+ for (i = 0; i < m_keyChanges.size(); ++i) {
+ if (m_keyChanges[i].first > layoutCoords.first)
+ break;
+ key = m_keyChanges[i].second;
+ }
+}
+
+ViewElementList::iterator
+NotationStaff::getClosestElementToLayoutX(double x,
+ Event *&clef,
+ Event *&key,
+ bool notesAndRestsOnly,
+ int proximityThreshold)
+{
+ START_TIMING;
+
+ double minDist = 10e9, prevDist = 10e9;
+
+ NotationElementList *notes = getViewElementList();
+ NotationElementList::iterator it, result;
+
+ // TODO: this is grossly inefficient
+
+ for (it = notes->begin(); it != notes->end(); ++it) {
+ NotationElement *el = static_cast<NotationElement*>(*it);
+
+ bool before = ((*it)->getLayoutX() < x);
+
+ if (!el->isNote() && !el->isRest()) {
+ if (before) {
+ if ((*it)->event()->isa(Clef::EventType)) {
+ clef = (*it)->event();
+ } else if ((*it)->event()->isa(::Rosegarden::Key::EventType)) {
+ key = (*it)->event();
+ }
+ }
+ if (notesAndRestsOnly)
+ continue;
+ }
+
+ double dx = x - (*it)->getLayoutX();
+ if (dx < 0)
+ dx = -dx;
+
+ if (dx < minDist) {
+ minDist = dx;
+ result = it;
+ } else if (!before) {
+ break;
+ }
+
+ prevDist = dx;
+ }
+
+ if (proximityThreshold > 0 && minDist > proximityThreshold) {
+ NOTATION_DEBUG << "NotationStaff::getClosestElementToLayoutX() : element is too far away : "
+ << minDist << endl;
+ return notes->end();
+ }
+
+ NOTATION_DEBUG << "NotationStaff::getClosestElementToLayoutX: found element at layout " << (*result)->getLayoutX() << " - we're at layout " << x << endl;
+
+ PRINT_ELAPSED("NotationStaff::getClosestElementToLayoutX");
+
+ return result;
+}
+
+ViewElementList::iterator
+NotationStaff::getElementUnderLayoutX(double x,
+ Event *&clef,
+ Event *&key)
+{
+ NotationElementList *notes = getViewElementList();
+ NotationElementList::iterator it;
+
+ // TODO: this is grossly inefficient
+
+ for (it = notes->begin(); it != notes->end(); ++it) {
+ NotationElement* el = static_cast<NotationElement*>(*it);
+
+ bool before = ((*it)->getLayoutX() <= x);
+
+ if (!el->isNote() && !el->isRest()) {
+ if (before) {
+ if ((*it)->event()->isa(Clef::EventType)) {
+ clef = (*it)->event();
+ } else if ((*it)->event()->isa(::Rosegarden::Key::EventType)) {
+ key = (*it)->event();
+ }
+ }
+ }
+
+ double airX, airWidth;
+ el->getLayoutAirspace(airX, airWidth);
+ if (x >= airX && x < airX + airWidth) {
+ return it;
+ } else if (!before) {
+ if (it != notes->begin())
+ --it;
+ return it;
+ }
+ }
+
+ return notes->end();
+}
+
+std::string
+NotationStaff::getNoteNameAtCanvasCoords(double x, int y,
+ Accidental) const
+{
+ Clef clef;
+ ::Rosegarden::Key key;
+ getClefAndKeyAtCanvasCoords(x, y, clef, key);
+
+ KConfig *config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+ int baseOctave = config->readNumEntry("midipitchoctave", -2);
+
+ Pitch p(getHeightAtCanvasCoords(x, y), clef, key);
+ //!!! i18n() how?
+ return p.getAsString(key.isSharp(), true, baseOctave);
+}
+
+void
+NotationStaff::renderElements(NotationElementList::iterator from,
+ NotationElementList::iterator to)
+{
+ // NOTATION_DEBUG << "NotationStaff " << this << "::renderElements()" << endl;
+ Profiler profiler("NotationStaff::renderElements");
+
+ emit setOperationName(i18n("Rendering staff %1...").arg(getId() + 1));
+ emit setProgress(0);
+
+ throwIfCancelled();
+
+ // These are only used when rendering keys, and we don't have the
+ // right start data here so we choose not to render keys at all in
+ // this method (see below) so that we can pass bogus clef and key
+ // data to renderSingleElement
+ Clef currentClef;
+ ::Rosegarden::Key currentKey;
+
+ int elementCount = 0;
+ timeT endTime =
+ (to != getViewElementList()->end() ? (*to)->getViewAbsoluteTime() :
+ getSegment().getEndMarkerTime());
+ timeT startTime = (from != to ? (*from)->getViewAbsoluteTime() : endTime);
+
+ for (NotationElementList::iterator it = from, nextIt = from;
+ it != to; it = nextIt) {
+
+ ++nextIt;
+
+ if (isDirectlyPrintable(*it)) {
+ // notes are renderable direct to the printer, so don't render
+ // them to the canvas here
+ continue;
+ }
+
+ if ((*it)->event()->isa(::Rosegarden::Key::EventType)) {
+ // force rendering in positionElements instead
+ NotationElement* el = static_cast<NotationElement*>(*it);
+ el->removeCanvasItem();
+ continue;
+ }
+
+ bool selected = isSelected(it);
+ // NOTATION_DEBUG << "Rendering at " << (*it)->getAbsoluteTime()
+ // << " (selected = " << selected << ")" << endl;
+
+ renderSingleElement(it, currentClef, currentKey, selected);
+
+ if ((endTime > startTime) &&
+ (++elementCount % 200 == 0)) {
+
+ timeT myTime = (*it)->getViewAbsoluteTime();
+ emit setProgress((myTime - startTime) * 100 / (endTime - startTime));
+ throwIfCancelled();
+ }
+ }
+
+ // NOTATION_DEBUG << "NotationStaff " << this << "::renderElements: "
+ // << elementCount << " elements rendered" << endl;
+}
+
+void
+NotationStaff::renderPrintable(timeT from, timeT to)
+{
+ if (!m_printPainter)
+ return ;
+
+ Profiler profiler("NotationStaff::renderElements");
+
+ emit setOperationName(i18n("Rendering notes on staff %1...").arg(getId() + 1));
+ emit setProgress(0);
+
+ throwIfCancelled();
+
+ // These are only used when rendering keys, and we don't do that
+ // here, so we don't care what they are
+ Clef currentClef;
+ ::Rosegarden::Key currentKey;
+
+ Composition *composition = getSegment().getComposition();
+ NotationElementList::iterator beginAt =
+ getViewElementList()->findTime(composition->getBarStartForTime(from));
+ NotationElementList::iterator endAt =
+ getViewElementList()->findTime(composition->getBarEndForTime(to));
+
+ int elementCount = 0;
+
+ for (NotationElementList::iterator it = beginAt, nextIt = beginAt;
+ it != endAt; it = nextIt) {
+
+ ++nextIt;
+
+ if (!isDirectlyPrintable(*it)) {
+ continue;
+ }
+
+ bool selected = isSelected(it);
+ // NOTATION_DEBUG << "Rendering at " << (*it)->getAbsoluteTime()
+ // << " (selected = " << selected << ")" << endl;
+
+ renderSingleElement(it, currentClef, currentKey, selected);
+
+ if ((to > from) && (++elementCount % 200 == 0)) {
+
+ timeT myTime = (*it)->getViewAbsoluteTime();
+ emit setProgress((myTime - from) * 100 / (to - from));
+ throwIfCancelled();
+ }
+ }
+
+ // NOTATION_DEBUG << "NotationStaff " << this << "::renderElements: "
+ // << elementCount << " elements rendered" << endl;
+}
+
+const NotationProperties &
+NotationStaff::getProperties() const
+{
+ return m_notationView->getProperties();
+}
+
+void
+NotationStaff::positionElements(timeT from, timeT to)
+{
+ // NOTATION_DEBUG << "NotationStaff " << this << "::positionElements()"
+ // << from << " -> " << to << endl;
+ Profiler profiler("NotationStaff::positionElements");
+
+ // Following 4 lines are a workaround to not have m_clefChanges and
+ // m_keyChanges truncated when positionElements() is called with
+ // args outside current segment.
+ // Maybe a better fix would be not to call positionElements() with
+ // such args ...
+ int startTime = getSegment().getStartTime();
+ if (from < startTime) from = startTime;
+ if (to < startTime) to = startTime;
+ if (to == from) return;
+
+ emit setOperationName(i18n("Positioning staff %1...").arg(getId() + 1));
+ emit setProgress(0);
+ throwIfCancelled();
+
+ const NotationProperties &properties(getProperties());
+
+ int elementsPositioned = 0;
+ int elementsRendered = 0; // diagnostic
+
+ Composition *composition = getSegment().getComposition();
+
+ timeT nextBarTime = composition->getBarEndForTime(to);
+
+ NotationElementList::iterator beginAt =
+ getViewElementList()->findTime(composition->getBarStartForTime(from));
+
+ NotationElementList::iterator endAt =
+ getViewElementList()->findTime(composition->getBarEndForTime(to));
+
+ if (beginAt == getViewElementList()->end())
+ return ;
+
+ truncateClefsAndKeysAt(static_cast<int>((*beginAt)->getLayoutX()));
+
+ Clef currentClef; // used for rendering key sigs
+ bool haveCurrentClef = false;
+
+ ::Rosegarden::Key currentKey;
+ bool haveCurrentKey = false;
+
+ for (NotationElementList::iterator it = beginAt, nextIt = beginAt;
+ it != endAt; it = nextIt) {
+
+ NotationElement * el = static_cast<NotationElement*>(*it);
+
+ ++nextIt;
+
+ if (el->event()->isa(Clef::EventType)) {
+
+ currentClef = Clef(*el->event());
+ m_clefChanges.push_back(ClefChange(int(el->getLayoutX()),
+ currentClef));
+ haveCurrentClef = true;
+
+ } else if (el->event()->isa(::Rosegarden::Key::EventType)) {
+
+ m_keyChanges.push_back
+ (KeyChange(int(el->getLayoutX()),
+ ::Rosegarden::Key(*el->event())));
+
+ if (!haveCurrentClef) { // need this to know how to present the key
+ currentClef = getSegment().getClefAtTime
+ (el->event()->getAbsoluteTime());
+ haveCurrentClef = true;
+ }
+
+ if (!haveCurrentKey) { // stores the key _before_ this one
+ currentKey = getSegment().getKeyAtTime
+ (el->event()->getAbsoluteTime() - 1);
+ haveCurrentKey = true;
+ }
+
+ } else if (isDirectlyPrintable(el)) {
+ // these are rendered by renderPrintable for printing
+ continue;
+ }
+
+ bool selected = isSelected(it);
+ bool needNewSprite = (selected != el->isSelected());
+
+ if (!el->getCanvasItem()) {
+
+ needNewSprite = true;
+
+ } else if (el->isNote() && !el->isRecentlyRegenerated()) {
+
+ // If the note's y-coordinate has changed, we should
+ // redraw it -- its stem direction may have changed, or it
+ // may need leger lines. This will happen e.g. if the
+ // user inserts a new clef; unfortunately this means
+ // inserting clefs is rather slow.
+
+ needNewSprite = needNewSprite || !elementNotMovedInY(el);
+
+ if (!needNewSprite) {
+
+ // If the event is a beamed or tied-forward note, then
+ // we might need a new sprite if the distance from
+ // this note to the next has changed (because the beam
+ // or tie is part of the note's sprite).
+
+ bool spanning = false;
+ (void)(el->event()->get
+ <Bool>
+ (properties.BEAMED, spanning));
+ if (!spanning) {
+ (void)(el->event()->get
+ <Bool>(BaseProperties::TIED_FORWARD, spanning));
+ }
+
+ if (spanning) {
+ needNewSprite =
+ (el->getViewAbsoluteTime() < nextBarTime ||
+ !elementShiftedOnly(it));
+ }
+ }
+
+ } else if (el->event()->isa(Indication::EventType) &&
+ !el->isRecentlyRegenerated()) {
+ needNewSprite = true;
+ }
+
+ if (needNewSprite) {
+ renderSingleElement(it, currentClef, currentKey, selected);
+ ++elementsRendered;
+ }
+
+ if (el->event()->isa(::Rosegarden::Key::EventType)) {
+ // update currentKey after rendering, not before
+ currentKey = ::Rosegarden::Key(*el->event());
+ }
+
+ if (!needNewSprite) {
+ LinedStaffCoords coords = getCanvasCoordsForLayoutCoords
+ (el->getLayoutX(), (int)el->getLayoutY());
+ el->reposition(coords.first, (double)coords.second);
+ }
+
+ el->setSelected(selected);
+
+ if ((to > from) &&
+ (++elementsPositioned % 300 == 0)) {
+ timeT myTime = el->getViewAbsoluteTime();
+ emit setProgress((myTime - from) * 100 / (to - from));
+ throwIfCancelled();
+ }
+ }
+
+ // NOTATION_DEBUG << "NotationStaff " << this << "::positionElements "
+ // << from << " -> " << to << ": "
+ // << elementsPositioned << " elements positioned, "
+ // << elementsRendered << " re-rendered"
+ // << endl;
+
+ // NotePixmapFactory::dumpStats(std::cerr);
+}
+
+void
+NotationStaff::truncateClefsAndKeysAt(int x)
+{
+ for (FastVector<ClefChange>::iterator i = m_clefChanges.begin();
+ i != m_clefChanges.end(); ++i) {
+ if (i->first >= x) {
+ m_clefChanges.erase(i, m_clefChanges.end());
+ break;
+ }
+ }
+
+ for (FastVector<KeyChange>::iterator i = m_keyChanges.begin();
+ i != m_keyChanges.end(); ++i) {
+ if (i->first >= x) {
+ m_keyChanges.erase(i, m_keyChanges.end());
+ break;
+ }
+ }
+}
+
+NotationElementList::iterator
+NotationStaff::findUnchangedBarStart(timeT from)
+{
+ NotationElementList *nel = (NotationElementList *)getViewElementList();
+
+ // Track back bar-by-bar until we find one whose start position
+ // hasn't changed
+
+ NotationElementList::iterator beginAt = nel->begin();
+ do {
+ from = getSegment().getComposition()->getBarStartForTime(from - 1);
+ beginAt = nel->findTime(from);
+ } while (beginAt != nel->begin() &&
+ (beginAt == nel->end() || !elementNotMoved(static_cast<NotationElement*>(*beginAt))));
+
+ return beginAt;
+}
+
+NotationElementList::iterator
+NotationStaff::findUnchangedBarEnd(timeT to)
+{
+ NotationElementList *nel = (NotationElementList *)getViewElementList();
+
+ // Track forward to the end, similarly. Here however it's very
+ // common for all the positions to have changed right up to the
+ // end of the piece; so we save time by assuming that to be the
+ // case if we get more than (arbitrary) 3 changed bars.
+
+ // We also record the start of the bar following the changed
+ // section, for later use.
+
+ NotationElementList::iterator endAt = nel->end();
+
+ int changedBarCount = 0;
+ NotationElementList::iterator candidate = nel->end();
+ do {
+ candidate = nel->findTime(getSegment().getBarEndForTime(to));
+ if (candidate != nel->end()) {
+ to = (*candidate)->getViewAbsoluteTime();
+ }
+ ++changedBarCount;
+ } while (changedBarCount < 4 &&
+ candidate != nel->end() &&
+ !elementNotMoved(static_cast<NotationElement*>(*candidate)));
+
+ if (changedBarCount < 4)
+ return candidate;
+ else
+ return endAt;
+}
+
+bool
+NotationStaff::elementNotMoved(NotationElement *elt)
+{
+ if (!elt->getCanvasItem())
+ return false;
+
+ LinedStaffCoords coords = getCanvasCoordsForLayoutCoords
+ (elt->getLayoutX(), (int)elt->getLayoutY());
+
+ bool ok =
+ (int)(elt->getCanvasX()) == (int)(coords.first) &&
+ (int)(elt->getCanvasY()) == (int)(coords.second);
+
+ if (!ok) {
+ NOTATION_DEBUG
+ << "elementNotMoved: elt at " << elt->getViewAbsoluteTime() <<
+ ", ok is " << ok << endl;
+ NOTATION_DEBUG << "(cf " << (int)(elt->getCanvasX()) << " vs "
+ << (int)(coords.first) << ", "
+ << (int)(elt->getCanvasY()) << " vs "
+ << (int)(coords.second) << ")" << endl;
+ } else {
+ NOTATION_DEBUG << "elementNotMoved: elt at " << elt->getViewAbsoluteTime()
+ << " is ok" << endl;
+ }
+
+ return ok;
+}
+
+bool
+NotationStaff::elementNotMovedInY(NotationElement *elt)
+{
+ if (!elt->getCanvasItem())
+ return false;
+
+ LinedStaffCoords coords = getCanvasCoordsForLayoutCoords
+ (elt->getLayoutX(), (int)elt->getLayoutY());
+
+ bool ok = (int)(elt->getCanvasY()) == (int)(coords.second);
+
+ // if (!ok) {
+ // NOTATION_DEBUG
+ // << "elementNotMovedInY: elt at " << elt->getAbsoluteTime() <<
+ // ", ok is " << ok << endl;
+ // NOTATION_DEBUG << "(cf " << (int)(elt->getCanvasY()) << " vs "
+ // << (int)(coords.second) << ")" << std::endl;
+ // }
+ return ok;
+}
+
+bool
+NotationStaff::elementShiftedOnly(NotationElementList::iterator i)
+{
+ int shift = 0;
+ bool ok = false;
+
+ for (NotationElementList::iterator j = i;
+ j != getViewElementList()->end(); ++j) {
+
+ NotationElement *elt = static_cast<NotationElement*>(*j);
+ if (!elt->getCanvasItem())
+ break;
+
+ LinedStaffCoords coords = getCanvasCoordsForLayoutCoords
+ (elt->getLayoutX(), (int)elt->getLayoutY());
+
+ // regard any shift in y as suspicious
+ if ((int)(elt->getCanvasY()) != (int)(coords.second))
+ break;
+
+ int myShift = (int)(elt->getCanvasX()) - (int)(coords.first);
+ if (j == i)
+ shift = myShift;
+ else if (myShift != shift)
+ break;
+
+ if (elt->getViewAbsoluteTime() > (*i)->getViewAbsoluteTime()) {
+ // all events up to and including this one have passed
+ ok = true;
+ break;
+ }
+ }
+
+ if (!ok) {
+ NOTATION_DEBUG
+ << "elementShiftedOnly: elt at " << (*i)->getViewAbsoluteTime()
+ << ", ok is " << ok << endl;
+ }
+
+ return ok;
+}
+
+bool
+NotationStaff::isDirectlyPrintable(ViewElement *velt)
+{
+ if (!m_printPainter)
+ return false;
+ return (velt->event()->isa(Note::EventType) ||
+ velt->event()->isa(Note::EventRestType) ||
+ velt->event()->isa(Text::EventType) ||
+ velt->event()->isa(Indication::EventType));
+}
+
+void
+NotationStaff::renderSingleElement(ViewElementList::iterator &vli,
+ const Clef &currentClef,
+ const ::Rosegarden::Key &currentKey,
+ bool selected)
+{
+ const NotationProperties &properties(getProperties());
+ static NotePixmapParameters restParams(Note::Crotchet, 0);
+
+ NotationElement* elt = static_cast<NotationElement*>(*vli);
+
+ bool invisible = false;
+ if (elt->event()->get
+ <Bool>(BaseProperties::INVISIBLE, invisible) && invisible) {
+ if (m_printPainter)
+ return ;
+ KConfig *config = kapp->config();
+ config->setGroup("Notation Options");
+ bool showInvisibles = config->readBoolEntry("showinvisibles", true);
+ if (!showInvisibles)
+ return ;
+ }
+
+ try {
+ m_notePixmapFactory->setNoteStyle
+ (NoteStyleFactory::getStyleForEvent(elt->event()));
+
+ } catch (NoteStyleFactory::StyleUnavailable u) {
+
+ std::cerr << "WARNING: Note style unavailable: "
+ << u.getMessage() << std::endl;
+
+ static bool warned = false;
+ if (!warned) {
+ KMessageBox::error(0, i18n(strtoqstr(u.getMessage())));
+ warned = true;
+ }
+ }
+
+ try {
+
+ QCanvasPixmap *pixmap = 0;
+
+ m_notePixmapFactory->setSelected(selected);
+ m_notePixmapFactory->setShaded(invisible);
+ int z = selected ? 3 : 0;
+
+ // these are actually only used for the printer stuff
+ LinedStaffCoords coords;
+ if (m_printPainter)
+ coords = getCanvasCoordsForLayoutCoords
+ (elt->getLayoutX(), (int)elt->getLayoutY());
+
+ FitPolicy policy = PretendItFittedAllAlong;
+
+ if (elt->isNote()) {
+
+ renderNote(vli);
+
+ } else if (elt->isRest()) {
+
+ bool ignoreRest = false;
+ // NotationHLayout sets this property if it finds the rest
+ // in the middle of a chord -- Quantizer still sometimes gets
+ // this wrong
+ elt->event()->get
+ <Bool>(properties.REST_TOO_SHORT, ignoreRest);
+
+ if (!ignoreRest) {
+
+ Note::Type note = elt->event()->get
+ <Int>(BaseProperties::NOTE_TYPE);
+ int dots = elt->event()->get
+ <Int>(BaseProperties::NOTE_DOTS);
+ restParams.setNoteType(note);
+ restParams.setDots(dots);
+ setTuplingParameters(elt, restParams);
+ restParams.setQuantized(false);
+ bool restOutside = false;
+ elt->event()->get
+ <Bool>(properties.REST_OUTSIDE_STAVE,
+ restOutside);
+ restParams.setRestOutside(restOutside);
+ if (restOutside) {
+ NOTATION_DEBUG << "NotationStaff::renderSingleElement() : rest outside staff" << endl;
+ if (note == Note::DoubleWholeNote) {
+ NOTATION_DEBUG << "NotationStaff::renderSingleElement() : breve rest needs leger lines" << endl;
+ restParams.setLegerLines(5);
+ }
+ }
+
+ if (m_printPainter) {
+ m_notePixmapFactory->drawRest
+ (restParams,
+ *m_printPainter, int(coords.first), coords.second);
+ } else {
+ pixmap = m_notePixmapFactory->makeRestPixmap(restParams);
+ }
+ }
+
+ } else if (elt->event()->isa(Clef::EventType)) {
+
+ pixmap = m_notePixmapFactory->makeClefPixmap
+ (Clef(*elt->event()));
+
+ } else if (elt->event()->isa(::Rosegarden::Key::EventType)) {
+
+ ::Rosegarden::Key key(*elt->event());
+ ::Rosegarden::Key cancelKey = currentKey;
+
+ if (m_keySigCancelMode == 0) { // only when entering C maj / A min
+
+ if (key.getAccidentalCount() != 0)
+ cancelKey = ::Rosegarden::Key();
+
+ } else if (m_keySigCancelMode == 1) { // only when reducing acc count
+
+ if (!(key.isSharp() == cancelKey.isSharp() &&
+ key.getAccidentalCount() < cancelKey.getAccidentalCount())) {
+ cancelKey = ::Rosegarden::Key();
+ }
+ }
+
+ pixmap = m_notePixmapFactory->makeKeyPixmap
+ (key, currentClef, cancelKey);
+
+ } else if (elt->event()->isa(Text::EventType)) {
+
+ policy = MoveBackToFit;
+
+ if (elt->event()->has(Text::TextTypePropertyName) &&
+ elt->event()->get
+ <String>
+ (Text::TextTypePropertyName) ==
+ Text::Annotation &&
+ !m_notationView->areAnnotationsVisible()) {
+
+ // nothing I guess
+
+ }
+ else if (elt->event()->has(Text::TextTypePropertyName) &&
+ elt->event()->get
+ <String>
+ (Text::TextTypePropertyName) ==
+ Text::LilyPondDirective &&
+ !m_notationView->areLilyPondDirectivesVisible()) {
+
+ // nothing here either
+
+ }
+ else {
+
+ try {
+ if (m_printPainter) {
+ Text text(*elt->event());
+ int length = m_notePixmapFactory->getTextWidth(text);
+ for (double w = -1, inc = 0; w != 0; inc += w) {
+ w = setPainterClipping(m_printPainter,
+ elt->getLayoutX(),
+ int(elt->getLayoutY()),
+ int(inc), length, coords,
+ policy);
+ m_notePixmapFactory->drawText
+ (text, *m_printPainter, int(coords.first), coords.second);
+ m_printPainter->restore();
+ }
+ } else {
+ pixmap = m_notePixmapFactory->makeTextPixmap
+ (Text(*elt->event()));
+ }
+ } catch (Exception e) { // Text ctor failed
+ NOTATION_DEBUG << "Bad text event" << endl;
+ }
+ }
+
+ } else if (elt->event()->isa(Indication::EventType)) {
+
+ policy = SplitToFit;
+
+ try {
+ Indication indication(*elt->event());
+
+ timeT indicationDuration = indication.getIndicationDuration();
+ timeT indicationEndTime =
+ elt->getViewAbsoluteTime() + indicationDuration;
+
+ NotationElementList::iterator indicationEnd =
+ getViewElementList()->findTime(indicationEndTime);
+
+ std::string indicationType = indication.getIndicationType();
+
+ int length, y1;
+
+ if ((indicationType == Indication::Slur ||
+ indicationType == Indication::PhrasingSlur) &&
+ indicationEnd != getViewElementList()->begin()) {
+ --indicationEnd;
+ }
+
+ if ((indicationType != Indication::Slur &&
+ indicationType != Indication::PhrasingSlur) &&
+ indicationEnd != getViewElementList()->begin() &&
+ (indicationEnd == getViewElementList()->end() ||
+ indicationEndTime ==
+ getSegment().getBarStartForTime(indicationEndTime))) {
+
+ while (indicationEnd == getViewElementList()->end() ||
+ (*indicationEnd)->getViewAbsoluteTime() >= indicationEndTime)
+ --indicationEnd;
+
+ double x, w;
+ static_cast<NotationElement *>(*indicationEnd)->
+ getLayoutAirspace(x, w);
+ length = (int)(x + w - elt->getLayoutX() -
+ m_notePixmapFactory->getBarMargin());
+
+ } else {
+
+ length = (int)((*indicationEnd)->getLayoutX() -
+ elt->getLayoutX());
+
+ if (indication.isOttavaType()) {
+ length -= m_notePixmapFactory->getNoteBodyWidth();
+ }
+ }
+
+ y1 = (int)(*indicationEnd)->getLayoutY();
+
+ if (length < m_notePixmapFactory->getNoteBodyWidth()) {
+ length = m_notePixmapFactory->getNoteBodyWidth();
+ }
+
+ if (indicationType == Indication::Crescendo ||
+ indicationType == Indication::Decrescendo) {
+
+ if (m_printPainter) {
+ for (double w = -1, inc = 0; w != 0; inc += w) {
+ w = setPainterClipping(m_printPainter,
+ elt->getLayoutX(),
+ int(elt->getLayoutY()),
+ int(inc), length, coords,
+ policy);
+ m_notePixmapFactory->drawHairpin
+ (length, indicationType == Indication::Crescendo,
+ *m_printPainter, int(coords.first), coords.second);
+ m_printPainter->restore();
+ }
+ } else {
+ pixmap = m_notePixmapFactory->makeHairpinPixmap
+ (length, indicationType == Indication::Crescendo);
+ }
+
+ } else if (indicationType == Indication::Slur ||
+ indicationType == Indication::PhrasingSlur) {
+
+ bool above = true;
+ long dy = 0;
+ long length = 10;
+
+ elt->event()->get
+ <Bool>(properties.SLUR_ABOVE, above);
+ elt->event()->get
+ <Int>(properties.SLUR_Y_DELTA, dy);
+ elt->event()->get
+ <Int>(properties.SLUR_LENGTH, length);
+
+ if (m_printPainter) {
+ for (double w = -1, inc = 0; w != 0; inc += w) {
+ w = setPainterClipping(m_printPainter,
+ elt->getLayoutX(),
+ int(elt->getLayoutY()),
+ int(inc), length, coords,
+ policy);
+ m_notePixmapFactory->drawSlur
+ (length, dy, above,
+ indicationType == Indication::PhrasingSlur,
+ *m_printPainter, int(coords.first), coords.second);
+ m_printPainter->restore();
+ }
+ } else {
+ pixmap = m_notePixmapFactory->makeSlurPixmap
+ (length, dy, above,
+ indicationType == Indication::PhrasingSlur);
+ }
+
+ } else {
+
+ int octaves = indication.getOttavaShift();
+
+ if (octaves != 0) {
+ if (m_printPainter) {
+ for (double w = -1, inc = 0; w != 0; inc += w) {
+ w = setPainterClipping(m_printPainter,
+ elt->getLayoutX(),
+ int(elt->getLayoutY()),
+ int(inc), length, coords,
+ policy);
+ m_notePixmapFactory->drawOttava
+ (length, octaves,
+ *m_printPainter, int(coords.first), coords.second);
+ m_printPainter->restore();
+ }
+ } else {
+ pixmap = m_notePixmapFactory->makeOttavaPixmap
+ (length, octaves);
+ }
+ } else {
+
+ NOTATION_DEBUG
+ << "Unrecognised indicationType " << indicationType << endl;
+ if (m_showUnknowns) {
+ pixmap = m_notePixmapFactory->makeUnknownPixmap();
+ }
+ }
+ }
+ } catch (...) {
+ NOTATION_DEBUG << "Bad indication!" << endl;
+ }
+
+ } else if (elt->event()->isa(Controller::EventType)) {
+
+ bool isSustain = false;
+
+ long controlNumber = 0;
+ elt->event()->get
+ <Int>(Controller::NUMBER, controlNumber);
+
+ Studio *studio = &m_notationView->getDocument()->getStudio();
+ Track *track = getSegment().getComposition()->getTrackById
+ (getSegment().getTrack());
+
+ if (track) {
+
+ Instrument *instrument = studio->getInstrumentById
+ (track->getInstrument());
+ if (instrument) {
+ MidiDevice *device = dynamic_cast<MidiDevice *>
+ (instrument->getDevice());
+ if (device) {
+ for (ControlList::const_iterator i =
+ device->getControlParameters().begin();
+ i != device->getControlParameters().end(); ++i) {
+ if (i->getType() == Controller::EventType &&
+ i->getControllerValue() == controlNumber) {
+ if (i->getName() == "Sustain" ||
+ strtoqstr(i->getName()) == i18n("Sustain")) {
+ isSustain = true;
+ }
+ break;
+ }
+ }
+ } else if (instrument->getDevice() &&
+ instrument->getDevice()->getType() == Device::SoftSynth) {
+ if (controlNumber == 64) {
+ isSustain = true;
+ }
+ }
+ }
+ }
+
+ if (isSustain) {
+ long value = 0;
+ elt->event()->get
+ <Int>(Controller::VALUE, value);
+ if (value > 0) {
+ pixmap = m_notePixmapFactory->makePedalDownPixmap();
+ } else {
+ pixmap = m_notePixmapFactory->makePedalUpPixmap();
+ }
+
+ } else {
+
+ if (m_showUnknowns) {
+ pixmap = m_notePixmapFactory->makeUnknownPixmap();
+ }
+ }
+ } else if (elt->event()->isa(Guitar::Chord::EventType)) {
+
+ // Create a guitar chord pixmap
+ try {
+
+ Guitar::Chord chord (*elt->event());
+
+ /* UNUSED - for printing, just use a large pixmap as below
+ if (m_printPainter) {
+
+ int length = m_notePixmapFactory->getTextWidth(text);
+ for (double w = -1, inc = 0; w != 0; inc += w) {
+ w = setPainterClipping(m_printPainter,
+ elt->getLayoutX(),
+ int(elt->getLayoutY()),
+ int(inc), length, coords,
+ policy);
+ m_notePixmapFactory->drawText
+ (text, *m_printPainter, int(coords.first), coords.second);
+ m_printPainter->restore();
+ }
+ } else {
+ */
+
+ pixmap = m_notePixmapFactory->makeGuitarChordPixmap (chord.getFingering(),
+ int(coords.first),
+ coords.second);
+ // }
+ } catch (Exception e) { // GuitarChord ctor failed
+ NOTATION_DEBUG << "Bad guitar chord event" << endl;
+ }
+
+ } else {
+
+ if (m_showUnknowns) {
+ pixmap = m_notePixmapFactory->makeUnknownPixmap();
+ }
+ }
+
+ // Show the result, one way or another
+
+ if (elt->isNote()) {
+
+ // No need, we already set and showed it in renderNote
+
+ } else if (pixmap) {
+
+ setPixmap(elt, pixmap, z, policy);
+
+ } else {
+ elt->removeCanvasItem();
+ }
+
+ // NOTATION_DEBUG << "NotationStaff::renderSingleElement: Setting selected at " << elt->getAbsoluteTime() << " to " << selected << endl;
+
+ } catch (...) {
+ std::cerr << "Event lacks the proper properties: "
+ << std::endl;
+ elt->event()->dump(std::cerr);
+ }
+
+ m_notePixmapFactory->setSelected(false);
+ m_notePixmapFactory->setShaded(false);
+}
+
+double
+NotationStaff::setPainterClipping(QPainter *painter, double lx, int ly,
+ double dx, double w, LinedStaffCoords &coords,
+ FitPolicy policy)
+{
+ painter->save();
+
+ // NOTATION_DEBUG << "NotationStaff::setPainterClipping: lx " << lx << ", dx " << dx << ", w " << w << endl;
+
+ coords = getCanvasCoordsForLayoutCoords(lx + dx, ly);
+ int row = getRowForLayoutX(lx + dx);
+ double rightMargin = getCanvasXForRightOfRow(row);
+ double available = rightMargin - coords.first;
+
+ // NOTATION_DEBUG << "NotationStaff::setPainterClipping: row " << row << ", rightMargin " << rightMargin << ", available " << available << endl;
+
+ switch (policy) {
+
+ case SplitToFit: {
+ bool fit = (w - dx <= available + m_notePixmapFactory->getNoteBodyWidth());
+ if (dx > 0.01 || !fit) {
+ int clipLeft = int(coords.first), clipWidth = int(available);
+ if (dx < 0.01) {
+ // never clip the left side of the first part of something
+ clipWidth += clipLeft;
+ clipLeft = 0;
+ }
+ QRect clip(clipLeft, coords.second - getRowSpacing() / 2,
+ clipWidth, getRowSpacing());
+ painter->setClipRect(clip, QPainter::CoordPainter);
+ coords.first -= dx;
+ }
+ if (fit) {
+ return 0.0;
+ }
+ return available;
+ }
+
+ case MoveBackToFit:
+ if (w - dx > available + m_notePixmapFactory->getNoteBodyWidth()) {
+ coords.first -= (w - dx) - available;
+ }
+ return 0.0;
+
+ default:
+ return 0.0;
+ }
+}
+
+void
+NotationStaff::setPixmap(NotationElement *elt, QCanvasPixmap *pixmap, int z,
+ FitPolicy policy)
+{
+ double layoutX = elt->getLayoutX();
+ int layoutY = (int)elt->getLayoutY();
+
+ elt->removeCanvasItem();
+
+ while (1) {
+
+ LinedStaffCoords coords =
+ getCanvasCoordsForLayoutCoords(layoutX, layoutY);
+
+ double canvasX = coords.first;
+ int canvasY = coords.second;
+
+ QCanvasItem *item = 0;
+
+ if (m_pageMode == LinearMode || policy == PretendItFittedAllAlong) {
+
+ item = new QCanvasNotationSprite(*elt, pixmap, m_canvas);
+
+ } else {
+
+ int row = getRowForLayoutX(layoutX);
+ double rightMargin = getCanvasXForRightOfRow(row);
+ double extent = canvasX + pixmap->width();
+
+ // NOTATION_DEBUG << "NotationStaff::setPixmap: row " << row << ", right margin " << rightMargin << ", extent " << extent << endl;
+
+ if (extent > rightMargin + m_notePixmapFactory->getNoteBodyWidth()) {
+
+ if (policy == SplitToFit) {
+
+ // NOTATION_DEBUG << "splitting at " << (rightMargin-canvasX) << endl;
+
+ std::pair<QPixmap, QPixmap> split =
+ PixmapFunctions::splitPixmap(*pixmap,
+ int(rightMargin - canvasX));
+
+ QCanvasPixmap *leftCanvasPixmap = new QCanvasPixmap
+ (split.first, QPoint(pixmap->offsetX(), pixmap->offsetY()));
+
+ QCanvasPixmap *rightCanvasPixmap = new QCanvasPixmap
+ (split.second, QPoint(0, pixmap->offsetY()));
+
+ item = new QCanvasNotationSprite(*elt, leftCanvasPixmap, m_canvas);
+ item->setZ(z);
+
+ if (elt->getCanvasItem()) {
+ elt->addCanvasItem(item, canvasX, canvasY);
+ } else {
+ elt->setCanvasItem(item, canvasX, canvasY);
+ }
+
+ item->show();
+
+ delete pixmap;
+ pixmap = rightCanvasPixmap;
+
+ layoutX += rightMargin - canvasX + 0.01; // ensure flip to next row
+
+ continue;
+
+ } else { // policy == MoveBackToFit
+
+ item = new QCanvasNotationSprite(*elt, pixmap, m_canvas);
+ elt->setLayoutX(elt->getLayoutX() - (extent - rightMargin));
+ coords = getCanvasCoordsForLayoutCoords(layoutX, layoutY);
+ canvasX = coords.first;
+ }
+ } else {
+ item = new QCanvasNotationSprite(*elt, pixmap, m_canvas);
+ }
+ }
+
+ item->setZ(z);
+ if (elt->getCanvasItem()) {
+ elt->addCanvasItem(item, canvasX, canvasY);
+ } else {
+ elt->setCanvasItem(item, canvasX, canvasY);
+ }
+ item->show();
+ break;
+ }
+}
+
+void
+NotationStaff::renderNote(ViewElementList::iterator &vli)
+{
+ NotationElement* elt = static_cast<NotationElement*>(*vli);
+
+ const NotationProperties &properties(getProperties());
+ static NotePixmapParameters params(Note::Crotchet, 0);
+
+ Note::Type note = elt->event()->get
+ <Int>(BaseProperties::NOTE_TYPE);
+ int dots = elt->event()->get
+ <Int>(BaseProperties::NOTE_DOTS);
+
+ Accidental accidental = Accidentals::NoAccidental;
+ (void)elt->event()->get
+ <String>(properties.DISPLAY_ACCIDENTAL, accidental);
+
+ bool cautionary = false;
+ if (accidental != Accidentals::NoAccidental) {
+ (void)elt->event()->get
+ <Bool>(properties.DISPLAY_ACCIDENTAL_IS_CAUTIONARY,
+ cautionary);
+ }
+
+ bool up = true;
+ // (void)(elt->event()->get<Bool>(properties.STEM_UP, up));
+ (void)(elt->event()->get
+ <Bool>(properties.VIEW_LOCAL_STEM_UP, up));
+
+ bool flag = true;
+ (void)(elt->event()->get
+ <Bool>(properties.DRAW_FLAG, flag));
+
+ bool beamed = false;
+ (void)(elt->event()->get
+ <Bool>(properties.BEAMED, beamed));
+
+ bool shifted = false;
+ (void)(elt->event()->get
+ <Bool>(properties.NOTE_HEAD_SHIFTED, shifted));
+
+ bool dotShifted = false;
+ (void)(elt->event()->get
+ <Bool>(properties.NOTE_DOT_SHIFTED, dotShifted));
+
+ long stemLength = m_notePixmapFactory->getNoteBodyHeight();
+ (void)(elt->event()->get
+ <Int>(properties.UNBEAMED_STEM_LENGTH, stemLength));
+
+ long heightOnStaff = 0;
+ int legerLines = 0;
+
+ (void)(elt->event()->get
+ <Int>(properties.HEIGHT_ON_STAFF, heightOnStaff));
+ if (heightOnStaff < 0) {
+ legerLines = heightOnStaff;
+ } else if (heightOnStaff > 8) {
+ legerLines = heightOnStaff - 8;
+ }
+
+ long slashes = 0;
+ (void)(elt->event()->get
+ <Int>(properties.SLASHES, slashes));
+
+ bool quantized = false;
+ if (m_colourQuantize && !elt->isTuplet()) {
+ quantized =
+ (elt->getViewAbsoluteTime() != elt->event()->getAbsoluteTime() ||
+ elt->getViewDuration() != elt->event()->getDuration());
+ }
+ params.setQuantized(quantized);
+
+ bool trigger = false;
+ if (elt->event()->has(BaseProperties::TRIGGER_SEGMENT_ID))
+ trigger = true;
+ params.setTrigger(trigger);
+
+ bool inRange = true;
+ Pitch p(*elt->event());
+ Segment *segment = &getSegment();
+ if (m_showRanges) {
+ int pitch = p.getPerformancePitch();
+ if (pitch > segment->getHighestPlayable() ||
+ pitch < segment->getLowestPlayable()) {
+ inRange = false;
+ }
+ }
+ params.setInRange(inRange);
+
+ params.setNoteType(note);
+ params.setDots(dots);
+ params.setAccidental(accidental);
+ params.setAccidentalCautionary(cautionary);
+ params.setNoteHeadShifted(shifted);
+ params.setNoteDotShifted(dotShifted);
+ params.setDrawFlag(flag);
+ params.setDrawStem(true);
+ params.setStemGoesUp(up);
+ params.setLegerLines(legerLines);
+ params.setSlashes(slashes);
+ params.setBeamed(false);
+ params.setIsOnLine(heightOnStaff % 2 == 0);
+ params.removeMarks();
+ params.setSafeVertDistance(0);
+
+ bool primary = false;
+ int safeVertDistance = 0;
+
+ if (elt->event()->get
+ <Bool>(properties.CHORD_PRIMARY_NOTE, primary)
+ && primary) {
+
+ long marks = 0;
+ elt->event()->get
+ <Int>(properties.CHORD_MARK_COUNT, marks);
+ if (marks) {
+ NotationChord chord(*getViewElementList(), vli,
+ m_segment.getComposition()->getNotationQuantizer(),
+ properties);
+ params.setMarks(chord.getMarksForChord());
+ }
+
+ // params.setMarks(Marks::getMarks(*elt->event()));
+
+ if (up && note < Note::Semibreve) {
+ safeVertDistance = m_notePixmapFactory->getStemLength();
+ safeVertDistance = std::max(safeVertDistance, int(stemLength));
+ }
+ }
+
+ long tieLength = 0;
+ (void)(elt->event()->get<Int>(properties.TIE_LENGTH, tieLength));
+ if (tieLength > 0) {
+ params.setTied(true);
+ params.setTieLength(tieLength);
+ } else {
+ params.setTied(false);
+ }
+
+ if (elt->event()->has(BaseProperties::TIE_IS_ABOVE)) {
+ params.setTiePosition
+ (true, elt->event()->get<Bool>(BaseProperties::TIE_IS_ABOVE));
+ } else {
+ params.setTiePosition(false, false); // the default
+ }
+
+ long accidentalShift = 0;
+ bool accidentalExtra = false;
+ if (elt->event()->get<Int>(properties.ACCIDENTAL_SHIFT, accidentalShift)) {
+ elt->event()->get<Bool>(properties.ACCIDENTAL_EXTRA_SHIFT, accidentalExtra);
+ }
+ params.setAccidentalShift(accidentalShift);
+ params.setAccExtraShift(accidentalExtra);
+
+ double airX, airWidth;
+ elt->getLayoutAirspace(airX, airWidth);
+ params.setWidth(int(airWidth));
+
+ if (beamed) {
+
+ if (elt->event()->get<Bool>(properties.CHORD_PRIMARY_NOTE, primary)
+ && primary) {
+
+ int myY = elt->event()->get<Int>(properties.BEAM_MY_Y);
+
+ stemLength = myY - (int)elt->getLayoutY();
+ if (stemLength < 0)
+ stemLength = -stemLength;
+
+ int nextBeamCount =
+ elt->event()->get
+ <Int>(properties.BEAM_NEXT_BEAM_COUNT);
+ int width =
+ elt->event()->get
+ <Int>(properties.BEAM_SECTION_WIDTH);
+ int gradient =
+ elt->event()->get
+ <Int>(properties.BEAM_GRADIENT);
+
+ bool thisPartialBeams(false), nextPartialBeams(false);
+ (void)elt->event()->get
+ <Bool>
+ (properties.BEAM_THIS_PART_BEAMS, thisPartialBeams);
+ (void)elt->event()->get
+ <Bool>
+ (properties.BEAM_NEXT_PART_BEAMS, nextPartialBeams);
+
+ params.setBeamed(true);
+ params.setNextBeamCount(nextBeamCount);
+ params.setThisPartialBeams(thisPartialBeams);
+ params.setNextPartialBeams(nextPartialBeams);
+ params.setWidth(width);
+ params.setGradient((double)gradient / 100.0);
+ if (up)
+ safeVertDistance = stemLength;
+
+ }
+ else {
+ params.setBeamed(false);
+ params.setDrawStem(false);
+ }
+ }
+
+ if (heightOnStaff < 7) {
+ int gap = (((7 - heightOnStaff) * m_notePixmapFactory->getLineSpacing()) / 2);
+ if (safeVertDistance < gap)
+ safeVertDistance = gap;
+ }
+
+ params.setStemLength(stemLength);
+ params.setSafeVertDistance(safeVertDistance);
+ setTuplingParameters(elt, params);
+
+ NotePixmapFactory *factory = m_notePixmapFactory;
+
+ if (elt->isGrace()) {
+ // lift this code from elsewhere to fix #1930309, and it seems to work a
+ // treat, as y'all Wrongpondians are wont to say
+ params.setLegerLines(heightOnStaff < 0 ? heightOnStaff :
+ heightOnStaff > 8 ? heightOnStaff - 8 : 0);
+ m_graceNotePixmapFactory->setSelected(m_notePixmapFactory->isSelected());
+ m_graceNotePixmapFactory->setShaded(m_notePixmapFactory->isShaded());
+ factory = m_graceNotePixmapFactory;
+ }
+
+ if (m_printPainter) {
+
+ // Return no canvas item, but instead render straight to
+ // the printer.
+
+ LinedStaffCoords coords = getCanvasCoordsForLayoutCoords
+ (elt->getLayoutX(), (int)elt->getLayoutY());
+
+ // We don't actually know how wide the note drawing will be,
+ // but we should be able to use a fairly pessimistic estimate
+ // without causing any problems
+ int length = tieLength + 10 * m_notePixmapFactory->getNoteBodyWidth();
+
+ for (double w = -1, inc = 0; w != 0; inc += w) {
+
+ w = setPainterClipping(m_printPainter,
+ elt->getLayoutX(),
+ int(elt->getLayoutY()),
+ int(inc), length, coords,
+ SplitToFit);
+
+ factory->drawNote
+ (params, *m_printPainter, int(coords.first), coords.second);
+
+ m_printPainter->restore(); // save() called by setPainterClipping
+ }
+
+ } else {
+
+ // The normal on-screen case
+
+ bool collision = false;
+ QCanvasItem * haloItem = 0;
+ if (m_showCollisions) {
+ collision = elt->isColliding();
+ if (collision) {
+ // Make collision halo
+ QCanvasPixmap *haloPixmap = factory->makeNoteHaloPixmap(params);
+ haloItem = new QCanvasNotationSprite(*elt, haloPixmap, m_canvas);
+ haloItem->setZ(-1);
+ }
+ }
+
+ QCanvasPixmap *pixmap = factory->makeNotePixmap(params);
+
+ int z = 0;
+ if (factory->isSelected())
+ z = 3;
+ else if (quantized)
+ z = 2;
+
+ setPixmap(elt, pixmap, z, SplitToFit);
+
+ if (collision) {
+ // Display collision halo
+ LinedStaffCoords coords =
+ getCanvasCoordsForLayoutCoords(elt->getLayoutX(),
+ elt->getLayoutY());
+ double canvasX = coords.first;
+ int canvasY = coords.second;
+ elt->addCanvasItem(haloItem, canvasX, canvasY);
+ haloItem->show();
+ }
+ }
+}
+
+void
+NotationStaff::setTuplingParameters(NotationElement *elt,
+ NotePixmapParameters &params)
+{
+ const NotationProperties &properties(getProperties());
+
+ params.setTupletCount(0);
+ long tuplingLineY = 0;
+ bool tupled = (elt->event()->get
+ <Int>(properties.TUPLING_LINE_MY_Y, tuplingLineY));
+
+ if (tupled) {
+
+ long tuplingLineWidth = 0;
+ if (!elt->event()->get
+ <Int>(properties.TUPLING_LINE_WIDTH, tuplingLineWidth)) {
+ std::cerr << "WARNING: Tupled event at " << elt->event()->getAbsoluteTime() << " has no tupling line width" << std::endl;
+ }
+
+ long tuplingLineGradient = 0;
+ if (!(elt->event()->get
+ <Int>(properties.TUPLING_LINE_GRADIENT,
+ tuplingLineGradient))) {
+ std::cerr << "WARNING: Tupled event at " << elt->event()->getAbsoluteTime() << " has no tupling line gradient" << std::endl;
+ }
+
+ bool tuplingLineFollowsBeam = false;
+ elt->event()->get
+ <Bool>(properties.TUPLING_LINE_FOLLOWS_BEAM,
+ tuplingLineFollowsBeam);
+
+ long tupletCount;
+ if (elt->event()->get<Int>
+ (BaseProperties::BEAMED_GROUP_UNTUPLED_COUNT, tupletCount)) {
+
+ params.setTupletCount(tupletCount);
+ params.setTuplingLineY(tuplingLineY - (int)elt->getLayoutY());
+ params.setTuplingLineWidth(tuplingLineWidth);
+ params.setTuplingLineGradient(double(tuplingLineGradient) / 100.0);
+ params.setTuplingLineFollowsBeam(tuplingLineFollowsBeam);
+ }
+ }
+}
+
+bool
+NotationStaff::isSelected(NotationElementList::iterator it)
+{
+ const EventSelection *selection =
+ m_notationView->getCurrentSelection();
+ return selection && selection->contains((*it)->event());
+}
+
+void
+NotationStaff::showPreviewNote(double layoutX, int heightOnStaff,
+ const Note &note, bool grace)
+{
+ NotePixmapFactory *npf = m_notePixmapFactory;
+ if (grace) npf = m_graceNotePixmapFactory;
+
+ NotePixmapParameters params(note.getNoteType(), note.getDots());
+ NotationRules rules;
+
+ params.setAccidental(Accidentals::NoAccidental);
+ params.setNoteHeadShifted(false);
+ params.setDrawFlag(true);
+ params.setDrawStem(true);
+ params.setStemGoesUp(rules.isStemUp(heightOnStaff));
+ params.setLegerLines(heightOnStaff < 0 ? heightOnStaff :
+ heightOnStaff > 8 ? heightOnStaff - 8 : 0);
+ params.setBeamed(false);
+ params.setIsOnLine(heightOnStaff % 2 == 0);
+ params.setTied(false);
+ params.setBeamed(false);
+ params.setTupletCount(0);
+ params.setSelected(false);
+ params.setHighlighted(true);
+
+ delete m_previewSprite;
+ m_previewSprite = new QCanvasSimpleSprite
+ (npf->makeNotePixmap(params), m_canvas);
+
+ int layoutY = getLayoutYForHeight(heightOnStaff);
+ LinedStaffCoords coords = getCanvasCoordsForLayoutCoords(layoutX, layoutY);
+
+ m_previewSprite->move(coords.first, (double)coords.second);
+ m_previewSprite->setZ(4);
+ m_previewSprite->show();
+ m_canvas->update();
+}
+
+void
+NotationStaff::clearPreviewNote()
+{
+ delete m_previewSprite;
+ m_previewSprite = 0;
+}
+
+bool
+NotationStaff::wrapEvent(Event *e)
+{
+ bool wrap = true;
+
+ /*!!! always wrap unknowns, just don't necessarily render them?
+
+ if (!m_showUnknowns) {
+ std::string etype = e->getType();
+ if (etype != Note::EventType &&
+ etype != Note::EventRestType &&
+ etype != Clef::EventType &&
+ etype != Key::EventType &&
+ etype != Indication::EventType &&
+ etype != Text::EventType) {
+ wrap = false;
+ }
+ }
+ */
+
+ if (wrap)
+ wrap = Staff::wrapEvent(e);
+
+ return wrap;
+}
+
+void
+NotationStaff::eventRemoved(const Segment *segment,
+ Event *event)
+{
+ LinedStaff::eventRemoved(segment, event);
+ m_notationView->handleEventRemoved(event);
+}
+
+void
+NotationStaff::markChanged(timeT from, timeT to, bool movedOnly)
+{
+ // first time through this, m_ready is false -- we mark it true
+
+ NOTATION_DEBUG << "NotationStaff::markChanged (" << from << " -> " << to << ") " << movedOnly << endl;
+
+ drawStaffName();//!!!
+
+ if (from == to) {
+
+ m_status.clear();
+
+ if (!movedOnly && m_ready) { // undo all the rendering we've already done
+ for (NotationElementList::iterator i = getViewElementList()->begin();
+ i != getViewElementList()->end(); ++i) {
+ static_cast<NotationElement *>(*i)->removeCanvasItem();
+ }
+
+ m_clefChanges.clear();
+ m_keyChanges.clear();
+ }
+
+ drawStaffName();
+
+ } else {
+
+ Segment *segment = &getSegment();
+ Composition *composition = segment->getComposition();
+
+ NotationElementList::iterator unchanged = findUnchangedBarEnd(to);
+
+ int finalBar;
+ if (unchanged == getViewElementList()->end()) {
+ finalBar = composition->getBarNumber(segment->getEndMarkerTime());
+ } else {
+ finalBar = composition->getBarNumber((*unchanged)->getViewAbsoluteTime());
+ }
+
+ int fromBar = composition->getBarNumber(from);
+ int toBar = composition->getBarNumber(to);
+ if (finalBar < toBar)
+ finalBar = toBar;
+
+ for (int bar = fromBar; bar <= finalBar; ++bar) {
+
+ if (bar > toBar)
+ movedOnly = true;
+
+ // NOTATION_DEBUG << "bar " << bar << " status " << m_status[bar] << endl;
+
+ if (bar >= m_lastRenderCheck.first &&
+ bar <= m_lastRenderCheck.second) {
+
+ // NOTATION_DEBUG << "bar " << bar << " rendering and positioning" << endl;
+
+ if (!movedOnly || m_status[bar] == UnRendered) {
+ renderElements
+ (getViewElementList()->findTime(composition->getBarStart(bar)),
+ getViewElementList()->findTime(composition->getBarEnd(bar)));
+ }
+ positionElements(composition->getBarStart(bar),
+ composition->getBarEnd(bar));
+ m_status[bar] = Positioned;
+
+ } else if (!m_ready) {
+ // NOTATION_DEBUG << "bar " << bar << " rendering and positioning" << endl;
+
+ // first time through -- we don't need a separate render phase,
+ // only to mark as not yet positioned
+ m_status[bar] = Rendered;
+
+ } else if (movedOnly) {
+ if (m_status[bar] == Positioned) {
+ // NOTATION_DEBUG << "bar " << bar << " marking unpositioned" << endl;
+ m_status[bar] = Rendered;
+ }
+
+ } else {
+ // NOTATION_DEBUG << "bar " << bar << " marking unrendered" << endl;
+
+ m_status[bar] = UnRendered;
+ }
+ }
+ }
+
+ m_ready = true;
+}
+
+void
+NotationStaff::setPrintPainter(QPainter *painter)
+{
+ m_printPainter = painter;
+}
+
+bool
+NotationStaff::checkRendered(timeT from, timeT to)
+{
+ if (!m_ready)
+ return false;
+ Composition *composition = getSegment().getComposition();
+ if (!composition) {
+ NOTATION_DEBUG << "NotationStaff::checkRendered: warning: segment has no composition -- is my paint event late?" << endl;
+ return false;
+ }
+
+ // NOTATION_DEBUG << "NotationStaff::checkRendered: " << from << " -> " << to << endl;
+
+ int fromBar = composition->getBarNumber(from);
+ int toBar = composition->getBarNumber(to);
+ bool something = false;
+
+ if (fromBar > toBar)
+ std::swap(fromBar, toBar);
+
+ for (int bar = fromBar; bar <= toBar; ++bar) {
+ // NOTATION_DEBUG << "NotationStaff::checkRendered: bar " << bar << " status "
+ // << m_status[bar] << endl;
+
+ switch (m_status[bar]) {
+
+ case UnRendered:
+ renderElements
+ (getViewElementList()->findTime(composition->getBarStart(bar)),
+ getViewElementList()->findTime(composition->getBarEnd(bar)));
+
+ case Rendered:
+ positionElements
+ (composition->getBarStart(bar),
+ composition->getBarEnd(bar));
+ m_lastRenderedBar = bar;
+
+ something = true;
+
+ case Positioned:
+ break;
+ }
+
+ m_status[bar] = Positioned;
+ }
+
+ m_lastRenderCheck = std::pair<int, int>(fromBar, toBar);
+ return something;
+}
+
+bool
+NotationStaff::doRenderWork(timeT from, timeT to)
+{
+ if (!m_ready)
+ return true;
+ Composition *composition = getSegment().getComposition();
+
+ int fromBar = composition->getBarNumber(from);
+ int toBar = composition->getBarNumber(to);
+
+ if (fromBar > toBar)
+ std::swap(fromBar, toBar);
+
+ for (int bar = fromBar; bar <= toBar; ++bar) {
+
+ switch (m_status[bar]) {
+
+ case UnRendered:
+ renderElements
+ (getViewElementList()->findTime(composition->getBarStart(bar)),
+ getViewElementList()->findTime(composition->getBarEnd(bar)));
+ m_status[bar] = Rendered;
+ return true;
+
+ case Rendered:
+ positionElements
+ (composition->getBarStart(bar),
+ composition->getBarEnd(bar));
+ m_status[bar] = Positioned;
+ m_lastRenderedBar = bar;
+ return true;
+
+ case Positioned:
+ // The bars currently displayed are rendered before the others.
+ // Later, when preceding bars are rendered, truncateClefsAndKeysAt()
+ // is called and possible clefs and/or keys from the bars previously
+ // rendered may be lost. Following code should restore these clefs
+ // and keys in m_clefChanges and m_keyChanges lists.
+ if (bar > m_lastRenderedBar)
+ checkAndCompleteClefsAndKeys(bar);
+ continue;
+ }
+ }
+
+ return false;
+}
+
+void
+NotationStaff::checkAndCompleteClefsAndKeys(int bar)
+{
+ // Look for Clef or Key in current bar
+ Composition *composition = getSegment().getComposition();
+ timeT barStartTime = composition->getBarStart(bar);
+ timeT barEndTime = composition->getBarEnd(bar);
+
+ for (ViewElementList::iterator it =
+ getViewElementList()->findTime(barStartTime);
+ (it != getViewElementList()->end())
+ && ((*it)->getViewAbsoluteTime() < barEndTime); ++it) {
+ if ((*it)->event()->isa(Clef::EventType)) {
+ // Clef found
+ Clef clef = *(*it)->event();
+
+ // Is this clef already in m_clefChanges list ?
+ int xClef = int((*it)->getLayoutX());
+ bool found = false;
+ for (int i = 0; i < m_clefChanges.size(); ++i) {
+ if ( (m_clefChanges[i].first == xClef)
+ && (m_clefChanges[i].second == clef)) {
+ found = true;
+ break;
+ }
+ }
+
+ // If not, add it
+ if (!found) {
+ m_clefChanges.push_back(ClefChange(xClef, clef));
+ }
+
+ } else if ((*it)->event()->isa(::Rosegarden::Key::EventType)) {
+ ::Rosegarden::Key key = *(*it)->event();
+
+ // Is this key already in m_keyChanges list ?
+ int xKey = int((*it)->getLayoutX());
+ bool found = false;
+ for (int i = 0; i < m_keyChanges.size(); ++i) {
+ if ( (m_keyChanges[i].first == xKey)
+ && (m_keyChanges[i].second == key)) {
+ found = true;
+ break;
+ }
+ }
+
+ // If not, add it
+ if (!found) {
+ m_keyChanges.push_back(KeyChange(xKey, key));
+ }
+ }
+ }
+}
+
+LinedStaff::BarStyle
+NotationStaff::getBarStyle(int barNo) const
+{
+ const Segment *s = &getSegment();
+ Composition *c = s->getComposition();
+
+ int firstBar = c->getBarNumber(s->getStartTime());
+ int lastNonEmptyBar = c->getBarNumber(s->getEndMarkerTime() - 1);
+
+ // Currently only the first and last bar in a segment have any
+ // possibility of getting special treatment:
+ if (barNo > firstBar && barNo <= lastNonEmptyBar)
+ return PlainBar;
+
+ // First and last bar in a repeating segment get repeat bars.
+
+ if (s->isRepeating()) {
+ if (barNo == firstBar)
+ return RepeatStartBar;
+ else if (barNo == lastNonEmptyBar + 1)
+ return RepeatEndBar;
+ }
+
+ if (barNo <= lastNonEmptyBar)
+ return PlainBar;
+
+ // Last bar on a given track gets heavy double bars. Exploit the
+ // fact that Composition's iterator returns segments in track
+ // order.
+
+ Segment *lastSegmentOnTrack = 0;
+
+ for (Composition::iterator i = c->begin(); i != c->end(); ++i) {
+ if ((*i)->getTrack() == s->getTrack()) {
+ lastSegmentOnTrack = *i;
+ } else if (lastSegmentOnTrack != 0) {
+ break;
+ }
+ }
+
+ if (&getSegment() == lastSegmentOnTrack)
+ return HeavyDoubleBar;
+ else
+ return PlainBar;
+}
+
+double
+NotationStaff::getBarInset(int barNo, bool isFirstBarInRow) const
+{
+ LinedStaff::BarStyle style = getBarStyle(barNo);
+
+ NOTATION_DEBUG << "getBarInset(" << barNo << "," << isFirstBarInRow << ")" << endl;
+
+ if (!(style == RepeatStartBar || style == RepeatBothBar))
+ return 0.0;
+
+ const Segment &s = getSegment();
+ Composition *composition = s.getComposition();
+ timeT barStart = composition->getBarStart(barNo);
+
+ double inset = 0.0;
+
+ NOTATION_DEBUG << "ready" << endl;
+
+ bool haveKey = false, haveClef = false;
+
+ ::Rosegarden::Key key;
+ ::Rosegarden::Key cancelKey;
+ Clef clef;
+
+ for (Segment::iterator i = s.findTime(barStart);
+ s.isBeforeEndMarker(i) && ((*i)->getNotationAbsoluteTime() == barStart);
+ ++i) {
+
+ NOTATION_DEBUG << "type " << (*i)->getType() << " at " << (*i)->getNotationAbsoluteTime() << endl;
+
+ if ((*i)->isa(::Rosegarden::Key::EventType)) {
+
+ try {
+ key = ::Rosegarden::Key(**i);
+
+ if (barNo > composition->getBarNumber(s.getStartTime())) {
+ cancelKey = s.getKeyAtTime(barStart - 1);
+ }
+
+ if (m_keySigCancelMode == 0) { // only when entering C maj / A min
+
+ if (key.getAccidentalCount() != 0)
+ cancelKey = ::Rosegarden::Key();
+
+ } else if (m_keySigCancelMode == 1) { // only when reducing acc count
+
+ if (!(key.isSharp() == cancelKey.isSharp() &&
+ key.getAccidentalCount() < cancelKey.getAccidentalCount())) {
+ cancelKey = ::Rosegarden::Key();
+ }
+ }
+
+ haveKey = true;
+
+ } catch (...) {
+ NOTATION_DEBUG << "getBarInset: Bad key in event" << endl;
+ }
+
+ } else if ((*i)->isa(Clef::EventType)) {
+
+ try {
+ clef = Clef(**i);
+ haveClef = true;
+ } catch (...) {
+ NOTATION_DEBUG << "getBarInset: Bad clef in event" << endl;
+ }
+ }
+ }
+
+ if (isFirstBarInRow) {
+ if (!haveKey) {
+ key = s.getKeyAtTime(barStart);
+ haveKey = true;
+ }
+ if (!haveClef) {
+ clef = s.getClefAtTime(barStart);
+ haveClef = true;
+ }
+ }
+
+ if (haveKey) {
+ inset += m_notePixmapFactory->getKeyWidth(key, cancelKey);
+ }
+ if (haveClef) {
+ inset += m_notePixmapFactory->getClefWidth(clef);
+ }
+ if (haveClef || haveKey) {
+ inset += m_notePixmapFactory->getBarMargin() / 3;
+ }
+ if (haveClef && haveKey) {
+ inset += m_notePixmapFactory->getNoteBodyWidth() / 2;
+ }
+
+ NOTATION_DEBUG << "getBarInset(" << barNo << "," << isFirstBarInRow << "): inset " << inset << endl;
+
+
+ return inset;
+}
+
+Rosegarden::ViewElement* NotationStaff::makeViewElement(Rosegarden::Event* e)
+{
+ return new NotationElement(e);
+}
+
+}
diff --git a/src/gui/editors/notation/NotationStaff.h b/src/gui/editors/notation/NotationStaff.h
new file mode 100644
index 0000000..4a0302c
--- /dev/null
+++ b/src/gui/editors/notation/NotationStaff.h
@@ -0,0 +1,488 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTATIONSTAFF_H_
+#define _RG_NOTATIONSTAFF_H_
+
+#include "base/FastVector.h"
+#include "base/Staff.h"
+#include "base/ViewElement.h"
+#include "gui/general/LinedStaff.h"
+#include "gui/general/ProgressReporter.h"
+#include <map>
+#include <set>
+#include <string>
+#include <utility>
+#include "base/Event.h"
+#include "NotationElement.h"
+
+
+class QPainter;
+class QCanvasPixmap;
+class QCanvasItem;
+class QCanvas;
+class LinedStaffCoords;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class TimeSignature;
+class SnapGrid;
+class Segment;
+class QCanvasSimpleSprite;
+class NotePixmapParameters;
+class NotePixmapFactory;
+class Note;
+class NotationView;
+class NotationProperties;
+class Key;
+class Event;
+class Clef;
+
+
+/**
+ * The Staff is a repository for information about the notation
+ * representation of a single Segment. This includes all of the
+ * NotationElements representing the Events on that Segment, the staff
+ * lines, as well as basic positional and size data. This class
+ * used to be in gui/staff.h, but it's been moved and renamed
+ * following the introduction of the core Staff base class, and
+ * much of the functionality has been extracted into the LinedStaff
+ * base class.
+ */
+
+class NotationStaff : public ProgressReporter, public LinedStaff
+{
+public:
+
+ /**
+ * Creates a new NotationStaff for the specified Segment
+ * \a id is the id of the staff in the NotationView
+ */
+ NotationStaff(QCanvas *, Segment *, SnapGrid *,
+ int id, NotationView *view,
+ std::string fontName, int resolution);
+ virtual ~NotationStaff();
+
+ /**
+ * Changes the resolution of the note pixmap factory and the
+ * staff lines, etc
+ */
+ virtual void changeFont(std::string fontName, int resolution);
+
+ void setLegerLineCount(int legerLineCount) {
+ if (legerLineCount == -1) m_legerLineCount = 8;
+ else m_legerLineCount = legerLineCount;
+ }
+
+ void setBarNumbersEvery(int barNumbersEvery) {
+ m_barNumbersEvery = barNumbersEvery;
+ }
+
+ LinedStaff::setPageMode;
+ LinedStaff::setPageWidth;
+ LinedStaff::setRowsPerPage;
+ LinedStaff::setRowSpacing;
+ LinedStaff::setConnectingLineLength;
+
+ /**
+ * Gets a read-only reference to the pixmap factory used by the
+ * staff. (For use by NotationHLayout, principally.) This
+ * reference isn't const because the NotePixmapFactory maintains
+ * too much state for its methods to be const, but you should
+ * treat the returned reference as if it were const anyway.
+ */
+ virtual NotePixmapFactory& getNotePixmapFactory(bool grace) {
+ return grace ? *m_graceNotePixmapFactory : *m_notePixmapFactory;
+ }
+
+ /**
+ * Generate or re-generate sprites for all the elements between
+ * from and to. Call this when you've just made a change,
+ * specifying the extents of the change in the from and to
+ * parameters.
+ *
+ * This method does not reposition any elements outside the given
+ * range -- so after any edit that may change the visible extents
+ * of a range, you will then need to call positionElements for the
+ * changed range and the entire remainder of the staff.
+ */
+ virtual void renderElements(NotationElementList::iterator from,
+ NotationElementList::iterator to);
+
+
+ /**
+ * Assign suitable coordinates to the elements on the staff,
+ * based entirely on the layout X and Y coordinates they were
+ * given by the horizontal and vertical layout processes.
+ *
+ * This is necessary because the sprites that are being positioned
+ * may have been created either after the layout process completed
+ * (by renderElements) or before (by the previous renderElements
+ * call, if the sprites are unchanged but have moved) -- so
+ * neither the layout nor renderElements can authoritatively set
+ * their final positions.
+ *
+ * This method also updates the selected-ness of any elements it
+ * sees (i.e. it turns the selected ones blue and the unselected
+ * ones black), and re-generates sprites for any elements for
+ * which it seems necessary. In general it will only notice a
+ * element needs regenerating if its position has changed, not if
+ * the nature of the element has changed, so this is no substitute
+ * for calling renderElements.
+ *
+ * The from and to arguments are used to indicate the extents of a
+ * changed area within the staff. The actual area within which the
+ * elements end up being repositioned will begin at the start of
+ * the bar containing the changed area's start, and will end at the
+ * start of the next bar whose first element hasn't moved, after
+ * the changed area's end.
+ *
+ * Call this after renderElements, or after changing the selection,
+ * passing from and to arguments corresponding to the times of those
+ * passed to renderElements.
+ */
+ virtual void positionElements(timeT from,
+ timeT to);
+
+ /**
+ * Re-render and position elements as necessary, based on the
+ * given extents and any information obtained from calls to
+ * markChanged(). This provides a render-on-demand mechanism. If
+ * you are going to use this rendering mechanism, it's generally
+ * wise to avoid explicitly calling
+ * renderElements/positionElements as well.
+ *
+ * Returns true if something needed re-rendering.
+ */
+ virtual bool checkRendered(timeT from,
+ timeT to);
+
+ /**
+ * Find something between the given times that has not yet been
+ * rendered, and render a small amount of it. Return true if it
+ * found something to do. This is to be used as a background work
+ * procedure for rendering not-yet-visible areas of notation.
+ */
+ virtual bool doRenderWork(timeT from,
+ timeT to);
+
+ /**
+ * Mark a region of staff as changed, for use by the on-demand
+ * rendering mechanism. If fromBar == toBar == -1, mark the
+ * entire staff as changed (and recover the memory used for its
+ * elements). Pass movedOnly as true to indicate that elements
+ * have not changed but only been repositioned, for example as a
+ * consequence of a modification on another staff that caused a
+ * relayout.
+ */
+ virtual void markChanged(timeT from = 0,
+ timeT to = 0,
+ bool movedOnly = false);
+
+ /**
+ * Set a painter as the printer output. If this painter is
+ * non-null, subsequent renderElements calls will only render
+ * those elements that cannot be rendered directly to a print
+ * painter; those that can, will be rendered by renderPrintable()
+ * instead.
+ */
+ virtual void setPrintPainter(QPainter *painter);
+
+ /**
+ * Render to the current print painter those elements that can be
+ * rendered directly to a print painter. If no print painter is
+ * set, do nothing.
+ */
+ virtual void renderPrintable(timeT from,
+ timeT to);
+
+ /**
+ * Insert time signature at x-coordinate \a x.
+ */
+ virtual void insertTimeSignature(double layoutX,
+ const TimeSignature &timeSig);
+
+ /**
+ * Delete all time signatures
+ */
+ virtual void deleteTimeSignatures();
+
+ /**
+ * Insert repeated clef and key at start of new line, at x-coordinate \a x.
+ */
+ virtual void insertRepeatedClefAndKey(double layoutX, int barNo);
+
+ /**
+ * Delete all repeated clefs and keys.
+ */
+ virtual void deleteRepeatedClefsAndKeys();
+
+ /**
+ * (Re)draw the staff name from the track's current name
+ */
+ virtual void drawStaffName();
+
+ /**
+ * Return true if the staff name as currently drawn is up-to-date
+ * with that in the composition
+ */
+ virtual bool isStaffNameUpToDate();
+
+ /**
+ * Return the clef and key in force at the given canvas
+ * coordinates
+ */
+ virtual void getClefAndKeyAtCanvasCoords(double x, int y,
+ Clef &clef,
+ ::Rosegarden::Key &key) const;
+
+ /**
+ * Return the note name (C4, Bb3, whatever) corresponding to the
+ * given canvas coordinates
+ */
+ virtual std::string getNoteNameAtCanvasCoords
+ (double x, int y,
+ Accidental accidental =
+ Accidentals::NoAccidental) const;
+
+ /**
+ * Find the NotationElement whose layout x-coord is closest to x,
+ * without regard to its y-coord.
+ *
+ * If notesAndRestsOnly is true, will return the closest note
+ * or rest but will never return any other kind of element.
+ *
+ * If the closest event is further than \a proximityThreshold
+ * horizontally away from x, in pixels, end() is returned.
+ * (If proximityThreshold is negative, there will be no limit
+ * to the distances that will be considered.)
+ *
+ * Also returns the clef and key in force at the given coordinate.
+ */
+ virtual ViewElementList::iterator getClosestElementToLayoutX
+ (double x, Event *&clef, Event *&key,
+ bool notesAndRestsOnly = false,
+ int proximityThreshold = 10);
+
+ /**
+ * Find the NotationElement "under" the given layout x-coord,
+ * without regard to its y-coord.
+ *
+ * Also returns the clef and key in force at the given coordinates.
+ */
+ virtual ViewElementList::iterator getElementUnderLayoutX
+ (double x, Event *&clef, Event *&key);
+
+ /**
+ * Draw a note on the staff to show an insert position prior to
+ * an insert.
+ */
+ virtual void showPreviewNote(double layoutX, int heightOnStaff,
+ const Note &note, bool grace);
+
+ /**
+ * Remove any visible preview note.
+ */
+ virtual void clearPreviewNote();
+
+ /**
+ * Overridden from Staff<T>.
+ * We want to avoid wrapping things like controller events, if
+ * our showUnknowns preference is off
+ */
+ virtual bool wrapEvent(Event *);
+
+ /**
+ * Override from Staff<T>
+ * Let tools know if their current element has gone
+ */
+ virtual void eventRemoved(const Segment *, Event *);
+
+ /**
+ * Return the view-local PropertyName definitions for this staff's view
+ */
+ const NotationProperties &getProperties() const;
+
+ virtual double getBarInset(int barNo, bool isFirstBarInRow) const;
+
+ /**
+ * Return the time at the given canvas coordinates
+ */
+ timeT getTimeAtCanvasCoords(double x, int y) const;
+
+protected:
+
+ virtual ViewElement* makeViewElement(Event*);
+
+ // definition of staff
+ virtual int getLineCount() const { return 5; }
+ virtual int getLegerLineCount() const { return m_legerLineCount; }
+ virtual int getBottomLineHeight() const { return 0; }
+ virtual int getHeightPerLine() const { return 2; }
+ virtual int showBarNumbersEvery() const { return m_barNumbersEvery; }
+
+ virtual BarStyle getBarStyle(int barNo) const;
+
+ /**
+ * Assign a suitable sprite to the given element (the clef is
+ * needed in case it's a key event, in which case we need to judge
+ * the correct pitch for the key)
+ */
+ virtual void renderSingleElement(ViewElementList::iterator &,
+ const Clef &,
+ const ::Rosegarden::Key &,
+ bool selected);
+
+ bool isDirectlyPrintable(ViewElement *elt);
+
+ void setTuplingParameters(NotationElement *, NotePixmapParameters &);
+
+ /**
+ * Set a sprite representing the given note event to the given notation element
+ */
+ virtual void renderNote(ViewElementList::iterator &);
+
+ /**
+ * Return a NotationElementList::iterator pointing to the
+ * start of a bar prior to the given time that doesn't appear
+ * to have been affected by any changes around that time
+ */
+ NotationElementList::iterator findUnchangedBarStart(timeT);
+
+ /**
+ * Return a NotationElementList::iterator pointing to the
+ * end of a bar subsequent to the given time that doesn't appear
+ * to have been affected by any changes around that time
+ */
+ NotationElementList::iterator findUnchangedBarEnd(timeT);
+
+ /**
+ * Return true if the element has a canvas item that is already
+ * at the correct coordinates
+ */
+ virtual bool elementNotMoved(NotationElement *);
+
+ /**
+ * Return true if the element has a canvas item that is already
+ * at the correct y-coordinate
+ */
+ virtual bool elementNotMovedInY(NotationElement *);
+
+ /**
+ * Returns true if the item at the given iterator appears to have
+ * moved horizontally without the spacing around it changing.
+ *
+ * In practice, calculates the offset between the intended layout
+ * and current canvas coordinates of the item at the given
+ * iterator, and returns true if this offset is equal to those of
+ * all other following iterators at the same time as well as the
+ * first iterator found at a greater time.
+ */
+ virtual bool elementShiftedOnly(NotationElementList::iterator);
+
+ enum FitPolicy {
+ PretendItFittedAllAlong = 0,
+ MoveBackToFit,
+ SplitToFit
+ };
+
+ /**
+ * Prepare a painter to draw an object of logical width w at
+ * layout-x coord x, starting at offset dx into the object, by
+ * setting the painter's clipping so as to crop the object at the
+ * right edge of the row if it would otherwise overrun. The
+ * return value is the amount of the object visible on this row
+ * (i.e. the increment in offset for the next call to this method)
+ * or zero if no crop was necessary. The canvas coords at which
+ * the object should subsequently be drawn are returned in coords.
+ *
+ * This function calls painter.save(), and the caller must call
+ * painter.restore() after use.
+ */
+ virtual double setPainterClipping(QPainter *, double layoutX, int layoutY,
+ double dx, double w, LinedStaffCoords &coords,
+ FitPolicy policy);
+
+ /**
+ * Set a single pixmap to a notation element, or split it into
+ * bits if it overruns the end of a row and set the bits
+ * separately.
+ */
+ virtual void setPixmap(NotationElement *, QCanvasPixmap *, int z,
+ FitPolicy policy);
+
+ bool isSelected(NotationElementList::iterator);
+
+ typedef std::set<QCanvasSimpleSprite *> SpriteSet;
+ SpriteSet m_timeSigs;
+
+ typedef std::set<QCanvasItem *> ItemSet;
+ ItemSet m_repeatedClefsAndKeys;
+
+ typedef std::pair<int, Clef> ClefChange;
+ FastVector<ClefChange> m_clefChanges;
+
+ typedef std::pair<int, ::Rosegarden::Key> KeyChange;
+ FastVector<KeyChange> m_keyChanges;
+
+ void truncateClefsAndKeysAt(int);
+
+ /** Verify that a possible Clef or Key in bar is already inserted
+ * in m_clefChange or m_keyChange.
+ * If not, do the insertion.
+ */
+ void checkAndCompleteClefsAndKeys(int bar);
+
+ NotePixmapFactory *m_notePixmapFactory;
+ NotePixmapFactory *m_graceNotePixmapFactory;
+ QCanvasSimpleSprite *m_previewSprite;
+ QCanvasSimpleSprite *m_staffName;
+ std::string m_staffNameText;
+ NotationView *m_notationView;
+ int m_legerLineCount;
+ int m_barNumbersEvery;
+ bool m_colourQuantize;
+ bool m_showUnknowns;
+ bool m_showRanges;
+ bool m_showCollisions;
+ int m_keySigCancelMode;
+
+ QPainter *m_printPainter;
+
+ enum BarStatus { UnRendered = 0, Rendered, Positioned };
+ typedef std::map<int, BarStatus> BarStatusMap;
+ BarStatusMap m_status;
+ std::pair<int, int> m_lastRenderCheck;
+ bool m_ready;
+
+ int m_lastRenderedBar;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationStrings.cpp b/src/gui/editors/notation/NotationStrings.cpp
new file mode 100644
index 0000000..6f8defd
--- /dev/null
+++ b/src/gui/editors/notation/NotationStrings.cpp
@@ -0,0 +1,301 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NotationStrings.h"
+#include <kapplication.h>
+
+#include <klocale.h>
+#include "misc/Strings.h"
+#include "document/ConfigGroups.h"
+#include "base/Exception.h"
+#include "base/NotationTypes.h"
+#include "gui/configuration/GeneralConfigurationPage.h"
+#include <kconfig.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+QString
+NotationStrings::addDots(QString s, int dots,
+ bool hyphenate, bool internationalize)
+{
+ if (!dots)
+ return s;
+
+ if (internationalize) {
+ if (dots > 1) {
+ if (hyphenate)
+ return i18n("%1-dotted-%2").arg(dots).arg(s);
+ else
+ return i18n("%1-dotted %2").arg(dots).arg(s);
+ } else {
+ if (hyphenate)
+ return i18n("dotted-%1").arg(s);
+ else
+ return i18n("dotted %1").arg(s);
+ }
+ } else {
+ if (dots > 1) {
+ if (hyphenate)
+ return QString("%1-dotted-%2").arg(dots).arg(s);
+ else
+ return QString("%1-dotted %2").arg(dots).arg(s);
+ } else {
+ if (hyphenate)
+ return QString("dotted-%1").arg(s);
+ else
+ return QString("dotted %1").arg(s);
+ }
+ }
+}
+
+QString
+NotationStrings::getNoteName(Note note, bool plural, bool triplet)
+{
+ Note::Type type = note.getNoteType();
+ int dots = note.getDots();
+
+ static const QString names[] = {
+ i18n("sixty-fourth note"), i18n("thirty-second note"),
+ i18n("sixteenth note"), i18n("eighth note"),
+ i18n("quarter note"), i18n("half note"),
+ i18n("whole note"), i18n("double whole note")
+ };
+ static const QString pluralnames[] = {
+ i18n("sixty-fourth notes"), i18n("thirty-second notes"),
+ i18n("sixteenth notes"), i18n("eighth notes"),
+ i18n("quarter notes"), i18n("half notes"),
+ i18n("whole notes"), i18n("double whole notes")
+ };
+
+ if (plural && triplet) {
+ return addDots(i18n("%1 triplets").arg(names[type]), dots, false, true); // TODO PLURAL - this is broken because it assumes there's only 1 plural form
+ } else if (plural) {
+ return addDots(pluralnames[type], dots, false, true);
+ } else if (triplet) {
+ return addDots(i18n("%1 triplet").arg(names[type]), dots, false, true);
+ } else {
+ return addDots(names[type], dots, false, true);
+ }
+}
+
+QString
+NotationStrings::getAmericanName(Note note, bool plural, bool triplet)
+{
+ Note::Type type = note.getNoteType();
+ int dots = note.getDots();
+
+ static const QString names[] = {
+ "sixty-fourth note", "thirty-second note",
+ "sixteenth note", "eighth note",
+ "quarter note", "half note",
+ "whole note", "double whole note"
+ };
+ static const QString pluralnames[] = {
+ "sixty-fourth notes", "thirty-second notes",
+ "sixteenth notes", "eighth notes",
+ "quarter notes", "half notes",
+ "whole notes", "double whole notes"
+ };
+
+ if (plural && triplet) {
+ return addDots(QString("%1 triplets").arg(names[type]), dots, false, false);
+ } else if (plural) {
+ return addDots(pluralnames[type], dots, false, false);
+ } else if (triplet) {
+ return addDots(QString("%1 triplet").arg(names[type]), dots, false, false);
+ } else {
+ return addDots(names[type], dots, false, false);
+ }
+}
+
+QString
+NotationStrings::getShortNoteName(Note note, bool plural, bool triplet)
+{
+ Note::Type type = note.getNoteType();
+ int dots = note.getDots();
+
+ static const QString names[] = {
+ i18n("64th"), i18n("32nd"), i18n("16th"), i18n("8th"),
+ i18n("quarter"), i18n("half"), i18n("whole"),
+ i18n("double whole")
+ };
+ static const QString pluralnames[] = {
+ i18n("64ths"), i18n("32nds"), i18n("16ths"), i18n("8ths"),
+ i18n("quarters"), i18n("halves"), i18n("wholes"),
+ i18n("double wholes")
+ };
+
+ if (plural && triplet) {
+ return addDots(i18n("%1 triplets").arg(names[type]), dots, false, true); // TODO - this is broken because it assumes there's only 1 plural form
+ } else if (plural) {
+ return addDots(pluralnames[type], dots, false, true);
+ } else if (triplet) {
+ return addDots(i18n("%1 triplet").arg(names[type]), dots, false, true);
+ } else {
+ return addDots(names[type], dots, false, true);
+ }
+}
+
+QString
+NotationStrings::getReferenceName(Note note, bool isRest)
+{
+ Note::Type type = note.getNoteType();
+ int dots = note.getDots();
+
+ static const QString names[] = {
+ "hemidemisemi", "demisemi", "semiquaver",
+ "quaver", "crotchet", "minim", "semibreve", "breve"
+ };
+
+ QString name(names[type]);
+ if (isRest)
+ name = "rest-" + name;
+ return addDots(name, dots, true, false);
+}
+
+Note
+NotationStrings::getNoteForName(QString name)
+{
+ std::string origName(qstrtostr(name));
+ int pos = name.find('-');
+ int dots = 0;
+
+ if (pos > 0 && pos < 6 && pos < int(name.length()) - 1) {
+ dots = name.left(pos).toInt();
+ name = name.right(name.length() - pos - 1);
+ if (dots < 2) {
+ throw MalformedNoteName("Non-numeric or invalid dot count in \"" +
+ origName + "\"");
+ }
+ }
+
+ if (name.length() > 7 &&
+ (name.left(7) == "dotted " || name.left(7) == "dotted-")) {
+ if (dots == 0)
+ dots = 1;
+ name = name.right(name.length() - 7);
+ } else {
+ if (dots > 1) {
+ throw MalformedNoteName("Dot count without dotted tag in \"" +
+ origName + "\"");
+ }
+ }
+
+ if (name.length() > 5 && name.right(5) == " note") {
+ name = name.left(name.length() - 5);
+ }
+
+ Note::Type type;
+
+ static const char *names[][4] = {
+ { "64th", "sixty-fourth", "hemidemisemi", "hemidemisemiquaver"
+ },
+ { "32nd", "thirty-second", "demisemi", "demisemiquaver" },
+ { "16th", "sixteenth", "semi", "semiquaver" },
+ { "8th", "eighth", 0, "quaver" },
+ { "quarter", 0, 0, "crotchet", },
+ { "half", 0, 0, "minim" },
+ { "whole", 0, 0, "semibreve" },
+ { "double whole", 0, 0, "breve" }
+ };
+
+ for (type = Note::Shortest; type <= Note::Longest; ++type) {
+ for (int i = 0; i < 4; ++i) {
+ if (!names[type][i])
+ continue;
+ if (name == names[type][i])
+ return Note(type, dots);
+ }
+ }
+
+ throw MalformedNoteName("Can't parse note name \"" + origName + "\"");
+}
+
+QString
+NotationStrings::makeNoteMenuLabel(timeT duration,
+ bool brief,
+ timeT &errorReturn,
+ bool plural)
+{
+ Note nearestNote = Note::getNearestNote(duration);
+ bool triplet = false;
+ errorReturn = 0;
+
+ if (duration == 0)
+ return "0";
+
+ if (nearestNote.getDuration() != duration) {
+ Note tripletNote = Note::getNearestNote(duration * 3 / 2);
+ if (tripletNote.getDuration() == duration * 3 / 2) {
+ nearestNote = tripletNote;
+ triplet = true;
+ } else {
+ errorReturn = duration - nearestNote.getDuration();
+ duration = nearestNote.getDuration();
+ }
+ }
+
+ KConfig *config = kapp->config();
+ config->setGroup(GeneralOptionsConfigGroup);
+ GeneralConfigurationPage::NoteNameStyle noteNameStyle =
+ (GeneralConfigurationPage::NoteNameStyle)
+ config->readUnsignedNumEntry
+ ("notenamestyle", GeneralConfigurationPage::Local);
+
+ if (brief) {
+
+ timeT wholeNote = Note(Note::Semibreve).getDuration();
+ if ((wholeNote / duration) * duration == wholeNote) {
+ return QString("1/%1").arg(wholeNote / duration);
+ } else if ((duration / wholeNote) * wholeNote == duration) {
+ return QString("%1/1").arg(duration / wholeNote);
+ } else {
+ return i18n("%1 ticks").arg(duration);
+ plural = false;
+ }
+
+ } else {
+ QString noteName;
+
+ switch (noteNameStyle) {
+
+ case GeneralConfigurationPage::American:
+ noteName = getAmericanName(nearestNote, plural, triplet);
+ break;
+
+ case GeneralConfigurationPage::Local:
+ noteName = getNoteName(nearestNote, plural, triplet);
+ break;
+ }
+
+ // Already internationalised, if appropriate
+ return noteName;
+ }
+}
+
+}
diff --git a/src/gui/editors/notation/NotationStrings.h b/src/gui/editors/notation/NotationStrings.h
new file mode 100644
index 0000000..d79dff3
--- /dev/null
+++ b/src/gui/editors/notation/NotationStrings.h
@@ -0,0 +1,121 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTATIONSTRINGS_H_
+#define _RG_NOTATIONSTRINGS_H_
+
+#include "base/Exception.h"
+#include "base/NotationTypes.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+/**
+ * String factory for note names, etc. used in the GUI
+ * Replaces use of base/NotationTypes.h strings which should
+ * be used only for non-user purposes.
+ */
+class NotationStrings
+{
+public:
+ NotationStrings();
+ ~NotationStrings();
+
+
+ /**
+ * Get the name of a note. The default return values are American
+ * (e.g. quarter note, dotted sixteenth note). If the app is
+ * internationalised, you will get return names local to your
+ * region. Note that this includes English note names- set your
+ * LC_LANG to en_GB.
+ */
+ static QString getNoteName(Note note,
+ bool plural = false, bool triplet = false);
+
+ /**
+ * Get the UNTRANSLATED American name of a note. This may be
+ * useful if the user has specified that they'd prefer American
+ * names to local names.
+ */
+ static QString getAmericanName(Note note,
+ bool plural = false, bool triplet = false);
+
+ /**
+ * Get the short name of a note. The default return values are
+ * American (e.g. quarter, dotted 16th). If the app is
+ * internationalised, you will get return names local to your
+ * region. Note that this includes English note names- set your
+ * LC_LANG to en_GB.
+ */
+ static QString getShortNoteName(Note note,
+ bool plural = false, bool triplet = false);
+
+
+ /**
+ * Get the UNTRANSLATED reference name of a note or rest. This is the
+ * formal name used to name pixmap files and the like, so the exact
+ * values of these strings are pretty sensitive.
+ */
+ static QString getReferenceName(Note note, bool isRest = false);
+
+ typedef Exception MalformedNoteName;
+
+ /**
+ * Get the note corresponding to the given string, which must be a
+ * reference name or an untranslated British, American or short name.
+ * May throw MalformedNoteName.
+ */
+ static Note getNoteForName(QString name);
+
+ /**
+ * Construct a label to describe the given duration as a note name in
+ * the proper locale. Uses the nearest available note to the duration
+ * and returns a non-zero value in errorReturn if it was not an exact
+ * match for the required duration.
+ */
+ static QString makeNoteMenuLabel(timeT duration,
+ bool brief,
+ timeT &errorReturn,
+ bool plural = false);
+
+private:
+ /**
+ * Return a string representing the dotted version of the input str.
+ */
+ static QString addDots(QString s, int dots,
+ bool hyphenate, bool internationalize);
+
+};
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationTool.cpp b/src/gui/editors/notation/NotationTool.cpp
new file mode 100644
index 0000000..8e82107
--- /dev/null
+++ b/src/gui/editors/notation/NotationTool.cpp
@@ -0,0 +1,57 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NotationTool.h"
+#include "misc/Debug.h"
+
+#include "gui/general/EditTool.h"
+#include "NotationView.h"
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+NotationTool::NotationTool(const QString& menuName, NotationView* view)
+ : EditTool(menuName, view),
+ m_nParentView(view)
+{}
+
+NotationTool::~NotationTool()
+{
+ NOTATION_DEBUG << "NotationTool::~NotationTool()" << endl;
+
+ // delete m_menu;
+ // m_parentView->factory()->removeClient(this);
+ // m_instance = 0;
+}
+
+void NotationTool::ready()
+{
+ m_nParentView->setCanvasCursor(Qt::arrowCursor);
+ m_nParentView->setHeightTracking(false);
+}
+
+}
diff --git a/src/gui/editors/notation/NotationTool.h b/src/gui/editors/notation/NotationTool.h
new file mode 100644
index 0000000..ab1020a
--- /dev/null
+++ b/src/gui/editors/notation/NotationTool.h
@@ -0,0 +1,93 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTATIONTOOL_H_
+#define _RG_NOTATIONTOOL_H_
+
+#include "gui/general/EditTool.h"
+
+
+class QString;
+
+
+namespace Rosegarden
+{
+
+class NotationView;
+
+
+/**
+ * Notation tool base class.
+ *
+ * A NotationTool represents one of the items on the notation toolbars
+ * (notes, rests, clefs, eraser, etc...). It handle mouse click events
+ * for the NotationView ('State' design pattern).
+ *
+ * A NotationTool can have a menu, normally activated through a right
+ * mouse button click. This menu is defined in an XML file, see
+ * NoteInserter and noteinserter.rc for an example.
+ *
+ * This class is a "semi-singleton", that is, only one instance per
+ * NotationView window is created. This is because menu creation is
+ * slow, and the fact that a tool can trigger the setting of another
+ * tool through a menu choice). This is maintained with the
+ * NotationToolBox class This means we can't rely on the ctor/dtor to
+ * perform setting up, like mouse cursor changes for instance. Use the
+ * ready() and stow() method for this.
+ *
+ * @see NotationView#setTool()
+ * @see NotationToolBox
+ */
+class NotationTool : public EditTool
+{
+ friend class NotationToolBox;
+
+public:
+ virtual ~NotationTool();
+
+ /**
+ * Is called by NotationView when the tool is set as current
+ * Add any setup here
+ */
+ virtual void ready();
+
+protected:
+ /**
+ * Create a new NotationTool
+ *
+ * \a menuName : the name of the menu defined in the XML rc file
+ */
+ NotationTool(const QString& menuName, NotationView*);
+
+ //--------------- Data members ---------------------------------
+
+ NotationView* m_nParentView;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationToolBox.cpp b/src/gui/editors/notation/NotationToolBox.cpp
new file mode 100644
index 0000000..769bcaf
--- /dev/null
+++ b/src/gui/editors/notation/NotationToolBox.cpp
@@ -0,0 +1,102 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NotationToolBox.h"
+
+#include "gui/general/EditToolBox.h"
+#include "gui/general/EditTool.h"
+#include "NotationView.h"
+#include "NoteInserter.h"
+#include "RestInserter.h"
+#include "ClefInserter.h"
+#include "TextInserter.h"
+#include "GuitarChordInserter.h"
+#include "NotationEraser.h"
+#include "NotationSelector.h"
+
+#include <qstring.h>
+#include <kmessagebox.h>
+
+namespace Rosegarden
+{
+
+NotationToolBox::NotationToolBox(NotationView *parent)
+ : EditToolBox(parent),
+ m_nParentView(parent)
+{
+ //m_tools.setAutoDelete(true);
+}
+
+EditTool* NotationToolBox::createTool(const QString& toolName)
+{
+ NotationTool* tool = 0;
+
+ QString toolNamelc = toolName.lower();
+
+ if (toolNamelc == NoteInserter::ToolName)
+
+ tool = new NoteInserter(m_nParentView);
+
+ else if (toolNamelc == RestInserter::ToolName)
+
+ tool = new RestInserter(m_nParentView);
+
+ else if (toolNamelc == ClefInserter::ToolName)
+
+ tool = new ClefInserter(m_nParentView);
+
+ else if (toolNamelc == TextInserter::ToolName)
+
+ tool = new TextInserter(m_nParentView);
+
+ else if (toolNamelc == GuitarChordInserter::ToolName)
+
+ tool = new GuitarChordInserter(m_nParentView);
+
+/* else if (toolNamelc == LilyPondDirectiveInserter::ToolName)
+
+ tool = new LilyPondDirectiveInserter(m_nParentView);*/
+
+ else if (toolNamelc == NotationEraser::ToolName)
+
+ tool = new NotationEraser(m_nParentView);
+
+ else if (toolNamelc == NotationSelector::ToolName)
+
+ tool = new NotationSelector(m_nParentView);
+
+ else {
+ KMessageBox::error(0, QString("NotationToolBox::createTool : unrecognised toolname %1 (%2)")
+ .arg(toolName).arg(toolNamelc));
+ return 0;
+ }
+
+ m_tools.insert(toolName, tool);
+
+ return tool;
+}
+
+}
+#include "NotationToolBox.moc"
diff --git a/src/gui/editors/notation/NotationToolBox.h b/src/gui/editors/notation/NotationToolBox.h
new file mode 100644
index 0000000..48b1202
--- /dev/null
+++ b/src/gui/editors/notation/NotationToolBox.h
@@ -0,0 +1,65 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTATIONTOOLBOX_H_
+#define _RG_NOTATIONTOOLBOX_H_
+
+#include "gui/general/EditToolBox.h"
+
+
+class QString;
+
+
+namespace Rosegarden
+{
+
+class NotationView;
+class EditTool;
+
+
+/**
+ * NotationToolBox : maintains a single instance of each registered tool
+ *
+ * Tools are fetched from a name
+ */
+class NotationToolBox : public EditToolBox
+{
+ Q_OBJECT
+public:
+ NotationToolBox(NotationView* parent);
+
+protected:
+ virtual EditTool* createTool(const QString& toolName);
+
+ //--------------- Data members ---------------------------------
+
+ NotationView* m_nParentView;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationVLayout.cpp b/src/gui/editors/notation/NotationVLayout.cpp
new file mode 100644
index 0000000..c746a30
--- /dev/null
+++ b/src/gui/editors/notation/NotationVLayout.cpp
@@ -0,0 +1,731 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 <cmath>
+#include "NotationVLayout.h"
+#include "misc/Debug.h"
+
+#include <klocale.h>
+#include "base/Composition.h"
+#include "base/Event.h"
+#include "base/LayoutEngine.h"
+#include "base/NotationRules.h"
+#include "base/NotationTypes.h"
+#include "base/NotationQuantizer.h"
+#include "base/Staff.h"
+#include "gui/general/ProgressReporter.h"
+#include "gui/editors/guitar/Chord.h"
+#include "NotationChord.h"
+#include "NotationElement.h"
+#include "NotationProperties.h"
+#include "NotationStaff.h"
+#include "NotePixmapFactory.h"
+#include <kmessagebox.h>
+#include <qobject.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+using namespace BaseProperties;
+
+
+NotationVLayout::NotationVLayout(Composition *c, NotePixmapFactory *npf,
+ const NotationProperties &properties,
+ QObject* parent, const char* name) :
+ ProgressReporter(parent, name),
+ m_composition(c),
+ m_npf(npf),
+ m_notationQuantizer(c->getNotationQuantizer()),
+ m_properties(properties)
+{
+ // empty
+}
+
+NotationVLayout::~NotationVLayout()
+{
+ // empty
+}
+
+NotationVLayout::SlurList &
+
+NotationVLayout::getSlurList(Staff &staff)
+{
+ SlurListMap::iterator i = m_slurs.find(&staff);
+ if (i == m_slurs.end()) {
+ m_slurs[&staff] = SlurList();
+ }
+
+ return m_slurs[&staff];
+}
+
+void
+NotationVLayout::reset()
+{
+ m_slurs.clear();
+}
+
+void
+NotationVLayout::resetStaff(Staff &staff, timeT, timeT)
+{
+ getSlurList(staff).clear();
+}
+
+void
+NotationVLayout::scanStaff(Staff &staffBase, timeT, timeT)
+{
+ START_TIMING;
+
+ NotationStaff &staff = dynamic_cast<NotationStaff &>(staffBase);
+ NotationElementList *notes = staff.getViewElementList();
+
+ NotationElementList::iterator from = notes->begin();
+ NotationElementList::iterator to = notes->end();
+ NotationElementList::iterator i;
+
+ for (i = from; i != to; ++i) {
+
+ NotationElement *el = static_cast<NotationElement*>(*i);
+
+ // Displaced Y will only be used for certain events -- in
+ // particular not for notes, whose y-coord is obviously kind
+ // of meaningful.
+ double displacedY = 0.0;
+ long dyRaw = 0;
+ el->event()->get<Int>(DISPLACED_Y, dyRaw);
+ displacedY = double(dyRaw * m_npf->getLineSpacing()) / 1000.0;
+
+ el->setLayoutY(staff.getLayoutYForHeight( -9) + displacedY);
+
+ if (el->isRest()) {
+
+ // rests for notes longer than the minim have hotspots
+ // aligned with the line above the middle line; the rest
+ // are aligned with the middle line
+
+ long noteType;
+ bool hasNoteType = el->event()->get
+ <Int>(NOTE_TYPE, noteType);
+ if (hasNoteType && noteType > Note::Minim) {
+ el->setLayoutY(staff.getLayoutYForHeight(6) + displacedY);
+ } else {
+ el->setLayoutY(staff.getLayoutYForHeight(4) + displacedY);
+ }
+
+ // Fix for bug 1090767 Rests outside staves have wrong glyphs
+ // by William <rosegarden4c AT orthoset.com>
+ // We use a "rest-outside-stave" glyph for any minim/semibreve/breve
+ // rest that has been displaced vertically e.g. by fine-positioning
+ // outside the stave. For small vertical displacements that keep
+ // the rest inside the stave, we use the "rest-inside-stave" glyph
+ // and also discretise the displacement into multiples of the
+ // stave-line spacing. The outside-stave glyphs match the character
+ // numbers 1D13A, 1D13B and 1D13C in the Unicode 4.0 standard.
+
+ if (hasNoteType && (displacedY > 0.1 || displacedY < -0.1)) {
+
+ // a fiddly check for transition from inside to outside:
+
+ int min = -1, max = 1;
+
+ switch (noteType) {
+ case Note::Breve:
+ min = -1;
+ max = 2;
+ break;
+ case Note::Semibreve:
+ min = -1;
+ max = 3;
+ break;
+ case Note::Minim:
+ min = -2;
+ max = 2;
+ break;
+ case Note::Crotchet:
+ min = -1;
+ max = 3;
+ break;
+ case Note::Quaver:
+ min = -2;
+ max = 3;
+ break;
+ case Note::Semiquaver:
+ min = -3;
+ max = 3;
+ break;
+ case Note::Demisemiquaver:
+ min = -3;
+ max = 4;
+ break;
+ case Note::Hemidemisemiquaver:
+ min = -4;
+ max = 4;
+ break;
+ }
+
+ bool outside = false;
+
+ if (noteType == Note::Breve) {
+ if (nearbyint(displacedY) < min * m_npf->getLineSpacing() ||
+ nearbyint(displacedY) > max * m_npf->getLineSpacing()) {
+ outside = true;
+ }
+ } else {
+ if ((int)displacedY < min * m_npf->getLineSpacing() ||
+ (int)displacedY > max * m_npf->getLineSpacing()) {
+ outside = true;
+ }
+ }
+
+ el->event()->setMaybe<Bool>(m_properties.REST_OUTSIDE_STAVE,
+ outside);
+
+ if (!outside) {
+ displacedY = (double)m_npf->getLineSpacing() *
+ (int(nearbyint((double)displacedY /
+ m_npf->getLineSpacing())));
+ if (noteType > Note::Minim)
+ el->setLayoutY(staff.getLayoutYForHeight(6) + displacedY);
+ else
+ el->setLayoutY(staff.getLayoutYForHeight(4) + displacedY);
+ }
+
+ // if (displacedY != 0.0)
+ // NOTATION_DEBUG << "REST_OUTSIDE_STAVE AFTER "
+ // << " : displacedY : " << displacedY
+ // << " line-spacing : " << m_npf->getLineSpacing()
+ // << " time : " << (el->getViewAbsoluteTime())
+ // << endl;
+ } else {
+ el->event()->setMaybe<Bool>(m_properties.REST_OUTSIDE_STAVE,
+ false);
+ }
+
+ } else if (el->isNote()) {
+
+ NotationChord chord(*notes, i, m_notationQuantizer, m_properties);
+ if (chord.size() == 0)
+ continue;
+
+ std::vector<int> h;
+ for (unsigned int j = 0; j < chord.size(); ++j) {
+ long height = 0;
+ if (!(*chord[j])->event()->get
+ <Int>
+ (m_properties.HEIGHT_ON_STAFF, height)) {
+ std::cerr << QString("ERROR: Event in chord at %1 has no HEIGHT_ON_STAFF property!\nThis is a bug (the program would previously have crashed by now)").arg((*chord[j])->getViewAbsoluteTime()) << std::endl;
+ (*chord[j])->event()->dump(std::cerr);
+ }
+ h.push_back(height);
+ }
+ bool stemmed = chord.hasStem();
+ bool stemUp = chord.hasStemUp();
+ bool hasNoteHeadShifted = chord.hasNoteHeadShifted();
+
+ unsigned int flaggedNote = (stemUp ? chord.size() - 1 : 0);
+
+ bool hasShifted = chord.hasNoteHeadShifted();
+
+ double y0 = -1E50; // A very unlikely Y layout value
+
+ for (unsigned int j = 0; j < chord.size(); ++j) {
+
+ el = static_cast<NotationElement*>(*chord[j]);
+ el->setLayoutY(staff.getLayoutYForHeight(h[j]));
+
+ // Look for collision
+ const double eps = 0.001;
+ Event *eel = el->event();
+ double y = el->getLayoutY();
+ if (eel->has("pitch")) {
+ el->setIsColliding(fabs(y - y0) < eps);
+ y0 = y;
+ }
+
+
+ // These calculations and assignments are pretty much final
+ // if the chord is not in a beamed group, but if it is then
+ // they will be reworked by NotationGroup::applyBeam, which
+ // is called from NotationHLayout::layout, which is called
+ // after this. Any inaccuracies here for beamed groups
+ // should be stamped out there.
+
+ // el->event()->setMaybe<Bool>(STEM_UP, stemUp);
+ el->event()->setMaybe<Bool>(m_properties.VIEW_LOCAL_STEM_UP, stemUp);
+
+ bool primary =
+ ((stemmed && stemUp) ? (j == 0) : (j == chord.size() - 1));
+ el->event()->setMaybe<Bool>
+ (m_properties.CHORD_PRIMARY_NOTE, primary);
+
+ if (primary) {
+ el->event()->setMaybe<Int>
+ (m_properties.CHORD_MARK_COUNT, chord.getMarkCountForChord());
+ }
+
+ bool shifted = chord.isNoteHeadShifted(chord[j]);
+ el->event()->setMaybe<Bool>
+ (m_properties.NOTE_HEAD_SHIFTED, shifted);
+
+ el->event()->setMaybe<Bool>
+ (m_properties.NOTE_DOT_SHIFTED, false);
+ if (hasShifted && stemUp) {
+ long dots = 0;
+ (void)el->event()->get
+ <Int>(NOTE_DOTS, dots);
+ if (dots > 0) {
+ el->event()->setMaybe<Bool>
+ (m_properties.NOTE_DOT_SHIFTED, true);
+ }
+ }
+
+ el->event()->setMaybe<Bool>
+ (m_properties.NEEDS_EXTRA_SHIFT_SPACE,
+ hasNoteHeadShifted && !stemUp);
+
+ el->event()->setMaybe<Bool>
+ (m_properties.DRAW_FLAG, j == flaggedNote);
+
+ int stemLength = -1;
+ if (j != flaggedNote) {
+ stemLength = staff.getLayoutYForHeight(h[flaggedNote]) -
+ staff.getLayoutYForHeight(h[j]);
+ if (stemLength < 0)
+ stemLength = -stemLength;
+ // NOTATION_DEBUG << "Setting stem length to "
+ // << stemLength << endl;
+ } else {
+ int minStemLength = stemLength;
+ if (h[j] < -2 && stemUp) {
+ //!!! needs tuning, & applying for beamed stems too
+ minStemLength = staff.getLayoutYForHeight(h[j]) -
+ staff.getLayoutYForHeight(4);
+ } else if (h[j] > 10 && !stemUp) {
+ minStemLength = staff.getLayoutYForHeight(4) -
+ staff.getLayoutYForHeight(h[j]);
+ }
+ stemLength = std::max(minStemLength, stemLength);
+ }
+
+ el->event()->setMaybe<Int>
+ (m_properties.UNBEAMED_STEM_LENGTH, stemLength);
+ }
+
+
+ // #938545 (Broken notation: Duplicated note can float
+ // outside stave) -- Need to cope with the case where a
+ // note that's not a member of a chord (different stem
+ // direction &c) falls between notes that are members.
+ // Not optimal, as we can end up scanning the chord
+ // multiple times (we'll return to it after scanning the
+ // contained note). [We can't just iterate over all
+ // elements within the chord (as we can in hlayout)
+ // because we need them in height order.]
+
+ i = chord.getFirstElementNotInChord();
+ if (i == notes->end())
+ i = chord.getFinalElement();
+ else
+ --i;
+
+ } else {
+
+ if (el->event()->isa(Clef::EventType)) {
+
+ // clef pixmaps have the hotspot placed to coincide
+ // with the pitch of the clef -- so the alto clef
+ // should be "on" the middle line, the treble clef
+ // "on" the line below the middle, etc
+
+ el->setLayoutY(staff.getLayoutYForHeight
+ (Clef(*el->event()).getAxisHeight()));
+
+ } else if (el->event()->isa(Rosegarden::Key::EventType)) {
+
+ el->setLayoutY(staff.getLayoutYForHeight(12));
+
+ } else if (el->event()->isa(Text::EventType)) {
+
+ std::string type = Text::UnspecifiedType;
+ el->event()->get<String>(Text::TextTypePropertyName, type);
+
+ if (type == Text::Dynamic ||
+ type == Text::LocalDirection ||
+ type == Text::UnspecifiedType) {
+ el->setLayoutY(staff.getLayoutYForHeight(-7) + displacedY);
+ } else if (type == Text::Lyric) {
+ long verse = 0;
+ el->event()->get<Int>(Text::LyricVersePropertyName, verse);
+ el->setLayoutY(staff.getLayoutYForHeight(-10 - 3 * verse) + displacedY);
+ } else if (type == Text::Annotation) {
+ el->setLayoutY(staff.getLayoutYForHeight(-13) + displacedY);
+ } else {
+ el->setLayoutY(staff.getLayoutYForHeight(22) + displacedY);
+ }
+
+ } else if (el->event()->isa(Indication::EventType)) {
+
+ try {
+ std::string indicationType =
+ el->event()->get
+ <String>(Indication::IndicationTypePropertyName);
+
+ if (indicationType == Indication::Slur ||
+ indicationType == Indication::PhrasingSlur) {
+ getSlurList(staff).push_back(i);
+ }
+
+ if (indicationType == Indication::OttavaUp ||
+ indicationType == Indication::QuindicesimaUp) {
+ el->setLayoutY(staff.getLayoutYForHeight(15) + displacedY);
+ } else {
+ el->setLayoutY(staff.getLayoutYForHeight( -9) + displacedY);
+ }
+ } catch (...) {
+ el->setLayoutY(staff.getLayoutYForHeight( -9) + displacedY);
+ }
+
+ } else if (el->event()->isa(Guitar::Chord::EventType)) {
+
+ el->setLayoutY(staff.getLayoutYForHeight(22) + displacedY);
+ }
+ }
+ }
+
+ PRINT_ELAPSED("NotationVLayout::scanStaff");
+}
+
+void
+NotationVLayout::finishLayout(timeT, timeT)
+{
+ START_TIMING;
+
+ for (SlurListMap::iterator mi = m_slurs.begin();
+ mi != m_slurs.end(); ++mi) {
+
+ for (SlurList::iterator si = mi->second.begin();
+ si != mi->second.end(); ++si) {
+
+ NotationElementList::iterator i = *si;
+ NotationStaff &staff = dynamic_cast<NotationStaff &>(*(mi->first));
+
+ positionSlur(staff, i);
+ }
+ }
+
+ PRINT_ELAPSED("NotationVLayout::finishLayout");
+}
+
+void
+NotationVLayout::positionSlur(NotationStaff &staff,
+ NotationElementList::iterator i)
+{
+ NotationRules rules;
+
+ bool phrasing = ((*i)->event()->get
+ <String>(Indication::IndicationTypePropertyName)
+ == Indication::PhrasingSlur);
+
+ NotationElementList::iterator scooter = i;
+
+ timeT slurDuration = (*i)->event()->getDuration();
+ if (slurDuration == 0 && (*i)->event()->has("indicationduration")) {
+ slurDuration = (*i)->event()->get
+ <Int>("indicationduration"); // obs property
+ }
+ timeT endTime = (*i)->getViewAbsoluteTime() + slurDuration;
+
+ bool haveStart = false;
+
+ int startTopHeight = 4, endTopHeight = 4,
+ startBottomHeight = 4, endBottomHeight = 4,
+ maxTopHeight = 4, minBottomHeight = 4,
+ maxCount = 0, minCount = 0;
+
+ int startX = (int)(*i)->getLayoutX(), endX = startX + 10;
+ bool startStemUp = false, endStemUp = false;
+ long startMarks = 0, endMarks = 0;
+ bool startTied = false, endTied = false;
+ bool beamAbove = false, beamBelow = false;
+ bool dynamic = false;
+
+ std::vector<Event *> stemUpNotes, stemDownNotes;
+
+ // Scan the notes spanned by the slur, recording the top and
+ // bottom heights of the first and last chords, plus the presence
+ // of any troublesome beams and high or low notes in the body.
+
+ while (scooter != staff.getViewElementList()->end()) {
+
+ if ((*scooter)->getViewAbsoluteTime() >= endTime)
+ break;
+ Event *event = (*scooter)->event();
+
+ if (event->isa(Note::EventType)) {
+
+ long h = 0;
+ if (!event->get
+ <Int>(m_properties.HEIGHT_ON_STAFF, h)) {
+ KMessageBox::sorry
+ ((QWidget *)parent(), i18n("Spanned note at %1 has no HEIGHT_ON_STAFF property!\nThis is a bug (the program would previously have crashed by now)").arg((*scooter)->getViewAbsoluteTime()));
+ event->dump(std::cerr);
+ }
+
+ bool stemUp = rules.isStemUp(h);
+ event->get
+ <Bool>(m_properties.VIEW_LOCAL_STEM_UP, stemUp);
+
+ bool beamed = false;
+ event->get
+ <Bool>(m_properties.BEAMED, beamed);
+
+ bool primary = false;
+
+ if (event->get
+ <Bool>
+ (m_properties.CHORD_PRIMARY_NOTE, primary) && primary) {
+
+ NotationChord chord(*(staff.getViewElementList()), scooter,
+ m_notationQuantizer, m_properties);
+
+ if (beamed) {
+ if (stemUp)
+ beamAbove = true;
+ else
+ beamBelow = true;
+ }
+
+ if (!haveStart) {
+
+ startBottomHeight = chord.getLowestNoteHeight();
+ startTopHeight = chord.getHighestNoteHeight();
+ minBottomHeight = startBottomHeight;
+ maxTopHeight = startTopHeight;
+
+ startX = (int)(*scooter)->getLayoutX();
+ startStemUp = stemUp;
+ startMarks = chord.getMarkCountForChord();
+
+ bool tied = false;
+ if ((event->get
+ <Bool>(TIED_FORWARD, tied) && tied) ||
+ (event->get<Bool>(TIED_BACKWARD, tied) && tied)) {
+ startTied = true;
+ }
+
+ haveStart = true;
+
+ } else {
+ if (chord.getLowestNoteHeight() < minBottomHeight) {
+ minBottomHeight = chord.getLowestNoteHeight();
+ ++minCount;
+ }
+ if (chord.getHighestNoteHeight() > maxTopHeight) {
+ maxTopHeight = chord.getHighestNoteHeight();
+ ++maxCount;
+ }
+ }
+
+ endBottomHeight = chord.getLowestNoteHeight();
+ endTopHeight = chord.getHighestNoteHeight();
+ endX = (int)(*scooter)->getLayoutX();
+ endStemUp = stemUp;
+ endMarks = chord.getMarkCountForChord();
+
+ bool tied = false;
+ if ((event->get
+ <Bool>(TIED_FORWARD, tied) && tied) ||
+ (event->get<Bool>(TIED_BACKWARD, tied) && tied)) {
+ endTied = true;
+ }
+ }
+
+ if (!beamed) {
+ if (stemUp)
+ stemUpNotes.push_back(event);
+ else
+ stemDownNotes.push_back(event);
+ }
+
+ } else if (event->isa(Indication::EventType)) {
+
+ try {
+ std::string indicationType =
+ event->get
+ <String>(Indication::IndicationTypePropertyName);
+
+ if (indicationType == Indication::Crescendo ||
+ indicationType == Indication::Decrescendo)
+ dynamic = true;
+ } catch (...) { }
+ }
+
+ ++scooter;
+ }
+
+ bool above = true;
+
+ if ((*i)->event()->has(NotationProperties::SLUR_ABOVE) &&
+ (*i)->event()->isPersistent<Bool>(NotationProperties::SLUR_ABOVE)) {
+
+ (*i)->event()->get
+ <Bool>(NotationProperties::SLUR_ABOVE, above);
+
+ } else if (phrasing) {
+
+ int score = 0; // for "above"
+
+ if (dynamic)
+ score += 2;
+
+ if (startStemUp == endStemUp) {
+ if (startStemUp)
+ score -= 2;
+ else
+ score += 2;
+ } else if (beamBelow != beamAbove) {
+ if (beamAbove)
+ score -= 2;
+ else
+ score += 2;
+ }
+
+ if (maxTopHeight < 6)
+ score += 1;
+ else if (minBottomHeight > 2)
+ score -= 1;
+
+ if (stemUpNotes.size() != stemDownNotes.size()) {
+ if (stemUpNotes.size() < stemDownNotes.size())
+ score += 1;
+ else
+ score -= 1;
+ }
+
+ above = (score >= 0);
+
+ } else {
+
+ if (startStemUp == endStemUp) {
+ above = !startStemUp;
+ } else if (beamBelow) {
+ above = true;
+ } else if (beamAbove) {
+ above = false;
+ } else if (stemUpNotes.size() != stemDownNotes.size()) {
+ above = (stemUpNotes.size() < stemDownNotes.size());
+ } else {
+ above = ((startTopHeight - 4) + (endTopHeight - 4) +
+ (4 - startBottomHeight) + (4 - endBottomHeight) <= 8);
+ }
+ }
+
+ // now choose the actual y-coord of the slur based on the side
+ // we've decided to put it on
+
+ int startHeight, endHeight;
+ int startOffset = 2, endOffset = 2;
+
+ if (above) {
+
+ if (!startStemUp)
+ startOffset += startMarks * 2;
+ else
+ startOffset += 5;
+
+ if (!endStemUp)
+ endOffset += startMarks * 2;
+ else
+ endOffset += 5;
+
+ startHeight = startTopHeight + startOffset;
+ endHeight = endTopHeight + endOffset;
+
+ bool maxRelevant = ((maxTopHeight != endTopHeight) || (maxCount > 1));
+ if (maxRelevant) {
+ int midHeight = (startHeight + endHeight) / 2;
+ if (maxTopHeight > midHeight - 1) {
+ startHeight += maxTopHeight - midHeight + 1;
+ endHeight += maxTopHeight - midHeight + 1;
+ }
+ }
+
+ } else {
+
+ if (startStemUp)
+ startOffset += startMarks * 2;
+ else
+ startOffset += 5;
+
+ if (endStemUp)
+ endOffset += startMarks * 2;
+ else
+ endOffset += 5;
+
+ startHeight = startBottomHeight - startOffset;
+ endHeight = endBottomHeight - endOffset;
+
+ bool minRelevant = ((minBottomHeight != endBottomHeight) || (minCount > 1));
+ if (minRelevant) {
+ int midHeight = (startHeight + endHeight) / 2;
+ if (minBottomHeight < midHeight + 1) {
+ startHeight -= midHeight - minBottomHeight + 1;
+ endHeight -= midHeight - minBottomHeight + 1;
+ }
+ }
+ }
+
+ int y0 = staff.getLayoutYForHeight(startHeight),
+ y1 = staff.getLayoutYForHeight(endHeight);
+
+ int dy = y1 - y0;
+ int length = endX - startX;
+ int diff = staff.getLayoutYForHeight(0) - staff.getLayoutYForHeight(3);
+ if (length < diff*10)
+ diff /= 2;
+ if (length > diff*3)
+ length -= diff / 2;
+ startX += diff;
+
+ (*i)->event()->setMaybe<Bool>(NotationProperties::SLUR_ABOVE, above);
+ (*i)->event()->setMaybe<Int>(m_properties.SLUR_Y_DELTA, dy);
+ (*i)->event()->setMaybe<Int>(m_properties.SLUR_LENGTH, length);
+
+ double displacedX = 0.0, displacedY = 0.0;
+
+ long dxRaw = 0;
+ (*i)->event()->get<Int>(DISPLACED_X, dxRaw);
+ displacedX = double(dxRaw * m_npf->getNoteBodyWidth()) / 1000.0;
+
+ long dyRaw = 0;
+ (*i)->event()->get<Int>(DISPLACED_Y, dyRaw);
+ displacedY = double(dyRaw * m_npf->getLineSpacing()) / 1000.0;
+
+ (*i)->setLayoutX(startX + displacedX);
+ (*i)->setLayoutY(y0 + displacedY);
+}
+
+}
diff --git a/src/gui/editors/notation/NotationVLayout.h b/src/gui/editors/notation/NotationVLayout.h
new file mode 100644
index 0000000..83a16c1
--- /dev/null
+++ b/src/gui/editors/notation/NotationVLayout.h
@@ -0,0 +1,122 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTATIONVLAYOUT_H_
+#define _RG_NOTATIONVLAYOUT_H_
+
+#include "base/FastVector.h"
+#include "base/LayoutEngine.h"
+#include "gui/general/ProgressReporter.h"
+#include <map>
+#include "base/Event.h"
+
+#include "NotationElement.h"
+
+
+class SlurList;
+class QObject;
+
+
+namespace Rosegarden
+{
+
+class Staff;
+class Quantizer;
+class Composition;
+class NotePixmapFactory;
+class NotationStaff;
+class NotationProperties;
+class Composition;
+
+
+/**
+ * Vertical notation layout
+ *
+ * computes the Y coordinate of notation elements
+ */
+
+class NotationVLayout : public ProgressReporter,
+ public VerticalLayoutEngine
+{
+public:
+ NotationVLayout(Composition *c, NotePixmapFactory *npf,
+ const NotationProperties &properties,
+ QObject* parent, const char* name = 0);
+
+ virtual ~NotationVLayout();
+
+ void setNotePixmapFactory(NotePixmapFactory *npf) {
+ m_npf = npf;
+ }
+
+ /**
+ * Resets internal data stores for all staffs
+ */
+ virtual void reset();
+
+ /**
+ * Resets internal data stores for a specific staff
+ */
+ virtual void resetStaff(Staff &,
+ timeT = 0,
+ timeT = 0);
+
+ /**
+ * Lay out a single staff.
+ */
+ virtual void scanStaff(Staff &,
+ timeT = 0,
+ timeT = 0);
+
+ /**
+ * Do any layout dependent on more than one staff. As it
+ * happens, we have none, but we do have some layout that
+ * depends on the final results from the horizontal layout
+ * (for slurs), so we should do that here
+ */
+ virtual void finishLayout(timeT = 0,
+ timeT = 0);
+
+private:
+ void positionSlur(NotationStaff &staff, NotationElementList::iterator i);
+
+ typedef FastVector<NotationElementList::iterator> SlurList;
+ typedef std::map<Staff *, SlurList> SlurListMap;
+
+ //--------------- Data members ---------------------------------
+
+ SlurListMap m_slurs;
+ SlurList &getSlurList(Staff &);
+
+ Composition *m_composition;
+ NotePixmapFactory *m_npf;
+ const Quantizer *m_notationQuantizer;
+ const NotationProperties &m_properties;
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotationView.cpp b/src/gui/editors/notation/NotationView.cpp
new file mode 100644
index 0000000..66cb4b3
--- /dev/null
+++ b/src/gui/editors/notation/NotationView.cpp
@@ -0,0 +1,7552 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NotationView.h"
+#include <list>
+#include <qlayout.h>
+#include "misc/Debug.h"
+#include <kapplication.h>
+
+#include "gui/editors/segment/TrackEditor.h"
+#include "gui/editors/segment/TrackButtons.h"
+#include "base/BaseProperties.h"
+#include <klocale.h>
+#include <kstddirs.h>
+#include "misc/Strings.h"
+#include "base/AnalysisTypes.h"
+#include "base/Clipboard.h"
+#include "base/Composition.h"
+#include "base/CompositionTimeSliceAdapter.h"
+#include "base/Configuration.h"
+#include "base/Device.h"
+#include "base/Event.h"
+#include "base/Exception.h"
+#include "base/Instrument.h"
+#include "base/MidiDevice.h"
+#include "base/MidiTypes.h"
+#include "base/NotationTypes.h"
+#include "base/Profiler.h"
+#include "base/PropertyName.h"
+#include "base/NotationQuantizer.h"
+#include "base/RealTime.h"
+#include "base/RulerScale.h"
+#include "base/Segment.h"
+#include "base/Selection.h"
+#include "base/Staff.h"
+#include "base/Studio.h"
+#include "base/Track.h"
+#include "ClefInserter.h"
+#include "commands/edit/AddDotCommand.h"
+#include "commands/edit/ClearTriggersCommand.h"
+#include "commands/edit/CollapseNotesCommand.h"
+#include "commands/edit/CopyCommand.h"
+#include "commands/edit/CutAndCloseCommand.h"
+#include "commands/edit/CutCommand.h"
+#include "commands/edit/EraseCommand.h"
+#include "commands/edit/EventEditCommand.h"
+#include "commands/edit/EventQuantizeCommand.h"
+#include "commands/edit/InsertTriggerNoteCommand.h"
+#include "commands/edit/PasteEventsCommand.h"
+#include "commands/edit/SetLyricsCommand.h"
+#include "commands/edit/SetNoteTypeCommand.h"
+#include "commands/edit/SetTriggerCommand.h"
+#include "commands/edit/TransposeCommand.h"
+#include "commands/notation/AddFingeringMarkCommand.h"
+#include "commands/notation/AddIndicationCommand.h"
+#include "commands/notation/AddMarkCommand.h"
+#include "commands/notation/AddSlashesCommand.h"
+#include "commands/notation/AddTextMarkCommand.h"
+#include "commands/notation/AutoBeamCommand.h"
+#include "commands/notation/BeamCommand.h"
+#include "commands/notation/BreakCommand.h"
+#include "commands/notation/ChangeSlurPositionCommand.h"
+#include "commands/notation/ChangeTiePositionCommand.h"
+#include "commands/notation/ChangeStemsCommand.h"
+#include "commands/notation/ChangeStyleCommand.h"
+#include "commands/notation/ClefInsertionCommand.h"
+#include "commands/notation/CollapseRestsCommand.h"
+#include "commands/notation/DeCounterpointCommand.h"
+#include "commands/notation/EraseEventCommand.h"
+#include "commands/notation/FixNotationQuantizeCommand.h"
+#include "commands/notation/IncrementDisplacementsCommand.h"
+#include "commands/notation/InterpretCommand.h"
+#include "commands/notation/KeyInsertionCommand.h"
+#include "commands/notation/MakeAccidentalsCautionaryCommand.h"
+#include "commands/notation/MakeChordCommand.h"
+#include "commands/notation/MakeNotesViableCommand.h"
+#include "commands/notation/MultiKeyInsertionCommand.h"
+#include "commands/notation/NormalizeRestsCommand.h"
+#include "commands/notation/RemoveFingeringMarksCommand.h"
+#include "commands/notation/RemoveMarksCommand.h"
+#include "commands/notation/RemoveNotationQuantizeCommand.h"
+#include "commands/notation/ResetDisplacementsCommand.h"
+#include "commands/notation/RespellCommand.h"
+#include "commands/notation/RestoreSlursCommand.h"
+#include "commands/notation/RestoreTiesCommand.h"
+#include "commands/notation/RestoreStemsCommand.h"
+#include "commands/notation/SetVisibilityCommand.h"
+#include "commands/notation/SustainInsertionCommand.h"
+#include "commands/notation/TextInsertionCommand.h"
+#include "commands/notation/TieNotesCommand.h"
+#include "commands/notation/TupletCommand.h"
+#include "commands/notation/UntieNotesCommand.h"
+#include "commands/notation/UnTupletCommand.h"
+#include "commands/segment/PasteToTriggerSegmentCommand.h"
+#include "commands/segment/SegmentSyncCommand.h"
+#include "commands/segment/SegmentTransposeCommand.h"
+#include "commands/segment/RenameTrackCommand.h"
+#include "document/RosegardenGUIDoc.h"
+#include "document/ConfigGroups.h"
+#include "document/io/LilyPondExporter.h"
+#include "GuitarChordInserter.h"
+#include "gui/application/SetWaitCursor.h"
+#include "gui/application/RosegardenGUIView.h"
+#include "gui/dialogs/ClefDialog.h"
+#include "gui/dialogs/EventEditDialog.h"
+#include "gui/dialogs/InterpretDialog.h"
+#include "gui/dialogs/IntervalDialog.h"
+#include "gui/dialogs/KeySignatureDialog.h"
+#include "gui/dialogs/LilyPondOptionsDialog.h"
+#include "gui/dialogs/LyricEditDialog.h"
+#include "gui/dialogs/MakeOrnamentDialog.h"
+#include "gui/dialogs/PasteNotationDialog.h"
+#include "gui/dialogs/QuantizeDialog.h"
+#include "gui/dialogs/SimpleEventEditDialog.h"
+#include "gui/dialogs/TextEventDialog.h"
+#include "gui/dialogs/TupletDialog.h"
+#include "gui/dialogs/UseOrnamentDialog.h"
+#include "gui/rulers/StandardRuler.h"
+#include "gui/general/ActiveItem.h"
+#include "gui/general/ClefIndex.h"
+#include "gui/general/EditViewBase.h"
+#include "gui/general/EditView.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/LinedStaff.h"
+#include "gui/general/LinedStaffManager.h"
+#include "gui/general/ProgressReporter.h"
+#include "gui/general/PresetHandlerDialog.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "gui/kdeext/KTmpStatusMsg.h"
+#include "gui/kdeext/QCanvasSimpleSprite.h"
+#include "gui/rulers/ChordNameRuler.h"
+#include "gui/rulers/RawNoteRuler.h"
+#include "gui/rulers/TempoRuler.h"
+#include "gui/rulers/LoopRuler.h"
+#include "gui/studio/StudioControl.h"
+#include "gui/dialogs/EventFilterDialog.h"
+#include "gui/widgets/ProgressBar.h"
+#include "gui/widgets/ProgressDialog.h"
+#include "gui/widgets/ScrollBoxDialog.h"
+#include "gui/widgets/ScrollBox.h"
+#include "gui/widgets/QDeferScrollView.h"
+#include "NotationCanvasView.h"
+#include "NotationElement.h"
+#include "NotationEraser.h"
+#include "NotationHLayout.h"
+#include "NotationProperties.h"
+#include "NotationSelector.h"
+#include "NotationStaff.h"
+#include "NotationStrings.h"
+#include "NotationToolBox.h"
+#include "NotationVLayout.h"
+#include "NoteFontFactory.h"
+#include "NoteInserter.h"
+#include "NotePixmapFactory.h"
+#include "NoteStyleFactory.h"
+#include "NoteStyle.h"
+#include "RestInserter.h"
+#include "sound/MappedEvent.h"
+#include "TextInserter.h"
+#include "HeadersGroup.h"
+#include <kaction.h>
+#include <kcombobox.h>
+#include <kconfig.h>
+#include <kglobal.h>
+#include <klineeditdlg.h>
+#include <kmessagebox.h>
+#include <kprinter.h>
+#include <kprocess.h>
+#include <kprogress.h>
+#include <kstatusbar.h>
+#include <kstdaction.h>
+#include <ktempfile.h>
+#include <ktoolbar.h>
+#include <kxmlguiclient.h>
+#include <qbrush.h>
+#include <qcanvas.h>
+#include <qcursor.h>
+#include <qdialog.h>
+#include <qevent.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qhbox.h>
+#include <qiconset.h>
+#include <qlabel.h>
+#include <qobject.h>
+#include <qpaintdevicemetrics.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qprinter.h>
+#include <qrect.h>
+#include <qregexp.h>
+#include <qsize.h>
+#include <qstring.h>
+#include <qtimer.h>
+#include <qwidget.h>
+#include <qvalidator.h>
+#include <algorithm>
+#include <qpushbutton.h>
+#include <qtooltip.h>
+
+
+namespace Rosegarden
+{
+
+class NoteActionData
+{
+public:
+ NoteActionData();
+ NoteActionData(const QString& _title,
+ QString _actionName,
+ QString _pixmapName,
+ int _keycode,
+ bool _rest,
+ Note::Type _noteType,
+ int _dots);
+
+ QString title;
+ QString actionName;
+ QString pixmapName;
+ int keycode;
+ bool rest;
+ Note::Type noteType;
+ int dots;
+};
+
+NoteActionData::NoteActionData()
+ : title(0),
+ actionName(0),
+ pixmapName(0),
+ keycode(0),
+ rest(false),
+ noteType(0),
+ dots(0)
+{
+}
+
+NoteActionData::NoteActionData(const QString& _title,
+ QString _actionName,
+ QString _pixmapName,
+ int _keycode,
+ bool _rest,
+ Note::Type _noteType,
+ int _dots)
+ : title(_title),
+ actionName(_actionName),
+ pixmapName(_pixmapName),
+ keycode(_keycode),
+ rest(_rest),
+ noteType(_noteType),
+ dots(_dots)
+{
+}
+
+
+class NoteChangeActionData
+{
+public:
+ NoteChangeActionData();
+ NoteChangeActionData(const QString &_title,
+ QString _actionName,
+ QString _pixmapName,
+ int _keycode,
+ bool _notationOnly,
+ Note::Type _noteType);
+
+ QString title;
+ QString actionName;
+ QString pixmapName;
+ int keycode;
+ bool notationOnly;
+ Note::Type noteType;
+};
+
+NoteChangeActionData::NoteChangeActionData()
+ : title(0),
+ actionName(0),
+ pixmapName(0),
+ keycode(0),
+ notationOnly(false),
+ noteType(0)
+{
+}
+
+NoteChangeActionData::NoteChangeActionData(const QString& _title,
+ QString _actionName,
+ QString _pixmapName,
+ int _keycode,
+ bool _notationOnly,
+ Note::Type _noteType)
+ : title(_title),
+ actionName(_actionName),
+ pixmapName(_pixmapName),
+ keycode(_keycode),
+ notationOnly(_notationOnly),
+ noteType(_noteType)
+{
+}
+
+
+class MarkActionData
+{
+public:
+ MarkActionData() :
+ title(0),
+ actionName(0),
+ keycode(0) { }
+
+ MarkActionData(const QString &_title,
+ QString _actionName,
+ int _keycode,
+ Mark _mark) :
+ title(_title),
+ actionName(_actionName),
+ keycode(_keycode),
+ mark(_mark) { }
+
+ QString title;
+ QString actionName;
+ int keycode;
+ Mark mark;
+};
+
+
+NotationView::NotationView(RosegardenGUIDoc *doc,
+ std::vector<Segment *> segments,
+ QWidget *parent,
+ bool showProgressive) :
+ EditView(doc, segments, 2, parent, "notationview"),
+ m_properties(getViewLocalPropertyPrefix()),
+ m_selectionCounter(0),
+ m_insertModeLabel(0),
+ m_annotationsLabel(0),
+ m_lilyPondDirectivesLabel(0),
+ m_progressBar(0),
+ m_currentNotePixmap(0),
+ m_hoveredOverNoteName(0),
+ m_hoveredOverAbsoluteTime(0),
+ m_currentStaff( -1),
+ m_lastFinishingStaff( -1),
+ m_title(0),
+ m_subtitle(0),
+ m_composer(0),
+ m_copyright(0),
+ m_insertionTime(0),
+ m_deferredCursorMove(NoCursorMoveNeeded),
+ m_lastNoteAction("crotchet"),
+ m_fontName(NoteFontFactory::getDefaultFontName()),
+ m_fontSize(NoteFontFactory::getDefaultSize(m_fontName)),
+ m_pageMode(LinedStaff::LinearMode),
+ m_leftGutter(20),
+ m_notePixmapFactory(new NotePixmapFactory(m_fontName, m_fontSize)),
+ m_hlayout(new NotationHLayout(&doc->getComposition(), m_notePixmapFactory,
+ m_properties, this)),
+ m_vlayout(new NotationVLayout(&doc->getComposition(), m_notePixmapFactory,
+ m_properties, this)),
+ m_chordNameRuler(0),
+ m_tempoRuler(0),
+ m_rawNoteRuler(0),
+ m_annotationsVisible(false),
+ m_lilyPondDirectivesVisible(false),
+ m_selectDefaultNote(0),
+ m_fontCombo(0),
+ m_fontSizeCombo(0),
+ m_spacingCombo(0),
+ m_fontSizeActionMenu(0),
+ m_pannerDialog(new ScrollBoxDialog(this, ScrollBox::FixHeight)),
+ m_renderTimer(0),
+ m_playTracking(true),
+ m_progressDisplayer(PROGRESS_NONE),
+ m_inhibitRefresh(true),
+ m_ok(false),
+ m_printMode(false),
+ m_printSize(8), // set in positionStaffs
+ m_showHeadersGroup(0),
+ m_headersGroupView(0),
+ m_headersGroup(0),
+ m_headersTopFrame(0),
+ m_showHeadersMenuEntry(0)
+{
+ initActionDataMaps(); // does something only the 1st time it's called
+
+ m_toolBox = new NotationToolBox(this);
+
+ assert(segments.size() > 0);
+ NOTATION_DEBUG << "NotationView ctor" << endl;
+
+
+ // Initialise the display-related defaults that will be needed
+ // by both the actions and the layout toolbar
+
+ m_config->setGroup(NotationViewConfigGroup);
+
+ m_showHeadersGroup = m_config->readNumEntry("shownotationheader",
+ HeadersGroup::DefaultShowMode);
+
+ m_fontName = qstrtostr(m_config->readEntry
+ ("notefont",
+ strtoqstr(NoteFontFactory::getDefaultFontName())));
+
+ try
+ {
+ (void)NoteFontFactory::getFont
+ (m_fontName,
+ NoteFontFactory::getDefaultSize(m_fontName));
+ } catch (Exception e)
+ {
+ m_fontName = NoteFontFactory::getDefaultFontName();
+ }
+
+ m_fontSize = m_config->readUnsignedNumEntry
+ ((segments.size() > 1 ? "multistaffnotesize" : "singlestaffnotesize"),
+ NoteFontFactory::getDefaultSize(m_fontName));
+
+ int defaultSpacing = m_config->readNumEntry("spacing", 100);
+ m_hlayout->setSpacing(defaultSpacing);
+
+ int defaultProportion = m_config->readNumEntry("proportion", 60);
+ m_hlayout->setProportion(defaultProportion);
+
+ delete m_notePixmapFactory;
+ m_notePixmapFactory = new NotePixmapFactory(m_fontName, m_fontSize);
+ m_hlayout->setNotePixmapFactory(m_notePixmapFactory);
+ m_vlayout->setNotePixmapFactory(m_notePixmapFactory);
+
+ setupActions();
+ // setupAddControlRulerMenu(); - too early for notation, moved to end of ctor.
+ initLayoutToolbar();
+ initStatusBar();
+
+ setBackgroundMode(PaletteBase);
+
+ QCanvas *tCanvas = new QCanvas(this);
+ tCanvas->resize(width() * 2, height() * 2);
+
+ setCanvasView(new NotationCanvasView(*this, tCanvas, getCentralWidget()));
+
+ updateViewCaption();
+
+ m_chordNameRuler = new ChordNameRuler
+ (m_hlayout, doc, segments, m_leftGutter, 20, getCentralWidget());
+ addRuler(m_chordNameRuler);
+ if (showProgressive)
+ m_chordNameRuler->show();
+
+ m_tempoRuler = new TempoRuler
+ (m_hlayout, doc, this, m_leftGutter, 24, false, getCentralWidget());
+ addRuler(m_tempoRuler);
+ m_tempoRuler->hide();
+ static_cast<TempoRuler *>(m_tempoRuler)->connectSignals();
+
+ m_rawNoteRuler = new RawNoteRuler
+ (m_hlayout, segments[0], m_leftGutter, 20, getCentralWidget());
+ addRuler(m_rawNoteRuler);
+ m_rawNoteRuler->show();
+
+ // All toolbars should be created before this is called
+ setAutoSaveSettings("NotationView", true);
+
+ // All rulers must have been created before this is called,
+ // or the program will crash
+ readOptions();
+
+
+ setBottomStandardRuler(new StandardRuler(getDocument(), m_hlayout, m_leftGutter, 25,
+ true, getBottomWidget()));
+
+ for (unsigned int i = 0; i < segments.size(); ++i)
+ {
+ m_staffs.push_back(new NotationStaff
+ (canvas(), segments[i], 0, // snap
+ i, this,
+ m_fontName, m_fontSize));
+ }
+
+
+ // HeadersGroup ctor must not be called before m_staffs initialization
+ m_headersGroupView = new QDeferScrollView(getCentralWidget());
+ QWidget * vport = m_headersGroupView->viewport();
+ m_headersGroup = new HeadersGroup(vport, this, &doc->getComposition());
+ m_headersGroupView->setVScrollBarMode(QScrollView::AlwaysOff);
+ m_headersGroupView->setHScrollBarMode(QScrollView::AlwaysOff);
+ m_headersGroupView->setFixedWidth(m_headersGroupView->contentsWidth());
+ m_canvasView->setLeftFixedWidget(m_headersGroupView);
+
+ // Add a close button just above the track headers.
+ // The grid layout is only here to maintain the button in a
+ // right place
+ m_headersTopFrame = new QFrame(getCentralWidget());
+ QGridLayout * headersTopGrid
+ = new QGridLayout(m_headersTopFrame, 2, 2);
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QCanvasPixmap pixmap(pixmapDir + "/misc/close.xpm");
+ QPushButton * hideHeadersButton
+ = new QPushButton(m_headersTopFrame);
+ headersTopGrid->addWidget(hideHeadersButton, 1, 1,
+ Qt::AlignRight | Qt::AlignBottom);
+ hideHeadersButton->setIconSet(QIconSet(pixmap));
+ hideHeadersButton->setFlat(true);
+ QToolTip::add(hideHeadersButton, i18n("Close track headers"));
+ headersTopGrid->setMargin(4);
+ setTopStandardRuler(new StandardRuler(getDocument(),
+ m_hlayout, m_leftGutter, 25,
+ false, getCentralWidget()), m_headersTopFrame);
+
+ m_topStandardRuler->getLoopRuler()->setBackgroundColor
+ (GUIPalette::getColour(GUIPalette::InsertCursorRuler));
+
+ connect(m_topStandardRuler->getLoopRuler(), SIGNAL(startMouseMove(int)),
+ m_canvasView, SLOT(startAutoScroll(int)));
+ connect(m_topStandardRuler->getLoopRuler(), SIGNAL(stopMouseMove()),
+ m_canvasView, SLOT(stopAutoScroll()));
+
+ connect(m_bottomStandardRuler->getLoopRuler(), SIGNAL(startMouseMove(int)),
+ m_canvasView, SLOT(startAutoScroll(int)));
+ connect(m_bottomStandardRuler->getLoopRuler(), SIGNAL(stopMouseMove()),
+ m_canvasView, SLOT(stopAutoScroll()));
+
+ // Following connection have to be done before calling setPageMode())
+ connect(m_headersGroup, SIGNAL(headersResized(int)),
+ this, SLOT(slotHeadersWidthChanged(int)));
+
+
+ //
+ // layout
+ //
+ ProgressDialog* progressDlg = 0;
+
+ if (showProgressive)
+ {
+ show();
+ ProgressDialog::processEvents();
+
+ NOTATION_DEBUG << "NotationView : setting up progress dialog" << endl;
+
+ progressDlg = new ProgressDialog(i18n("Starting..."),
+ 100, this);
+ progressDlg->setAutoClose(false);
+ progressDlg->setAutoReset(true);
+ progressDlg->setMinimumDuration(1000);
+ setupProgress(progressDlg);
+
+ m_progressDisplayer = PROGRESS_DIALOG;
+ }
+
+ m_chordNameRuler->setStudio(&getDocument()->getStudio());
+
+ m_currentStaff = 0;
+ m_staffs[0]->setCurrent(true);
+
+ m_config->setGroup(NotationViewConfigGroup);
+ int layoutMode = m_config->readNumEntry("layoutmode", 0);
+
+ try
+ {
+
+ LinedStaff::PageMode mode = LinedStaff::LinearMode;
+ if (layoutMode == 1)
+ mode = LinedStaff::ContinuousPageMode;
+ else if (layoutMode == 2)
+ mode = LinedStaff::MultiPageMode;
+
+ setPageMode(mode);
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ m_staffs[i]->getSegment().getRefreshStatus
+ (m_segmentsRefreshStatusIds[i]).setNeedsRefresh(false);
+ }
+
+ m_ok = true;
+
+ } catch (ProgressReporter::Cancelled c)
+ {
+ // when cancelled, m_ok is false -- checked by calling method
+ NOTATION_DEBUG << "NotationView ctor : layout Cancelled" << endl;
+ }
+
+ NOTATION_DEBUG << "NotationView ctor : m_ok = " << m_ok << endl;
+
+ delete progressDlg;
+
+ // at this point we can return if operation was cancelled
+ if (!isOK())
+ {
+ setOutOfCtor();
+ return ;
+ }
+
+
+ // otherwise, carry on
+ setupDefaultProgress();
+
+ //
+ // Connect signals
+ //
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(renderRequired(double, double)),
+ this, SLOT(slotCheckRendered(double, double)));
+
+ m_topStandardRuler->connectRulerToDocPointer(doc);
+ m_bottomStandardRuler->connectRulerToDocPointer(doc);
+
+ // Disconnect the default connection for this signal from the
+ // top ruler, and connect our own instead
+
+ QObject::disconnect
+ (m_topStandardRuler->getLoopRuler(),
+ SIGNAL(setPointerPosition(timeT)), 0, 0);
+
+ QObject::connect
+ (m_topStandardRuler->getLoopRuler(),
+ SIGNAL(setPointerPosition(timeT)),
+ this, SLOT(slotSetInsertCursorPosition(timeT)));
+
+ QObject::connect
+ (m_topStandardRuler,
+ SIGNAL(dragPointerToPosition(timeT)),
+ this, SLOT(slotSetInsertCursorPosition(timeT)));
+
+ connect(m_bottomStandardRuler, SIGNAL(dragPointerToPosition(timeT)),
+ this, SLOT(slotSetPointerPosition(timeT)));
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(itemPressed(int, int, QMouseEvent*, NotationElement*)),
+ this, SLOT (slotItemPressed(int, int, QMouseEvent*, NotationElement*)));
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(activeItemPressed(QMouseEvent*, QCanvasItem*)),
+ this, SLOT (slotActiveItemPressed(QMouseEvent*, QCanvasItem*)));
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(nonNotationItemPressed(QMouseEvent*, QCanvasItem*)),
+ this, SLOT (slotNonNotationItemPressed(QMouseEvent*, QCanvasItem*)));
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(textItemPressed(QMouseEvent*, QCanvasItem*)),
+ this, SLOT (slotTextItemPressed(QMouseEvent*, QCanvasItem*)));
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(mouseMoved(QMouseEvent*)),
+ this, SLOT (slotMouseMoved(QMouseEvent*)));
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(mouseReleased(QMouseEvent*)),
+ this, SLOT (slotMouseReleased(QMouseEvent*)));
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(hoveredOverNoteChanged(const QString&)),
+ this, SLOT (slotHoveredOverNoteChanged(const QString&)));
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(hoveredOverAbsoluteTimeChanged(unsigned int)),
+ this, SLOT (slotHoveredOverAbsoluteTimeChanged(unsigned int)));
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(zoomIn()), this, SLOT(slotZoomIn()));
+
+ QObject::connect
+ (getCanvasView(), SIGNAL(zoomOut()), this, SLOT(slotZoomOut()));
+
+ QObject::connect
+ (m_pannerDialog->scrollbox(), SIGNAL(valueChanged(const QPoint &)),
+ getCanvasView(), SLOT(slotSetScrollPos(const QPoint &)));
+
+ QObject::connect
+ (getCanvasView()->horizontalScrollBar(), SIGNAL(valueChanged(int)),
+ m_pannerDialog->scrollbox(), SLOT(setViewX(int)));
+
+ QObject::connect
+ (getCanvasView()->verticalScrollBar(), SIGNAL(valueChanged(int)),
+ m_pannerDialog->scrollbox(), SLOT(setViewY(int)));
+
+ QObject::connect
+ (doc, SIGNAL(pointerPositionChanged(timeT)),
+ this, SLOT(slotSetPointerPosition(timeT)));
+
+ //
+ // Connect vertical scrollbars between canvas and notation header
+ QObject::connect
+ (getCanvasView()->verticalScrollBar(), SIGNAL(valueChanged(int)),
+ this, SLOT(slotVerticalScrollHeadersGroup(int)));
+
+ QObject::connect
+ (getCanvasView()->verticalScrollBar(), SIGNAL(sliderMoved(int)),
+ this, SLOT(slotVerticalScrollHeadersGroup(int)));
+
+ QObject::connect
+ (m_headersGroupView, SIGNAL(gotWheelEvent(QWheelEvent*)),
+ getCanvasView(), SLOT(slotExternalWheelEvent(QWheelEvent*)));
+
+ // Ensure notation header keeps the right bottom margin when user
+ // toggles the canvas view bottom rulers
+ connect(getCanvasView(), SIGNAL(bottomWidgetHeightChanged(int)),
+ this, SLOT(slotCanvasBottomWidgetHeightChanged(int)));
+
+ // Signal canvas horizontal scroll to notation header
+ QObject::connect
+ (getCanvasView(), SIGNAL(contentsMoving(int, int)),
+ this, SLOT(slotUpdateHeaders(int, int)));
+
+ // Connect the close notation headers button
+ QObject::connect(hideHeadersButton, SIGNAL(clicked()),
+ this, SLOT(slotHideHeadersGroup()));
+
+ stateChanged("have_selection", KXMLGUIClient::StateReverse);
+ stateChanged("have_notes_in_selection", KXMLGUIClient::StateReverse);
+ stateChanged("have_rests_in_selection", KXMLGUIClient::StateReverse);
+ stateChanged("have_multiple_staffs",
+ (m_staffs.size() > 1 ? KXMLGUIClient::StateNoReverse :
+ KXMLGUIClient::StateReverse));
+ stateChanged("rest_insert_tool_current", KXMLGUIClient::StateReverse);
+ slotTestClipboard();
+
+ if (getSegmentsOnlyRestsAndClefs())
+ {
+ m_selectDefaultNote->activate();
+ stateChanged("note_insert_tool_current",
+ KXMLGUIClient::StateNoReverse);
+ } else
+ {
+ actionCollection()->action("select")->activate();
+ stateChanged("note_insert_tool_current",
+ KXMLGUIClient::StateReverse);
+ }
+
+ timeT start = doc->getComposition().getLoopStart();
+ timeT end = doc->getComposition().getLoopEnd();
+ m_topStandardRuler->getLoopRuler()->slotSetLoopMarker(start, end);
+ m_bottomStandardRuler->getLoopRuler()->slotSetLoopMarker(start, end);
+
+ slotSetInsertCursorPosition(0);
+ slotSetPointerPosition(doc->getComposition().getPosition());
+ setCurrentSelection(0, false, true);
+ slotUpdateInsertModeStatus();
+ m_chordNameRuler->repaint();
+ m_tempoRuler->repaint();
+ m_rawNoteRuler->repaint();
+ m_inhibitRefresh = false;
+
+ // slotCheckRendered(0, getCanvasView()->visibleWidth());
+ // getCanvasView()->repaintContents();
+ updateView();
+
+ QObject::connect
+ (this, SIGNAL(renderComplete()),
+ getCanvasView(), SLOT(slotRenderComplete()));
+
+ if (parent)
+ {
+ const TrackButtons * trackLabels =
+ ((RosegardenGUIView*)parent)->getTrackEditor()->getTrackButtons();
+ QObject::connect
+ (trackLabels, SIGNAL(nameChanged()),
+ this, SLOT(slotUpdateStaffName()));
+ }
+
+ setConfigDialogPageIndex(3);
+ setOutOfCtor();
+
+ // Property and Control Rulers
+ //
+ if (getCurrentSegment()->getViewFeatures())
+ slotShowVelocityControlRuler();
+ setupControllerTabs();
+
+ setupAddControlRulerMenu();
+ setRewFFwdToAutoRepeat();
+
+ slotCompositionStateUpdate();
+
+ NOTATION_DEBUG << "NotationView ctor exiting" << endl;
+}
+
+NotationView::NotationView(RosegardenGUIDoc *doc,
+ std::vector<Segment *> segments,
+ QWidget *parent,
+ NotationView *referenceView)
+ : EditView(doc, segments, 1, 0, "printview"),
+ m_properties(getViewLocalPropertyPrefix()),
+ m_selectionCounter(0),
+ m_currentNotePixmap(0),
+ m_hoveredOverNoteName(0),
+ m_hoveredOverAbsoluteTime(0),
+ m_lastFinishingStaff( -1),
+ m_title(0),
+ m_subtitle(0),
+ m_composer(0),
+ m_copyright(0),
+ m_insertionTime(0),
+ m_deferredCursorMove(NoCursorMoveNeeded),
+ m_lastNoteAction("crotchet"),
+ m_fontName(NoteFontFactory::getDefaultFontName()),
+ m_fontSize(NoteFontFactory::getDefaultSize(m_fontName)),
+ m_pageMode(LinedStaff::LinearMode),
+ m_leftGutter(0),
+ m_notePixmapFactory(new NotePixmapFactory(m_fontName, m_fontSize)),
+ m_hlayout(new NotationHLayout(&doc->getComposition(), m_notePixmapFactory,
+ m_properties, this)),
+ m_vlayout(new NotationVLayout(&doc->getComposition(), m_notePixmapFactory,
+ m_properties, this)),
+ m_chordNameRuler(0),
+ m_tempoRuler(0),
+ m_rawNoteRuler(0),
+ m_annotationsVisible(false),
+ m_lilyPondDirectivesVisible(false),
+ m_selectDefaultNote(0),
+ m_fontCombo(0),
+ m_fontSizeCombo(0),
+ m_spacingCombo(0),
+ m_fontSizeActionMenu(0),
+ m_pannerDialog(0),
+ m_renderTimer(0),
+ m_playTracking(false),
+ m_progressDisplayer(PROGRESS_NONE),
+ m_inhibitRefresh(true),
+ m_ok(false),
+ m_printMode(true),
+ m_printSize(8), // set in positionStaffs
+ m_showHeadersGroup(0),
+ m_headersGroupView(0),
+ m_headersGroup(0),
+ m_headersTopFrame(0),
+ m_showHeadersMenuEntry(0)
+{
+ assert(segments.size() > 0);
+ NOTATION_DEBUG << "NotationView print ctor" << endl;
+
+
+ // Initialise the display-related defaults that will be needed
+ // by both the actions and the layout toolbar
+
+ m_config->setGroup(NotationViewConfigGroup);
+
+ if (referenceView)
+ {
+ m_fontName = referenceView->m_fontName;
+ } else
+ {
+ m_fontName = qstrtostr(m_config->readEntry
+ ("notefont",
+ strtoqstr(NoteFontFactory::getDefaultFontName())));
+ }
+
+
+ // Force largest font size
+ std::vector<int> sizes = NoteFontFactory::getAllSizes(m_fontName);
+ m_fontSize = sizes[sizes.size() - 1];
+
+ if (referenceView)
+ {
+ m_hlayout->setSpacing(referenceView->m_hlayout->getSpacing());
+ m_hlayout->setProportion(referenceView->m_hlayout->getProportion());
+ } else
+ {
+ int defaultSpacing = m_config->readNumEntry("spacing", 100);
+ m_hlayout->setSpacing(defaultSpacing);
+ int defaultProportion = m_config->readNumEntry("proportion", 60);
+ m_hlayout->setProportion(defaultProportion);
+ }
+
+ delete m_notePixmapFactory;
+ m_notePixmapFactory = new NotePixmapFactory(m_fontName, m_fontSize);
+ m_hlayout->setNotePixmapFactory(m_notePixmapFactory);
+ m_vlayout->setNotePixmapFactory(m_notePixmapFactory);
+
+ setBackgroundMode(PaletteBase);
+ m_config->setGroup(NotationViewConfigGroup);
+
+ QCanvas *tCanvas = new QCanvas(this);
+ tCanvas->resize(width() * 2, height() * 2); //!!!
+
+ setCanvasView(new NotationCanvasView(*this, tCanvas, getCentralWidget()));
+ canvas()->retune(128); // tune for larger canvas
+
+ for (unsigned int i = 0; i < segments.size(); ++i)
+ {
+ m_staffs.push_back(new NotationStaff(canvas(), segments[i], 0, // snap
+ i, this,
+ m_fontName, m_fontSize));
+ }
+
+ m_currentStaff = 0;
+ m_staffs[0]->setCurrent(true);
+
+ ProgressDialog* progressDlg = 0;
+
+ if (parent)
+ {
+
+ ProgressDialog::processEvents();
+
+ NOTATION_DEBUG << "NotationView : setting up progress dialog" << endl;
+
+ progressDlg = new ProgressDialog(i18n("Preparing to print..."),
+ 100, parent);
+ progressDlg->setAutoClose(false);
+ progressDlg->setAutoReset(true);
+ progressDlg->setMinimumDuration(1000);
+ setupProgress(progressDlg);
+
+ m_progressDisplayer = PROGRESS_DIALOG;
+ }
+
+ try
+ {
+
+ setPageMode(LinedStaff::MultiPageMode); // also positions and renders the staffs!
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ m_staffs[i]->getSegment().getRefreshStatus
+ (m_segmentsRefreshStatusIds[i]).setNeedsRefresh(false);
+ }
+
+ m_ok = true;
+
+ } catch (ProgressReporter::Cancelled c)
+ {
+ // when cancelled, m_ok is false -- checked by calling method
+ NOTATION_DEBUG << "NotationView ctor : layout Cancelled" << endl;
+ }
+
+ NOTATION_DEBUG << "NotationView ctor : m_ok = " << m_ok << endl;
+
+ delete progressDlg;
+
+ if (!isOK())
+ {
+ setOutOfCtor();
+ return ; // In case more code is added there later
+ }
+
+ setOutOfCtor(); // keep this as last call in the ctor
+}
+
+NotationView::~NotationView()
+{
+ NOTATION_DEBUG << "-> ~NotationView()" << endl;
+
+ if (!m_printMode && m_ok)
+ slotSaveOptions();
+
+ delete m_chordNameRuler;
+
+ delete m_renderTimer;
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ for (Segment::iterator j = m_staffs[i]->getSegment().begin();
+ j != m_staffs[i]->getSegment().end(); ++j) {
+ removeViewLocalProperties(*j);
+ }
+ delete m_staffs[i]; // this will erase all "notes" canvas items
+ }
+
+ PixmapArrayGC::deleteAll();
+ Profiles::getInstance()->dump();
+
+ NOTATION_DEBUG << "<- ~NotationView()" << endl;
+}
+
+void
+NotationView::removeViewLocalProperties(Event *e)
+{
+ Event::PropertyNames names(e->getPropertyNames());
+ std::string prefix(getViewLocalPropertyPrefix());
+
+ for (Event::PropertyNames::iterator i = names.begin();
+ i != names.end(); ++i) {
+ if (i->getName().substr(0, prefix.size()) == prefix) {
+ e->unset(*i);
+ }
+ }
+}
+
+const NotationProperties &
+NotationView::getProperties() const
+{
+ return m_properties;
+}
+
+void NotationView::positionStaffs()
+{
+ NOTATION_DEBUG << "NotationView::positionStaffs" << endl;
+
+ m_config->setGroup(NotationViewConfigGroup);
+ m_printSize = m_config->readUnsignedNumEntry("printingnotesize", 5);
+
+ int minTrack = 0, maxTrack = 0;
+ bool haveMinTrack = false;
+ typedef std::map<int, int> TrackIntMap;
+ TrackIntMap trackHeights;
+ TrackIntMap trackCoords;
+
+ int pageWidth, pageHeight, leftMargin, topMargin;
+ pageWidth = getPageWidth();
+ pageHeight = getPageHeight();
+ leftMargin = 0, topMargin = 0;
+ getPageMargins(leftMargin, topMargin);
+
+ int accumulatedHeight;
+ int rowsPerPage = 1;
+ int legerLines = 8;
+ if (m_pageMode != LinedStaff::LinearMode)
+ legerLines = 7;
+ int rowGapPercent = (m_staffs.size() > 1 ? 40 : 10);
+ int aimFor = -1;
+
+ bool done = false;
+
+ int titleHeight = 0;
+
+ if (m_title)
+ delete m_title;
+ if (m_subtitle)
+ delete m_subtitle;
+ if (m_composer)
+ delete m_composer;
+ if (m_copyright)
+ delete m_copyright;
+ m_title = m_subtitle = m_composer = m_copyright = 0;
+
+ if (m_pageMode == LinedStaff::MultiPageMode) {
+
+ const Configuration &metadata =
+ getDocument()->getComposition().getMetadata();
+
+ QFont defaultFont(NotePixmapFactory::defaultSerifFontFamily);
+ m_config->setGroup(NotationViewConfigGroup);
+ QFont font = m_config->readFontEntry("textfont", &defaultFont);
+ font.setPixelSize(m_fontSize * 5);
+ QFontMetrics metrics(font);
+
+ if (metadata.has(CompositionMetadataKeys::Title)) {
+ QString title(strtoqstr(metadata.get<String>
+ (CompositionMetadataKeys::Title)));
+ m_title = new QCanvasText(title, font, canvas());
+ m_title->setX(m_leftGutter + pageWidth / 2 - metrics.width(title) / 2);
+ m_title->setY(20 + topMargin / 4 + metrics.ascent());
+ m_title->show();
+ titleHeight += metrics.height() * 3 / 2 + topMargin / 4;
+ }
+
+ font.setPixelSize(m_fontSize * 3);
+ metrics = QFontMetrics(font);
+
+ if (metadata.has(CompositionMetadataKeys::Subtitle)) {
+ QString subtitle(strtoqstr(metadata.get<String>
+ (CompositionMetadataKeys::Subtitle)));
+ m_subtitle = new QCanvasText(subtitle, font, canvas());
+ m_subtitle->setX(m_leftGutter + pageWidth / 2 - metrics.width(subtitle) / 2);
+ m_subtitle->setY(20 + titleHeight + metrics.ascent());
+ m_subtitle->show();
+ titleHeight += metrics.height() * 3 / 2;
+ }
+
+ if (metadata.has(CompositionMetadataKeys::Composer)) {
+ QString composer(strtoqstr(metadata.get<String>
+ (CompositionMetadataKeys::Composer)));
+ m_composer = new QCanvasText(composer, font, canvas());
+ m_composer->setX(m_leftGutter + pageWidth - metrics.width(composer) - leftMargin);
+ m_composer->setY(20 + titleHeight + metrics.ascent());
+ m_composer->show();
+ titleHeight += metrics.height() * 3 / 2;
+ }
+
+ font.setPixelSize(m_fontSize * 2);
+ metrics = QFontMetrics(font);
+
+ if (metadata.has(CompositionMetadataKeys::Copyright)) {
+ QString copyright(strtoqstr(metadata.get<String>
+ (CompositionMetadataKeys::Copyright)));
+ m_copyright = new QCanvasText(copyright, font, canvas());
+ m_copyright->setX(m_leftGutter + leftMargin);
+ m_copyright->setY(20 + pageHeight - topMargin - metrics.descent());
+ m_copyright->show();
+ }
+ }
+
+ while (1) {
+
+ accumulatedHeight = 0;
+ int maxTrackHeight = 0;
+
+ trackHeights.clear();
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+
+ m_staffs[i]->setLegerLineCount(legerLines);
+
+ int height = m_staffs[i]->getHeightOfRow();
+ TrackId trackId = m_staffs[i]->getSegment().getTrack();
+ Track *track =
+ m_staffs[i]->getSegment().getComposition()->
+ getTrackById(trackId);
+
+ if (!track)
+ continue; // This Should Not Happen, My Friend
+
+ int trackPosition = track->getPosition();
+
+ TrackIntMap::iterator hi = trackHeights.find(trackPosition);
+ if (hi == trackHeights.end()) {
+ trackHeights.insert(TrackIntMap::value_type
+ (trackPosition, height));
+ } else if (height > hi->second) {
+ hi->second = height;
+ }
+
+ if (height > maxTrackHeight)
+ maxTrackHeight = height;
+
+ if (trackPosition < minTrack || !haveMinTrack) {
+ minTrack = trackPosition;
+ haveMinTrack = true;
+ }
+ if (trackPosition > maxTrack) {
+ maxTrack = trackPosition;
+ }
+ }
+
+ for (int i = minTrack; i <= maxTrack; ++i) {
+ TrackIntMap::iterator hi = trackHeights.find(i);
+ if (hi != trackHeights.end()) {
+ trackCoords[i] = accumulatedHeight;
+ accumulatedHeight += hi->second;
+ }
+ }
+
+ accumulatedHeight += maxTrackHeight * rowGapPercent / 100;
+
+ if (done)
+ break;
+
+ if (m_pageMode != LinedStaff::MultiPageMode) {
+
+ rowsPerPage = 0;
+ done = true;
+ break;
+
+ } else {
+
+ // Check how well all this stuff actually fits on the
+ // page. If things don't fit as well as we'd like, modify
+ // at most one parameter so as to save some space, then
+ // loop around again and see if it worked. This iterative
+ // approach is inefficient but the time spent here is
+ // neglible in context, and it's a simple way to code it.
+
+ int staffPageHeight = pageHeight - topMargin * 2 - titleHeight;
+ rowsPerPage = staffPageHeight / accumulatedHeight;
+
+ if (rowsPerPage < 1) {
+
+ if (legerLines > 5)
+ --legerLines;
+ else if (rowGapPercent > 20)
+ rowGapPercent -= 10;
+ else if (legerLines > 4)
+ --legerLines;
+ else if (rowGapPercent > 0)
+ rowGapPercent -= 10;
+ else if (legerLines > 3)
+ --legerLines;
+ else if (m_printSize > 3)
+ --m_printSize;
+ else { // just accept that we'll have to overflow
+ rowsPerPage = 1;
+ done = true;
+ }
+
+ } else {
+
+ if (aimFor == rowsPerPage) {
+
+ titleHeight +=
+ (staffPageHeight - (rowsPerPage * accumulatedHeight)) / 2;
+
+ done = true;
+
+ } else {
+
+ if (aimFor == -1)
+ aimFor = rowsPerPage + 1;
+
+ // we can perhaps accommodate another row, with care
+ if (legerLines > 5)
+ --legerLines;
+ else if (rowGapPercent > 20)
+ rowGapPercent -= 10;
+ else if (legerLines > 3)
+ --legerLines;
+ else if (rowGapPercent > 0)
+ rowGapPercent -= 10;
+ else { // no, we can't
+ rowGapPercent = 0;
+ legerLines = 8;
+ done = true;
+ }
+ }
+ }
+ }
+ }
+
+ m_hlayout->setPageWidth(pageWidth - leftMargin * 2);
+
+ int topGutter = 0;
+
+ if (m_pageMode == LinedStaff::MultiPageMode) {
+
+ topGutter = 20;
+
+ } else if (m_pageMode == LinedStaff::ContinuousPageMode) {
+
+ // fewer leger lines above staff than in linear mode --
+ // compensate for this on the top staff
+ topGutter = m_notePixmapFactory->getLineSpacing() * 2;
+ }
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+
+ TrackId trackId = m_staffs[i]->getSegment().getTrack();
+ Track *track =
+ m_staffs[i]->getSegment().getComposition()->
+ getTrackById(trackId);
+
+ if (!track)
+ continue; // Once Again, My Friend, You Should Never See Me Here
+
+ int trackPosition = track->getPosition();
+
+ m_staffs[i]->setTitleHeight(titleHeight);
+ m_staffs[i]->setRowSpacing(accumulatedHeight);
+
+ if (trackPosition < maxTrack) {
+ m_staffs[i]->setConnectingLineLength(trackHeights[trackPosition]);
+ }
+
+ if (trackPosition == minTrack &&
+ m_pageMode != LinedStaff::LinearMode) {
+ m_staffs[i]->setBarNumbersEvery(5);
+ } else {
+ m_staffs[i]->setBarNumbersEvery(0);
+ }
+
+ m_staffs[i]->setX(m_leftGutter);
+ m_staffs[i]->setY(topGutter + trackCoords[trackPosition] + topMargin);
+ m_staffs[i]->setPageWidth(pageWidth - leftMargin * 2);
+ m_staffs[i]->setRowsPerPage(rowsPerPage);
+ m_staffs[i]->setPageMode(m_pageMode);
+ m_staffs[i]->setMargin(leftMargin);
+
+ NOTATION_DEBUG << "NotationView::positionStaffs: set staff's page width to "
+ << (pageWidth - leftMargin * 2) << endl;
+
+ }
+
+
+ if (!m_printMode) {
+ // Destroy then recreate all track headers
+ hideHeadersGroup();
+ m_headersGroup->removeAllHeaders();
+ if (m_pageMode == LinedStaff::LinearMode) {
+ for (int i = minTrack; i <= maxTrack; ++i) {
+ TrackIntMap::iterator hi = trackHeights.find(i);
+ if (hi != trackHeights.end()) {
+ TrackId trackId = getDocument()->getComposition()
+ .getTrackByPosition(i)->getId();
+ m_headersGroup->addHeader(trackId, trackHeights[i],
+ trackCoords[i], getCanvasLeftX());
+ }
+ }
+
+ m_headersGroup->completeToHeight(canvas()->height());
+
+ m_headersGroupView->addChild(m_headersGroup);
+
+ getCanvasView()->updateLeftWidgetGeometry();
+
+ if ( (m_showHeadersGroup == HeadersGroup::ShowAlways)
+ || ( (m_showHeadersGroup == HeadersGroup::ShowWhenNeeded)
+ && (m_headersGroup->getUsedHeight()
+ > getCanvasView()->visibleHeight()))) {
+ m_headersGroup->slotUpdateAllHeaders(getCanvasLeftX(), 0, true);
+ showHeadersGroup();
+
+ // Disable menu entry when headers are shown
+ m_showHeadersMenuEntry->setEnabled(false);
+ } else {
+ // Enable menu entry when headers are hidden
+ m_showHeadersMenuEntry->setEnabled(true);
+ }
+ } else {
+ // Disable menu entry when not in linear mode
+ m_showHeadersMenuEntry->setEnabled(false);
+ }
+ }
+}
+
+void NotationView::slotCanvasBottomWidgetHeightChanged(int newHeight)
+{
+ getCanvasView()->updateLeftWidgetGeometry();
+}
+
+void NotationView::positionPages()
+{
+ if (m_printMode)
+ return ;
+
+ QPixmap background;
+ QPixmap deskBackground;
+ bool haveBackground = false;
+
+ m_config->setGroup(NotationViewConfigGroup);
+ if (m_config->readBoolEntry("backgroundtextures", true)) {
+ QString pixmapDir =
+ KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ if (background.load(QString("%1/misc/bg-paper-cream.xpm").
+ arg(pixmapDir))) {
+ haveBackground = true;
+ }
+ // we're happy to ignore errors from this one:
+ deskBackground.load(QString("%1/misc/bg-desktop.xpm").arg(pixmapDir));
+ }
+
+ int pageWidth = getPageWidth();
+ int pageHeight = getPageHeight();
+ int leftMargin = 0, topMargin = 0;
+ getPageMargins(leftMargin, topMargin);
+ int maxPageCount = 1;
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ int pageCount = m_staffs[i]->getPageCount();
+ if (pageCount > maxPageCount)
+ maxPageCount = pageCount;
+ }
+
+ for (unsigned int i = 0; i < m_pages.size(); ++i) {
+ delete m_pages[i];
+ delete m_pageNumbers[i];
+ }
+ m_pages.clear();
+ m_pageNumbers.clear();
+
+ if (m_pageMode != LinedStaff::MultiPageMode) {
+ if (haveBackground) {
+ canvas()->setBackgroundPixmap(background);
+ getCanvasView()->setBackgroundMode(Qt::FixedPixmap);
+ getCanvasView()->setPaletteBackgroundPixmap(background);
+ getCanvasView()->setErasePixmap(background);
+ }
+ } else {
+ if (haveBackground) {
+ canvas()->setBackgroundPixmap(deskBackground);
+ getCanvasView()->setBackgroundMode(Qt::FixedPixmap);
+ getCanvasView()->setPaletteBackgroundPixmap(background);
+ getCanvasView()->setErasePixmap(background);
+ }
+
+ QFont pageNumberFont;
+ pageNumberFont.setPixelSize(m_fontSize * 2);
+ QFontMetrics pageNumberMetrics(pageNumberFont);
+
+ for (int page = 0; page < maxPageCount; ++page) {
+
+ int x = m_leftGutter + pageWidth * page + leftMargin / 4;
+ int y = 20;
+ int w = pageWidth - leftMargin / 2;
+ int h = pageHeight;
+
+ QString str = QString("%1").arg(page + 1);
+ QCanvasText *text = new QCanvasText(str, pageNumberFont, canvas());
+ text->setX(m_leftGutter + pageWidth * page + pageWidth - pageNumberMetrics.width(str) - leftMargin);
+ text->setY(y + h - pageNumberMetrics.descent() - topMargin);
+ text->setZ( -999);
+ text->show();
+ m_pageNumbers.push_back(text);
+
+ QCanvasRectangle *rect = new QCanvasRectangle(x, y, w, h, canvas());
+ if (haveBackground)
+ rect->setBrush(QBrush(Qt::white, background));
+ rect->setPen(Qt::black);
+ rect->setZ( -1000);
+ rect->show();
+ m_pages.push_back(rect);
+ }
+
+ updateThumbnails(false);
+ }
+
+ m_config->setGroup(NotationViewConfigGroup);
+}
+
+void NotationView::slotUpdateStaffName()
+{
+ LinedStaff *staff = getLinedStaff(m_currentStaff);
+ staff->drawStaffName();
+ m_headersGroup->slotUpdateAllHeaders(getCanvasLeftX(), 0, true);
+}
+
+void NotationView::slotSaveOptions()
+{
+ m_config->setGroup(NotationViewConfigGroup);
+
+ m_config->writeEntry("Show Chord Name Ruler", getToggleAction("show_chords_ruler")->isChecked());
+ m_config->writeEntry("Show Raw Note Ruler", getToggleAction("show_raw_note_ruler")->isChecked());
+ m_config->writeEntry("Show Tempo Ruler", getToggleAction("show_tempo_ruler")->isChecked());
+ m_config->writeEntry("Show Annotations", m_annotationsVisible);
+ m_config->writeEntry("Show LilyPond Directives", m_lilyPondDirectivesVisible);
+
+ m_config->sync();
+}
+
+void NotationView::setOneToolbar(const char *actionName,
+ const char *toolbarName)
+{
+ KToggleAction *action = getToggleAction(actionName);
+ if (!action) {
+ std::cerr << "WARNING: No such action as " << actionName << std::endl;
+ return ;
+ }
+ QWidget *toolbar = toolBar(toolbarName);
+ if (!toolbar) {
+ std::cerr << "WARNING: No such toolbar as " << toolbarName << std::endl;
+ return ;
+ }
+ action->setChecked(!toolbar->isHidden());
+}
+
+void NotationView::readOptions()
+{
+ EditView::readOptions();
+
+ setOneToolbar("show_tools_toolbar", "Tools Toolbar");
+ setOneToolbar("show_notes_toolbar", "Notes Toolbar");
+ setOneToolbar("show_rests_toolbar", "Rests Toolbar");
+ setOneToolbar("show_clefs_toolbar", "Clefs Toolbar");
+ setOneToolbar("show_group_toolbar", "Group Toolbar");
+ setOneToolbar("show_marks_toolbar", "Marks Toolbar");
+ setOneToolbar("show_layout_toolbar", "Layout Toolbar");
+ setOneToolbar("show_transport_toolbar", "Transport Toolbar");
+ setOneToolbar("show_accidentals_toolbar", "Accidentals Toolbar");
+ setOneToolbar("show_meta_toolbar", "Meta Toolbar");
+
+ m_config->setGroup(NotationViewConfigGroup);
+
+ bool opt;
+
+ opt = m_config->readBoolEntry("Show Chord Name Ruler", false);
+ getToggleAction("show_chords_ruler")->setChecked(opt);
+ slotToggleChordsRuler();
+
+ opt = m_config->readBoolEntry("Show Raw Note Ruler", true);
+ getToggleAction("show_raw_note_ruler")->setChecked(opt);
+ slotToggleRawNoteRuler();
+
+ opt = m_config->readBoolEntry("Show Tempo Ruler", true);
+ getToggleAction("show_tempo_ruler")->setChecked(opt);
+ slotToggleTempoRuler();
+
+ opt = m_config->readBoolEntry("Show Annotations", true);
+ m_annotationsVisible = opt;
+ getToggleAction("show_annotations")->setChecked(opt);
+ slotUpdateAnnotationsStatus();
+ // slotToggleAnnotations();
+
+ opt = m_config->readBoolEntry("Show LilyPond Directives", true);
+ m_lilyPondDirectivesVisible = opt;
+ getToggleAction("show_lilypond_directives")->setChecked(opt);
+ slotUpdateLilyPondDirectivesStatus();
+}
+
+void NotationView::setupActions()
+{
+ KStdAction::print(this, SLOT(slotFilePrint()), actionCollection());
+ KStdAction::printPreview(this, SLOT(slotFilePrintPreview()),
+ actionCollection());
+
+ new KAction(i18n("Print &with LilyPond..."), 0, 0, this,
+ SLOT(slotPrintLilyPond()), actionCollection(),
+ "file_print_lilypond");
+
+ new KAction(i18n("Preview with Lil&yPond..."), 0, 0, this,
+ SLOT(slotPreviewLilyPond()), actionCollection(),
+ "file_preview_lilypond");
+
+ EditViewBase::setupActions("notation.rc");
+ EditView::setupActions();
+
+ KRadioAction* noteAction = 0;
+
+ // View menu stuff
+
+ KActionMenu *fontActionMenu =
+ new KActionMenu(i18n("Note &Font"), this, "note_font_actionmenu");
+
+ std::set
+ <std::string> fs(NoteFontFactory::getFontNames());
+ std::vector<std::string> f(fs.begin(), fs.end());
+ std::sort(f.begin(), f.end());
+
+ for (std::vector<std::string>::iterator i = f.begin(); i != f.end(); ++i) {
+
+ QString fontQName(strtoqstr(*i));
+
+ KToggleAction *fontAction =
+ new KToggleAction
+ (fontQName, 0, this, SLOT(slotChangeFontFromAction()),
+ actionCollection(), "note_font_" + fontQName);
+
+ fontAction->setChecked(*i == m_fontName);
+ fontActionMenu->insert(fontAction);
+ }
+
+ actionCollection()->insert(fontActionMenu);
+
+ m_fontSizeActionMenu =
+ new KActionMenu(i18n("Si&ze"), this, "note_font_size_actionmenu");
+ setupFontSizeMenu();
+
+ actionCollection()->insert(m_fontSizeActionMenu);
+
+ m_showHeadersMenuEntry
+ = new KAction(i18n("Show Track Headers"), 0, this,
+ SLOT(slotShowHeadersGroup()),
+ actionCollection(), "show_track_headers");
+
+ KActionMenu *spacingActionMenu =
+ new KActionMenu(i18n("S&pacing"), this, "stretch_actionmenu");
+
+ int defaultSpacing = m_hlayout->getSpacing();
+ std::vector<int> spacings = NotationHLayout::getAvailableSpacings();
+
+ for (std::vector<int>::iterator i = spacings.begin();
+ i != spacings.end(); ++i) {
+
+ KToggleAction *spacingAction =
+ new KToggleAction
+ (QString("%1%").arg(*i), 0, this,
+ SLOT(slotChangeSpacingFromAction()),
+ actionCollection(), QString("spacing_%1").arg(*i));
+
+ spacingAction->setExclusiveGroup("spacing");
+ spacingAction->setChecked(*i == defaultSpacing);
+ spacingActionMenu->insert(spacingAction);
+ }
+
+ actionCollection()->insert(spacingActionMenu);
+
+ KActionMenu *proportionActionMenu =
+ new KActionMenu(i18n("Du&ration Factor"), this, "proportion_actionmenu");
+
+ int defaultProportion = m_hlayout->getProportion();
+ std::vector<int> proportions = NotationHLayout::getAvailableProportions();
+
+ for (std::vector<int>::iterator i = proportions.begin();
+ i != proportions.end(); ++i) {
+
+ QString name = QString("%1%").arg(*i);
+ if (*i == 0)
+ name = i18n("None");
+
+ KToggleAction *proportionAction =
+ new KToggleAction
+ (name, 0, this,
+ SLOT(slotChangeProportionFromAction()),
+ actionCollection(), QString("proportion_%1").arg(*i));
+
+ proportionAction->setExclusiveGroup("proportion");
+ proportionAction->setChecked(*i == defaultProportion);
+ proportionActionMenu->insert(proportionAction);
+ }
+
+ actionCollection()->insert(proportionActionMenu);
+
+ KActionMenu *styleActionMenu =
+ new KActionMenu(i18n("Note &Style"), this, "note_style_actionmenu");
+
+ std::vector<NoteStyleName> styles
+ (NoteStyleFactory::getAvailableStyleNames());
+
+ for (std::vector<NoteStyleName>::iterator i = styles.begin();
+ i != styles.end(); ++i) {
+
+ QString styleQName(strtoqstr(*i));
+
+ KAction *styleAction =
+ new KAction
+ (styleQName, 0, this, SLOT(slotSetStyleFromAction()),
+ actionCollection(), "style_" + styleQName);
+
+ styleActionMenu->insert(styleAction);
+ }
+
+ actionCollection()->insert(styleActionMenu);
+
+ KActionMenu *ornamentActionMenu =
+ new KActionMenu(i18n("Use Ornament"), this, "ornament_actionmenu");
+
+
+
+ new KAction
+ (i18n("Insert Rest"), Key_P, this, SLOT(slotInsertRest()),
+ actionCollection(), QString("insert_rest"));
+
+ new KAction
+ (i18n("Switch from Note to Rest"), Key_T, this,
+ SLOT(slotSwitchFromNoteToRest()),
+ actionCollection(), QString("switch_from_note_to_rest"));
+
+ new KAction
+ (i18n("Switch from Rest to Note"), Key_Y, this,
+ SLOT(slotSwitchFromRestToNote()),
+ actionCollection(), QString("switch_from_rest_to_note"));
+
+
+ // setup Notes menu & toolbar
+ QIconSet icon;
+
+ for (NoteActionDataMap::Iterator actionDataIter = m_noteActionDataMap->begin();
+ actionDataIter != m_noteActionDataMap->end();
+ ++actionDataIter) {
+
+ NoteActionData noteActionData = **actionDataIter;
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ (noteActionData.pixmapName)));
+ noteAction = new KRadioAction(noteActionData.title,
+ icon,
+ noteActionData.keycode,
+ this,
+ SLOT(slotNoteAction()),
+ actionCollection(),
+ noteActionData.actionName);
+ noteAction->setExclusiveGroup("notes");
+
+ if (noteActionData.noteType == Note::Crotchet &&
+ noteActionData.dots == 0 && !noteActionData.rest) {
+ m_selectDefaultNote = noteAction;
+ }
+ }
+
+ // Note duration change actions
+ for (NoteChangeActionDataMap::Iterator actionDataIter = m_noteChangeActionDataMap->begin();
+ actionDataIter != m_noteChangeActionDataMap->end();
+ ++actionDataIter) {
+
+ NoteChangeActionData data = **actionDataIter;
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ (data.pixmapName)));
+
+ KAction *action = new KAction(data.title,
+ icon,
+ data.keycode,
+ this,
+ SLOT(slotNoteChangeAction()),
+ actionCollection(),
+ data.actionName);
+ }
+
+ //
+ // Accidentals
+ //
+ static QString actionsAccidental[][4] =
+ {
+ { i18n("No accidental"), "1slotNoAccidental()", "no_accidental", "accidental-none" },
+ { i18n("Follow previous accidental"), "1slotFollowAccidental()", "follow_accidental", "accidental-follow" },
+ { i18n("Sharp"), "1slotSharp()", "sharp_accidental", "accidental-sharp" },
+ { i18n("Flat"), "1slotFlat()", "flat_accidental", "accidental-flat" },
+ { i18n("Natural"), "1slotNatural()", "natural_accidental", "accidental-natural" },
+ { i18n("Double sharp"), "1slotDoubleSharp()", "double_sharp_accidental", "accidental-doublesharp" },
+ { i18n("Double flat"), "1slotDoubleFlat()", "double_flat_accidental", "accidental-doubleflat" }
+ };
+
+ for (unsigned int i = 0;
+ i < sizeof(actionsAccidental) / sizeof(actionsAccidental[0]); ++i) {
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ (actionsAccidental[i][3])));
+ noteAction = new KRadioAction(actionsAccidental[i][0], icon, 0, this,
+ actionsAccidental[i][1],
+ actionCollection(), actionsAccidental[i][2]);
+ noteAction->setExclusiveGroup("accidentals");
+ }
+
+
+ //
+ // Clefs
+ //
+
+ // Treble
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("clef-treble")));
+ noteAction = new KRadioAction(i18n("&Treble Clef"), icon, 0, this,
+ SLOT(slotTrebleClef()),
+ actionCollection(), "treble_clef");
+ noteAction->setExclusiveGroup("notes");
+
+ // Alto
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("clef-alto")));
+ noteAction = new KRadioAction(i18n("&Alto Clef"), icon, 0, this,
+ SLOT(slotAltoClef()),
+ actionCollection(), "alto_clef");
+ noteAction->setExclusiveGroup("notes");
+
+ // Tenor
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("clef-tenor")));
+ noteAction = new KRadioAction(i18n("Te&nor Clef"), icon, 0, this,
+ SLOT(slotTenorClef()),
+ actionCollection(), "tenor_clef");
+ noteAction->setExclusiveGroup("notes");
+
+ // Bass
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("clef-bass")));
+ noteAction = new KRadioAction(i18n("&Bass Clef"), icon, 0, this,
+ SLOT(slotBassClef()),
+ actionCollection(), "bass_clef");
+ noteAction->setExclusiveGroup("notes");
+
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("text")));
+ noteAction = new KRadioAction(i18n("&Text"), icon, Key_F8, this,
+ SLOT(slotText()),
+ actionCollection(), "text");
+ noteAction->setExclusiveGroup("notes");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("guitarchord")));
+ noteAction = new KRadioAction(i18n("&Guitar Chord"), icon, Key_F9, this,
+ SLOT(slotGuitarChord()),
+ actionCollection(), "guitarchord");
+ noteAction->setExclusiveGroup("notes");
+
+ /* icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("lilypond")));
+ noteAction = new KRadioAction(i18n("Lil&ypond Directive"), icon, Key_F9, this,
+ SLOT(slotLilyPondDirective()),
+ actionCollection(), "lilypond_directive");
+ noteAction->setExclusiveGroup("notes"); */
+
+
+ //
+ // Edition tools (eraser, selector...)
+ //
+ noteAction = new KRadioAction(i18n("&Erase"), "eraser", Key_F4,
+ this, SLOT(slotEraseSelected()),
+ actionCollection(), "erase");
+ noteAction->setExclusiveGroup("notes");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("select")));
+ noteAction = new KRadioAction(i18n("&Select and Edit"), icon, Key_F2,
+ this, SLOT(slotSelectSelected()),
+ actionCollection(), "select");
+ noteAction->setExclusiveGroup("notes");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("step_by_step")));
+ new KToggleAction(i18n("Ste&p Recording"), icon, 0, this,
+ SLOT(slotToggleStepByStep()), actionCollection(),
+ "toggle_step_by_step");
+
+
+ // Edit menu
+ new KAction(i18n("Select from Sta&rt"), 0, this,
+ SLOT(slotEditSelectFromStart()), actionCollection(),
+ "select_from_start");
+
+ new KAction(i18n("Select to &End"), 0, this,
+ SLOT(slotEditSelectToEnd()), actionCollection(),
+ "select_to_end");
+
+ new KAction(i18n("Select Whole St&aff"), Key_A + CTRL, this,
+ SLOT(slotEditSelectWholeStaff()), actionCollection(),
+ "select_whole_staff");
+
+ new KAction(i18n("C&ut and Close"), CTRL + SHIFT + Key_X, this,
+ SLOT(slotEditCutAndClose()), actionCollection(),
+ "cut_and_close");
+
+ new KAction(i18n("Pa&ste..."), CTRL + SHIFT + Key_V, this,
+ SLOT(slotEditGeneralPaste()), actionCollection(),
+ "general_paste");
+
+ new KAction(i18n("De&lete"), Key_Delete, this,
+ SLOT(slotEditDelete()), actionCollection(),
+ "delete");
+
+ new KAction(i18n("Move to Staff Above"), 0, this,
+ SLOT(slotMoveEventsUpStaff()), actionCollection(),
+ "move_events_up_staff");
+
+ new KAction(i18n("Move to Staff Below"), 0, this,
+ SLOT(slotMoveEventsDownStaff()), actionCollection(),
+ "move_events_down_staff");
+
+ //
+ // Settings menu
+ //
+ int layoutMode = m_config->readNumEntry("layoutmode", 0);
+
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+
+ QCanvasPixmap pixmap(pixmapDir + "/toolbar/linear-layout.xpm");
+ icon = QIconSet(pixmap);
+ KRadioAction *linearModeAction = new KRadioAction
+ (i18n("&Linear Layout"), icon, 0, this, SLOT(slotLinearMode()),
+ actionCollection(), "linear_mode");
+ linearModeAction->setExclusiveGroup("layoutMode");
+ if (layoutMode == 0)
+ linearModeAction->setChecked(true);
+
+ pixmap.load(pixmapDir + "/toolbar/continuous-page-mode.xpm");
+ icon = QIconSet(pixmap);
+ KRadioAction *continuousPageModeAction = new KRadioAction
+ (i18n("&Continuous Page Layout"), icon, 0, this, SLOT(slotContinuousPageMode()),
+ actionCollection(), "continuous_page_mode");
+ continuousPageModeAction->setExclusiveGroup("layoutMode");
+ if (layoutMode == 1)
+ continuousPageModeAction->setChecked(true);
+
+ pixmap.load(pixmapDir + "/toolbar/multi-page-mode.xpm");
+ icon = QIconSet(pixmap);
+ KRadioAction *multiPageModeAction = new KRadioAction
+ (i18n("&Multiple Page Layout"), icon, 0, this, SLOT(slotMultiPageMode()),
+ actionCollection(), "multi_page_mode");
+ multiPageModeAction->setExclusiveGroup("layoutMode");
+ if (layoutMode == 2)
+ multiPageModeAction->setChecked(true);
+
+ new KToggleAction(i18n("Show Ch&ord Name Ruler"), 0, this,
+ SLOT(slotToggleChordsRuler()),
+ actionCollection(), "show_chords_ruler");
+
+ new KToggleAction(i18n("Show Ra&w Note Ruler"), 0, this,
+ SLOT(slotToggleRawNoteRuler()),
+ actionCollection(), "show_raw_note_ruler");
+
+ new KToggleAction(i18n("Show &Tempo Ruler"), 0, this,
+ SLOT(slotToggleTempoRuler()),
+ actionCollection(), "show_tempo_ruler");
+
+ new KToggleAction(i18n("Show &Annotations"), 0, this,
+ SLOT(slotToggleAnnotations()),
+ actionCollection(), "show_annotations");
+
+ new KToggleAction(i18n("Show Lily&Pond Directives"), 0, this,
+ SLOT(slotToggleLilyPondDirectives()),
+ actionCollection(), "show_lilypond_directives");
+
+ new KAction(i18n("Open L&yric Editor"), 0, this, SLOT(slotEditLyrics()),
+ actionCollection(), "lyric_editor");
+
+ //
+ // Group menu
+ //
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("group-beam")));
+
+ new KAction(BeamCommand::getGlobalName(), icon, Key_B + CTRL, this,
+ SLOT(slotGroupBeam()), actionCollection(), "beam");
+
+ new KAction(AutoBeamCommand::getGlobalName(), 0, this,
+ SLOT(slotGroupAutoBeam()), actionCollection(), "auto_beam");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("group-unbeam")));
+
+ new KAction(BreakCommand::getGlobalName(), icon, Key_U + CTRL, this,
+ SLOT(slotGroupBreak()), actionCollection(), "break_group");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("group-simple-tuplet")));
+
+ new KAction(TupletCommand::getGlobalName(true), icon, Key_R + CTRL, this,
+ SLOT(slotGroupSimpleTuplet()), actionCollection(), "simple_tuplet");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("group-tuplet")));
+
+ new KAction(TupletCommand::getGlobalName(false), icon, Key_T + CTRL, this,
+ SLOT(slotGroupGeneralTuplet()), actionCollection(), "tuplet");
+
+ new KAction(UnTupletCommand::getGlobalName(), 0, this,
+ SLOT(slotGroupUnTuplet()), actionCollection(), "break_tuplets");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap
+ (NotePixmapFactory::makeToolbarPixmap("triplet")));
+ (new KToggleAction(i18n("Trip&let Insert Mode"), icon, Key_G,
+ this, SLOT(slotUpdateInsertModeStatus()),
+ actionCollection(), "triplet_mode"))->
+ setChecked(false);
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap
+ (NotePixmapFactory::makeToolbarPixmap("chord")));
+ (new KToggleAction(i18n("C&hord Insert Mode"), icon, Key_H,
+ this, SLOT(slotUpdateInsertModeStatus()),
+ actionCollection(), "chord_mode"))->
+ setChecked(false);
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap
+ (NotePixmapFactory::makeToolbarPixmap("group-grace")));
+ (new KToggleAction(i18n("Grace Insert Mode"), icon, 0,
+ this, SLOT(slotUpdateInsertModeStatus()),
+ actionCollection(), "grace_mode"))->
+ setChecked(false);
+/*!!!
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("group-grace")));
+
+ new KAction(GraceCommand::getGlobalName(), icon, 0, this,
+ SLOT(slotGroupGrace()), actionCollection(), "grace");
+
+ new KAction(UnGraceCommand::getGlobalName(), 0, this,
+ SLOT(slotGroupUnGrace()), actionCollection(), "ungrace");
+*/
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("group-slur")));
+
+ new KAction(AddIndicationCommand::getGlobalName
+ (Indication::Slur), icon, Key_ParenRight, this,
+ SLOT(slotGroupSlur()), actionCollection(), "slur");
+
+ new KAction(AddIndicationCommand::getGlobalName
+ (Indication::PhrasingSlur), 0, Key_ParenRight + CTRL, this,
+ SLOT(slotGroupPhrasingSlur()), actionCollection(), "phrasing_slur");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("group-glissando")));
+
+ new KAction(AddIndicationCommand::getGlobalName
+ (Indication::Glissando), icon, 0, this,
+ SLOT(slotGroupGlissando()), actionCollection(), "glissando");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("group-crescendo")));
+
+ new KAction(AddIndicationCommand::getGlobalName
+ (Indication::Crescendo), icon, Key_Less, this,
+ SLOT(slotGroupCrescendo()), actionCollection(), "crescendo");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("group-decrescendo")));
+
+ new KAction(AddIndicationCommand::getGlobalName
+ (Indication::Decrescendo), icon, Key_Greater, this,
+ SLOT(slotGroupDecrescendo()), actionCollection(), "decrescendo");
+
+ new KAction(AddIndicationCommand::getGlobalName
+ (Indication::QuindicesimaUp), 0, 0, this,
+ SLOT(slotGroupOctave2Up()), actionCollection(), "octave_2up");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("group-ottava")));
+
+ new KAction(AddIndicationCommand::getGlobalName
+ (Indication::OttavaUp), icon, 0, this,
+ SLOT(slotGroupOctaveUp()), actionCollection(), "octave_up");
+
+ new KAction(AddIndicationCommand::getGlobalName
+ (Indication::OttavaDown), 0, 0, this,
+ SLOT(slotGroupOctaveDown()), actionCollection(), "octave_down");
+
+ new KAction(AddIndicationCommand::getGlobalName
+ (Indication::QuindicesimaDown), 0, 0, this,
+ SLOT(slotGroupOctave2Down()), actionCollection(), "octave_2down");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("group-chord")));
+ new KAction(MakeChordCommand::getGlobalName(), icon, 0, this,
+ SLOT(slotGroupMakeChord()), actionCollection(), "make_chord");
+
+ // setup Transforms menu
+ new KAction(NormalizeRestsCommand::getGlobalName(), Key_N + CTRL, this,
+ SLOT(slotTransformsNormalizeRests()), actionCollection(),
+ "normalize_rests");
+
+ new KAction(CollapseRestsCommand::getGlobalName(), 0, this,
+ SLOT(slotTransformsCollapseRests()), actionCollection(),
+ "collapse_rests_aggressively");
+
+ new KAction(CollapseNotesCommand::getGlobalName(), Key_Equal + CTRL, this,
+ SLOT(slotTransformsCollapseNotes()), actionCollection(),
+ "collapse_notes");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transforms-tie")));
+
+ new KAction(TieNotesCommand::getGlobalName(), icon, Key_AsciiTilde, this,
+ SLOT(slotTransformsTieNotes()), actionCollection(),
+ "tie_notes");
+
+ new KAction(UntieNotesCommand::getGlobalName(), 0, this,
+ SLOT(slotTransformsUntieNotes()), actionCollection(),
+ "untie_notes");
+
+ new KAction(MakeNotesViableCommand::getGlobalName(), 0, this,
+ SLOT(slotTransformsMakeNotesViable()), actionCollection(),
+ "make_notes_viable");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transforms-decounterpoint")));
+
+ new KAction(DeCounterpointCommand::getGlobalName(), icon, 0, this,
+ SLOT(slotTransformsDeCounterpoint()), actionCollection(),
+ "de_counterpoint");
+
+ new KAction(ChangeStemsCommand::getGlobalName(true),
+ 0, Key_PageUp + CTRL, this,
+ SLOT(slotTransformsStemsUp()), actionCollection(),
+ "stems_up");
+
+ new KAction(ChangeStemsCommand::getGlobalName(false),
+ 0, Key_PageDown + CTRL, this,
+ SLOT(slotTransformsStemsDown()), actionCollection(),
+ "stems_down");
+
+ new KAction(RestoreStemsCommand::getGlobalName(), 0, this,
+ SLOT(slotTransformsRestoreStems()), actionCollection(),
+ "restore_stems");
+
+ new KAction(ChangeSlurPositionCommand::getGlobalName(true),
+ 0, this,
+ SLOT(slotTransformsSlursAbove()), actionCollection(),
+ "slurs_above");
+
+ new KAction(ChangeSlurPositionCommand::getGlobalName(false),
+ 0, this,
+ SLOT(slotTransformsSlursBelow()), actionCollection(),
+ "slurs_below");
+
+ new KAction(RestoreSlursCommand::getGlobalName(), 0, this,
+ SLOT(slotTransformsRestoreSlurs()), actionCollection(),
+ "restore_slurs");
+
+ new KAction(ChangeTiePositionCommand::getGlobalName(true),
+ 0, this,
+ SLOT(slotTransformsTiesAbove()), actionCollection(),
+ "ties_above");
+
+ new KAction(ChangeTiePositionCommand::getGlobalName(false),
+ 0, this,
+ SLOT(slotTransformsTiesBelow()), actionCollection(),
+ "ties_below");
+
+ new KAction(RestoreTiesCommand::getGlobalName(), 0, this,
+ SLOT(slotTransformsRestoreTies()), actionCollection(),
+ "restore_ties");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("accmenu-doubleflat")));
+
+ new KAction(RespellCommand::getGlobalName
+ (RespellCommand::Set, Accidentals::DoubleFlat),
+ icon, 0, this,
+ SLOT(slotRespellDoubleFlat()), actionCollection(),
+ "respell_doubleflat");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("accmenu-flat")));
+
+ new KAction(RespellCommand::getGlobalName
+ (RespellCommand::Set, Accidentals::Flat),
+ icon, 0, this,
+ SLOT(slotRespellFlat()), actionCollection(),
+ "respell_flat");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("accmenu-natural")));
+
+ new KAction(RespellCommand::getGlobalName
+ (RespellCommand::Set, Accidentals::Natural),
+ icon, 0, this,
+ SLOT(slotRespellNatural()), actionCollection(),
+ "respell_natural");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("accmenu-sharp")));
+
+ new KAction(RespellCommand::getGlobalName
+ (RespellCommand::Set, Accidentals::Sharp),
+ icon, 0, this,
+ SLOT(slotRespellSharp()), actionCollection(),
+ "respell_sharp");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("accmenu-doublesharp")));
+
+ new KAction(RespellCommand::getGlobalName
+ (RespellCommand::Set, Accidentals::DoubleSharp),
+ icon, 0, this,
+ SLOT(slotRespellDoubleSharp()), actionCollection(),
+ "respell_doublesharp");
+
+ new KAction(RespellCommand::getGlobalName
+ (RespellCommand::Up, Accidentals::NoAccidental),
+ Key_Up + CTRL + SHIFT, this,
+ SLOT(slotRespellUp()), actionCollection(),
+ "respell_up");
+
+ new KAction(RespellCommand::getGlobalName
+ (RespellCommand::Down, Accidentals::NoAccidental),
+ Key_Down + CTRL + SHIFT, this,
+ SLOT(slotRespellDown()), actionCollection(),
+ "respell_down");
+
+ new KAction(RespellCommand::getGlobalName
+ (RespellCommand::Restore, Accidentals::NoAccidental),
+ 0, this,
+ SLOT(slotRespellRestore()), actionCollection(),
+ "respell_restore");
+
+ new KAction(MakeAccidentalsCautionaryCommand::getGlobalName(true),
+ 0, this,
+ SLOT(slotShowCautionary()), actionCollection(),
+ "show_cautionary");
+
+ new KAction(MakeAccidentalsCautionaryCommand::getGlobalName(false),
+ 0, this,
+ SLOT(slotCancelCautionary()), actionCollection(),
+ "cancel_cautionary");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("quantize")));
+
+ new KAction(EventQuantizeCommand::getGlobalName(), icon, Key_Equal, this,
+ SLOT(slotTransformsQuantize()), actionCollection(),
+ "quantize");
+
+ new KAction(FixNotationQuantizeCommand::getGlobalName(), 0,
+ this, SLOT(slotTransformsFixQuantization()), actionCollection(),
+ "fix_quantization");
+
+ new KAction(RemoveNotationQuantizeCommand::getGlobalName(), 0,
+ this, SLOT(slotTransformsRemoveQuantization()), actionCollection(),
+ "remove_quantization");
+
+ new KAction(InterpretCommand::getGlobalName(), 0,
+ this, SLOT(slotTransformsInterpret()), actionCollection(),
+ "interpret");
+
+ new KAction(i18n("&Dump selected events to stderr"), 0, this,
+ SLOT(slotDebugDump()), actionCollection(), "debug_dump");
+
+ for (MarkActionDataMap::Iterator i = m_markActionDataMap->begin();
+ i != m_markActionDataMap->end(); ++i) {
+
+ const MarkActionData &markActionData = **i;
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap
+ (NotePixmapFactory::makeMarkMenuPixmap(markActionData.mark)));
+
+ new KAction(markActionData.title,
+ icon,
+ markActionData.keycode,
+ this,
+ SLOT(slotAddMark()),
+ actionCollection(),
+ markActionData.actionName);
+ }
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("text-mark")));
+
+ new KAction(AddTextMarkCommand::getGlobalName(), icon, 0, this,
+ SLOT(slotMarksAddTextMark()), actionCollection(),
+ "add_text_mark");
+
+ new KAction(AddFingeringMarkCommand::getGlobalName("0"), 0, Key_0 + ALT, this,
+ SLOT(slotMarksAddFingeringMarkFromAction()), actionCollection(),
+ "add_fingering_0");
+
+ new KAction(AddFingeringMarkCommand::getGlobalName("1"), 0, Key_1 + ALT, this,
+ SLOT(slotMarksAddFingeringMarkFromAction()), actionCollection(),
+ "add_fingering_1");
+
+ new KAction(AddFingeringMarkCommand::getGlobalName("2"), 0, Key_2 + ALT, this,
+ SLOT(slotMarksAddFingeringMarkFromAction()), actionCollection(),
+ "add_fingering_2");
+
+ new KAction(AddFingeringMarkCommand::getGlobalName("3"), 0, Key_3 + ALT, this,
+ SLOT(slotMarksAddFingeringMarkFromAction()), actionCollection(),
+ "add_fingering_3");
+
+ new KAction(AddFingeringMarkCommand::getGlobalName("4"), 0, Key_4 + ALT, this,
+ SLOT(slotMarksAddFingeringMarkFromAction()), actionCollection(),
+ "add_fingering_4");
+
+ new KAction(AddFingeringMarkCommand::getGlobalName("5"), 0, Key_5 + ALT, this,
+ SLOT(slotMarksAddFingeringMarkFromAction()), actionCollection(),
+ "add_fingering_5");
+
+ new KAction(AddFingeringMarkCommand::getGlobalName("+"), 0, Key_9 + ALT, this,
+ SLOT(slotMarksAddFingeringMarkFromAction()), actionCollection(),
+ "add_fingering_plus");
+
+ new KAction(AddFingeringMarkCommand::getGlobalName(), 0, 0, this,
+ SLOT(slotMarksAddFingeringMark()), actionCollection(),
+ "add_fingering_mark");
+
+ new KAction(RemoveMarksCommand::getGlobalName(), 0, this,
+ SLOT(slotMarksRemoveMarks()), actionCollection(),
+ "remove_marks");
+
+ new KAction(RemoveFingeringMarksCommand::getGlobalName(), 0, this,
+ SLOT(slotMarksRemoveFingeringMarks()), actionCollection(),
+ "remove_fingering_marks");
+
+ new KAction(i18n("Ma&ke Ornament..."), 0, this,
+ SLOT(slotMakeOrnament()), actionCollection(),
+ "make_ornament");
+
+ new KAction(i18n("Trigger &Ornament..."), 0, this,
+ SLOT(slotUseOrnament()), actionCollection(),
+ "use_ornament");
+
+ new KAction(i18n("Remove Ornament..."), 0, this,
+ SLOT(slotRemoveOrnament()), actionCollection(),
+ "remove_ornament");
+
+ static QString slashTitles[] = {
+ i18n("&None"), "&1", "&2", "&3", "&4", "&5"
+ };
+ for (int i = 0; i <= 5; ++i) {
+ new KAction(slashTitles[i], 0, this,
+ SLOT(slotAddSlashes()), actionCollection(),
+ QString("slashes_%1").arg(i));
+ }
+
+ new KAction(ClefInsertionCommand::getGlobalName(), 0, this,
+ SLOT(slotEditAddClef()), actionCollection(),
+ "add_clef");
+
+ new KAction(KeyInsertionCommand::getGlobalName(), 0, this,
+ SLOT(slotEditAddKeySignature()), actionCollection(),
+ "add_key_signature");
+
+ new KAction(SustainInsertionCommand::getGlobalName(true), 0, this,
+ SLOT(slotEditAddSustainDown()), actionCollection(),
+ "add_sustain_down");
+
+ new KAction(SustainInsertionCommand::getGlobalName(false), 0, this,
+ SLOT(slotEditAddSustainUp()), actionCollection(),
+ "add_sustain_up");
+
+ new KAction(TransposeCommand::getDiatonicGlobalName(false), 0, this,
+ SLOT(slotEditTranspose()), actionCollection(),
+ "transpose_segment");
+
+ new KAction(i18n("Convert Notation For..."), 0, this,
+ SLOT(slotEditSwitchPreset()), actionCollection(),
+ "switch_preset");
+
+
+ // setup Settings menu
+ static QString actionsToolbars[][4] =
+ {
+ { i18n("Show T&ools Toolbar"), "1slotToggleToolsToolBar()", "show_tools_toolbar", "palette-tools" },
+ { i18n("Show &Notes Toolbar"), "1slotToggleNotesToolBar()", "show_notes_toolbar", "palette-notes" },
+ { i18n("Show &Rests Toolbar"), "1slotToggleRestsToolBar()", "show_rests_toolbar", "palette-rests" },
+ { i18n("Show &Accidentals Toolbar"), "1slotToggleAccidentalsToolBar()", "show_accidentals_toolbar", "palette-accidentals" },
+ { i18n("Show Cle&fs Toolbar"), "1slotToggleClefsToolBar()", "show_clefs_toolbar",
+ "palette-clefs" },
+ { i18n("Show &Marks Toolbar"), "1slotToggleMarksToolBar()", "show_marks_toolbar",
+ "palette-marks" },
+ { i18n("Show &Group Toolbar"), "1slotToggleGroupToolBar()", "show_group_toolbar",
+ "palette-group" },
+ { i18n("Show &Layout Toolbar"), "1slotToggleLayoutToolBar()", "show_layout_toolbar",
+ "palette-font" },
+ { i18n("Show Trans&port Toolbar"), "1slotToggleTransportToolBar()", "show_transport_toolbar",
+ "palette-transport" },
+ { i18n("Show M&eta Toolbar"), "1slotToggleMetaToolBar()", "show_meta_toolbar",
+ "palette-meta" }
+ };
+
+ for (unsigned int i = 0;
+ i < sizeof(actionsToolbars) / sizeof(actionsToolbars[0]); ++i) {
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap(actionsToolbars[i][3])));
+
+ new KToggleAction(actionsToolbars[i][0], icon, 0,
+ this, actionsToolbars[i][1],
+ actionCollection(), actionsToolbars[i][2]);
+ }
+
+ new KAction(i18n("Cursor &Back"), 0, Key_Left, this,
+ SLOT(slotStepBackward()), actionCollection(),
+ "cursor_back");
+
+ new KAction(i18n("Cursor &Forward"), 0, Key_Right, this,
+ SLOT(slotStepForward()), actionCollection(),
+ "cursor_forward");
+
+ new KAction(i18n("Cursor Ba&ck Bar"), 0, Key_Left + CTRL, this,
+ SLOT(slotJumpBackward()), actionCollection(),
+ "cursor_back_bar");
+
+ new KAction(i18n("Cursor For&ward Bar"), 0, Key_Right + CTRL, this,
+ SLOT(slotJumpForward()), actionCollection(),
+ "cursor_forward_bar");
+
+ new KAction(i18n("Cursor Back and Se&lect"), SHIFT + Key_Left, this,
+ SLOT(slotExtendSelectionBackward()), actionCollection(),
+ "extend_selection_backward");
+
+ new KAction(i18n("Cursor Forward and &Select"), SHIFT + Key_Right, this,
+ SLOT(slotExtendSelectionForward()), actionCollection(),
+ "extend_selection_forward");
+
+ new KAction(i18n("Cursor Back Bar and Select"), SHIFT + CTRL + Key_Left, this,
+ SLOT(slotExtendSelectionBackwardBar()), actionCollection(),
+ "extend_selection_backward_bar");
+
+ new KAction(i18n("Cursor Forward Bar and Select"), SHIFT + CTRL + Key_Right, this,
+ SLOT(slotExtendSelectionForwardBar()), actionCollection(),
+ "extend_selection_forward_bar");
+
+ /*!!! not here yet
+ new KAction(i18n("Move Selection Left"), Key_Minus, this,
+ SLOT(slotMoveSelectionLeft()), actionCollection(),
+ "move_selection_left");
+ */
+
+ new KAction(i18n("Cursor to St&art"), 0,
+ /* #1025717: conflicting meanings for ctrl+a - dupe with Select All
+ Key_A + CTRL, */ this,
+ SLOT(slotJumpToStart()), actionCollection(),
+ "cursor_start");
+
+ new KAction(i18n("Cursor to &End"), 0, Key_E + CTRL, this,
+ SLOT(slotJumpToEnd()), actionCollection(),
+ "cursor_end");
+
+ new KAction(i18n("Cursor &Up Staff"), 0, Key_Up + SHIFT, this,
+ SLOT(slotCurrentStaffUp()), actionCollection(),
+ "cursor_up_staff");
+
+ new KAction(i18n("Cursor &Down Staff"), 0, Key_Down + SHIFT, this,
+ SLOT(slotCurrentStaffDown()), actionCollection(),
+ "cursor_down_staff");
+
+ new KAction(i18n("Cursor Pre&vious Segment"), 0, Key_Prior + ALT, this,
+ SLOT(slotCurrentSegmentPrior()), actionCollection(),
+ "cursor_prior_segment");
+
+ new KAction(i18n("Cursor Ne&xt Segment"), 0, Key_Next + ALT, this,
+ SLOT(slotCurrentSegmentNext()), actionCollection(),
+ "cursor_next_segment");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-cursor-to-pointer")));
+ new KAction(i18n("Cursor to &Playback Pointer"), icon, 0, this,
+ SLOT(slotJumpCursorToPlayback()), actionCollection(),
+ "cursor_to_playback_pointer");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-play")));
+ KAction *play = new KAction(i18n("&Play"), icon, Key_Enter, this,
+ SIGNAL(play()), actionCollection(), "play");
+ // Alternative shortcut for Play
+ KShortcut playShortcut = play->shortcut();
+ playShortcut.append( KKey(Key_Return + CTRL) );
+ play->setShortcut(playShortcut);
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-stop")));
+ new KAction(i18n("&Stop"), icon, Key_Insert, this,
+ SIGNAL(stop()), actionCollection(), "stop");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-rewind")));
+ new KAction(i18n("Re&wind"), icon, Key_End, this,
+ SIGNAL(rewindPlayback()), actionCollection(),
+ "playback_pointer_back_bar");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-ffwd")));
+ new KAction(i18n("&Fast Forward"), icon, Key_PageDown, this,
+ SIGNAL(fastForwardPlayback()), actionCollection(),
+ "playback_pointer_forward_bar");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-rewind-end")));
+ new KAction(i18n("Rewind to &Beginning"), icon, 0, this,
+ SIGNAL(rewindPlaybackToBeginning()), actionCollection(),
+ "playback_pointer_start");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-ffwd-end")));
+ new KAction(i18n("Fast Forward to &End"), icon, 0, this,
+ SIGNAL(fastForwardPlaybackToEnd()), actionCollection(),
+ "playback_pointer_end");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-pointer-to-cursor")));
+ new KAction(i18n("Playback Pointer to &Cursor"), icon, 0, this,
+ SLOT(slotJumpPlaybackToCursor()), actionCollection(),
+ "playback_pointer_to_cursor");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-solo")));
+ new KToggleAction(i18n("&Solo"), icon, 0, this,
+ SLOT(slotToggleSolo()), actionCollection(),
+ "toggle_solo");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-tracking")));
+ (new KToggleAction(i18n("Scro&ll to Follow Playback"), icon, Key_Pause, this,
+ SLOT(slotToggleTracking()), actionCollection(),
+ "toggle_tracking"))->setChecked(m_playTracking);
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap
+ ("transport-panic")));
+ new KAction(i18n("Panic"), icon, Key_P + CTRL + ALT, this,
+ SIGNAL(panic()), actionCollection(), "panic");
+
+ new KAction(i18n("Set Loop to Selection"), Key_Semicolon + CTRL, this,
+ SLOT(slotPreviewSelection()), actionCollection(),
+ "preview_selection");
+
+ new KAction(i18n("Clear L&oop"), Key_Colon + CTRL, this,
+ SLOT(slotClearLoop()), actionCollection(),
+ "clear_loop");
+
+ new KAction(i18n("Clear Selection"), Key_Escape, this,
+ SLOT(slotClearSelection()), actionCollection(),
+ "clear_selection");
+
+ // QString pixmapDir =
+ // KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ // icon = QIconSet(QCanvasPixmap(pixmapDir + "/toolbar/eventfilter.xpm"));
+ new KAction(i18n("&Filter Selection"), "filter", Key_F + CTRL, this,
+ SLOT(slotFilterSelection()), actionCollection(),
+ "filter_selection");
+
+ new KAction(i18n("Push &Left"), 0, this,
+ SLOT(slotFinePositionLeft()), actionCollection(),
+ "fine_position_left");
+
+ new KAction(i18n("Push &Right"), 0, this,
+ SLOT(slotFinePositionRight()), actionCollection(),
+ "fine_position_right");
+
+ new KAction(i18n("Push &Up"), 0, this,
+ SLOT(slotFinePositionUp()), actionCollection(),
+ "fine_position_up");
+
+ new KAction(i18n("Push &Down"), 0, this,
+ SLOT(slotFinePositionDown()), actionCollection(),
+ "fine_position_down");
+
+ new KAction(i18n("&Restore Positions"), 0, this,
+ SLOT(slotFinePositionRestore()), actionCollection(),
+ "fine_position_restore");
+
+ new KAction(i18n("Make &Invisible"), 0, this,
+ SLOT(slotMakeInvisible()), actionCollection(),
+ "make_invisible");
+
+ new KAction(i18n("Make &Visible"), 0, this,
+ SLOT(slotMakeVisible()), actionCollection(),
+ "make_visible");
+
+ new KAction(i18n("Toggle Dot"), Key_Period, this,
+ SLOT(slotToggleDot()), actionCollection(),
+ "toggle_dot");
+
+ new KAction(i18n("Add Dot"), Key_Period + CTRL, this,
+ SLOT(slotAddDot()), actionCollection(),
+ "add_dot");
+
+ new KAction(i18n("Add Dot"), Key_Period + CTRL + ALT, this,
+ SLOT(slotAddDotNotationOnly()), actionCollection(),
+ "add_notation_dot");
+
+ createGUI(getRCFileName(), false);
+}
+
+bool
+NotationView::isInChordMode()
+{
+ return ((KToggleAction *)actionCollection()->action("chord_mode"))->
+ isChecked();
+}
+
+bool
+NotationView::isInTripletMode()
+{
+ return ((KToggleAction *)actionCollection()->action("triplet_mode"))->
+ isChecked();
+}
+
+bool
+NotationView::isInGraceMode()
+{
+ return ((KToggleAction *)actionCollection()->action("grace_mode"))->
+ isChecked();
+}
+
+void
+NotationView::setupFontSizeMenu(std::string oldFontName)
+{
+ if (oldFontName != "") {
+
+ std::vector<int> sizes = NoteFontFactory::getScreenSizes(oldFontName);
+
+ for (unsigned int i = 0; i < sizes.size(); ++i) {
+ KAction *action =
+ actionCollection()->action
+ (QString("note_font_size_%1").arg(sizes[i]));
+ m_fontSizeActionMenu->remove
+ (action);
+
+ // Don't delete -- that could cause a crash when this
+ // function is called from the action itself. Instead
+ // we reuse and reinsert existing actions below.
+ }
+ }
+
+ std::vector<int> sizes = NoteFontFactory::getScreenSizes(m_fontName);
+
+ for (unsigned int i = 0; i < sizes.size(); ++i) {
+
+ QString actionName = QString("note_font_size_%1").arg(sizes[i]);
+
+ KToggleAction *sizeAction = dynamic_cast<KToggleAction *>
+ (actionCollection()->action(actionName));
+
+ if (!sizeAction) {
+ sizeAction =
+ new KToggleAction(i18n("1 pixel", "%n pixels", sizes[i]),
+ 0, this,
+ SLOT(slotChangeFontSizeFromAction()),
+ actionCollection(), actionName);
+ }
+
+ sizeAction->setChecked(sizes[i] == m_fontSize);
+ m_fontSizeActionMenu->insert(sizeAction);
+ }
+}
+
+LinedStaff *
+NotationView::getLinedStaff(int i)
+{
+ return getNotationStaff(i);
+}
+
+LinedStaff *
+NotationView::getLinedStaff(const Segment &segment)
+{
+ return getNotationStaff(segment);
+}
+
+NotationStaff *
+NotationView::getNotationStaff(const Segment &segment)
+{
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ if (&(m_staffs[i]->getSegment()) == &segment)
+ return m_staffs[i];
+ }
+ return 0;
+}
+
+bool NotationView::isCurrentStaff(int i)
+{
+ return getCurrentSegment() == &(m_staffs[i]->getSegment());
+}
+
+void NotationView::initLayoutToolbar()
+{
+ KToolBar *layoutToolbar = toolBar("Layout Toolbar");
+
+ if (!layoutToolbar) {
+ std::cerr
+ << "NotationView::initLayoutToolbar() : layout toolbar not found"
+ << std::endl;
+ return ;
+ }
+
+ new QLabel(i18n(" Font: "), layoutToolbar, "font label");
+
+ //
+ // font combo
+ //
+ m_fontCombo = new KComboBox(layoutToolbar);
+ m_fontCombo->setEditable(false);
+
+ std::set
+ <std::string> fs(NoteFontFactory::getFontNames());
+ std::vector<std::string> f(fs.begin(), fs.end());
+ std::sort(f.begin(), f.end());
+
+ bool foundFont = false;
+
+ for (std::vector<std::string>::iterator i = f.begin(); i != f.end(); ++i) {
+
+ QString fontQName(strtoqstr(*i));
+
+ m_fontCombo->insertItem(fontQName);
+ if (fontQName.lower() == strtoqstr(m_fontName).lower()) {
+ m_fontCombo->setCurrentItem(m_fontCombo->count() - 1);
+ foundFont = true;
+ }
+ }
+
+ if (!foundFont) {
+ KMessageBox::sorry
+ (this, i18n("Unknown font \"%1\", using default").arg
+ (strtoqstr(m_fontName)));
+ m_fontName = NoteFontFactory::getDefaultFontName();
+ }
+
+ connect(m_fontCombo, SIGNAL(activated(const QString &)),
+ this, SLOT(slotChangeFont(const QString &)));
+
+ new QLabel(i18n(" Size: "), layoutToolbar, "size label");
+
+ QString value;
+
+ //
+ // font size combo
+ //
+ std::vector<int> sizes = NoteFontFactory::getScreenSizes(m_fontName);
+ m_fontSizeCombo = new KComboBox(layoutToolbar, "font size combo");
+
+ for (std::vector<int>::iterator i = sizes.begin(); i != sizes.end(); ++i) {
+
+ value.setNum(*i);
+ m_fontSizeCombo->insertItem(value);
+ }
+ // set combo's current value to default
+ value.setNum(m_fontSize);
+ m_fontSizeCombo->setCurrentText(value);
+
+ connect(m_fontSizeCombo, SIGNAL(activated(const QString&)),
+ this, SLOT(slotChangeFontSizeFromStringValue(const QString&)));
+
+ new QLabel(i18n(" Spacing: "), layoutToolbar, "spacing label");
+
+ //
+ // spacing combo
+ //
+ int defaultSpacing = m_hlayout->getSpacing();
+ std::vector<int> spacings = NotationHLayout::getAvailableSpacings();
+
+ m_spacingCombo = new KComboBox(layoutToolbar, "spacing combo");
+ for (std::vector<int>::iterator i = spacings.begin(); i != spacings.end(); ++i) {
+
+ value.setNum(*i);
+ value += "%";
+ m_spacingCombo->insertItem(value);
+ }
+ // set combo's current value to default
+ value.setNum(defaultSpacing);
+ value += "%";
+ m_spacingCombo->setCurrentText(value);
+
+ connect(m_spacingCombo, SIGNAL(activated(const QString&)),
+ this, SLOT(slotChangeSpacingFromStringValue(const QString&)));
+}
+
+void NotationView::initStatusBar()
+{
+ KStatusBar* sb = statusBar();
+
+ m_hoveredOverNoteName = new QLabel(sb);
+ m_hoveredOverNoteName->setMinimumWidth(32);
+
+ m_hoveredOverAbsoluteTime = new QLabel(sb);
+ m_hoveredOverAbsoluteTime->setMinimumWidth(160);
+
+ sb->addWidget(m_hoveredOverAbsoluteTime);
+ sb->addWidget(m_hoveredOverNoteName);
+
+ QHBox *hbox = new QHBox(sb);
+ m_currentNotePixmap = new QLabel(hbox);
+ m_currentNotePixmap->setMinimumWidth(20);
+ m_insertModeLabel = new QLabel(hbox);
+ m_annotationsLabel = new QLabel(hbox);
+ m_lilyPondDirectivesLabel = new QLabel(hbox);
+ sb->addWidget(hbox);
+
+ sb->insertItem(KTmpStatusMsg::getDefaultMsg(),
+ KTmpStatusMsg::getDefaultId(), 1);
+ sb->setItemAlignment(KTmpStatusMsg::getDefaultId(),
+ AlignLeft | AlignVCenter);
+
+ m_selectionCounter = new QLabel(sb);
+ sb->addWidget(m_selectionCounter);
+
+ m_progressBar = new ProgressBar(100, true, sb);
+ m_progressBar->setMinimumWidth(100);
+ sb->addWidget(m_progressBar);
+}
+
+QSize NotationView::getViewSize()
+{
+ return canvas()->size();
+}
+
+void NotationView::setViewSize(QSize s)
+{
+ canvas()->resize(s.width(), s.height());
+
+ if ( (m_pageMode == LinedStaff::LinearMode)
+ && (m_showHeadersGroup != HeadersGroup::ShowNever)) {
+ m_headersGroup->completeToHeight(s.height());
+ }
+}
+
+void
+NotationView::setPageMode(LinedStaff::PageMode pageMode)
+{
+ m_pageMode = pageMode;
+
+ if (pageMode != LinedStaff::LinearMode) {
+ if (m_topStandardRuler)
+ m_topStandardRuler->hide();
+ if (m_bottomStandardRuler)
+ m_bottomStandardRuler->hide();
+ if (m_chordNameRuler)
+ m_chordNameRuler->hide();
+ if (m_rawNoteRuler)
+ m_rawNoteRuler->hide();
+ if (m_tempoRuler)
+ m_tempoRuler->hide();
+ hideHeadersGroup();
+ } else {
+ if (m_topStandardRuler)
+ m_topStandardRuler->show();
+ if (m_bottomStandardRuler)
+ m_bottomStandardRuler->show();
+ if (m_chordNameRuler && getToggleAction("show_chords_ruler")->isChecked())
+ m_chordNameRuler->show();
+ if (m_rawNoteRuler && getToggleAction("show_raw_note_ruler")->isChecked())
+ m_rawNoteRuler->show();
+ if (m_tempoRuler && getToggleAction("show_tempo_ruler")->isChecked())
+ m_tempoRuler->show();
+ showHeadersGroup();
+ }
+
+ stateChanged("linear_mode",
+ (pageMode == LinedStaff::LinearMode ? KXMLGUIClient::StateNoReverse :
+ KXMLGUIClient::StateReverse));
+
+ int pageWidth = getPageWidth();
+ int topMargin = 0, leftMargin = 0;
+ getPageMargins(leftMargin, topMargin);
+
+ m_hlayout->setPageMode(pageMode != LinedStaff::LinearMode);
+ m_hlayout->setPageWidth(pageWidth - leftMargin * 2);
+
+ NOTATION_DEBUG << "NotationView::setPageMode: set layout's page width to "
+ << (pageWidth - leftMargin * 2) << endl;
+
+ positionStaffs();
+
+ bool layoutApplied = applyLayout();
+ if (!layoutApplied)
+ KMessageBox::sorry(0, "Couldn't apply layout");
+ else {
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ m_staffs[i]->markChanged();
+ }
+ }
+
+ if (!m_printMode) {
+ // Layout is done : Time related to left of canvas should now
+ // correctly be determined and track headers contents be drawn.
+ m_headersGroup->slotUpdateAllHeaders(0, 0, true);
+ }
+
+ positionPages();
+
+ if (!m_printMode) {
+ updateView();
+ slotSetInsertCursorPosition(getInsertionTime(), false, false);
+ slotSetPointerPosition(getDocument()->getComposition().getPosition(), false);
+ }
+
+ Profiles::getInstance()->dump();
+}
+
+int
+NotationView::getPageWidth()
+{
+ if (m_pageMode != LinedStaff::MultiPageMode) {
+
+ if (isInPrintMode() && getCanvasView() && getCanvasView()->canvas())
+ return getCanvasView()->canvas()->width();
+
+ if (getCanvasView()) {
+ return
+ getCanvasView()->width() -
+ getCanvasView()->verticalScrollBar()->width() -
+ m_leftGutter - 10;
+ }
+
+ return width() - 50;
+
+ } else {
+
+ //!!! For the moment we use A4 for this calculation
+
+ double printSizeMm = 25.4 * ((double)m_printSize / 72.0);
+ double mmPerPixel = printSizeMm / (double)m_notePixmapFactory->getSize();
+ return (int)(210.0 / mmPerPixel);
+ }
+}
+
+int
+NotationView::getPageHeight()
+{
+ if (m_pageMode != LinedStaff::MultiPageMode) {
+
+ if (isInPrintMode() && getCanvasView() && getCanvasView()->canvas())
+ return getCanvasView()->canvas()->height();
+
+ if (getCanvasView()) {
+ return getCanvasView()->height();
+ }
+
+ return (height() > 200 ? height() - 100 : height());
+
+ } else {
+
+ //!!! For the moment we use A4 for this calculation
+
+ double printSizeMm = 25.4 * ((double)m_printSize / 72.0);
+ double mmPerPixel = printSizeMm / (double)m_notePixmapFactory->getSize();
+ return (int)(297.0 / mmPerPixel);
+ }
+}
+
+void
+NotationView::getPageMargins(int &left, int &top)
+{
+ if (m_pageMode != LinedStaff::MultiPageMode) {
+
+ left = 0;
+ top = 0;
+
+ } else {
+
+ //!!! For the moment we use A4 for this calculation
+
+ double printSizeMm = 25.4 * ((double)m_printSize / 72.0);
+ double mmPerPixel = printSizeMm / (double)m_notePixmapFactory->getSize();
+ left = (int)(20.0 / mmPerPixel);
+ top = (int)(15.0 / mmPerPixel);
+ }
+}
+
+void
+NotationView::scrollToTime(timeT t)
+{
+
+ double notationViewLayoutCoord = m_hlayout->getXForTime(t);
+
+ // Doesn't appear to matter which staff we use
+ //!!! actually it probably does matter, if they don't have the same extents
+ double notationViewCanvasCoord =
+ getLinedStaff(0)->getCanvasCoordsForLayoutCoords
+ (notationViewLayoutCoord, 0).first;
+
+ // HK: I could have sworn I saw a hard-coded scroll happen somewhere
+ // (i.e. a default extra scroll to make up for the staff not beginning on
+ // the left edge) but now I can't find it.
+ getCanvasView()->slotScrollHorizSmallSteps
+ (int(notationViewCanvasCoord)); // + DEFAULT_STAFF_OFFSET);
+}
+
+RulerScale*
+NotationView::getHLayout()
+{
+ return m_hlayout;
+}
+
+void
+NotationView::paintEvent(QPaintEvent *e)
+{
+ m_inPaintEvent = true;
+
+ // This is duplicated here from EditViewBase, because (a) we need
+ // to know about the segment being removed before we try to check
+ // the staff names etc., and (b) it's not safe to call close()
+ // from EditViewBase::paintEvent if we're then going to try to do
+ // some more work afterwards in this function
+
+ if (isCompositionModified()) {
+
+ // Check if one of the segments we display has been removed
+ // from the composition.
+ //
+ // For the moment we'll have to close the view if any of the
+ // segments we handle has been deleted.
+
+ for (unsigned int i = 0; i < m_segments.size(); ++i) {
+
+ if (!m_segments[i]->getComposition()) {
+ // oops, I think we've been deleted
+ close();
+ return ;
+ }
+ }
+ }
+
+ int topMargin = 0, leftMargin = 0;
+ getPageMargins(leftMargin, topMargin);
+
+ if (m_pageMode == LinedStaff::ContinuousPageMode) {
+ // relayout if the window width changes significantly in continuous page mode
+ int diff = int(getPageWidth() - leftMargin * 2 - m_hlayout->getPageWidth());
+ if (diff < -10 || diff > 10) {
+ setPageMode(m_pageMode);
+ refreshSegment(0, 0, 0);
+ }
+
+ } else if (m_pageMode == LinedStaff::LinearMode) {
+ // resize canvas again if the window height has changed significantly
+ if (getCanvasView() && getCanvasView()->canvas()) {
+ int diff = int(getPageHeight() - getCanvasView()->canvas()->height());
+ if (diff > 10) {
+ readjustCanvasSize();
+ }
+ }
+ }
+
+ // check for staff name changes
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ if (!m_staffs[i]->isStaffNameUpToDate()) {
+ refreshSegment(0);
+ break;
+ }
+ }
+
+ m_inPaintEvent = false;
+
+ EditView::paintEvent(e);
+
+ m_inPaintEvent = false;
+
+ // now deal with any backlog of insertable notes that appeared
+ // during paint (because it's not safe to modify a segment from
+ // within a sub-event-loop in a processEvents call from a paint)
+ if (!m_pendingInsertableNotes.empty()) {
+ std::vector<std::pair<int, int> > notes = m_pendingInsertableNotes;
+ m_pendingInsertableNotes.clear();
+ for (unsigned int i = 0; i < notes.size(); ++i) {
+ slotInsertableNoteEventReceived(notes[i].first, notes[i].second, true);
+ }
+ }
+
+ slotSetOperationNameAndStatus(i18n(" Ready."));
+}
+
+bool NotationView::applyLayout(int staffNo, timeT startTime, timeT endTime)
+{
+ slotSetOperationNameAndStatus(i18n("Laying out score..."));
+ ProgressDialog::processEvents();
+
+ m_hlayout->setStaffCount(m_staffs.size());
+
+ Profiler profiler("NotationView::applyLayout");
+ unsigned int i;
+
+ for (i = 0; i < m_staffs.size(); ++i) {
+
+ if (staffNo >= 0 && (int)i != staffNo)
+ continue;
+
+ slotSetOperationNameAndStatus(i18n("Laying out staff %1...").arg(i + 1));
+ ProgressDialog::processEvents();
+
+ m_hlayout->resetStaff(*m_staffs[i], startTime, endTime);
+ m_vlayout->resetStaff(*m_staffs[i], startTime, endTime);
+ m_hlayout->scanStaff(*m_staffs[i], startTime, endTime);
+ m_vlayout->scanStaff(*m_staffs[i], startTime, endTime);
+ }
+
+ slotSetOperationNameAndStatus(i18n("Reconciling staffs..."));
+ ProgressDialog::processEvents();
+
+ m_hlayout->finishLayout(startTime, endTime);
+ m_vlayout->finishLayout(startTime, endTime);
+
+ // find the last finishing staff for future use
+
+ timeT lastFinishingStaffEndTime = 0;
+ bool haveEndTime = false;
+ m_lastFinishingStaff = -1;
+
+ timeT firstStartingStaffStartTime = 0;
+ bool haveStartTime = false;
+ int firstStartingStaff = -1;
+
+ for (i = 0; i < m_staffs.size(); ++i) {
+
+ timeT thisStartTime = m_staffs[i]->getSegment().getStartTime();
+ if (thisStartTime < firstStartingStaffStartTime || !haveStartTime) {
+ firstStartingStaffStartTime = thisStartTime;
+ haveStartTime = true;
+ firstStartingStaff = i;
+ }
+
+ timeT thisEndTime = m_staffs[i]->getSegment().getEndTime();
+ if (thisEndTime > lastFinishingStaffEndTime || !haveEndTime) {
+ lastFinishingStaffEndTime = thisEndTime;
+ haveEndTime = true;
+ m_lastFinishingStaff = i;
+ }
+ }
+
+ readjustCanvasSize();
+ if (m_topStandardRuler) {
+ m_topStandardRuler->update();
+ }
+ if (m_bottomStandardRuler) {
+ m_bottomStandardRuler->update();
+ }
+ if (m_tempoRuler && m_tempoRuler->isVisible()) {
+ m_tempoRuler->update();
+ }
+ if (m_rawNoteRuler && m_rawNoteRuler->isVisible()) {
+ m_rawNoteRuler->update();
+ }
+
+ return true;
+}
+
+void NotationView::setCurrentSelectedNote(const char *pixmapName,
+ bool rest, Note::Type n, int dots)
+{
+ NoteInserter* inserter = 0;
+
+ if (rest)
+ inserter = dynamic_cast<NoteInserter*>(m_toolBox->getTool(RestInserter::ToolName));
+ else
+ inserter = dynamic_cast<NoteInserter*>(m_toolBox->getTool(NoteInserter::ToolName));
+
+ inserter->slotSetNote(n);
+ inserter->slotSetDots(dots);
+
+ setTool(inserter);
+
+ m_currentNotePixmap->setPixmap
+ (NotePixmapFactory::toQPixmap
+ (NotePixmapFactory::makeToolbarPixmap(pixmapName, true)));
+
+ emit changeCurrentNote(rest, n);
+}
+
+void NotationView::setCurrentSelectedNote(const NoteActionData &noteAction)
+{
+ setCurrentSelectedNote(noteAction.pixmapName,
+ noteAction.rest,
+ noteAction.noteType,
+ noteAction.dots);
+}
+
+void NotationView::setCurrentSelection(EventSelection* s, bool preview,
+ bool redrawNow)
+{
+ //!!! rather too much here shared with matrixview -- could much of
+ // this be in editview?
+
+ if (m_currentEventSelection == s)
+ return ;
+ NOTATION_DEBUG << "XXX " << endl;
+
+ EventSelection *oldSelection = m_currentEventSelection;
+ m_currentEventSelection = s;
+
+ // positionElements is overkill here, but we hope it's not too
+ // much overkill (if that's not a contradiction)
+
+ timeT startA, endA, startB, endB;
+
+ if (oldSelection) {
+ startA = oldSelection->getStartTime();
+ endA = oldSelection->getEndTime();
+ startB = s ? s->getStartTime() : startA;
+ endB = s ? s->getEndTime() : endA;
+ } else {
+ // we know they can't both be null -- first thing we tested above
+ startA = startB = s->getStartTime();
+ endA = endB = s->getEndTime();
+ }
+
+ // refreshSegment takes start==end to mean refresh everything
+ if (startA == endA)
+ ++endA;
+ if (startB == endB)
+ ++endB;
+
+ bool updateRequired = true;
+
+ // play previews if appropriate -- also permits an optimisation
+ // for the case where the selection is unchanged (quite likely
+ // when sweeping)
+
+ if (s && preview) {
+
+ bool foundNewEvent = false;
+
+ for (EventSelection::eventcontainer::iterator i =
+ s->getSegmentEvents().begin();
+ i != s->getSegmentEvents().end(); ++i) {
+
+ if (oldSelection && oldSelection->getSegment() == s->getSegment()
+ && oldSelection->contains(*i))
+ continue;
+
+ foundNewEvent = true;
+
+ long pitch;
+ if (!(*i)->get
+ <Int>(BaseProperties::PITCH,
+ pitch)) continue;
+
+ long velocity = -1;
+ (void)(*i)->get
+ <Int>(BaseProperties::VELOCITY,
+ velocity);
+
+ if (!((*i)->has(BaseProperties::TIED_BACKWARD) &&
+ (*i)->get
+ <Bool>
+ (BaseProperties::TIED_BACKWARD)))
+ playNote(s->getSegment(), pitch, velocity);
+ }
+
+ if (!foundNewEvent) {
+ if (oldSelection &&
+ oldSelection->getSegment() == s->getSegment() &&
+ oldSelection->getSegmentEvents().size() ==
+ s->getSegmentEvents().size())
+ updateRequired = false;
+ }
+ }
+
+ if (updateRequired) {
+
+ if (!s || !oldSelection ||
+ (endA >= startB && endB >= startA &&
+ oldSelection->getSegment() == s->getSegment())) {
+
+ // the regions overlap: use their union and just do one refresh
+
+ Segment &segment(s ? s->getSegment() :
+ oldSelection->getSegment());
+
+ if (redrawNow) {
+ // recolour the events now
+ getLinedStaff(segment)->positionElements(std::min(startA, startB),
+ std::max(endA, endB));
+ } else {
+ // mark refresh status and then request a repaint
+ segment.getRefreshStatus
+ (m_segmentsRefreshStatusIds
+ [getLinedStaff(segment)->getId()]).
+ push(std::min(startA, startB), std::max(endA, endB));
+ }
+
+ } else {
+ // do two refreshes, one for each -- here we know neither is null
+
+ if (redrawNow) {
+ // recolour the events now
+ getLinedStaff(oldSelection->getSegment())->positionElements(startA,
+ endA);
+
+ getLinedStaff(s->getSegment())->positionElements(startB, endB);
+ } else {
+ // mark refresh status and then request a repaint
+
+ oldSelection->getSegment().getRefreshStatus
+ (m_segmentsRefreshStatusIds
+ [getLinedStaff(oldSelection->getSegment())->getId()]).
+ push(startA, endA);
+
+ s->getSegment().getRefreshStatus
+ (m_segmentsRefreshStatusIds
+ [getLinedStaff(s->getSegment())->getId()]).
+ push(startB, endB);
+ }
+ }
+
+ if (s) {
+ // make the staff containing the selection current
+ int staffId = getLinedStaff(s->getSegment())->getId();
+ if (staffId != m_currentStaff)
+ slotSetCurrentStaff(staffId);
+ }
+ }
+
+ delete oldSelection;
+
+ statusBar()->changeItem(KTmpStatusMsg::getDefaultMsg(),
+ KTmpStatusMsg::getDefaultId());
+
+ if (s) {
+ int eventsSelected = s->getSegmentEvents().size();
+ m_selectionCounter->setText
+ (i18n(" 1 event selected ",
+ " %n events selected ", eventsSelected));
+ } else {
+ m_selectionCounter->setText(i18n(" No selection "));
+ }
+ m_selectionCounter->update();
+
+ setMenuStates();
+
+ if (redrawNow)
+ updateView();
+ else
+ update();
+
+ NOTATION_DEBUG << "XXX " << endl;
+}
+
+void NotationView::setSingleSelectedEvent(int staffNo, Event *event,
+ bool preview, bool redrawNow)
+{
+ setSingleSelectedEvent(getStaff(staffNo)->getSegment(), event,
+ preview, redrawNow);
+}
+
+void NotationView::setSingleSelectedEvent(Segment &segment, Event *event,
+ bool preview, bool redrawNow)
+{
+ EventSelection *selection = new EventSelection(segment);
+ selection->addEvent(event);
+ setCurrentSelection(selection, preview, redrawNow);
+}
+
+bool NotationView::canPreviewAnotherNote()
+{
+ static time_t lastCutOff = 0;
+ static int sinceLastCutOff = 0;
+
+ time_t now = time(0);
+ ++sinceLastCutOff;
+
+ if ((now - lastCutOff) > 0) {
+ sinceLastCutOff = 0;
+ lastCutOff = now;
+ NOTATION_DEBUG << "NotationView::canPreviewAnotherNote: reset" << endl;
+ } else {
+ if (sinceLastCutOff >= 20) {
+ // don't permit more than 20 notes per second or so, to
+ // avoid gungeing up the sound drivers
+ NOTATION_DEBUG << "Rejecting preview (too busy)" << endl;
+ return false;
+ }
+ NOTATION_DEBUG << "NotationView::canPreviewAnotherNote: ok" << endl;
+ }
+
+ return true;
+}
+
+void NotationView::playNote(Segment &s, int pitch, int velocity)
+{
+ Composition &comp = getDocument()->getComposition();
+ Studio &studio = getDocument()->getStudio();
+ Track *track = comp.getTrackById(s.getTrack());
+
+ Instrument *ins =
+ studio.getInstrumentById(track->getInstrument());
+
+ // check for null instrument
+ //
+ if (ins == 0)
+ return ;
+
+ if (!canPreviewAnotherNote())
+ return ;
+
+ if (velocity < 0)
+ velocity = MidiMaxValue;
+
+ MappedEvent mE(ins->getId(),
+ MappedEvent::MidiNoteOneShot,
+ pitch + s.getTranspose(),
+ velocity,
+ RealTime::zeroTime,
+ RealTime(0, 250000000),
+ RealTime::zeroTime);
+
+ StudioControl::sendMappedEvent(mE);
+}
+
+void NotationView::showPreviewNote(int staffNo, double layoutX,
+ int pitch, int height,
+ const Note &note, bool grace,
+ int velocity)
+{
+ m_staffs[staffNo]->showPreviewNote(layoutX, height, note, grace);
+ playNote(m_staffs[staffNo]->getSegment(), pitch, velocity);
+}
+
+void NotationView::clearPreviewNote()
+{
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ m_staffs[i]->clearPreviewNote();
+ }
+}
+
+void NotationView::setNotePixmapFactory(NotePixmapFactory* f)
+{
+ delete m_notePixmapFactory;
+ m_notePixmapFactory = f;
+ if (m_hlayout)
+ m_hlayout->setNotePixmapFactory(m_notePixmapFactory);
+ if (m_vlayout)
+ m_vlayout->setNotePixmapFactory(m_notePixmapFactory);
+}
+
+Segment *
+NotationView::getCurrentSegment()
+{
+ Staff *staff = getCurrentStaff();
+ return (staff ? &staff->getSegment() : 0);
+}
+
+bool
+NotationView::hasSegment(Segment *segment)
+{
+ for (unsigned int i = 0; i < m_segments.size(); ++i) {
+ if (segment == m_segments[i]) return true;
+ }
+ return false;
+}
+
+
+LinedStaff *
+NotationView::getCurrentLinedStaff()
+{
+ return getLinedStaff(m_currentStaff);
+}
+
+LinedStaff *
+NotationView::getStaffAbove()
+{
+ if (m_staffs.size() < 2) return 0;
+
+ Composition *composition =
+ m_staffs[m_currentStaff]->getSegment().getComposition();
+
+ Track *track = composition->
+ getTrackById(m_staffs[m_currentStaff]->getSegment().getTrack());
+ if (!track) return 0;
+
+ int position = track->getPosition();
+ Track *newTrack = 0;
+
+ while ((newTrack = composition->getTrackByPosition(--position))) {
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ if (m_staffs[i]->getSegment().getTrack() == newTrack->getId()) {
+ return m_staffs[i];
+ }
+ }
+ }
+
+ return 0;
+}
+
+LinedStaff *
+NotationView::getStaffBelow()
+{
+ if (m_staffs.size() < 2) return 0;
+
+ Composition *composition =
+ m_staffs[m_currentStaff]->getSegment().getComposition();
+
+ Track *track = composition->
+ getTrackById(m_staffs[m_currentStaff]->getSegment().getTrack());
+ if (!track) return 0;
+
+ int position = track->getPosition();
+ Track *newTrack = 0;
+
+ while ((newTrack = composition->getTrackByPosition(++position))) {
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ if (m_staffs[i]->getSegment().getTrack() == newTrack->getId()) {
+ return m_staffs[i];
+ }
+ }
+ }
+
+ return 0;
+}
+
+timeT
+NotationView::getInsertionTime()
+{
+ return m_insertionTime;
+}
+
+timeT
+NotationView::getInsertionTime(Clef &clef,
+ Rosegarden::Key &key)
+{
+ // This fuss is solely to recover the clef and key: we already
+ // set m_insertionTime to the right value when we first placed
+ // the insert cursor. We could get clef and key directly from
+ // the segment but the staff has a more efficient lookup
+
+ LinedStaff *staff = m_staffs[m_currentStaff];
+ double layoutX = staff->getLayoutXOfInsertCursor();
+ if (layoutX < 0) layoutX = 0;
+ Event *clefEvt = 0, *keyEvt = 0;
+ (void)staff->getElementUnderLayoutX(layoutX, clefEvt, keyEvt);
+
+ if (clefEvt) clef = Clef(*clefEvt);
+ else clef = Clef();
+
+ if (keyEvt) key = Rosegarden::Key(*keyEvt);
+ else key = Rosegarden::Key();
+
+ return m_insertionTime;
+}
+
+LinedStaff*
+NotationView::getStaffForCanvasCoords(int x, int y) const
+{
+ // (i) Do not change staff, if mouse was clicked within the current staff.
+ LinedStaff *s = m_staffs[m_currentStaff];
+ if (s->containsCanvasCoords(x, y)) {
+ LinedStaff::LinedStaffCoords coords =
+ s->getLayoutCoordsForCanvasCoords(x, y);
+
+ timeT t = m_hlayout->getTimeForX(coords.first);
+ // In order to find the correct starting and ending bar of the segment,
+ // make infinitesimal shifts (+1 and -1) towards its center.
+ timeT t0 = getDocument()->getComposition().getBarStartForTime(m_staffs[m_currentStaff]->getSegment().getStartTime()+1);
+ timeT t1 = getDocument()->getComposition().getBarEndForTime(m_staffs[m_currentStaff]->getSegment().getEndTime()-1);
+ if (t >= t0 && t < t1) {
+ return m_staffs[m_currentStaff];
+ }
+ }
+ // (ii) Find staff under cursor, if clicked outside the current staff.
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+
+ LinedStaff *s = m_staffs[i];
+
+ if (s->containsCanvasCoords(x, y)) {
+
+ LinedStaff::LinedStaffCoords coords =
+ s->getLayoutCoordsForCanvasCoords(x, y);
+
+ timeT t = m_hlayout->getTimeForX(coords.first);
+ // In order to find the correct starting and ending bar of the segment,
+ // make infinitesimal shifts (+1 and -1) towards its center.
+ timeT t0 = getDocument()->getComposition().getBarStartForTime(m_staffs[i]->getSegment().getStartTime()+1);
+ timeT t1 = getDocument()->getComposition().getBarEndForTime(m_staffs[i]->getSegment().getEndTime()-1);
+ if (t >= t0 && t < t1) {
+ return m_staffs[i];
+ }
+ }
+ }
+
+ return 0;
+}
+
+void NotationView::updateView()
+{
+ slotCheckRendered
+ (getCanvasView()->contentsX(),
+ getCanvasView()->contentsX() + getCanvasView()->visibleWidth());
+ canvas()->update();
+}
+
+void NotationView::print(bool previewOnly)
+{
+ if (m_staffs.size() == 0) {
+ KMessageBox::error(0, "Nothing to print");
+ return ;
+ }
+
+ Profiler profiler("NotationView::print");
+
+ // We need to be in multi-page mode at this point
+
+ int pageWidth = getPageWidth();
+ int pageHeight = getPageHeight();
+ int leftMargin = 0, topMargin = 0;
+ getPageMargins(leftMargin, topMargin);
+ int maxPageCount = 1;
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ int pageCount = m_staffs[i]->getPageCount();
+ NOTATION_DEBUG << "NotationView::print(): staff " << i << " reports " << pageCount << " pages " << endl;
+ if (pageCount > maxPageCount)
+ maxPageCount = pageCount;
+ }
+
+ KPrinter printer(true, QPrinter::HighResolution);
+
+ printer.setPageSelection(KPrinter::ApplicationSide);
+ printer.setMinMax(1, maxPageCount + 1);
+
+ if (previewOnly)
+ printer.setPreviewOnly(true);
+ else if (!printer.setup((QWidget *)parent()))
+ return ;
+
+ QPaintDeviceMetrics pdm(&printer);
+ QPainter printpainter(&printer);
+
+ // Ideally we should aim to retain the aspect ratio and to move the
+ // staffs so as to be centred after scaling. But because we haven't
+ // got around to the latter, let's lose the former too and just
+ // expand to fit.
+
+ // Retain aspect ratio when scaling
+ double ratioX = (double)pdm.width() / (double)(pageWidth - leftMargin * 2),
+ ratioY = (double)pdm.height() / (double)(pageHeight - topMargin * 2);
+ double ratio = std::min(ratioX, ratioY);
+ printpainter.scale(ratio, ratio);
+
+ // printpainter.scale((double)pdm.width() / (double)(pageWidth - leftMargin*2),
+ // (double)pdm.height() / (double)(pageHeight - topMargin*2));
+ printpainter.translate( -leftMargin, -topMargin);
+
+ QValueList<int> pages = printer.pageList();
+
+ for (QValueList<int>::Iterator pli = pages.begin();
+ pli != pages.end(); ) { // incremented just below
+
+ int page = *pli - 1;
+ ++pli;
+ if (page < 0 || page >= maxPageCount)
+ continue;
+
+ NOTATION_DEBUG << "Printing page " << page << endl;
+
+ QRect pageRect(m_leftGutter + leftMargin + pageWidth * page,
+ topMargin,
+ pageWidth - leftMargin,
+ pageHeight - topMargin);
+
+ for (size_t i = 0; i < m_staffs.size(); ++i) {
+
+ LinedStaff *staff = m_staffs[i];
+
+ LinedStaff::LinedStaffCoords cc0 = staff->getLayoutCoordsForCanvasCoords
+ (pageRect.x(), pageRect.y());
+
+ LinedStaff::LinedStaffCoords cc1 = staff->getLayoutCoordsForCanvasCoords
+ (pageRect.x() + pageRect.width(), pageRect.y() + pageRect.height());
+
+ timeT t0 = m_hlayout->getTimeForX(cc0.first);
+ timeT t1 = m_hlayout->getTimeForX(cc1.first);
+
+ m_staffs[i]->setPrintPainter(&printpainter);
+ m_staffs[i]->checkRendered(t0, t1);
+ }
+
+ // Supplying doublebuffer==true to this method appears to
+ // slow down printing considerably but without it we get
+ // all sorts of horrible artifacts (possibly related to
+ // mishandling of pixmap masks?) in qt-3.0. Let's permit
+ // it as a "hidden" option.
+
+ m_config->setGroup(NotationViewConfigGroup);
+
+ NOTATION_DEBUG << "NotationView::print: calling QCanvas::drawArea" << endl;
+
+ {
+ Profiler profiler("NotationView::print(QCanvas::drawArea)");
+
+ if (m_config->readBoolEntry("forcedoublebufferprinting", false)) {
+ getCanvasView()->canvas()->drawArea(pageRect, &printpainter, true);
+ } else {
+#if QT_VERSION >= 0x030100
+ getCanvasView()->canvas()->drawArea(pageRect, &printpainter, false);
+#else
+
+ getCanvasView()->canvas()->drawArea(pageRect, &printpainter, true);
+#endif
+
+ }
+
+ }
+
+ NOTATION_DEBUG << "NotationView::print: QCanvas::drawArea done" << endl;
+
+ for (size_t i = 0; i < m_staffs.size(); ++i) {
+
+ LinedStaff *staff = m_staffs[i];
+
+ LinedStaff::LinedStaffCoords cc0 = staff->getLayoutCoordsForCanvasCoords
+ (pageRect.x(), pageRect.y());
+
+ LinedStaff::LinedStaffCoords cc1 = staff->getLayoutCoordsForCanvasCoords
+ (pageRect.x() + pageRect.width(), pageRect.y() + pageRect.height());
+
+ timeT t0 = m_hlayout->getTimeForX(cc0.first);
+ timeT t1 = m_hlayout->getTimeForX(cc1.first);
+
+ m_staffs[i]->renderPrintable(t0, t1);
+ }
+
+ printpainter.translate( -pageWidth, 0);
+
+ if (pli != pages.end() && *pli - 1 < maxPageCount)
+ printer.newPage();
+
+ for (size_t i = 0; i < m_staffs.size(); ++i) {
+ m_staffs[i]->markChanged(); // recover any memory used for this page
+ PixmapArrayGC::deleteAll();
+ }
+ }
+
+ for (size_t i = 0; i < m_staffs.size(); ++i) {
+ for (Segment::iterator j = m_staffs[i]->getSegment().begin();
+ j != m_staffs[i]->getSegment().end(); ++j) {
+ removeViewLocalProperties(*j);
+ }
+ delete m_staffs[i];
+ }
+ m_staffs.clear();
+
+ printpainter.end();
+
+ Profiles::getInstance()->dump();
+}
+
+void
+NotationView::updateThumbnails(bool complete)
+{
+ if (m_pageMode != LinedStaff::MultiPageMode)
+ return ;
+
+ int pageWidth = getPageWidth();
+ int pageHeight = getPageHeight();
+ int leftMargin = 0, topMargin = 0;
+ getPageMargins(leftMargin, topMargin);
+ int maxPageCount = 1;
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ int pageCount = m_staffs[i]->getPageCount();
+ if (pageCount > maxPageCount)
+ maxPageCount = pageCount;
+ }
+
+ int thumbScale = 20;
+ QPixmap thumbnail(canvas()->width() / thumbScale,
+ canvas()->height() / thumbScale);
+ thumbnail.fill(Qt::white);
+ QPainter thumbPainter(&thumbnail);
+
+ if (complete) {
+
+ thumbPainter.scale(1.0 / double(thumbScale), 1.0 / double(thumbScale));
+ thumbPainter.setPen(Qt::black);
+ thumbPainter.setBrush(Qt::white);
+
+ /*
+ QCanvas *canvas = getCanvasView()->canvas();
+ canvas->drawArea(QRect(0, 0, canvas->width(), canvas->height()),
+ &thumbPainter, false);
+ */
+ // hide small texts, as we get a crash in Xft when trying to
+ // render them at this scale
+ if (m_title)
+ m_title->hide();
+ if (m_subtitle)
+ m_subtitle->hide();
+ if (m_composer)
+ m_composer->hide();
+ if (m_copyright)
+ m_copyright->hide();
+
+ for (size_t page = 0; page < static_cast<size_t>(maxPageCount); ++page) {
+
+ bool havePageNumber = ((m_pageNumbers.size() > page) &&
+ (m_pageNumbers[page] != 0));
+ if (havePageNumber)
+ m_pageNumbers[page]->hide();
+
+ QRect pageRect(m_leftGutter + leftMargin * 2 + pageWidth * page,
+ topMargin * 2,
+ pageWidth - leftMargin*3,
+ pageHeight - topMargin*3);
+
+ QCanvas *canvas = getCanvasView()->canvas();
+ canvas->drawArea(pageRect, &thumbPainter, false);
+
+ if (havePageNumber)
+ m_pageNumbers[page]->show();
+ }
+
+ if (m_title)
+ m_title->show();
+ if (m_subtitle)
+ m_subtitle->show();
+ if (m_composer)
+ m_composer->show();
+ if (m_copyright)
+ m_copyright->show();
+
+ } else {
+
+ thumbPainter.setPen(Qt::black);
+
+ for (int page = 0; page < maxPageCount; ++page) {
+
+ int x = m_leftGutter + pageWidth * page + leftMargin / 4;
+ int y = 20;
+ int w = pageWidth - leftMargin / 2;
+ int h = pageHeight;
+
+ QString str = QString("%1").arg(page + 1);
+
+ thumbPainter.drawRect(x / thumbScale, y / thumbScale,
+ w / thumbScale, h / thumbScale);
+
+ int tx = (x + w / 2) / thumbScale, ty = (y + h / 2) / thumbScale;
+ tx -= thumbPainter.fontMetrics().width(str) / 2;
+ thumbPainter.drawText(tx, ty, str);
+ }
+ }
+
+ thumbPainter.end();
+ if (m_pannerDialog)
+ m_pannerDialog->scrollbox()->setThumbnail(thumbnail);
+}
+
+void NotationView::refreshSegment(Segment *segment,
+ timeT startTime, timeT endTime)
+{
+ NOTATION_DEBUG << "*** " << endl;
+
+ if (m_inhibitRefresh)
+ return ;
+ NOTATION_DEBUG << "NotationView::refreshSegment(" << segment << "," << startTime << "," << endTime << ")" << endl;
+ Profiler foo("NotationView::refreshSegment");
+
+ emit usedSelection();
+
+ if (segment) {
+ LinedStaff *staff = getLinedStaff(*segment);
+ if (staff)
+ applyLayout(staff->getId(), startTime, endTime);
+ } else {
+ applyLayout( -1, startTime, endTime);
+ }
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+
+ Segment *ssegment = &m_staffs[i]->getSegment();
+ bool thisStaff = (ssegment == segment || segment == 0);
+ m_staffs[i]->markChanged(startTime, endTime, !thisStaff);
+ }
+
+ PixmapArrayGC::deleteAll();
+
+ statusBar()->changeItem(KTmpStatusMsg::getDefaultMsg(),
+ KTmpStatusMsg::getDefaultId());
+
+ Event::dumpStats(std::cerr);
+ if (m_deferredCursorMove == NoCursorMoveNeeded) {
+ slotSetInsertCursorPosition(getInsertionTime(), false, false);
+ } else {
+ doDeferredCursorMove();
+ }
+ slotSetPointerPosition(getDocument()->getComposition().getPosition(), false);
+
+ if (m_currentEventSelection &&
+ m_currentEventSelection->getSegmentEvents().size() == 0) {
+ delete m_currentEventSelection;
+ m_currentEventSelection = 0;
+ //!!!??? was that the right thing to do?
+ }
+
+ setMenuStates();
+ slotSetOperationNameAndStatus(i18n(" Ready."));
+ NOTATION_DEBUG << "*** " << endl;
+}
+
+void NotationView::setMenuStates()
+{
+ // 1. set selection-related states
+
+ // Clear states first, then enter only those ones that apply
+ // (so as to avoid ever clearing one after entering another, in
+ // case the two overlap at all)
+ stateChanged("have_selection", KXMLGUIClient::StateReverse);
+ stateChanged("have_notes_in_selection", KXMLGUIClient::StateReverse);
+ stateChanged("have_rests_in_selection", KXMLGUIClient::StateReverse);
+
+ if (m_currentEventSelection) {
+
+ NOTATION_DEBUG << "NotationView::setMenuStates: Have selection; it's " << m_currentEventSelection << " covering range from " << m_currentEventSelection->getStartTime() << " to " << m_currentEventSelection->getEndTime() << " (" << m_currentEventSelection->getSegmentEvents().size() << " events)" << endl;
+
+ stateChanged("have_selection", KXMLGUIClient::StateNoReverse);
+ if (m_currentEventSelection->contains
+ (Note::EventType)) {
+ stateChanged("have_notes_in_selection",
+ KXMLGUIClient::StateNoReverse);
+ }
+ if (m_currentEventSelection->contains
+ (Note::EventRestType)) {
+ stateChanged("have_rests_in_selection",
+ KXMLGUIClient::StateNoReverse);
+ }
+ }
+
+ // 2. set inserter-related states
+
+ // #1372863 -- RestInserter is a subclass of NoteInserter, so we
+ // need to test dynamic_cast<RestInserter *> before
+ // dynamic_cast<NoteInserter *> (which will succeed for both)
+
+ if (dynamic_cast<RestInserter *>(m_tool)) {
+ NOTATION_DEBUG << "Have rest inserter " << endl;
+ stateChanged("note_insert_tool_current", StateReverse);
+ stateChanged("rest_insert_tool_current", StateNoReverse);
+ } else if (dynamic_cast<NoteInserter *>(m_tool)) {
+ NOTATION_DEBUG << "Have note inserter " << endl;
+ stateChanged("note_insert_tool_current", StateNoReverse);
+ stateChanged("rest_insert_tool_current", StateReverse);
+ } else {
+ NOTATION_DEBUG << "Have neither inserter " << endl;
+ stateChanged("note_insert_tool_current", StateReverse);
+ stateChanged("rest_insert_tool_current", StateReverse);
+ }
+}
+
+#define UPDATE_PROGRESS(n) \
+ progressCount += (n); \
+ if (progressTotal > 0) { \
+ emit setProgress(progressCount * 100 / progressTotal); \
+ ProgressDialog::processEvents(); \
+ }
+
+void NotationView::readjustCanvasSize()
+{
+ Profiler profiler("NotationView::readjustCanvasSize");
+
+ double maxWidth = 0.0;
+ int maxHeight = 0;
+
+ slotSetOperationNameAndStatus(i18n("Sizing and allocating canvas..."));
+ ProgressDialog::processEvents();
+
+ int progressTotal = m_staffs.size() + 2;
+ int progressCount = 0;
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+
+ LinedStaff &staff = *m_staffs[i];
+
+ staff.sizeStaff(*m_hlayout);
+ UPDATE_PROGRESS(1);
+
+ if (staff.getTotalWidth() + staff.getX() > maxWidth) {
+ maxWidth = staff.getTotalWidth() + staff.getX() + 1;
+ }
+
+ if (staff.getTotalHeight() + staff.getY() > maxHeight) {
+ maxHeight = staff.getTotalHeight() + staff.getY() + 1;
+ }
+ }
+
+ int topMargin = 0, leftMargin = 0;
+ getPageMargins(leftMargin, topMargin);
+
+ int pageWidth = getPageWidth();
+ int pageHeight = getPageHeight();
+
+ NOTATION_DEBUG << "NotationView::readjustCanvasSize: maxHeight is "
+ << maxHeight << ", page height is " << pageHeight << endl
+ << " - maxWidth is " << maxWidth << ", page width is " << pageWidth << endl;
+
+
+ if (m_pageMode == LinedStaff::LinearMode) {
+ maxWidth = ((maxWidth / pageWidth) + 1) * pageWidth;
+ if (maxHeight < pageHeight)
+ maxHeight = pageHeight;
+ } else {
+ if (maxWidth < pageWidth)
+ maxWidth = pageWidth;
+ if (maxHeight < pageHeight + topMargin*2)
+ maxHeight = pageHeight + topMargin * 2;
+ }
+
+ // now get the EditView to do the biz
+ readjustViewSize(QSize(int(maxWidth), maxHeight), true);
+
+ UPDATE_PROGRESS(2);
+
+ if (m_pannerDialog) {
+
+ if (m_pageMode != LinedStaff::MultiPageMode) {
+ m_pannerDialog->hide();
+
+ } else {
+
+ m_pannerDialog->show();
+
+ m_pannerDialog->setPageSize
+ (QSize(canvas()->width(),
+ canvas()->height()));
+
+ m_pannerDialog->scrollbox()->setViewSize
+ (QSize(getCanvasView()->width(),
+ getCanvasView()->height()));
+ }
+ }
+
+ // Give a correct vertical alignment to track headers
+ if ((m_pageMode == LinedStaff::LinearMode) && m_showHeadersGroup) {
+ m_headersGroupView->setContentsPos(0, getCanvasView()->contentsY());
+ }
+}
+
+void NotationView::slotNoteAction()
+{
+ const QObject* sigSender = sender();
+
+ NoteActionDataMap::Iterator noteAct =
+ m_noteActionDataMap->find(sigSender->name());
+
+ if (noteAct != m_noteActionDataMap->end()) {
+ m_lastNoteAction = sigSender->name();
+ setCurrentSelectedNote(**noteAct);
+ setMenuStates();
+ } else {
+ std::cerr << "NotationView::slotNoteAction() : couldn't find NoteActionData named '"
+ << sigSender->name() << "'\n";
+ }
+}
+
+void NotationView::slotLastNoteAction()
+{
+ KAction *action = actionCollection()->action(m_lastNoteAction);
+ if (!action)
+ action = actionCollection()->action("crotchet");
+
+ if (action) {
+ action->activate();
+ } else {
+ std::cerr << "NotationView::slotNoteAction() : couldn't find action named '"
+ << m_lastNoteAction << "' or 'crotchet'\n";
+ }
+}
+
+void NotationView::slotAddMark()
+{
+ const QObject *s = sender();
+ if (!m_currentEventSelection)
+ return ;
+
+ MarkActionDataMap::Iterator i = m_markActionDataMap->find(s->name());
+
+ if (i != m_markActionDataMap->end()) {
+ addCommandToHistory(new AddMarkCommand
+ ((**i).mark, *m_currentEventSelection));
+ }
+}
+
+void NotationView::slotNoteChangeAction()
+{
+ const QObject* sigSender = sender();
+
+ NoteChangeActionDataMap::Iterator noteAct =
+ m_noteChangeActionDataMap->find(sigSender->name());
+
+ if (noteAct != m_noteChangeActionDataMap->end()) {
+ slotSetNoteDurations((**noteAct).noteType, (**noteAct).notationOnly);
+ } else {
+ std::cerr << "NotationView::slotNoteChangeAction() : couldn't find NoteChangeAction named '"
+ << sigSender->name() << "'\n";
+ }
+}
+
+void NotationView::initActionDataMaps()
+{
+ static bool called = false;
+ static int keys[] =
+ { Key_0, Key_3, Key_6, Key_8, Key_4, Key_2, Key_1, Key_5 };
+
+ if (called)
+ return ;
+ called = true;
+
+ m_noteActionDataMap = new NoteActionDataMap;
+
+ for (int rest = 0; rest < 2; ++rest) {
+ for (int dots = 0; dots < 2; ++dots) {
+ for (int type = Note::Longest; type >= Note::Shortest; --type) {
+ if (dots && (type == Note::Longest))
+ continue;
+
+ QString refName
+ (NotationStrings::getReferenceName(Note(type, dots), rest == 1));
+
+ QString shortName(refName);
+ shortName.replace(QRegExp("-"), "_");
+
+ QString titleName
+ (NotationStrings::getNoteName(Note(type, dots)));
+
+ titleName = titleName.left(1).upper() +
+ titleName.right(titleName.length() - 1);
+
+ if (rest) {
+ titleName.replace(QRegExp(i18n("note")), i18n("rest"));
+ }
+
+ int keycode = keys[type - Note::Shortest];
+ if (dots) // keycode += CTRL; -- used below for note change action
+ keycode = 0;
+ if (rest) // keycode += SHIFT; -- can't do shift+numbers
+ keycode = 0;
+
+ m_noteActionDataMap->insert
+ (shortName, new NoteActionData
+ (titleName, shortName, refName, keycode,
+ rest > 0, type, dots));
+ }
+ }
+ }
+
+ m_noteChangeActionDataMap = new NoteChangeActionDataMap;
+
+ for (int notationOnly = 0; notationOnly <= 1; ++notationOnly) {
+ for (int type = Note::Longest; type >= Note::Shortest; --type) {
+
+ QString refName
+ (NotationStrings::getReferenceName(Note(type, 0), false));
+
+ QString shortName(QString("change_%1%2")
+ .arg(notationOnly ? "notation_" : "").arg(refName));
+ shortName.replace(QRegExp("-"), "_");
+
+ QString titleName
+ (NotationStrings::getNoteName(Note(type, 0)));
+
+ titleName = titleName.left(1).upper() +
+ titleName.right(titleName.length() - 1);
+
+ int keycode = keys[type - Note::Shortest];
+ keycode += CTRL;
+ if (notationOnly)
+ keycode += ALT;
+
+ m_noteChangeActionDataMap->insert
+ (shortName, new NoteChangeActionData
+ (titleName, shortName, refName, keycode,
+ notationOnly ? true : false, type));
+ }
+ }
+
+ m_markActionDataMap = new MarkActionDataMap;
+
+ std::vector<Mark> marks = Marks::getStandardMarks();
+ for (unsigned int i = 0; i < marks.size(); ++i) {
+
+ Mark mark = marks[i];
+ QString markName(strtoqstr(mark));
+ QString actionName = QString("add_%1").arg(markName);
+
+ m_markActionDataMap->insert
+ (actionName, new MarkActionData
+ (AddMarkCommand::getGlobalName(mark),
+ actionName, 0, mark));
+ }
+
+}
+
+void NotationView::setupProgress(KProgress* bar)
+{
+ if (bar) {
+ NOTATION_DEBUG << "NotationView::setupProgress(bar)\n";
+
+ connect(m_hlayout, SIGNAL(setProgress(int)),
+ bar, SLOT(setValue(int)));
+
+ connect(m_hlayout, SIGNAL(incrementProgress(int)),
+ bar, SLOT(advance(int)));
+
+ connect(this, SIGNAL(setProgress(int)),
+ bar, SLOT(setValue(int)));
+
+ connect(this, SIGNAL(incrementProgress(int)),
+ bar, SLOT(advance(int)));
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ connect(m_staffs[i], SIGNAL(setProgress(int)),
+ bar, SLOT(setValue(int)));
+
+ connect(m_staffs[i], SIGNAL(incrementProgress(int)),
+ bar, SLOT(advance(int)));
+ }
+ }
+
+}
+
+void NotationView::setupProgress(ProgressDialog* dialog)
+{
+ NOTATION_DEBUG << "NotationView::setupProgress(dialog)" << endl;
+ disconnectProgress();
+
+ if (dialog) {
+ setupProgress(dialog->progressBar());
+
+ connect(dialog, SIGNAL(cancelClicked()),
+ m_hlayout, SLOT(slotCancel()));
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ connect(m_staffs[i], SIGNAL(setOperationName(QString)),
+ this, SLOT(slotSetOperationNameAndStatus(QString)));
+
+ connect(dialog, SIGNAL(cancelClicked()),
+ m_staffs[i], SLOT(slotCancel()));
+ }
+
+ connect(this, SIGNAL(setOperationName(QString)),
+ dialog, SLOT(slotSetOperationName(QString)));
+ m_progressDisplayer = PROGRESS_DIALOG;
+ }
+
+}
+
+void NotationView::slotSetOperationNameAndStatus(QString name)
+{
+ emit setOperationName(name);
+ statusBar()->changeItem(QString(" %1").arg(name),
+ KTmpStatusMsg::getDefaultId());
+}
+
+void NotationView::disconnectProgress()
+{
+ NOTATION_DEBUG << "NotationView::disconnectProgress()" << endl;
+
+ m_hlayout->disconnect();
+ disconnect(SIGNAL(setProgress(int)));
+ disconnect(SIGNAL(incrementProgress(int)));
+ disconnect(SIGNAL(setOperationName(QString)));
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ m_staffs[i]->disconnect();
+ }
+}
+
+void NotationView::setupDefaultProgress()
+{
+ if (m_progressDisplayer != PROGRESS_BAR) {
+ NOTATION_DEBUG << "NotationView::setupDefaultProgress()" << endl;
+ disconnectProgress();
+ setupProgress(m_progressBar);
+ m_progressDisplayer = PROGRESS_BAR;
+ }
+}
+
+void NotationView::updateViewCaption()
+{
+ if (m_segments.size() == 1) {
+
+ TrackId trackId = m_segments[0]->getTrack();
+ Track *track =
+ m_segments[0]->getComposition()->getTrackById(trackId);
+
+ int trackPosition = -1;
+ if (track)
+ trackPosition = track->getPosition();
+ // std::cout << std::endl << std::endl << std::endl << "DEBUG TITLE BAR: " << getDocument()->getTitle() << std::endl << std::endl << std::endl;
+ setCaption(i18n("%1 - Segment Track #%2 - Notation")
+ .arg(getDocument()->getTitle())
+ .arg(trackPosition + 1));
+
+ } else if (m_segments.size() == getDocument()->getComposition().getNbSegments()) {
+
+ setCaption(i18n("%1 - All Segments - Notation")
+ .arg(getDocument()->getTitle()));
+
+ } else {
+
+ setCaption(i18n("%1 - Segment - Notation", "%1 - %n Segments - Notation", m_segments.size())
+ .arg(getDocument()->getTitle()));
+
+ }
+}
+
+NotationView::NoteActionDataMap* NotationView::m_noteActionDataMap = 0;
+
+NotationView::NoteChangeActionDataMap* NotationView::m_noteChangeActionDataMap = 0;
+
+NotationView::MarkActionDataMap* NotationView::m_markActionDataMap = 0;
+
+
+/// SLOTS
+
+
+void
+NotationView::slotUpdateInsertModeStatus()
+{
+ QString tripletMessage = i18n("Triplet");
+ QString chordMessage = i18n("Chord");
+ QString graceMessage = i18n("Grace");
+ QString message;
+
+ if (isInTripletMode()) {
+ message = i18n("%1 %2").arg(message).arg(tripletMessage);
+ }
+
+ if (isInChordMode()) {
+ message = i18n("%1 %2").arg(message).arg(chordMessage);
+ }
+
+ if (isInGraceMode()) {
+ message = i18n("%1 %2").arg(message).arg(graceMessage);
+ }
+
+ m_insertModeLabel->setText(message);
+}
+
+void
+NotationView::slotUpdateAnnotationsStatus()
+{
+ if (!areAnnotationsVisible()) {
+ for (int i = 0; i < getStaffCount(); ++i) {
+ Segment &s = getStaff(i)->getSegment();
+ for (Segment::iterator j = s.begin(); j != s.end(); ++j) {
+ if ((*j)->isa(Text::EventType) &&
+ ((*j)->get<String>(Text::TextTypePropertyName)
+ == Text::Annotation)) {
+ m_annotationsLabel->setText(i18n("Hidden annotations"));
+ return ;
+ }
+ }
+ }
+ }
+ m_annotationsLabel->setText("");
+ getToggleAction("show_annotations")->setChecked(areAnnotationsVisible());
+}
+
+void
+NotationView::slotUpdateLilyPondDirectivesStatus()
+{
+ if (!areLilyPondDirectivesVisible()) {
+ for (int i = 0; i < getStaffCount(); ++i) {
+ Segment &s = getStaff(i)->getSegment();
+ for (Segment::iterator j = s.begin(); j != s.end(); ++j) {
+ if ((*j)->isa(Text::EventType) &&
+ ((*j)->get
+ <String>
+ (Text::TextTypePropertyName)
+ == Text::LilyPondDirective)) {
+ m_lilyPondDirectivesLabel->setText(i18n("Hidden LilyPond directives"));
+ return ;
+ }
+ }
+ }
+ }
+ m_lilyPondDirectivesLabel->setText("");
+ getToggleAction("show_lilypond_directives")->setChecked(areLilyPondDirectivesVisible());
+}
+
+void
+NotationView::slotChangeSpacingFromStringValue(const QString& spacingT)
+{
+ // spacingT has a '%' at the end
+ //
+ int spacing = spacingT.left(spacingT.length() - 1).toInt();
+ slotChangeSpacing(spacing);
+}
+
+void
+NotationView::slotChangeSpacingFromAction()
+{
+ const QObject *s = sender();
+ QString name = s->name();
+
+ if (name.left(8) == "spacing_") {
+ int spacing = name.right(name.length() - 8).toInt();
+
+ if (spacing > 0)
+ slotChangeSpacing(spacing);
+
+ } else {
+ KMessageBox::sorry
+ (this, i18n("Unknown spacing action %1").arg(name));
+ }
+}
+
+void
+NotationView::slotChangeSpacing(int spacing)
+{
+ if (m_hlayout->getSpacing() == spacing)
+ return ;
+
+ m_hlayout->setSpacing(spacing);
+
+ // m_spacingSlider->setSize(spacing);
+
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action(QString("spacing_%1").arg(spacing)));
+ if (action)
+ action->setChecked(true);
+ else {
+ std::cerr
+ << "WARNING: Expected action \"spacing_" << spacing
+ << "\" to be a KToggleAction, but it isn't (or doesn't exist)"
+ << std::endl;
+ }
+
+ positionStaffs();
+ applyLayout();
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ m_staffs[i]->markChanged();
+ }
+
+ positionPages();
+ updateControlRulers(true);
+ updateView();
+}
+
+void
+NotationView::slotChangeProportionFromIndex(int n)
+{
+ std::vector<int> proportions = m_hlayout->getAvailableProportions();
+ if (n >= (int)proportions.size())
+ n = proportions.size() - 1;
+ slotChangeProportion(proportions[n]);
+}
+
+void
+NotationView::slotChangeProportionFromAction()
+{
+ const QObject *s = sender();
+ QString name = s->name();
+
+ if (name.left(11) == "proportion_") {
+ int proportion = name.right(name.length() - 11).toInt();
+ slotChangeProportion(proportion);
+
+ } else {
+ KMessageBox::sorry
+ (this, i18n("Unknown proportion action %1").arg(name));
+ }
+}
+
+void
+NotationView::slotChangeProportion(int proportion)
+{
+ if (m_hlayout->getProportion() == proportion)
+ return ;
+
+ m_hlayout->setProportion(proportion);
+
+ // m_proportionSlider->setSize(proportion);
+
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action(QString("proportion_%1").arg(proportion)));
+ if (action)
+ action->setChecked(true);
+ else {
+ std::cerr
+ << "WARNING: Expected action \"proportion_" << proportion
+ << "\" to be a KToggleAction, but it isn't (or doesn't exist)"
+ << std::endl;
+ }
+
+ positionStaffs();
+ applyLayout();
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ m_staffs[i]->markChanged();
+ }
+
+ positionPages();
+ updateControlRulers(true);
+ updateView();
+}
+
+void
+NotationView::slotChangeFontFromAction()
+{
+ const QObject *s = sender();
+ QString name = s->name();
+ if (name.left(10) == "note_font_") {
+ name = name.right(name.length() - 10);
+ slotChangeFont(name);
+ } else {
+ KMessageBox::sorry
+ (this, i18n("Unknown font action %1").arg(name));
+ }
+}
+
+void
+NotationView::slotChangeFontSizeFromAction()
+{
+ const QObject *s = sender();
+ QString name = s->name();
+
+ if (name.left(15) == "note_font_size_") {
+ name = name.right(name.length() - 15);
+ bool ok = false;
+ int size = name.toInt(&ok);
+ if (ok)
+ slotChangeFont(m_fontName, size);
+ else {
+ KMessageBox::sorry
+ (this, i18n("Unknown font size %1").arg(name));
+ }
+ } else {
+ KMessageBox::sorry
+ (this, i18n("Unknown font size action %1").arg(name));
+ }
+}
+
+void
+NotationView::slotChangeFont(const QString &newName)
+{
+ NOTATION_DEBUG << "changeFont: " << newName << endl;
+ slotChangeFont(std::string(newName.utf8()));
+}
+
+void
+NotationView::slotChangeFont(std::string newName)
+{
+ int newSize = m_fontSize;
+
+ if (!NoteFontFactory::isAvailableInSize(newName, newSize)) {
+
+ int defaultSize = NoteFontFactory::getDefaultSize(newName);
+ newSize = m_config->readUnsignedNumEntry
+ ((getStaffCount() > 1 ?
+ "multistaffnotesize" : "singlestaffnotesize"), defaultSize);
+
+ if (!NoteFontFactory::isAvailableInSize(newName, newSize)) {
+ newSize = defaultSize;
+ }
+ }
+
+ slotChangeFont(newName, newSize);
+}
+
+void
+NotationView::slotChangeFontSize(int newSize)
+{
+ slotChangeFont(m_fontName, newSize);
+}
+
+void
+NotationView::slotChangeFontSizeFromStringValue(const QString& sizeT)
+{
+ int size = sizeT.toInt();
+ slotChangeFont(m_fontName, size);
+}
+
+void
+NotationView::slotZoomIn()
+{
+ std::vector<int> sizes = NoteFontFactory::getScreenSizes(m_fontName);
+ for (int i = 0; i + 1 < sizes.size(); ++i) {
+ if (sizes[i] == m_fontSize) {
+ slotChangeFontSize(sizes[i + 1]);
+ return ;
+ }
+ }
+}
+
+void
+NotationView::slotZoomOut()
+{
+ std::vector<int> sizes = NoteFontFactory::getScreenSizes(m_fontName);
+ for (int i = 1; i < sizes.size(); ++i) {
+ if (sizes[i] == m_fontSize) {
+ slotChangeFontSize(sizes[i - 1]);
+ return ;
+ }
+ }
+}
+
+void
+NotationView::slotChangeFont(std::string newName, int newSize)
+{
+ if (newName == m_fontName && newSize == m_fontSize)
+ return ;
+
+ NotePixmapFactory* npf = 0;
+
+ try {
+ npf = new NotePixmapFactory(newName, newSize);
+ } catch (...) {
+ return ;
+ }
+
+ bool changedFont = (newName != m_fontName || newSize != m_fontSize);
+
+ std::string oldName = m_fontName;
+ m_fontName = newName;
+ m_fontSize = newSize;
+ setNotePixmapFactory(npf);
+
+ // update the various GUI elements
+
+ std::set<std::string> fs(NoteFontFactory::getFontNames());
+ std::vector<std::string> f(fs.begin(), fs.end());
+ std::sort(f.begin(), f.end());
+
+ for (unsigned int i = 0; i < f.size(); ++i) {
+ bool thisOne = (f[i] == m_fontName);
+ if (thisOne)
+ m_fontCombo->setCurrentItem(i);
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("note_font_" + strtoqstr(f[i])));
+ NOTATION_DEBUG << "inspecting " << f[i] << (action ? ", have action" : ", no action") << endl;
+ if (action)
+ action->setChecked(thisOne);
+ else {
+ std::cerr
+ << "WARNING: Expected action \"note_font_" << f[i]
+ << "\" to be a KToggleAction, but it isn't (or doesn't exist)"
+ << std::endl;
+ }
+ }
+
+ NOTATION_DEBUG << "about to reinitialise sizes" << endl;
+
+ std::vector<int> sizes = NoteFontFactory::getScreenSizes(m_fontName);
+ m_fontSizeCombo->clear();
+ QString value;
+ for (std::vector<int>::iterator i = sizes.begin(); i != sizes.end(); ++i) {
+ value.setNum(*i);
+ m_fontSizeCombo->insertItem(value);
+ }
+ value.setNum(m_fontSize);
+ m_fontSizeCombo->setCurrentText(value);
+
+ setupFontSizeMenu(oldName);
+
+ if (!changedFont)
+ return ; // might have been called to initialise menus etc
+
+ NOTATION_DEBUG << "about to change font" << endl;
+
+ if (m_pageMode == LinedStaff::MultiPageMode) {
+
+ int pageWidth = getPageWidth();
+ int topMargin = 0, leftMargin = 0;
+ getPageMargins(leftMargin, topMargin);
+
+ m_hlayout->setPageWidth(pageWidth - leftMargin * 2);
+ }
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ m_staffs[i]->changeFont(m_fontName, m_fontSize);
+ }
+
+ NOTATION_DEBUG << "about to position staffs" << endl;
+
+ positionStaffs();
+
+ bool layoutApplied = applyLayout();
+ if (!layoutApplied)
+ KMessageBox::sorry(0, "Couldn't apply layout");
+ else {
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ m_staffs[i]->markChanged();
+ }
+ }
+
+ positionPages();
+ updateControlRulers(true);
+ updateView();
+}
+
+void
+NotationView::slotFilePrint()
+{
+ KTmpStatusMsg msg(i18n("Printing..."), this);
+
+ SetWaitCursor waitCursor;
+ NotationView printingView(getDocument(), m_segments,
+ (QWidget *)parent(), this);
+
+ if (!printingView.isOK()) {
+ NOTATION_DEBUG << "Print : operation cancelled\n";
+ return ;
+ }
+
+ printingView.print();
+}
+
+void
+NotationView::slotFilePrintPreview()
+{
+ KTmpStatusMsg msg(i18n("Previewing..."), this);
+
+ SetWaitCursor waitCursor;
+ NotationView printingView(getDocument(), m_segments,
+ (QWidget *)parent(), this);
+
+ if (!printingView.isOK()) {
+ NOTATION_DEBUG << "Print preview : operation cancelled\n";
+ return ;
+ }
+
+ printingView.print(true);
+}
+
+std::map<KProcess *, KTempFile *> NotationView::m_lilyTempFileMap;
+
+void NotationView::slotPrintLilyPond()
+{
+ KTmpStatusMsg msg(i18n("Printing LilyPond file..."), this);
+ KTempFile *file = new KTempFile(QString::null, ".ly");
+ file->setAutoDelete(true);
+ if (!file->name()) {
+ // CurrentProgressDialog::freeze();
+ KMessageBox::sorry(this, i18n("Failed to open a temporary file for LilyPond export."));
+ delete file;
+ }
+ if (!exportLilyPondFile(file->name(), true)) {
+ return ;
+ }
+ KProcess *proc = new KProcess;
+ *proc << "rosegarden-lilypondview";
+ *proc << "--graphical";
+ *proc << "--print";
+ *proc << file->name();
+ connect(proc, SIGNAL(processExited(KProcess *)),
+ this, SLOT(slotLilyPondViewProcessExited(KProcess *)));
+ m_lilyTempFileMap[proc] = file;
+ proc->start(KProcess::NotifyOnExit);
+}
+
+void NotationView::slotPreviewLilyPond()
+{
+ KTmpStatusMsg msg(i18n("Previewing LilyPond file..."), this);
+ KTempFile *file = new KTempFile(QString::null, ".ly");
+ file->setAutoDelete(true);
+ if (!file->name()) {
+ // CurrentProgressDialog::freeze();
+ KMessageBox::sorry(this, i18n("Failed to open a temporary file for LilyPond export."));
+ delete file;
+ }
+ if (!exportLilyPondFile(file->name(), true)) {
+ return ;
+ }
+ KProcess *proc = new KProcess;
+ *proc << "rosegarden-lilypondview";
+ *proc << "--graphical";
+ *proc << "--pdf";
+ *proc << file->name();
+ connect(proc, SIGNAL(processExited(KProcess *)),
+ this, SLOT(slotLilyPondViewProcessExited(KProcess *)));
+ m_lilyTempFileMap[proc] = file;
+ proc->start(KProcess::NotifyOnExit);
+}
+
+void NotationView::slotLilyPondViewProcessExited(KProcess *p)
+{
+ delete m_lilyTempFileMap[p];
+ m_lilyTempFileMap.erase(p);
+ delete p;
+}
+
+bool NotationView::exportLilyPondFile(QString file, bool forPreview)
+{
+ QString caption = "", heading = "";
+ if (forPreview) {
+ caption = i18n("LilyPond Preview Options");
+ heading = i18n("LilyPond preview options");
+ }
+
+ LilyPondOptionsDialog dialog(this, m_doc, caption, heading);
+ if (dialog.exec() != QDialog::Accepted) {
+ return false;
+ }
+
+ ProgressDialog progressDlg(i18n("Exporting LilyPond file..."),
+ 100,
+ this);
+
+ LilyPondExporter e(this, m_doc, std::string(QFile::encodeName(file)));
+
+ connect(&e, SIGNAL(setProgress(int)),
+ progressDlg.progressBar(), SLOT(setValue(int)));
+
+ connect(&e, SIGNAL(incrementProgress(int)),
+ progressDlg.progressBar(), SLOT(advance(int)));
+
+ if (!e.write()) {
+ // CurrentProgressDialog::freeze();
+ KMessageBox::sorry(this, i18n("Export failed. The file could not be opened for writing."));
+ return false;
+ }
+
+ return true;
+}
+
+void NotationView::slotEditCut()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Cutting selection to clipboard..."), this);
+
+ addCommandToHistory(new CutCommand(*m_currentEventSelection,
+ getDocument()->getClipboard()));
+}
+
+void NotationView::slotEditDelete()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Deleting selection..."), this);
+
+ addCommandToHistory(new EraseCommand(*m_currentEventSelection));
+}
+
+void NotationView::slotEditCopy()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Copying selection to clipboard..."), this);
+
+ addCommandToHistory(new CopyCommand(*m_currentEventSelection,
+ getDocument()->getClipboard()));
+}
+
+void NotationView::slotEditCutAndClose()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Cutting selection to clipboard..."), this);
+
+ addCommandToHistory(new CutAndCloseCommand(*m_currentEventSelection,
+ getDocument()->getClipboard()));
+}
+
+static const QString RESTRICTED_PASTE_FAILED_DESCRIPTION = i18n(
+ "The Restricted paste type requires enough empty " \
+ "space (containing only rests) at the paste position " \
+ "to hold all of the events to be pasted.\n" \
+ "Not enough space was found.\n" \
+ "If you want to paste anyway, consider using one of " \
+ "the other paste types from the \"Paste...\" option " \
+ "on the Edit menu. You can also change the default " \
+ "paste type to something other than Restricted if " \
+ "you wish."
+ );
+
+void NotationView::slotEditPaste()
+{
+ Clipboard * clipboard = getDocument()->getClipboard();
+
+ if (clipboard->isEmpty()) {
+ slotStatusHelpMsg(i18n("Clipboard is empty"));
+ return ;
+ }
+ if (!clipboard->isSingleSegment()) {
+ slotStatusHelpMsg(i18n("Can't paste multiple Segments into one"));
+ return ;
+ }
+
+ slotStatusHelpMsg(i18n("Inserting clipboard contents..."));
+
+ LinedStaff *staff = getCurrentLinedStaff();
+ Segment &segment = staff->getSegment();
+
+ // Paste at cursor position
+ //
+ timeT insertionTime = getInsertionTime();
+ timeT endTime = insertionTime +
+ (clipboard->getSingleSegment()->getEndTime() -
+ clipboard->getSingleSegment()->getStartTime());
+
+ KConfig *config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+ PasteEventsCommand::PasteType defaultType = (PasteEventsCommand::PasteType)
+ config->readUnsignedNumEntry("pastetype",
+ PasteEventsCommand::Restricted);
+
+ PasteEventsCommand *command = new PasteEventsCommand
+ (segment, clipboard, insertionTime, defaultType);
+
+ if (!command->isPossible()) {
+ KMessageBox::detailedError
+ (this,
+ i18n("Couldn't paste at this point."), RESTRICTED_PASTE_FAILED_DESCRIPTION);
+ } else {
+ addCommandToHistory(command);
+ setCurrentSelection(new EventSelection(command->getPastedEvents()));
+ slotSetInsertCursorPosition(endTime, true, false);
+ }
+}
+
+void NotationView::slotEditGeneralPaste()
+{
+ Clipboard *clipboard = getDocument()->getClipboard();
+
+ if (clipboard->isEmpty()) {
+ slotStatusHelpMsg(i18n("Clipboard is empty"));
+ return ;
+ }
+
+ slotStatusHelpMsg(i18n("Inserting clipboard contents..."));
+
+ LinedStaff *staff = getCurrentLinedStaff();
+ Segment &segment = staff->getSegment();
+
+ KConfig *config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+ PasteEventsCommand::PasteType defaultType = (PasteEventsCommand::PasteType)
+ config->readUnsignedNumEntry("pastetype",
+ PasteEventsCommand::Restricted);
+
+ PasteNotationDialog dialog(this, defaultType);
+
+ if (dialog.exec() == QDialog::Accepted) {
+
+ PasteEventsCommand::PasteType type = dialog.getPasteType();
+ if (dialog.setAsDefault()) {
+ config->setGroup(NotationViewConfigGroup);
+ config->writeEntry("pastetype", type);
+ }
+
+ timeT insertionTime = getInsertionTime();
+ timeT endTime = insertionTime +
+ (clipboard->getSingleSegment()->getEndTime() -
+ clipboard->getSingleSegment()->getStartTime());
+
+ PasteEventsCommand *command = new PasteEventsCommand
+ (segment, clipboard, insertionTime, type);
+
+ if (!command->isPossible()) {
+ KMessageBox::detailedError
+ (this,
+ i18n("Couldn't paste at this point."),
+ i18n(RESTRICTED_PASTE_FAILED_DESCRIPTION));
+ } else {
+ addCommandToHistory(command);
+ setCurrentSelection(new EventSelection
+ (segment, insertionTime, endTime));
+ slotSetInsertCursorPosition(endTime, true, false);
+ }
+ }
+}
+
+void
+NotationView::slotMoveEventsUpStaff()
+{
+ LinedStaff *targetStaff = getStaffAbove();
+ if (!targetStaff) return;
+ if (!m_currentEventSelection) return;
+ Segment &targetSegment = targetStaff->getSegment();
+
+ KMacroCommand *command = new KMacroCommand(i18n("Move Events to Staff Above"));
+
+ timeT insertionTime = m_currentEventSelection->getStartTime();
+
+ Clipboard *c = new Clipboard;
+ CopyCommand *cc = new CopyCommand(*m_currentEventSelection, c);
+ cc->execute();
+
+ command->addCommand(new EraseCommand(*m_currentEventSelection));;
+
+ command->addCommand(new PasteEventsCommand
+ (targetSegment, c,
+ insertionTime,
+ PasteEventsCommand::NoteOverlay));
+
+ addCommandToHistory(command);
+
+ delete c;
+}
+
+void
+NotationView::slotMoveEventsDownStaff()
+{
+ LinedStaff *targetStaff = getStaffBelow();
+ if (!targetStaff) return;
+ if (!m_currentEventSelection) return;
+ Segment &targetSegment = targetStaff->getSegment();
+
+ KMacroCommand *command = new KMacroCommand(i18n("Move Events to Staff Below"));
+
+ timeT insertionTime = m_currentEventSelection->getStartTime();
+
+ Clipboard *c = new Clipboard;
+ CopyCommand *cc = new CopyCommand(*m_currentEventSelection, c);
+ cc->execute();
+
+ command->addCommand(new EraseCommand(*m_currentEventSelection));;
+
+ command->addCommand(new PasteEventsCommand
+ (targetSegment, c,
+ insertionTime,
+ PasteEventsCommand::NoteOverlay));
+
+ addCommandToHistory(command);
+
+ delete c;
+}
+
+void NotationView::slotPreviewSelection()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ getDocument()->slotSetLoop(m_currentEventSelection->getStartTime(),
+ m_currentEventSelection->getEndTime());
+}
+
+void NotationView::slotClearLoop()
+{
+ getDocument()->slotSetLoop(0, 0);
+}
+
+void NotationView::slotClearSelection()
+{
+ // Actually we don't clear the selection immediately: if we're
+ // using some tool other than the select tool, then the first
+ // press switches us back to the select tool.
+
+ NotationSelector *selector = dynamic_cast<NotationSelector *>(m_tool);
+
+ if (!selector) {
+ slotSelectSelected();
+ } else {
+ setCurrentSelection(0);
+ }
+}
+
+void NotationView::slotEditSelectFromStart()
+{
+ timeT t = getInsertionTime();
+ Segment &segment = m_staffs[m_currentStaff]->getSegment();
+ setCurrentSelection(new EventSelection(segment,
+ segment.getStartTime(),
+ t));
+}
+
+void NotationView::slotEditSelectToEnd()
+{
+ timeT t = getInsertionTime();
+ Segment &segment = m_staffs[m_currentStaff]->getSegment();
+ setCurrentSelection(new EventSelection(segment,
+ t,
+ segment.getEndMarkerTime()));
+}
+
+void NotationView::slotEditSelectWholeStaff()
+{
+ Segment &segment = m_staffs[m_currentStaff]->getSegment();
+ setCurrentSelection(new EventSelection(segment,
+ segment.getStartTime(),
+ segment.getEndMarkerTime()));
+}
+
+void NotationView::slotFilterSelection()
+{
+ NOTATION_DEBUG << "NotationView::slotFilterSelection" << endl;
+
+ Segment *segment = getCurrentSegment();
+ EventSelection *existingSelection = m_currentEventSelection;
+ if (!segment || !existingSelection)
+ return ;
+
+ EventFilterDialog dialog(this);
+ if (dialog.exec() == QDialog::Accepted) {
+ NOTATION_DEBUG << "slotFilterSelection- accepted" << endl;
+
+ bool haveEvent = false;
+
+ EventSelection *newSelection = new EventSelection(*segment);
+ EventSelection::eventcontainer &ec =
+ existingSelection->getSegmentEvents();
+ for (EventSelection::eventcontainer::iterator i =
+ ec.begin(); i != ec.end(); ++i) {
+ if (dialog.keepEvent(*i)) {
+ haveEvent = true;
+ newSelection->addEvent(*i);
+ }
+ }
+
+ if (haveEvent)
+ setCurrentSelection(newSelection);
+ else
+ setCurrentSelection(0);
+ }
+}
+
+void NotationView::slotFinePositionLeft()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Pushing selection left..."), this);
+
+ // half a note body width
+ addCommandToHistory(new IncrementDisplacementsCommand
+ (*m_currentEventSelection, -500, 0));
+}
+
+void NotationView::slotFinePositionRight()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Pushing selection right..."), this);
+
+ // half a note body width
+ addCommandToHistory(new IncrementDisplacementsCommand
+ (*m_currentEventSelection, 500, 0));
+}
+
+void NotationView::slotFinePositionUp()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Pushing selection up..."), this);
+
+ // half line height
+ addCommandToHistory(new IncrementDisplacementsCommand
+ (*m_currentEventSelection, 0, -500));
+}
+
+void NotationView::slotFinePositionDown()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Pushing selection down..."), this);
+
+ // half line height
+ addCommandToHistory(new IncrementDisplacementsCommand
+ (*m_currentEventSelection, 0, 500));
+}
+
+void NotationView::slotFinePositionRestore()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Restoring computed positions..."), this);
+
+ addCommandToHistory(new ResetDisplacementsCommand(*m_currentEventSelection));
+}
+
+void NotationView::slotMakeVisible()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Making visible..."), this);
+
+ addCommandToHistory(new SetVisibilityCommand(*m_currentEventSelection, true));
+}
+
+void NotationView::slotMakeInvisible()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Making invisible..."), this);
+
+ addCommandToHistory(new SetVisibilityCommand(*m_currentEventSelection, false));
+}
+
+void NotationView::slotToggleToolsToolBar()
+{
+ toggleNamedToolBar("Tools Toolbar");
+}
+
+void NotationView::slotToggleNotesToolBar()
+{
+ toggleNamedToolBar("Notes Toolbar");
+}
+
+void NotationView::slotToggleRestsToolBar()
+{
+ toggleNamedToolBar("Rests Toolbar");
+}
+
+void NotationView::slotToggleAccidentalsToolBar()
+{
+ toggleNamedToolBar("Accidentals Toolbar");
+}
+
+void NotationView::slotToggleClefsToolBar()
+{
+ toggleNamedToolBar("Clefs Toolbar");
+}
+
+void NotationView::slotToggleMetaToolBar()
+{
+ toggleNamedToolBar("Meta Toolbar");
+}
+
+void NotationView::slotToggleMarksToolBar()
+{
+ toggleNamedToolBar("Marks Toolbar");
+}
+
+void NotationView::slotToggleGroupToolBar()
+{
+ toggleNamedToolBar("Group Toolbar");
+}
+
+void NotationView::slotToggleLayoutToolBar()
+{
+ toggleNamedToolBar("Layout Toolbar");
+}
+
+void NotationView::slotToggleTransportToolBar()
+{
+ toggleNamedToolBar("Transport Toolbar");
+}
+
+void NotationView::toggleNamedToolBar(const QString& toolBarName, bool* force)
+{
+ KToolBar *namedToolBar = toolBar(toolBarName);
+
+ if (!namedToolBar) {
+ NOTATION_DEBUG << "NotationView::toggleNamedToolBar() : toolBar "
+ << toolBarName << " not found" << endl;
+ return ;
+ }
+
+ if (!force) {
+
+ if (namedToolBar->isVisible())
+ namedToolBar->hide();
+ else
+ namedToolBar->show();
+ } else {
+
+ if (*force)
+ namedToolBar->show();
+ else
+ namedToolBar->hide();
+ }
+
+ setSettingsDirty();
+
+}
+
+void NotationView::slotGroupBeam()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Beaming group..."), this);
+
+ addCommandToHistory(new BeamCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotGroupAutoBeam()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Auto-beaming selection..."), this);
+
+ addCommandToHistory(new AutoBeamCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotGroupBreak()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Breaking groups..."), this);
+
+ addCommandToHistory(new BreakCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotGroupSimpleTuplet()
+{
+ slotGroupTuplet(true);
+}
+
+void NotationView::slotGroupGeneralTuplet()
+{
+ slotGroupTuplet(false);
+}
+
+void NotationView::slotGroupTuplet(bool simple)
+{
+ timeT t = 0;
+ timeT unit = 0;
+ int tupled = 2;
+ int untupled = 3;
+ Segment *segment = 0;
+ bool hasTimingAlready = false;
+
+ if (m_currentEventSelection) {
+
+ t = m_currentEventSelection->getStartTime();
+
+ timeT duration = m_currentEventSelection->getTotalDuration();
+ Note::Type unitType =
+ Note::getNearestNote(duration / 3, 0).getNoteType();
+ unit = Note(unitType).getDuration();
+
+ if (!simple) {
+ TupletDialog dialog(this, unitType, duration);
+ if (dialog.exec() != QDialog::Accepted)
+ return ;
+ unit = Note(dialog.getUnitType()).getDuration();
+ tupled = dialog.getTupledCount();
+ untupled = dialog.getUntupledCount();
+ hasTimingAlready = dialog.hasTimingAlready();
+ }
+
+ segment = &m_currentEventSelection->getSegment();
+
+ } else {
+
+ t = getInsertionTime();
+
+ NoteInserter *currentInserter = dynamic_cast<NoteInserter *>
+ (m_toolBox->getTool(NoteInserter::ToolName));
+
+ Note::Type unitType;
+
+ if (currentInserter) {
+ unitType = currentInserter->getCurrentNote().getNoteType();
+ } else {
+ unitType = Note::Quaver;
+ }
+
+ unit = Note(unitType).getDuration();
+
+ if (!simple) {
+ TupletDialog dialog(this, unitType);
+ if (dialog.exec() != QDialog::Accepted)
+ return ;
+ unit = Note(dialog.getUnitType()).getDuration();
+ tupled = dialog.getTupledCount();
+ untupled = dialog.getUntupledCount();
+ hasTimingAlready = dialog.hasTimingAlready();
+ }
+
+ segment = &m_staffs[m_currentStaff]->getSegment();
+ }
+
+ addCommandToHistory(new TupletCommand
+ (*segment, t, unit, untupled, tupled, hasTimingAlready));
+
+ if (!hasTimingAlready) {
+ slotSetInsertCursorPosition(t + (unit * tupled), true, false);
+ }
+}
+
+void NotationView::slotGroupUnTuplet()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Untupleting..."), this);
+
+ addCommandToHistory(new UnTupletCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotGroupSlur()
+{
+ KTmpStatusMsg msg(i18n("Adding slur..."), this);
+ slotAddIndication(Indication::Slur, i18n("slur"));
+}
+
+void NotationView::slotGroupPhrasingSlur()
+{
+ KTmpStatusMsg msg(i18n("Adding phrasing slur..."), this);
+ slotAddIndication(Indication::PhrasingSlur, i18n("phrasing slur"));
+}
+
+void NotationView::slotGroupGlissando()
+{
+ KTmpStatusMsg msg(i18n("Adding glissando..."), this);
+ slotAddIndication(Indication::Glissando, i18n("glissando"));
+}
+
+void NotationView::slotGroupCrescendo()
+{
+ KTmpStatusMsg msg(i18n("Adding crescendo..."), this);
+ slotAddIndication(Indication::Crescendo, i18n("dynamic"));
+}
+
+void NotationView::slotGroupDecrescendo()
+{
+ KTmpStatusMsg msg(i18n("Adding decrescendo..."), this);
+ slotAddIndication(Indication::Decrescendo, i18n("dynamic"));
+}
+
+void NotationView::slotGroupOctave2Up()
+{
+ KTmpStatusMsg msg(i18n("Adding octave..."), this);
+ slotAddIndication(Indication::QuindicesimaUp, i18n("ottava"));
+}
+
+void NotationView::slotGroupOctaveUp()
+{
+ KTmpStatusMsg msg(i18n("Adding octave..."), this);
+ slotAddIndication(Indication::OttavaUp, i18n("ottava"));
+}
+
+void NotationView::slotGroupOctaveDown()
+{
+ KTmpStatusMsg msg(i18n("Adding octave..."), this);
+ slotAddIndication(Indication::OttavaDown, i18n("ottava"));
+}
+
+void NotationView::slotGroupOctave2Down()
+{
+ KTmpStatusMsg msg(i18n("Adding octave..."), this);
+ slotAddIndication(Indication::QuindicesimaDown, i18n("ottava"));
+}
+
+void NotationView::slotAddIndication(std::string type, QString desc)
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ AddIndicationCommand *command =
+ new AddIndicationCommand(type, *m_currentEventSelection);
+
+ if (command->canExecute()) {
+ addCommandToHistory(command);
+ setSingleSelectedEvent(m_currentEventSelection->getSegment(),
+ command->getLastInsertedEvent());
+ } else {
+ KMessageBox::sorry(this, i18n("Can't add overlapping %1 indications").arg(desc)); // TODO PLURAL - how many 'indications' ?
+ delete command;
+ }
+}
+
+void NotationView::slotGroupMakeChord()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Making chord..."), this);
+
+ MakeChordCommand *command =
+ new MakeChordCommand(*m_currentEventSelection);
+
+ addCommandToHistory(command);
+}
+
+void NotationView::slotTransformsNormalizeRests()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Normalizing rests..."), this);
+
+ addCommandToHistory(new NormalizeRestsCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotTransformsCollapseRests()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Collapsing rests..."), this);
+
+ addCommandToHistory(new CollapseRestsCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotTransformsCollapseNotes()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Collapsing notes..."), this);
+
+ addCommandToHistory(new CollapseNotesCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotTransformsTieNotes()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Tying notes..."), this);
+
+ addCommandToHistory(new TieNotesCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotTransformsUntieNotes()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Untying notes..."), this);
+
+ addCommandToHistory(new UntieNotesCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotTransformsMakeNotesViable()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Making notes viable..."), this);
+
+ addCommandToHistory(new MakeNotesViableCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotTransformsDeCounterpoint()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Removing counterpoint..."), this);
+
+ addCommandToHistory(new DeCounterpointCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotTransformsStemsUp()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Pointing stems up..."), this);
+
+ addCommandToHistory(new ChangeStemsCommand
+ (true, *m_currentEventSelection));
+}
+
+void NotationView::slotTransformsStemsDown()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Pointing stems down..."), this);
+
+ addCommandToHistory(new ChangeStemsCommand
+ (false, *m_currentEventSelection));
+
+}
+
+void NotationView::slotTransformsRestoreStems()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Restoring computed stem directions..."), this);
+
+ addCommandToHistory(new RestoreStemsCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotTransformsSlursAbove()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Positioning slurs..."), this);
+
+ addCommandToHistory(new ChangeSlurPositionCommand
+ (true, *m_currentEventSelection));
+}
+
+void NotationView::slotTransformsSlursBelow()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Positioning slurs..."), this);
+
+ addCommandToHistory(new ChangeSlurPositionCommand
+ (false, *m_currentEventSelection));
+
+}
+
+void NotationView::slotTransformsRestoreSlurs()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Restoring slur positions..."), this);
+
+ addCommandToHistory(new RestoreSlursCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotTransformsTiesAbove()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Positioning ties..."), this);
+
+ addCommandToHistory(new ChangeTiePositionCommand
+ (true, *m_currentEventSelection));
+}
+
+void NotationView::slotTransformsTiesBelow()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Positioning ties..."), this);
+
+ addCommandToHistory(new ChangeTiePositionCommand
+ (false, *m_currentEventSelection));
+
+}
+
+void NotationView::slotTransformsRestoreTies()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Restoring tie positions..."), this);
+
+ addCommandToHistory(new RestoreTiesCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotTransformsFixQuantization()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Fixing notation quantization..."), this);
+
+ addCommandToHistory(new FixNotationQuantizeCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotTransformsRemoveQuantization()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Removing notation quantization..."), this);
+
+ addCommandToHistory(new RemoveNotationQuantizeCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotSetStyleFromAction()
+{
+ const QObject *s = sender();
+ QString name = s->name();
+
+ if (!m_currentEventSelection)
+ return ;
+
+ if (name.left(6) == "style_") {
+ name = name.right(name.length() - 6);
+
+ KTmpStatusMsg msg(i18n("Changing to %1 style...").arg(name),
+ this);
+
+ addCommandToHistory(new ChangeStyleCommand
+ (NoteStyleName(qstrtostr(name)),
+ *m_currentEventSelection));
+ } else {
+ KMessageBox::sorry
+ (this, i18n("Unknown style action %1").arg(name));
+ }
+}
+
+void NotationView::slotInsertNoteFromAction()
+{
+ const QObject *s = sender();
+ QString name = s->name();
+
+ Segment &segment = m_staffs[m_currentStaff]->getSegment();
+
+ NoteInserter *noteInserter = dynamic_cast<NoteInserter *>(m_tool);
+ if (!noteInserter) {
+ KMessageBox::sorry(this, i18n("No note duration selected"));
+ return ;
+ }
+
+ int pitch = 0;
+ Accidental accidental =
+ Accidentals::NoAccidental;
+
+ timeT time(getInsertionTime());
+ Rosegarden::Key key = segment.getKeyAtTime(time);
+ Clef clef = segment.getClefAtTime(time);
+
+ try {
+
+ pitch = getPitchFromNoteInsertAction(name, accidental, clef, key);
+
+ } catch (...) {
+
+ KMessageBox::sorry
+ (this, i18n("Unknown note insert action %1").arg(name));
+ return ;
+ }
+
+ KTmpStatusMsg msg(i18n("Inserting note"), this);
+
+ NOTATION_DEBUG << "Inserting note at pitch " << pitch << endl;
+
+ noteInserter->insertNote(segment, time, pitch, accidental);
+}
+
+void NotationView::slotInsertRest()
+{
+ Segment &segment = m_staffs[m_currentStaff]->getSegment();
+ timeT time = getInsertionTime();
+
+ RestInserter *restInserter = dynamic_cast<RestInserter *>(m_tool);
+
+ if (!restInserter) {
+
+ NoteInserter *noteInserter = dynamic_cast<NoteInserter *>(m_tool);
+ if (!noteInserter) {
+ KMessageBox::sorry(this, i18n("No note duration selected"));
+ return ;
+ }
+
+ Note note(noteInserter->getCurrentNote());
+
+ restInserter = dynamic_cast<RestInserter*>
+ (m_toolBox->getTool(RestInserter::ToolName));
+
+ restInserter->slotSetNote(note.getNoteType());
+ restInserter->slotSetDots(note.getDots());
+ }
+
+ restInserter->insertNote(segment, time,
+ 0, Accidentals::NoAccidental, true);
+}
+
+void NotationView::slotSwitchFromRestToNote()
+{
+ RestInserter *restInserter = dynamic_cast<RestInserter *>(m_tool);
+ if (!restInserter) {
+ KMessageBox::sorry(this, i18n("No rest duration selected"));
+ return ;
+ }
+
+ Note note(restInserter->getCurrentNote());
+
+ QString actionName = NotationStrings::getReferenceName(note, false);
+ actionName = actionName.replace("-", "_");
+
+ KRadioAction *action = dynamic_cast<KRadioAction *>
+ (actionCollection()->action(actionName));
+
+ if (!action) {
+ std::cerr << "WARNING: Failed to find note action \""
+ << actionName << "\"" << std::endl;
+ } else {
+ action->activate();
+ }
+
+ NoteInserter *noteInserter = dynamic_cast<NoteInserter*>
+ (m_toolBox->getTool(NoteInserter::ToolName));
+
+ if (noteInserter) {
+ noteInserter->slotSetNote(note.getNoteType());
+ noteInserter->slotSetDots(note.getDots());
+ setTool(noteInserter);
+ }
+
+ setMenuStates();
+}
+
+void NotationView::slotSwitchFromNoteToRest()
+{
+ NoteInserter *noteInserter = dynamic_cast<NoteInserter *>(m_tool);
+ if (!noteInserter) {
+ KMessageBox::sorry(this, i18n("No note duration selected"));
+ return ;
+ }
+
+ Note note(noteInserter->getCurrentNote());
+
+ QString actionName = NotationStrings::getReferenceName(note, true);
+ actionName = actionName.replace("-", "_");
+
+ KRadioAction *action = dynamic_cast<KRadioAction *>
+ (actionCollection()->action(actionName));
+
+ if (!action) {
+ std::cerr << "WARNING: Failed to find rest action \""
+ << actionName << "\"" << std::endl;
+ } else {
+ action->activate();
+ }
+
+ RestInserter *restInserter = dynamic_cast<RestInserter*>
+ (m_toolBox->getTool(RestInserter::ToolName));
+
+ if (restInserter) {
+ restInserter->slotSetNote(note.getNoteType());
+ restInserter->slotSetDots(note.getDots());
+ setTool(restInserter);
+ }
+
+ setMenuStates();
+}
+
+void NotationView::slotToggleDot()
+{
+ NoteInserter *noteInserter = dynamic_cast<NoteInserter *>(m_tool);
+ if (noteInserter) {
+ Note note(noteInserter->getCurrentNote());
+ if (note.getNoteType() == Note::Shortest ||
+ note.getNoteType() == Note::Longest)
+ return ;
+ noteInserter->slotSetDots(note.getDots() ? 0 : 1);
+ setTool(noteInserter);
+ } else {
+ RestInserter *restInserter = dynamic_cast<RestInserter *>(m_tool);
+ if (restInserter) {
+ Note note(restInserter->getCurrentNote());
+ if (note.getNoteType() == Note::Shortest ||
+ note.getNoteType() == Note::Longest)
+ return ;
+ restInserter->slotSetDots(note.getDots() ? 0 : 1);
+ setTool(restInserter);
+ } else {
+ KMessageBox::sorry(this, i18n("No note or rest duration selected"));
+ }
+ }
+
+ setMenuStates();
+}
+
+void NotationView::slotRespellDoubleFlat()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Forcing accidentals..."), this);
+
+ addCommandToHistory(new RespellCommand(RespellCommand::Set,
+ Accidentals::DoubleFlat,
+ *m_currentEventSelection));
+}
+
+void NotationView::slotRespellFlat()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Forcing accidentals..."), this);
+
+ addCommandToHistory(new RespellCommand(RespellCommand::Set,
+ Accidentals::Flat,
+ *m_currentEventSelection));
+}
+
+void NotationView::slotRespellNatural()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Forcing accidentals..."), this);
+
+ addCommandToHistory(new RespellCommand(RespellCommand::Set,
+ Accidentals::Natural,
+ *m_currentEventSelection));
+}
+
+void NotationView::slotRespellSharp()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Forcing accidentals..."), this);
+
+ addCommandToHistory(new RespellCommand(RespellCommand::Set,
+ Accidentals::Sharp,
+ *m_currentEventSelection));
+}
+
+void NotationView::slotRespellDoubleSharp()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Forcing accidentals..."), this);
+
+ addCommandToHistory(new RespellCommand(RespellCommand::Set,
+ Accidentals::DoubleSharp,
+ *m_currentEventSelection));
+}
+
+void NotationView::slotRespellUp()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Forcing accidentals..."), this);
+
+ addCommandToHistory(new RespellCommand(RespellCommand::Up,
+ Accidentals::NoAccidental,
+ *m_currentEventSelection));
+}
+
+void NotationView::slotRespellDown()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Forcing accidentals..."), this);
+
+ addCommandToHistory(new RespellCommand(RespellCommand::Down,
+ Accidentals::NoAccidental,
+ *m_currentEventSelection));
+}
+
+void NotationView::slotRespellRestore()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Restoring accidentals..."), this);
+
+ addCommandToHistory(new RespellCommand(RespellCommand::Restore,
+ Accidentals::NoAccidental,
+ *m_currentEventSelection));
+}
+
+void NotationView::slotShowCautionary()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Showing cautionary accidentals..."), this);
+
+ addCommandToHistory(new MakeAccidentalsCautionaryCommand
+ (true, *m_currentEventSelection));
+}
+
+void NotationView::slotCancelCautionary()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Cancelling cautionary accidentals..."), this);
+
+ addCommandToHistory(new MakeAccidentalsCautionaryCommand
+ (false, *m_currentEventSelection));
+}
+
+void NotationView::slotTransformsQuantize()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ QuantizeDialog dialog(this, true);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ KTmpStatusMsg msg(i18n("Quantizing..."), this);
+ addCommandToHistory(new EventQuantizeCommand
+ (*m_currentEventSelection,
+ dialog.getQuantizer()));
+ }
+}
+
+void NotationView::slotTransformsInterpret()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ InterpretDialog dialog(this);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ KTmpStatusMsg msg(i18n("Interpreting selection..."), this);
+ addCommandToHistory(new InterpretCommand
+ (*m_currentEventSelection,
+ getDocument()->getComposition().getNotationQuantizer(),
+ dialog.getInterpretations()));
+ }
+}
+
+void NotationView::slotSetNoteDurations(Note::Type type, bool notationOnly)
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Setting note durations..."), this);
+ addCommandToHistory(new SetNoteTypeCommand(*m_currentEventSelection, type, notationOnly));
+}
+
+void NotationView::slotAddDot()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Adding dot..."), this);
+ addCommandToHistory(new AddDotCommand(*m_currentEventSelection, false));
+}
+
+void NotationView::slotAddDotNotationOnly()
+{
+ if (!m_currentEventSelection)
+ return ;
+ KTmpStatusMsg msg(i18n("Adding dot..."), this);
+ addCommandToHistory(new AddDotCommand(*m_currentEventSelection, true));
+}
+
+void NotationView::slotAddSlashes()
+{
+ const QObject *s = sender();
+ if (!m_currentEventSelection)
+ return ;
+
+ QString name = s->name();
+ int slashes = name.right(1).toInt();
+
+ addCommandToHistory(new AddSlashesCommand
+ (slashes, *m_currentEventSelection));
+}
+
+void NotationView::slotMarksAddTextMark()
+{
+ if (m_currentEventSelection) {
+ bool pressedOK = false;
+
+ QString txt = KLineEditDlg::getText(i18n("Text: "), "", &pressedOK, this);
+
+ if (pressedOK) {
+ addCommandToHistory(new AddTextMarkCommand
+ (qstrtostr(txt), *m_currentEventSelection));
+ }
+ }
+}
+
+void NotationView::slotMarksAddFingeringMark()
+{
+ if (m_currentEventSelection) {
+ bool pressedOK = false;
+
+ QString txt = KLineEditDlg::getText(i18n("Fingering: "), "", &pressedOK, this);
+
+ if (pressedOK) {
+ addCommandToHistory(new AddFingeringMarkCommand
+ (qstrtostr(txt), *m_currentEventSelection));
+ }
+ }
+}
+
+void NotationView::slotMarksAddFingeringMarkFromAction()
+{
+ const QObject *s = sender();
+ QString name = s->name();
+
+ if (name.left(14) == "add_fingering_") {
+
+ QString fingering = name.right(name.length() - 14);
+
+ if (fingering == "plus")
+ fingering = "+";
+
+ if (m_currentEventSelection) {
+ addCommandToHistory(new AddFingeringMarkCommand
+ (qstrtostr(fingering), *m_currentEventSelection));
+ }
+ }
+}
+
+void NotationView::slotMarksRemoveMarks()
+{
+ if (m_currentEventSelection)
+ addCommandToHistory(new RemoveMarksCommand
+ (*m_currentEventSelection));
+}
+
+void NotationView::slotMarksRemoveFingeringMarks()
+{
+ if (m_currentEventSelection)
+ addCommandToHistory(new RemoveFingeringMarksCommand
+ (*m_currentEventSelection));
+}
+
+void
+NotationView::slotMakeOrnament()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ EventSelection::eventcontainer &ec =
+ m_currentEventSelection->getSegmentEvents();
+
+ int basePitch = -1;
+ int baseVelocity = -1;
+ NoteStyle *style = NoteStyleFactory::getStyle(NoteStyleFactory::DefaultStyle);
+
+ for (EventSelection::eventcontainer::iterator i =
+ ec.begin(); i != ec.end(); ++i) {
+ if ((*i)->isa(Note::EventType)) {
+ if ((*i)->has(BaseProperties::PITCH)) {
+ basePitch = (*i)->get
+ <Int>
+ (BaseProperties::PITCH);
+ style = NoteStyleFactory::getStyleForEvent(*i);
+ if (baseVelocity != -1)
+ break;
+ }
+ if ((*i)->has(BaseProperties::VELOCITY)) {
+ baseVelocity = (*i)->get
+ <Int>
+ (BaseProperties::VELOCITY);
+ if (basePitch != -1)
+ break;
+ }
+ }
+ }
+
+ Staff *staff = getCurrentStaff();
+ Segment &segment = staff->getSegment();
+
+ timeT absTime = m_currentEventSelection->getStartTime();
+ timeT duration = m_currentEventSelection->getTotalDuration();
+ Note note(Note::getNearestNote(duration));
+
+ Track *track =
+ segment.getComposition()->getTrackById(segment.getTrack());
+ QString name;
+ int barNo = segment.getComposition()->getBarNumber(absTime);
+ if (track) {
+ name = QString(i18n("Ornament track %1 bar %2").arg(track->getPosition() + 1).arg(barNo + 1));
+ } else {
+ name = QString(i18n("Ornament bar %1").arg(barNo + 1));
+ }
+
+ MakeOrnamentDialog dialog(this, name, basePitch);
+ if (dialog.exec() != QDialog::Accepted)
+ return ;
+
+ name = dialog.getName();
+ basePitch = dialog.getBasePitch();
+
+ KMacroCommand *command = new KMacroCommand(i18n("Make Ornament"));
+
+ command->addCommand(new CutCommand
+ (*m_currentEventSelection,
+ getDocument()->getClipboard()));
+
+ command->addCommand(new PasteToTriggerSegmentCommand
+ (&getDocument()->getComposition(),
+ getDocument()->getClipboard(),
+ name, basePitch));
+
+ command->addCommand(new InsertTriggerNoteCommand
+ (segment, absTime, note, basePitch, baseVelocity,
+ style->getName(),
+ getDocument()->getComposition().getNextTriggerSegmentId(),
+ true,
+ BaseProperties::TRIGGER_SEGMENT_ADJUST_SQUISH,
+ Marks::NoMark)); //!!!
+
+ addCommandToHistory(command);
+}
+
+void
+NotationView::slotUseOrnament()
+{
+ // Take an existing note and match an ornament to it.
+
+ if (!m_currentEventSelection)
+ return ;
+
+ UseOrnamentDialog dialog(this, &getDocument()->getComposition());
+ if (dialog.exec() != QDialog::Accepted)
+ return ;
+
+ addCommandToHistory(new SetTriggerCommand(*m_currentEventSelection,
+ dialog.getId(),
+ true,
+ dialog.getRetune(),
+ dialog.getTimeAdjust(),
+ dialog.getMark(),
+ i18n("Use Ornament")));
+}
+
+void
+NotationView::slotRemoveOrnament()
+{
+ if (!m_currentEventSelection)
+ return ;
+
+ addCommandToHistory(new ClearTriggersCommand(*m_currentEventSelection,
+ i18n("Remove Ornaments")));
+}
+
+void NotationView::slotEditAddClef()
+{
+ Staff *staff = getCurrentStaff();
+ Segment &segment = staff->getSegment();
+ static Clef lastClef;
+ Clef clef;
+ Rosegarden::Key key;
+ timeT insertionTime = getInsertionTime(clef, key);
+
+ ClefDialog dialog(this, m_notePixmapFactory, lastClef);
+
+ if (dialog.exec() == QDialog::Accepted) {
+
+ ClefDialog::ConversionType conversion = dialog.getConversionType();
+
+ bool shouldChangeOctave = (conversion != ClefDialog::NoConversion);
+ bool shouldTranspose = (conversion == ClefDialog::Transpose);
+
+ addCommandToHistory
+ (new ClefInsertionCommand
+ (segment, insertionTime, dialog.getClef(),
+ shouldChangeOctave, shouldTranspose));
+
+ lastClef = dialog.getClef();
+ }
+}
+
+void NotationView::slotEditAddKeySignature()
+{
+ Staff *staff = getCurrentStaff();
+ Segment &segment = staff->getSegment();
+ Clef clef;
+ Rosegarden::Key key;
+ timeT insertionTime = getInsertionTime(clef, key);
+
+ //!!! experimental:
+ CompositionTimeSliceAdapter adapter
+ (&getDocument()->getComposition(), insertionTime,
+ getDocument()->getComposition().getDuration());
+ AnalysisHelper helper;
+ key = helper.guessKey(adapter);
+
+ KeySignatureDialog dialog
+ (this, m_notePixmapFactory, clef, key, true, true,
+ i18n("Estimated key signature shown"));
+
+ if (dialog.exec() == QDialog::Accepted &&
+ dialog.isValid()) {
+
+ KeySignatureDialog::ConversionType conversion =
+ dialog.getConversionType();
+
+ bool transposeKey = dialog.shouldBeTransposed();
+ bool applyToAll = dialog.shouldApplyToAll();
+ bool ignorePercussion = dialog.shouldIgnorePercussion();
+
+ if (applyToAll) {
+ addCommandToHistory
+ (new MultiKeyInsertionCommand
+ (getDocument(),
+ insertionTime, dialog.getKey(),
+ conversion == KeySignatureDialog::Convert,
+ conversion == KeySignatureDialog::Transpose,
+ transposeKey,
+ ignorePercussion));
+ } else {
+ addCommandToHistory
+ (new KeyInsertionCommand
+ (segment,
+ insertionTime, dialog.getKey(),
+ conversion == KeySignatureDialog::Convert,
+ conversion == KeySignatureDialog::Transpose,
+ transposeKey,
+ false));
+ }
+ }
+}
+
+void NotationView::slotEditAddSustain(bool down)
+{
+ Staff *staff = getCurrentStaff();
+ Segment &segment = staff->getSegment();
+ timeT insertionTime = getInsertionTime();
+
+ Studio *studio = &getDocument()->getStudio();
+ Track *track = segment.getComposition()->getTrackById(segment.getTrack());
+
+ if (track) {
+
+ Instrument *instrument = studio->getInstrumentById
+ (track->getInstrument());
+ if (instrument) {
+ MidiDevice *device = dynamic_cast<MidiDevice *>
+ (instrument->getDevice());
+ if (device) {
+ for (ControlList::const_iterator i =
+ device->getControlParameters().begin();
+ i != device->getControlParameters().end(); ++i) {
+
+ if (i->getType() == Controller::EventType &&
+ (i->getName() == "Sustain" ||
+ strtoqstr(i->getName()) == i18n("Sustain"))) {
+
+ addCommandToHistory
+ (new SustainInsertionCommand(segment, insertionTime, down,
+ i->getControllerValue()));
+ return ;
+ }
+ }
+ } else if (instrument->getDevice() &&
+ instrument->getDevice()->getType() == Device::SoftSynth) {
+ addCommandToHistory
+ (new SustainInsertionCommand(segment, insertionTime, down, 64));
+ }
+ }
+ }
+
+ KMessageBox::sorry(this, i18n("There is no sustain controller defined for this device.\nPlease ensure the device is configured correctly in the Manage MIDI Devices dialog in the main window."));
+}
+
+void NotationView::slotEditAddSustainDown()
+{
+ slotEditAddSustain(true);
+}
+
+void NotationView::slotEditAddSustainUp()
+{
+ slotEditAddSustain(false);
+}
+
+void NotationView::slotEditTranspose()
+{
+ IntervalDialog intervalDialog(this, true, true);
+ int ok = intervalDialog.exec();
+
+ int semitones = intervalDialog.getChromaticDistance();
+ int steps = intervalDialog.getDiatonicDistance();
+
+ if (!ok || (semitones == 0 && steps == 0)) return;
+
+ // TODO combine commands into one
+ for (int i = 0; i < m_segments.size(); i++)
+ {
+ addCommandToHistory(new SegmentTransposeCommand(*(m_segments[i]),
+ intervalDialog.getChangeKey(), steps, semitones,
+ intervalDialog.getTransposeSegmentBack()));
+ }
+}
+
+void NotationView::slotEditSwitchPreset()
+{
+ PresetHandlerDialog dialog(this, true);
+
+ if (dialog.exec() != QDialog::Accepted) return;
+
+ if (dialog.getConvertAllSegments()) {
+ // get all segments for this track and convert them.
+ Composition& comp = getDocument()->getComposition();
+ TrackId selectedTrack = getCurrentSegment()->getTrack();
+
+ // satisfy #1885251 the way that seems most reasonble to me at the
+ // moment, only changing track parameters when acting on all segments on
+ // this track from the notation view
+ //
+ //!!! This won't be undoable, and I'm not sure if that's seriously
+ // wrong, or just mildly wrong, but I'm betting somebody will tell me
+ // about it if this was inappropriate
+ Track *track = comp.getTrackById(selectedTrack);
+ track->setPresetLabel(dialog.getName());
+ track->setClef(dialog.getClef());
+ track->setTranspose(dialog.getTranspose());
+ track->setLowestPlayable(dialog.getLowRange());
+ track->setHighestPlayable(dialog.getHighRange());
+
+ addCommandToHistory(new SegmentSyncCommand(comp.getSegments(), selectedTrack,
+ dialog.getTranspose(),
+ dialog.getLowRange(),
+ dialog.getHighRange(),
+ clefIndexToClef(dialog.getClef())));
+ } else {
+ addCommandToHistory(new SegmentSyncCommand(m_segments,
+ dialog.getTranspose(),
+ dialog.getLowRange(),
+ dialog.getHighRange(),
+ clefIndexToClef(dialog.getClef())));
+ }
+
+ m_doc->slotDocumentModified();
+ emit updateView();
+}
+
+void NotationView::slotEditElement(NotationStaff *staff,
+ NotationElement *element, bool advanced)
+{
+ if (advanced) {
+
+ EventEditDialog dialog(this, *element->event(), true);
+
+ if (dialog.exec() == QDialog::Accepted &&
+ dialog.isModified()) {
+
+ EventEditCommand *command = new EventEditCommand
+ (staff->getSegment(),
+ element->event(),
+ dialog.getEvent());
+
+ addCommandToHistory(command);
+ }
+
+ } else if (element->event()->isa(Clef::EventType)) {
+
+ try {
+ ClefDialog dialog(this, m_notePixmapFactory,
+ Clef(*element->event()));
+
+ if (dialog.exec() == QDialog::Accepted) {
+
+ ClefDialog::ConversionType conversion = dialog.getConversionType();
+ bool shouldChangeOctave = (conversion != ClefDialog::NoConversion);
+ bool shouldTranspose = (conversion == ClefDialog::Transpose);
+ addCommandToHistory
+ (new ClefInsertionCommand
+ (staff->getSegment(), element->event()->getAbsoluteTime(),
+ dialog.getClef(), shouldChangeOctave, shouldTranspose));
+ }
+ } catch (Exception e) {
+ std::cerr << e.getMessage() << std::endl;
+ }
+
+ return ;
+
+ } else if (element->event()->isa(Rosegarden::Key::EventType)) {
+
+ try {
+ Clef clef(staff->getSegment().getClefAtTime
+ (element->event()->getAbsoluteTime()));
+ KeySignatureDialog dialog
+ (this, m_notePixmapFactory, clef, Rosegarden::Key(*element->event()),
+ false, true);
+
+ if (dialog.exec() == QDialog::Accepted &&
+ dialog.isValid()) {
+
+ KeySignatureDialog::ConversionType conversion =
+ dialog.getConversionType();
+
+ addCommandToHistory
+ (new KeyInsertionCommand
+ (staff->getSegment(),
+ element->event()->getAbsoluteTime(), dialog.getKey(),
+ conversion == KeySignatureDialog::Convert,
+ conversion == KeySignatureDialog::Transpose,
+ dialog.shouldBeTransposed(),
+ dialog.shouldIgnorePercussion()));
+ }
+
+ } catch (Exception e) {
+ std::cerr << e.getMessage() << std::endl;
+ }
+
+ return ;
+
+ } else if (element->event()->isa(Text::EventType)) {
+
+ try {
+ TextEventDialog dialog
+ (this, m_notePixmapFactory, Text(*element->event()));
+ if (dialog.exec() == QDialog::Accepted) {
+ TextInsertionCommand *command = new TextInsertionCommand
+ (staff->getSegment(),
+ element->event()->getAbsoluteTime(),
+ dialog.getText());
+ KMacroCommand *macroCommand = new KMacroCommand(command->name());
+ macroCommand->addCommand(new EraseEventCommand(staff->getSegment(),
+ element->event(), false));
+ macroCommand->addCommand(command);
+ addCommandToHistory(macroCommand);
+ }
+ } catch (Exception e) {
+ std::cerr << e.getMessage() << std::endl;
+ }
+
+ return ;
+
+ } else if (element->isNote() &&
+ element->event()->has(BaseProperties::TRIGGER_SEGMENT_ID)) {
+
+ int id = element->event()->get
+ <Int>
+ (BaseProperties::TRIGGER_SEGMENT_ID);
+ emit editTriggerSegment(id);
+ return ;
+
+ } else {
+
+ SimpleEventEditDialog dialog(this, getDocument(), *element->event(), false);
+
+ if (dialog.exec() == QDialog::Accepted &&
+ dialog.isModified()) {
+
+ EventEditCommand *command = new EventEditCommand
+ (staff->getSegment(),
+ element->event(),
+ dialog.getEvent());
+
+ addCommandToHistory(command);
+ }
+ }
+}
+
+void NotationView::slotBeginLilyPondRepeat()
+{}
+
+void NotationView::slotDebugDump()
+{
+ if (m_currentEventSelection) {
+ EventSelection::eventcontainer &ec =
+ m_currentEventSelection->getSegmentEvents();
+ int n = 0;
+ for (EventSelection::eventcontainer::iterator i =
+ ec.begin();
+ i != ec.end(); ++i) {
+ std::cerr << "\n" << n++ << " [" << (*i) << "]" << std::endl;
+ (*i)->dump(std::cerr);
+ }
+ }
+}
+
+void
+NotationView::slotSetPointerPosition(timeT time)
+{
+ slotSetPointerPosition(time, m_playTracking);
+}
+
+void
+NotationView::slotSetPointerPosition(timeT time, bool scroll)
+{
+ Composition &comp = getDocument()->getComposition();
+ int barNo = comp.getBarNumber(time);
+
+ int minCy = 0;
+ double cx = 0;
+ bool haveMinCy = false;
+
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+
+ double layoutX = m_hlayout->getXForTimeByEvent(time);
+ Segment &seg = m_staffs[i]->getSegment();
+
+ bool good = true;
+
+ if (barNo >= m_hlayout->getLastVisibleBarOnStaff(*m_staffs[i])) {
+ if (seg.isRepeating() && time < seg.getRepeatEndTime()) {
+ timeT mappedTime =
+ seg.getStartTime() +
+ ((time - seg.getStartTime()) %
+ (seg.getEndMarkerTime() - seg.getStartTime()));
+ layoutX = m_hlayout->getXForTimeByEvent(mappedTime);
+ } else {
+ good = false;
+ }
+ } else if (barNo < m_hlayout->getFirstVisibleBarOnStaff(*m_staffs[i])) {
+ good = false;
+ }
+
+ if (!good) {
+
+ m_staffs[i]->hidePointer();
+
+ } else {
+
+ m_staffs[i]->setPointerPosition(layoutX);
+
+ int cy;
+ m_staffs[i]->getPointerPosition(cx, cy);
+
+ if (!haveMinCy || cy < minCy) {
+ minCy = cy;
+ haveMinCy = true;
+ }
+ }
+ }
+
+ if (m_pageMode == LinedStaff::LinearMode) {
+ // be careful not to prevent user from scrolling up and down
+ haveMinCy = false;
+ }
+
+ if (scroll) {
+ getCanvasView()->slotScrollHoriz(int(cx));
+ if (haveMinCy) {
+ getCanvasView()->slotScrollVertToTop(minCy);
+ }
+ }
+
+ updateView();
+}
+
+void
+NotationView::slotUpdateRecordingSegment(Segment *segment,
+ timeT updateFrom)
+{
+ NOTATION_DEBUG << "NotationView::slotUpdateRecordingSegment: segment " << segment << ", updateFrom " << updateFrom << ", end time " << segment->getEndMarkerTime() << endl;
+ if (updateFrom >= segment->getEndMarkerTime())
+ return ;
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ if (&m_staffs[i]->getSegment() == segment) {
+ refreshSegment(segment, 0, 0);
+ }
+ }
+ NOTATION_DEBUG << "NotationView::slotUpdateRecordingSegment: don't have segment " << segment << endl;
+}
+
+void
+NotationView::slotSetCurrentStaff(double x, int y)
+{
+ unsigned int staffNo;
+ for (staffNo = 0; staffNo < m_staffs.size(); ++staffNo) {
+ if (m_staffs[staffNo]->containsCanvasCoords(x, y))
+ break;
+ }
+
+ if (staffNo < m_staffs.size()) {
+ slotSetCurrentStaff(staffNo);
+ }
+}
+
+void
+NotationView::slotSetCurrentStaff(int staffNo)
+{
+ NOTATION_DEBUG << "NotationView::slotSetCurrentStaff(" << staffNo << ")" << endl;
+
+ if (m_currentStaff != staffNo) {
+
+ m_staffs[m_currentStaff]->setCurrent(false);
+
+ m_currentStaff = staffNo;
+
+ m_staffs[m_currentStaff]->setCurrent(true);
+
+ Segment *segment = &m_staffs[m_currentStaff]->getSegment();
+
+ m_chordNameRuler->setCurrentSegment(segment);
+ m_rawNoteRuler->setCurrentSegment(segment);
+ m_rawNoteRuler->repaint();
+ setControlRulersCurrentSegment();
+
+ updateView();
+
+ slotSetInsertCursorPosition(getInsertionTime(), false, false);
+
+ m_headersGroup->setCurrent(
+ m_staffs[staffNo]->getSegment().getTrack());
+ }
+}
+
+void
+NotationView::slotCurrentStaffUp()
+{
+ LinedStaff *staff = getStaffAbove();
+ if (!staff) return;
+ slotSetCurrentStaff(staff->getId());
+}
+
+void
+NotationView::slotCurrentStaffDown()
+{
+ LinedStaff *staff = getStaffBelow();
+ if (!staff) return;
+ slotSetCurrentStaff(staff->getId());
+}
+
+void
+NotationView::slotCurrentSegmentPrior()
+{
+ if (m_staffs.size() < 2)
+ return ;
+
+ Composition *composition =
+ m_staffs[m_currentStaff]->getSegment().getComposition();
+
+ Track *track = composition->
+ getTrackById(m_staffs[m_currentStaff]->getSegment().getTrack());
+ if (!track)
+ return ;
+
+ int lastStaffOnTrack = -1;
+
+ //
+ // TODO: Cycle segments through rather in time order?
+ // Cycle only segments in the field of view?
+ //
+ for (int i = m_staffs.size()-1; i >= 0; --i) {
+ if (m_staffs[i]->getSegment().getTrack() == track->getId()) {
+ if (lastStaffOnTrack < 0) {
+ lastStaffOnTrack = i;
+ }
+ if (i < m_currentStaff) {
+ slotSetCurrentStaff(i);
+ slotEditSelectWholeStaff();
+ return ;
+ }
+ }
+ }
+ if (lastStaffOnTrack >= 0) {
+ slotSetCurrentStaff(lastStaffOnTrack);
+ slotEditSelectWholeStaff();
+ return ;
+ }
+}
+
+void
+NotationView::slotCurrentSegmentNext()
+{
+ if (m_staffs.size() < 2)
+ return ;
+
+ Composition *composition =
+ m_staffs[m_currentStaff]->getSegment().getComposition();
+
+ Track *track = composition->
+ getTrackById(m_staffs[m_currentStaff]->getSegment().getTrack());
+ if (!track)
+ return ;
+
+ int firstStaffOnTrack = -1;
+
+ //
+ // TODO: Cycle segments through rather in time order?
+ // Cycle only segments in the field of view?
+ //
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ if (m_staffs[i]->getSegment().getTrack() == track->getId()) {
+ if (firstStaffOnTrack < 0) {
+ firstStaffOnTrack = i;
+ }
+ if (i > m_currentStaff) {
+ slotSetCurrentStaff(i);
+ slotEditSelectWholeStaff();
+ return ;
+ }
+ }
+ }
+ if (firstStaffOnTrack >= 0) {
+ slotSetCurrentStaff(firstStaffOnTrack);
+ slotEditSelectWholeStaff();
+ return ;
+ }
+}
+
+void
+NotationView::slotSetInsertCursorPosition(double x, int y, bool scroll,
+ bool updateNow)
+{
+ NOTATION_DEBUG << "NotationView::slotSetInsertCursorPosition: x " << x << ", y " << y << ", scroll " << scroll << ", now " << updateNow << endl;
+
+ slotSetCurrentStaff(x, y);
+
+ LinedStaff *staff = getLinedStaff(m_currentStaff);
+ Event *clefEvt, *keyEvt;
+ NotationElementList::iterator i =
+ staff->getElementUnderCanvasCoords(x, y, clefEvt, keyEvt);
+
+ if (i == staff->getViewElementList()->end()) {
+ slotSetInsertCursorPosition(staff->getSegment().getEndTime(), scroll,
+ updateNow);
+ } else {
+ slotSetInsertCursorPosition((*i)->getViewAbsoluteTime(), scroll,
+ updateNow);
+ }
+}
+
+void
+NotationView::slotSetInsertCursorPosition(timeT t, bool scroll, bool updateNow)
+{
+ NOTATION_DEBUG << "NotationView::slotSetInsertCursorPosition: time " << t << ", scroll " << scroll << ", now " << updateNow << endl;
+
+ m_insertionTime = t;
+ if (scroll) {
+ m_deferredCursorMove = CursorMoveAndMakeVisible;
+ } else {
+ m_deferredCursorMove = CursorMoveOnly;
+ }
+ if (updateNow)
+ doDeferredCursorMove();
+}
+
+void
+NotationView::slotSetInsertCursorAndRecentre(timeT t, double cx, int,
+ bool updateNow)
+{
+ NOTATION_DEBUG << "NotationView::slotSetInsertCursorAndRecentre: time " << t << ", cx " << cx << ", now " << updateNow << ", contentsx" << getCanvasView()->contentsX() << ", w " << getCanvasView()->visibleWidth() << endl;
+
+ m_insertionTime = t;
+
+ // We only do the scroll bit if cx is in the right two-thirds of
+ // the window
+
+ if (cx < (getCanvasView()->contentsX() +
+ getCanvasView()->visibleWidth() / 3)) {
+
+ m_deferredCursorMove = CursorMoveOnly;
+ } else {
+ m_deferredCursorMove = CursorMoveAndScrollToPosition;
+ m_deferredCursorScrollToX = cx;
+ }
+
+ if (updateNow)
+ doDeferredCursorMove();
+}
+
+void
+NotationView::doDeferredCursorMove()
+{
+ NOTATION_DEBUG << "NotationView::doDeferredCursorMove: m_deferredCursorMove == " << m_deferredCursorMove << endl;
+
+ if (m_deferredCursorMove == NoCursorMoveNeeded) {
+ return ;
+ }
+
+ DeferredCursorMoveType type = m_deferredCursorMove;
+ m_deferredCursorMove = NoCursorMoveNeeded;
+
+ timeT t = m_insertionTime;
+
+ if (m_staffs.size() == 0)
+ return ;
+ LinedStaff *staff = getCurrentLinedStaff();
+ Segment &segment = staff->getSegment();
+
+ if (t < segment.getStartTime()) {
+ t = segment.getStartTime();
+ }
+ if (t > segment.getEndTime()) {
+ t = segment.getEndTime();
+ }
+
+ NotationElementList::iterator i =
+ staff->getViewElementList()->findNearestTime(t);
+
+ while (i != staff->getViewElementList()->end() &&
+ !static_cast<NotationElement*>(*i)->getCanvasItem())
+ ++i;
+
+ if (i == staff->getViewElementList()->end()) {
+ //!!! ???
+ if (m_insertionTime >= staff->getSegment().getStartTime()) {
+ i = staff->getViewElementList()->begin();
+ }
+ m_insertionTime = staff->getSegment().getStartTime();
+ } else {
+ m_insertionTime = static_cast<NotationElement*>(*i)->getViewAbsoluteTime();
+ }
+
+ if (i == staff->getViewElementList()->end() ||
+ t == segment.getEndTime() ||
+ t == segment.getBarStartForTime(t)) {
+
+ staff->setInsertCursorPosition(*m_hlayout, t);
+
+ if (type == CursorMoveAndMakeVisible) {
+ double cx;
+ int cy;
+ staff->getInsertCursorPosition(cx, cy);
+ getCanvasView()->slotScrollHoriz(int(cx));
+ getCanvasView()->slotScrollVertSmallSteps(cy);
+ }
+
+ } else {
+
+ // prefer a note or rest, if there is one, to a non-spacing event
+ if (!static_cast<NotationElement*>(*i)->isNote() &&
+ !static_cast<NotationElement*>(*i)->isRest()) {
+ NotationElementList::iterator j = i;
+ while (j != staff->getViewElementList()->end()) {
+ if (static_cast<NotationElement*>(*j)->getViewAbsoluteTime() !=
+ static_cast<NotationElement*>(*i)->getViewAbsoluteTime())
+ break;
+ if (static_cast<NotationElement*>(*j)->getCanvasItem()) {
+ if (static_cast<NotationElement*>(*j)->isNote() ||
+ static_cast<NotationElement*>(*j)->isRest()) {
+ i = j;
+ break;
+ }
+ }
+ ++j;
+ }
+ }
+
+ if (static_cast<NotationElement*>(*i)->getCanvasItem()) {
+
+ staff->setInsertCursorPosition
+ (static_cast<NotationElement*>(*i)->getCanvasX() - 2,
+ int(static_cast<NotationElement*>(*i)->getCanvasY()));
+
+ if (type == CursorMoveAndMakeVisible) {
+ getCanvasView()->slotScrollHoriz
+ (int(static_cast<NotationElement*>(*i)->getCanvasX()) - 4);
+ }
+ } else {
+ std::cerr << "WARNING: No canvas item for this notation element:";
+ (*i)->event()->dump(std::cerr);
+ }
+ }
+
+ if (type == CursorMoveAndScrollToPosition) {
+
+ // get current canvas x of insert cursor, which might not be
+ // what we just set
+
+ double ccx = 0.0;
+
+ NotationElementList::iterator i =
+ staff->getViewElementList()->findTime(t);
+
+ if (i == staff->getViewElementList()->end()) {
+ if (i == staff->getViewElementList()->begin())
+ return ;
+ double lx, lwidth;
+ --i;
+ if (static_cast<NotationElement*>(*i)->getCanvasItem()) {
+ ccx = static_cast<NotationElement*>(*i)->getCanvasX();
+ static_cast<NotationElement*>(*i)->getLayoutAirspace(lx, lwidth);
+ } else {
+ std::cerr << "WARNING: No canvas item for this notation element*:";
+ (*i)->event()->dump(std::cerr);
+ }
+ ccx += lwidth;
+ } else {
+ if (static_cast<NotationElement*>(*i)->getCanvasItem()) {
+ ccx = static_cast<NotationElement*>(*i)->getCanvasX();
+ } else {
+ std::cerr << "WARNING: No canvas item for this notation element*:";
+ (*i)->event()->dump(std::cerr);
+ }
+ }
+
+ QScrollBar* hbar = getCanvasView()->horizontalScrollBar();
+ hbar->setValue(int(hbar->value() - (m_deferredCursorScrollToX - ccx)));
+ }
+
+ updateView();
+}
+
+void
+NotationView::slotJumpCursorToPlayback()
+{
+ slotSetInsertCursorPosition(getDocument()->getComposition().getPosition());
+}
+
+void
+NotationView::slotJumpPlaybackToCursor()
+{
+ emit jumpPlaybackTo(getInsertionTime());
+}
+
+void
+NotationView::slotToggleTracking()
+{
+ m_playTracking = !m_playTracking;
+}
+
+void NotationView::slotNoAccidental()
+{
+ emit changeAccidental(Accidentals::NoAccidental, false);
+}
+
+void NotationView::slotFollowAccidental()
+{
+ emit changeAccidental(Accidentals::NoAccidental, true);
+}
+
+void NotationView::slotSharp()
+{
+ emit changeAccidental(Accidentals::Sharp, false);
+}
+
+void NotationView::slotFlat()
+{
+ emit changeAccidental(Accidentals::Flat, false);
+}
+
+void NotationView::slotNatural()
+{
+ emit changeAccidental(Accidentals::Natural, false);
+}
+
+void NotationView::slotDoubleSharp()
+{
+ emit changeAccidental(Accidentals::DoubleSharp, false);
+}
+
+void NotationView::slotDoubleFlat()
+{
+ emit changeAccidental(Accidentals::DoubleFlat, false);
+}
+
+void NotationView::slotTrebleClef()
+{
+ m_currentNotePixmap->setPixmap
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("clef-treble")));
+ setTool(m_toolBox->getTool(ClefInserter::ToolName));
+
+ dynamic_cast<ClefInserter*>(m_tool)->setClef(Clef::Treble);
+ setMenuStates();
+}
+
+void NotationView::slotAltoClef()
+{
+ m_currentNotePixmap->setPixmap
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("clef-alto")));
+ setTool(m_toolBox->getTool(ClefInserter::ToolName));
+
+ dynamic_cast<ClefInserter*>(m_tool)->setClef(Clef::Alto);
+ setMenuStates();
+}
+
+void NotationView::slotTenorClef()
+{
+ m_currentNotePixmap->setPixmap
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("clef-tenor")));
+ setTool(m_toolBox->getTool(ClefInserter::ToolName));
+
+ dynamic_cast<ClefInserter*>(m_tool)->setClef(Clef::Tenor);
+ setMenuStates();
+}
+
+void NotationView::slotBassClef()
+{
+ m_currentNotePixmap->setPixmap
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("clef-bass")));
+ setTool(m_toolBox->getTool(ClefInserter::ToolName));
+
+ dynamic_cast<ClefInserter*>(m_tool)->setClef(Clef::Bass);
+ setMenuStates();
+}
+
+void NotationView::slotText()
+{
+ m_currentNotePixmap->setPixmap
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("text")));
+ setTool(m_toolBox->getTool(TextInserter::ToolName));
+ setMenuStates();
+}
+
+void NotationView::slotGuitarChord()
+{
+ m_currentNotePixmap->setPixmap
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::makeToolbarPixmap("guitarchord")));
+ setTool(m_toolBox->getTool(GuitarChordInserter::ToolName));
+ setMenuStates();
+}
+
+void NotationView::slotEraseSelected()
+{
+ NOTATION_DEBUG << "NotationView::slotEraseSelected()" << endl;
+ setTool(m_toolBox->getTool(NotationEraser::ToolName));
+ setMenuStates();
+}
+
+void NotationView::slotSelectSelected()
+{
+ NOTATION_DEBUG << "NotationView::slotSelectSelected()" << endl;
+ setTool(m_toolBox->getTool(NotationSelector::ToolName));
+ setMenuStates();
+}
+
+void NotationView::slotLinearMode()
+{
+ setPageMode(LinedStaff::LinearMode);
+}
+
+void NotationView::slotContinuousPageMode()
+{
+ setPageMode(LinedStaff::ContinuousPageMode);
+}
+
+void NotationView::slotMultiPageMode()
+{
+ setPageMode(LinedStaff::MultiPageMode);
+}
+
+void NotationView::slotToggleChordsRuler()
+{
+ if (m_hlayout->isPageMode())
+ return ;
+ toggleWidget(m_chordNameRuler, "show_chords_ruler");
+}
+
+void NotationView::slotToggleRawNoteRuler()
+{
+ if (m_hlayout->isPageMode())
+ return ;
+ toggleWidget(m_rawNoteRuler, "show_raw_note_ruler");
+}
+
+void NotationView::slotToggleTempoRuler()
+{
+ if (m_hlayout->isPageMode())
+ return ;
+ toggleWidget(m_tempoRuler, "show_tempo_ruler");
+}
+
+void NotationView::slotToggleAnnotations()
+{
+ m_annotationsVisible = !m_annotationsVisible;
+ slotUpdateAnnotationsStatus();
+ //!!! use refresh mechanism
+ refreshSegment(0, 0, 0);
+}
+
+void NotationView::slotToggleLilyPondDirectives()
+{
+ m_lilyPondDirectivesVisible = !m_lilyPondDirectivesVisible;
+ slotUpdateLilyPondDirectivesStatus();
+ //!!! use refresh mechanism
+ refreshSegment(0, 0, 0);
+}
+
+void NotationView::slotEditLyrics()
+{
+ Staff *staff = getCurrentStaff();
+ Segment &segment = staff->getSegment();
+
+ LyricEditDialog dialog(this, &segment);
+
+ if (dialog.exec() == QDialog::Accepted) {
+
+ KMacroCommand *macro = new KMacroCommand
+ (SetLyricsCommand::getGlobalName());
+
+ for (int i = 0; i < dialog.getVerseCount(); ++i) {
+ SetLyricsCommand *command = new SetLyricsCommand
+ (&segment, i, dialog.getLyricData(i));
+ macro->addCommand(command);
+ }
+
+ addCommandToHistory(macro);
+ }
+}
+
+void NotationView::slotItemPressed(int height, int staffNo,
+ QMouseEvent* e,
+ NotationElement* el)
+{
+ NOTATION_DEBUG << "NotationView::slotItemPressed(height = "
+ << height << ", staffNo = " << staffNo
+ << ")" << endl;
+
+ if (staffNo < 0 && el != 0) {
+ // We have an element but no staff -- that's because the
+ // element extended outside the staff region. But we need
+ // to handle it properly, so we rather laboriously need to
+ // find out which staff it was.
+ for (unsigned int i = 0; i < m_staffs.size(); ++i) {
+ if (m_staffs[i]->getViewElementList()->findSingle(el) !=
+ m_staffs[i]->getViewElementList()->end()) {
+ staffNo = m_staffs[i]->getId();
+ break;
+ }
+ }
+ }
+
+ ButtonState btnState = e->state();
+
+ if (btnState & ControlButton) { // on ctrl-click, set cursor position
+
+ slotSetInsertCursorPosition(e->x(), (int)e->y());
+
+ } else {
+
+ setActiveItem(0);
+
+ timeT unknownTime = 0;
+
+ if (e->type() == QEvent::MouseButtonDblClick) {
+ m_tool->handleMouseDoubleClick(unknownTime, height,
+ staffNo, e, el);
+ } else {
+ m_tool->handleMousePress(unknownTime, height,
+ staffNo, e, el);
+ }
+ }
+}
+
+void NotationView::slotNonNotationItemPressed(QMouseEvent *e, QCanvasItem *it)
+{
+ if (e->type() != QEvent::MouseButtonDblClick)
+ return ;
+
+ Staff *staff = getStaffForCanvasCoords(e->x(), e->y());
+ if (!staff)
+ return ;
+
+ NOTATION_DEBUG << "NotationView::slotNonNotationItemPressed(doubly)" << endl;
+
+ if (dynamic_cast<QCanvasStaffNameSprite *>(it)) {
+
+ std::string name =
+ staff->getSegment().getComposition()->
+ getTrackById(staff->getSegment().getTrack())->getLabel();
+
+ bool ok = false;
+ QRegExpValidator validator(QRegExp(".*"), this); // empty is OK
+
+ QString newText = KLineEditDlg::getText(QString("Change staff name"),
+ QString("Enter new staff name"),
+ strtoqstr(name),
+ &ok,
+ this,
+ &validator);
+
+ if (ok) {
+ addCommandToHistory(new RenameTrackCommand
+ (staff->getSegment().getComposition(),
+ staff->getSegment().getTrack(),
+ qstrtostr(newText)));
+
+ emit staffLabelChanged(staff->getSegment().getTrack(), newText);
+ }
+
+ } else if (dynamic_cast<QCanvasTimeSigSprite *>(it)) {
+
+ double layoutX = (dynamic_cast<QCanvasTimeSigSprite *>(it))->getLayoutX();
+ emit editTimeSignature(m_hlayout->getTimeForX(layoutX));
+ }
+}
+
+void NotationView::slotTextItemPressed(QMouseEvent *e, QCanvasItem *it)
+{
+ if (e->type() != QEvent::MouseButtonDblClick)
+ return ;
+
+ if (it == m_title) {
+ emit editMetadata(strtoqstr(CompositionMetadataKeys::Title.getName()));
+ } else if (it == m_subtitle) {
+ emit editMetadata(strtoqstr(CompositionMetadataKeys::Subtitle.getName()));
+ } else if (it == m_composer) {
+ emit editMetadata(strtoqstr(CompositionMetadataKeys::Composer.getName()));
+ } else if (it == m_copyright) {
+ emit editMetadata(strtoqstr(CompositionMetadataKeys::Copyright.getName()));
+ } else {
+ return ;
+ }
+
+ positionStaffs();
+}
+
+void NotationView::slotMouseMoved(QMouseEvent *e)
+{
+ if (activeItem()) {
+ activeItem()->handleMouseMove(e);
+ updateView();
+ } else {
+ int follow = m_tool->handleMouseMove(0, 0, // unknown time and height
+ e);
+
+ if (getCanvasView()->isTimeForSmoothScroll()) {
+
+ if (follow & RosegardenCanvasView::FollowHorizontal) {
+ getCanvasView()->slotScrollHorizSmallSteps(e->x());
+ }
+
+ if (follow & RosegardenCanvasView::FollowVertical) {
+ getCanvasView()->slotScrollVertSmallSteps(e->y());
+ }
+
+ }
+ }
+}
+
+void NotationView::slotMouseReleased(QMouseEvent *e)
+{
+ if (activeItem()) {
+ activeItem()->handleMouseRelease(e);
+ setActiveItem(0);
+ updateView();
+ } else
+ m_tool->handleMouseRelease(0, 0, // unknown time and height
+ e);
+}
+
+void
+NotationView::slotHoveredOverNoteChanged(const QString &noteName)
+{
+ m_hoveredOverNoteName->setText(QString(" ") + noteName);
+}
+
+void
+NotationView::slotHoveredOverAbsoluteTimeChanged(unsigned int time)
+{
+ timeT t = time;
+ RealTime rt =
+ getDocument()->getComposition().getElapsedRealTime(t);
+ long ms = rt.msec();
+
+ int bar, beat, fraction, remainder;
+ getDocument()->getComposition().getMusicalTimeForAbsoluteTime
+ (t, bar, beat, fraction, remainder);
+
+ // QString message;
+ // QString format("%ld (%ld.%03lds)");
+ // format = i18n("Time: %1").arg(format);
+ // message.sprintf(format, t, rt.sec, ms);
+
+ QString message = i18n("Time: %1 (%2.%3s)")
+ .arg(QString("%1-%2-%3-%4")
+ .arg(QString("%1").arg(bar + 1).rightJustify(3, '0'))
+ .arg(QString("%1").arg(beat).rightJustify(2, '0'))
+ .arg(QString("%1").arg(fraction).rightJustify(2, '0'))
+ .arg(QString("%1").arg(remainder).rightJustify(2, '0')))
+ .arg(rt.sec)
+ .arg(QString("%1").arg(ms).rightJustify(3, '0'));
+
+ m_hoveredOverAbsoluteTime->setText(message);
+}
+
+void
+NotationView::slotInsertableNoteEventReceived(int pitch, int velocity, bool noteOn)
+{
+ //!!! Problematic. Ideally we wouldn't insert events into windows
+ //that weren't actually visible, otherwise all hell could break
+ //loose (metaphorically speaking, I should probably add). I did
+ //think of checking isActiveWindow() and returning if the current
+ //window wasn't active, but that will prevent anyone from
+ //step-recording from e.g. vkeybd, which cannot be used without
+ //losing focus (and thus active-ness) from the Rosegarden window.
+
+ //!!! I know -- we'll keep track of which edit view (or main view,
+ //or mixer, etc) is active, and we'll only allow insertion into
+ //the most recently activated. How about that?
+
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("toggle_step_by_step"));
+ if (!action) {
+ NOTATION_DEBUG << "WARNING: No toggle_step_by_step action" << endl;
+ return ;
+ }
+ if (!action->isChecked())
+ return ;
+
+ Segment &segment = m_staffs[m_currentStaff]->getSegment();
+
+ NoteInserter *noteInserter = dynamic_cast<NoteInserter *>(m_tool);
+ if (!noteInserter) {
+ static bool showingError = false;
+ if (showingError)
+ return ;
+ showingError = true;
+ KMessageBox::sorry(this, i18n("Can't insert note: No note duration selected"));
+ showingError = false;
+ return ;
+ }
+
+ if (m_inPaintEvent) {
+ NOTATION_DEBUG << "NotationView::slotInsertableNoteEventReceived: in paint event already" << endl;
+ if (noteOn) {
+ m_pendingInsertableNotes.push_back(std::pair<int, int>(pitch, velocity));
+ }
+ return ;
+ }
+
+ // If the segment is transposed, we want to take that into
+ // account. But the note has already been played back to the user
+ // at its untransposed pitch, because that's done by the MIDI THRU
+ // code in the sequencer which has no way to know whether a note
+ // was intended for step recording. So rather than adjust the
+ // pitch for playback according to the transpose setting, we have
+ // to adjust the stored pitch in the opposite direction.
+
+ pitch -= segment.getTranspose();
+
+ // KTmpStatusMsg msg(i18n("Inserting note"), this);
+
+ // We need to ensure that multiple notes hit at once come out as
+ // chords, without imposing the interpretation that overlapping
+ // notes are always chords and without getting too involved with
+ // the actual absolute times of the notes (this is still step
+ // editing, not proper recording).
+
+ // First, if we're in chord mode, there's no problem.
+
+ static int numberOfNotesOn = 0;
+ static timeT insertionTime = getInsertionTime();
+ static time_t lastInsertionTime = 0;
+
+ if (isInChordMode()) {
+ if (!noteOn)
+ return ;
+ NOTATION_DEBUG << "Inserting note in chord at pitch " << pitch << endl;
+ noteInserter->insertNote(segment, getInsertionTime(), pitch,
+ Accidentals::NoAccidental,
+ true);
+
+ } else {
+
+ if (!noteOn) {
+ numberOfNotesOn--;
+ } else if (noteOn) {
+ // Rules:
+ //
+ // * If no other note event has turned up within half a
+ // second, insert this note and advance.
+ //
+ // * Relatedly, if this note is within half a second of
+ // the previous one, they're chords. Insert the previous
+ // one, don't advance, and use the same rules for this.
+ //
+ // * If a note event turns up before that time has elapsed,
+ // we need to wait for the note-off events: if the second
+ // note happened less than half way through the first,
+ // it's a chord.
+ //
+ // We haven't implemented these yet... For now:
+ //
+ // Rules (hjj):
+ //
+ // * The overlapping notes are always included in to a chord.
+ // This is the most convenient for step inserting of chords.
+ //
+ // * The timer resets the numberOfNotesOn, if noteOff signals were
+ // drop out for some reason (which has not been encountered yet).
+
+ time_t now;
+ time (&now);
+ double elapsed = difftime(now, lastInsertionTime);
+ time (&lastInsertionTime);
+
+ if (numberOfNotesOn <= 0 || elapsed > 10.0 ) {
+ numberOfNotesOn = 0;
+ insertionTime = getInsertionTime();
+ }
+ numberOfNotesOn++;
+
+ noteInserter->insertNote(segment, insertionTime, pitch,
+ Accidentals::NoAccidental,
+ true);
+ }
+ }
+}
+
+void
+NotationView::slotInsertableNoteOnReceived(int pitch, int velocity)
+{
+ NOTATION_DEBUG << "NotationView::slotInsertableNoteOnReceived: " << pitch << endl;
+ slotInsertableNoteEventReceived(pitch, velocity, true);
+}
+
+void
+NotationView::slotInsertableNoteOffReceived(int pitch, int velocity)
+{
+ NOTATION_DEBUG << "NotationView::slotInsertableNoteOffReceived: " << pitch << endl;
+ slotInsertableNoteEventReceived(pitch, velocity, false);
+}
+
+void
+NotationView::slotInsertableTimerElapsed()
+{}
+
+void
+NotationView::slotToggleStepByStep()
+{
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("toggle_step_by_step"));
+ if (!action) {
+ NOTATION_DEBUG << "WARNING: No toggle_step_by_step action" << endl;
+ return ;
+ }
+ if (action->isChecked()) { // after toggling, that is
+ emit stepByStepTargetRequested(this);
+ } else {
+ emit stepByStepTargetRequested(0);
+ }
+}
+
+void
+NotationView::slotStepByStepTargetRequested(QObject *obj)
+{
+ KToggleAction *action = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("toggle_step_by_step"));
+ if (!action) {
+ NOTATION_DEBUG << "WARNING: No toggle_step_by_step action" << endl;
+ return ;
+ }
+ action->setChecked(obj == this);
+}
+
+void
+NotationView::slotCheckRendered(double cx0, double cx1)
+{
+ // NOTATION_DEBUG << "slotCheckRendered(" << cx0 << "," << cx1 << ")" << endl;
+
+ bool something = false;
+
+ for (size_t i = 0; i < m_staffs.size(); ++i) {
+
+ LinedStaff *staff = m_staffs[i];
+
+ LinedStaff::LinedStaffCoords cc0 = staff->getLayoutCoordsForCanvasCoords
+ (cx0, 0);
+
+ LinedStaff::LinedStaffCoords cc1 = staff->getLayoutCoordsForCanvasCoords
+ (cx1, staff->getTotalHeight() + staff->getY());
+
+ timeT t0 = m_hlayout->getTimeForX(cc0.first);
+ timeT t1 = m_hlayout->getTimeForX(cc1.first);
+
+ if (dynamic_cast<NotationStaff *>(staff)->checkRendered(t0, t1)) {
+ something = true; //!!!
+ }
+ }
+
+ if (something) {
+ emit renderComplete();
+ if (m_renderTimer)
+ delete m_renderTimer;
+ m_renderTimer = new QTimer(this);
+ connect(m_renderTimer, SIGNAL(timeout()), SLOT(slotRenderSomething()));
+ m_renderTimer->start(0, true);
+ }
+
+ if (m_deferredCursorMove != NoCursorMoveNeeded)
+ doDeferredCursorMove();
+}
+
+void
+NotationView::slotRenderSomething()
+{
+ delete m_renderTimer;
+ m_renderTimer = 0;
+ static clock_t lastWork = 0;
+
+ clock_t now = clock();
+ long elapsed = ((now - lastWork) * 1000 / CLOCKS_PER_SEC);
+ if (elapsed < 70) {
+ m_renderTimer = new QTimer(this);
+ connect(m_renderTimer, SIGNAL(timeout()), SLOT(slotRenderSomething()));
+ m_renderTimer->start(0, true);
+ return ;
+ }
+ lastWork = now;
+
+ for (size_t i = 0; i < m_staffs.size(); ++i) {
+
+ if (m_staffs[i]->doRenderWork(m_staffs[i]->getSegment().getStartTime(),
+ m_staffs[i]->getSegment().getEndTime())) {
+ m_renderTimer = new QTimer(this);
+ connect(m_renderTimer, SIGNAL(timeout()), SLOT(slotRenderSomething()));
+ m_renderTimer->start(0, true);
+ return ;
+ }
+ }
+
+ PixmapArrayGC::deleteAll();
+ NOTATION_DEBUG << "NotationView::slotRenderSomething: updating thumbnails" << endl;
+ updateThumbnails(true);
+
+ // Update track headers when rendering is done
+ // (better late than never)
+ m_headersGroup->slotUpdateAllHeaders(getCanvasLeftX(), 0, true);
+ m_headersGroupView->setContentsPos(getCanvasView()->contentsX(),
+ getCanvasView()->contentsY());
+}
+
+NotationCanvasView* NotationView::getCanvasView()
+{
+ return dynamic_cast<NotationCanvasView *>(m_canvasView);
+}
+
+void
+NotationView::slotVerticalScrollHeadersGroup(int y)
+{
+ m_headersGroupView->setContentsPos(0, y);
+}
+
+void
+NotationView::slotShowHeadersGroup()
+{
+ m_showHeadersGroup = HeadersGroup::ShowAlways;
+ showHeadersGroup();
+
+ // Disable menu entry when headers are shown
+ m_showHeadersMenuEntry->setEnabled(false);
+}
+
+void
+NotationView::slotHideHeadersGroup()
+{
+ m_showHeadersGroup = HeadersGroup::ShowNever;
+ hideHeadersGroup();
+
+ // Enable menu entry when headers are hidden
+ m_showHeadersMenuEntry->setEnabled(true);
+}
+
+void
+NotationView::showHeadersGroup()
+{
+ if (m_headersGroupView && (m_pageMode == LinedStaff::LinearMode)) {
+ m_headersGroupView->show();
+ m_headersTopFrame->show();
+ m_rulerBoxFiller->show();
+ }
+}
+
+void
+NotationView::hideHeadersGroup()
+{
+ if (m_headersGroupView) {
+ m_headersGroupView->hide();
+ m_headersTopFrame->hide();
+ m_rulerBoxFiller->hide();
+ }
+}
+
+void
+NotationView::slotUpdateHeaders(int x, int y)
+{
+ m_headersGroup->slotUpdateAllHeaders(x, y);
+ m_headersGroupView->setContentsPos(x, y);
+}
+
+void
+NotationView::slotHeadersWidthChanged(int w)
+{
+ m_headersTopFrame->setFixedWidth(w);
+ m_rulerBoxFiller->setFixedWidth(w);
+ m_canvasView->updateLeftWidgetGeometry();
+}
+
+
+int
+NotationView::getCanvasVisibleWidth()
+{
+ if (getCanvasView()) {
+ return getCanvasView()->visibleWidth();
+ } else {
+ return -1;
+ }
+}
+
+int
+NotationView::getHeadersTopFrameMinWidth()
+{
+ /// TODO : use a real button width got from a real button
+
+ // 2 buttons (2 x 24) + 2 margins (2 x 4) + buttons spacing (4)
+ return 4 + 24 + 4 + 24 + 4;
+}
+
+}
+#include "NotationView.moc"
diff --git a/src/gui/editors/notation/NotationView.h b/src/gui/editors/notation/NotationView.h
new file mode 100644
index 0000000..7678f8a
--- /dev/null
+++ b/src/gui/editors/notation/NotationView.h
@@ -0,0 +1,1131 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTATIONVIEW_H_
+#define _RG_NOTATIONVIEW_H_
+
+#include "base/NotationTypes.h"
+#include "base/Track.h"
+#include "gui/general/EditView.h"
+#include "gui/general/LinedStaff.h"
+#include "gui/general/LinedStaffManager.h"
+#include "NotationProperties.h"
+#include "NotationCanvasView.h"
+#include <string>
+#include <kprocess.h>
+#include <ktempfile.h>
+#include <qmap.h>
+#include <qsize.h>
+#include <qstring.h>
+#include <vector>
+#include "base/Event.h"
+#include "gui/general/ClefIndex.h"
+
+
+class QWidget;
+class QTimer;
+class QPaintEvent;
+class QObject;
+class QMouseEvent;
+class QLabel;
+class QCursor;
+class QCanvasItem;
+class QCanvas;
+class KProgress;
+class KComboBox;
+class KActionMenu;
+class KAction;
+
+
+namespace Rosegarden
+{
+
+class Staff;
+class Segment;
+class ScrollBoxDialog;
+class RulerScale;
+class RosegardenGUIDoc;
+class RawNoteRuler;
+class ProgressDialog;
+class ProgressBar;
+class NotePixmapFactory;
+class NotationVLayout;
+class NotationStaff;
+class NotationHLayout;
+class NotationElement;
+class NoteActionData;
+class NoteActionDataMap;
+class MarkActionData;
+class MarkActionDataMap;
+class NoteChangeActionData;
+class NoteChangeActionDataMap;
+class Key;
+class EventSelection;
+class Event;
+class Clef;
+class ChordNameRuler;
+class QDeferScrollView;
+class HeadersGroup;
+
+
+/**
+ * NotationView is a view for one or more Staff objects, each of
+ * which contains the notation data associated with a Segment.
+ * NotationView owns the Staff objects it displays.
+ *
+ * This class manages the relationship between NotationHLayout/
+ * NotationVLayout and Staff data, as well as using rendering the
+ * actual notes (using NotePixmapFactory to generate the pixmaps).
+ */
+
+class NotationView : public EditView,
+ public LinedStaffManager
+{
+ friend class NoteInserter;
+ friend class ClefInserter;
+ friend class NotationEraser;
+ friend class NotationSelectionPaster;
+ friend class LilyPondExporter;
+
+ Q_OBJECT
+
+public:
+ explicit NotationView(RosegardenGUIDoc *doc,
+ std::vector<Segment *> segments,
+ QWidget *parent,
+ bool showProgressive); // update during initial render?
+
+ /**
+ * Constructor for printing only. If parent is provided, a
+ * progress dialog will be shown -- otherwise not. If another
+ * NotationView is provided, the fonts and other settings used
+ * for printing will be taken from that view.
+ */
+ explicit NotationView(RosegardenGUIDoc *doc,
+ std::vector<Segment *> segments,
+ QWidget *parent,
+ NotationView *referenceView);
+
+ ~NotationView();
+
+// void initialLayout();
+
+ /// constructed successfully? (main reason it might not is user hit Cancel)
+ bool isOK() const { return m_ok; }
+
+ /**
+ * Return the view-local PropertyName definitions for this view
+ */
+ const NotationProperties &getProperties() const;
+
+ /// Return the number of staffs
+ int getStaffCount() { return m_staffs.size(); }
+
+ /// Return a pointer to the staff at the specified index
+ Staff *getStaff(int i) {
+ return getLinedStaff(i);
+ }
+
+ /// Return a pointer to the staff corresponding to the given segment
+ Staff *getStaff(const Segment &segment) {
+ return getLinedStaff(segment);
+ }
+
+ /// Return a pointer to the staff at the specified index
+ LinedStaff *getLinedStaff(int i);
+
+ /// Return a pointer to the staff corresponding to the given segment
+ LinedStaff *getLinedStaff(const Segment &segment);
+
+ /// Return a pointer to the staff at the specified index
+ NotationStaff *getNotationStaff(int i) {
+ if (i >= 0 && unsigned(i) < m_staffs.size()) return m_staffs[i];
+ else return 0;
+ }
+
+ /// Return a pointer to the staff corresponding to the given segment
+ NotationStaff *getNotationStaff(const Segment &segment);
+
+ /// Return true if the staff at the specified index is the current one
+ bool isCurrentStaff(int i);
+
+ QCanvas* canvas() { return getCanvasView()->canvas(); }
+
+ void setCanvasCursor(const QCursor &cursor) {
+ getCanvasView()->viewport()->setCursor(cursor);
+ }
+
+ void setHeightTracking(bool t) {
+ getCanvasView()->setHeightTracking(t);
+ }
+
+ /**
+ * Returns true if the view is actually for printing
+ */
+ bool isInPrintMode() { return m_printMode; }
+
+ /**
+ * Set the note or rest selected by the user from the toolbars
+ */
+ void setCurrentSelectedNote(const char *pixmapName,
+ bool isRest, Note::Type,
+ int dots = 0);
+
+ /**
+ * Set the note or rest selected by the user from the toolbars
+ */
+ void setCurrentSelectedNote(const NoteActionData &);
+
+ /**
+ * Discover whether chord-mode insertions are enabled (as opposed
+ * to the default melody-mode)
+ */
+ bool isInChordMode();
+
+ /**
+ * Discover whether triplet-mode insertions are enabled
+ */
+ bool isInTripletMode();
+
+ /**
+ * Discover whether grace-mode insertions are enabled
+ */
+ bool isInGraceMode();
+
+ /**
+ * Discover whether annotations are being displayed or not
+ */
+ bool areAnnotationsVisible() { return m_annotationsVisible; }
+
+ /**
+ * Discover whether LilyPond directives are being displayed or not
+ */
+ bool areLilyPondDirectivesVisible() { return m_lilyPondDirectivesVisible; }
+
+ /**
+ * Set the current event selection.
+ *
+ * If preview is true, sound the selection as well.
+ *
+ * If redrawNow is true, recolour the elements on the canvas;
+ * otherwise just line up a refresh for the next paint event.
+ *
+ * (If the selection has changed as part of a modification to a
+ * segment, redrawNow should be unnecessary and undesirable, as a
+ * paint event will occur in the next event loop following the
+ * command invocation anyway.)
+ */
+ virtual void setCurrentSelection(EventSelection*,
+ bool preview = false,
+ bool redrawNow = false);
+
+ /**
+ * Set the current event selection to a single event
+ */
+ void setSingleSelectedEvent(int staffNo,
+ Event *event,
+ bool preview = false,
+ bool redrawNow = false);
+
+ /**
+ * Set the current event selection to a single event
+ */
+ void setSingleSelectedEvent(Segment &segment,
+ Event *event,
+ bool preview = false,
+ bool redrawNow = false);
+
+ /**
+ * Show and sound the given note. The height is used for display,
+ * the pitch for performance, so the two need not correspond (e.g.
+ * under ottava there may be octave differences).
+ */
+ void showPreviewNote(int staffNo, double layoutX,
+ int pitch, int height,
+ const Note &note,
+ bool grace,
+ int velocity = -1);
+
+ /// Remove any visible preview note
+ void clearPreviewNote();
+
+ /// Sound the given note
+ void playNote(Segment &segment, int pitch, int velocity = -1);
+
+ /// Switches between page- and linear- layout modes
+ void setPageMode(LinedStaff::PageMode mode);
+
+ /// Returns the page width according to the layout mode (page/linear)
+ int getPageWidth();
+
+ /// Returns the page height according to the layout mode (page/linear)
+ int getPageHeight();
+
+ /// Returns the margins within the page (zero if not in MultiPageMode)
+ void getPageMargins(int &left, int &top);
+
+ /// Scrolls the view such that the given time is centered
+ void scrollToTime(timeT t);
+
+ NotePixmapFactory *getNotePixmapFactory() const {
+ return m_notePixmapFactory;
+ }
+
+ virtual void refreshSegment(Segment *segment,
+ timeT startTime = 0,
+ timeT endTime = 0);
+
+ /**
+ * From LinedStaffManager
+ */
+ virtual LinedStaff* getStaffForCanvasCoords(int x, int y) const;
+
+
+ /**
+ * Overridden from EditView
+ */
+ virtual void updateView();
+
+ /**
+ * Render segments on printing painter. This uses the current
+ * font size and layout, rather than the optimal ones for the
+ * printer configuration (notation editing is not quite WYSIWYG,
+ * and we may be in a non-page mode).
+ *
+ * To print optimally use slotFilePrint, which will create
+ * another NotationView with the optimal settings and call print
+ * on that.
+ */
+ virtual void print(bool previewOnly = false);
+
+ /**
+ * Return X of the left of the canvas visible part.
+ */
+ double getCanvasLeftX() { return getCanvasView()->contentsX(); }
+
+ virtual RulerScale* getHLayout();
+
+ /**
+ * Return the notation window width
+ */
+ int getCanvasVisibleWidth();
+
+ /**
+ * Return the minimal width which shall be allocated to
+ * the track headers top frame.
+ * (The width of the close button + the width of an info
+ * button still to come).
+ */
+ int getHeadersTopFrameMinWidth();
+
+public slots:
+
+ /**
+ * Print the current set of segments, by creating another
+ * NotationView with the printing configuration but the same
+ * segments, font etc as this view and asking it to print.
+ */
+ void slotFilePrint();
+
+ /**
+ * Preview the current set of segments, by creating another
+ * NotationView with the printing configuration but the same
+ * segments, font etc as this view and asking it to preview.
+ */
+ void slotFilePrintPreview();
+
+ /**
+ * export a LilyPond file
+ */
+ bool exportLilyPondFile(QString url, bool forPreview = false);
+
+ /**
+ * Export to a temporary file and process
+ */
+ void slotPrintLilyPond();
+ void slotPreviewLilyPond();
+ void slotLilyPondViewProcessExited(KProcess *);
+
+ /**
+ * put the marked text/object into the clipboard and remove it
+ * from the document
+ */
+ void slotEditCut();
+
+ /**
+ * put the marked text/object into the clipboard
+ */
+ void slotEditCopy();
+
+ /**
+ * paste the clipboard into the document
+ */
+ void slotEditPaste();
+
+ /**
+ * cut the selection and close the gap, moving subsequent events
+ * towards the start of the segment
+ */
+ void slotEditCutAndClose();
+
+ /**
+ * paste the clipboard into the document, offering a choice for how
+ */
+ void slotEditGeneralPaste();
+
+ /**
+ * delete the selection (cut without the copy)
+ */
+ void slotEditDelete();
+
+ /**
+ * move the selection to the staff above
+ */
+ void slotMoveEventsUpStaff();
+
+ /**
+ * move the selection to the staff below
+ */
+ void slotMoveEventsDownStaff();
+
+ /**
+ * toggles the tools toolbar
+ */
+ void slotToggleToolsToolBar();
+
+ /**
+ * toggles the notes toolbar
+ */
+ void slotToggleNotesToolBar();
+
+ /**
+ * toggles the rests toolbar
+ */
+ void slotToggleRestsToolBar();
+
+ /**
+ * toggles the accidentals toolbar
+ */
+ void slotToggleAccidentalsToolBar();
+
+ /**
+ * toggles the clefs toolbar
+ */
+ void slotToggleClefsToolBar();
+
+ /**
+ * toggles the marks toolbar
+ */
+ void slotToggleMarksToolBar();
+
+ /**
+ * toggles the group toolbar
+ */
+ void slotToggleGroupToolBar();
+
+ /**
+ * toggles the layout toolbar
+ */
+ void slotToggleLayoutToolBar();
+
+ /**
+ * toggles the transport toolbar
+ */
+ void slotToggleTransportToolBar();
+
+ /**
+ * toggles the meta toolbar
+ */
+ void slotToggleMetaToolBar();
+
+ /// note switch slot
+ void slotNoteAction();
+
+ /// switch to last selected note
+ void slotLastNoteAction();
+
+ /// accidental switch slots
+ void slotNoAccidental();
+ void slotFollowAccidental();
+ void slotSharp();
+ void slotFlat();
+ void slotNatural();
+ void slotDoubleSharp();
+ void slotDoubleFlat();
+
+ /// clef switch slots
+ void slotTrebleClef();
+ void slotAltoClef();
+ void slotTenorClef();
+ void slotBassClef();
+
+ /// text tool
+ void slotText();
+
+ /// guitar chord tool
+ void slotGuitarChord();
+
+ /// editing tools
+ void slotEraseSelected();
+ void slotSelectSelected();
+
+ void slotToggleStepByStep();
+
+ /// status stuff
+ void slotUpdateInsertModeStatus();
+ void slotUpdateAnnotationsStatus();
+ void slotUpdateLilyPondDirectivesStatus();
+
+ /// edit menu
+ void slotPreviewSelection();
+ void slotClearLoop();
+ void slotClearSelection();
+ void slotEditSelectFromStart();
+ void slotEditSelectToEnd();
+ void slotEditSelectWholeStaff();
+ void slotFilterSelection();
+
+ /// view menu
+ void slotLinearMode();
+ void slotContinuousPageMode();
+ void slotMultiPageMode();
+ void slotToggleChordsRuler();
+ void slotToggleRawNoteRuler();
+ void slotToggleTempoRuler();
+ void slotToggleAnnotations();
+ void slotToggleLilyPondDirectives();
+ void slotEditLyrics();
+
+ /// Notation header slots
+ void slotShowHeadersGroup();
+ void slotHideHeadersGroup();
+ void slotVerticalScrollHeadersGroup(int);
+ void slotUpdateHeaders(int x, int y);
+ void slotHeadersWidthChanged(int w);
+
+ /// Adjust notation header view when bottom ruler added or removed
+ void slotCanvasBottomWidgetHeightChanged(int);
+
+ /// group slots
+ void slotGroupBeam();
+ void slotGroupAutoBeam();
+ void slotGroupBreak();
+ void slotGroupSimpleTuplet();
+ void slotGroupGeneralTuplet();
+ void slotGroupTuplet(bool simple);
+ void slotGroupUnTuplet();
+ void slotGroupSlur();
+ void slotGroupPhrasingSlur();
+ void slotGroupGlissando();
+ void slotGroupCrescendo();
+ void slotGroupDecrescendo();
+ void slotGroupMakeChord();
+ void slotGroupOctave2Up();
+ void slotGroupOctaveUp();
+ void slotGroupOctaveDown();
+ void slotGroupOctave2Down();
+ void slotAddIndication(std::string type, QString cat);
+
+ /// transforms slots
+ void slotTransformsNormalizeRests();
+ void slotTransformsCollapseRests();
+ void slotTransformsCollapseNotes();
+ void slotTransformsTieNotes();
+ void slotTransformsUntieNotes();
+ void slotTransformsMakeNotesViable();
+ void slotTransformsDeCounterpoint();
+ void slotTransformsStemsUp();
+ void slotTransformsStemsDown();
+ void slotTransformsRestoreStems();
+ void slotTransformsSlursAbove();
+ void slotTransformsSlursBelow();
+ void slotTransformsRestoreSlurs();
+ void slotTransformsTiesAbove();
+ void slotTransformsTiesBelow();
+ void slotTransformsRestoreTies();
+ void slotTransformsQuantize();
+ void slotTransformsFixQuantization();
+ void slotTransformsRemoveQuantization();
+ void slotTransformsInterpret();
+
+ void slotRespellDoubleFlat();
+ void slotRespellFlat();
+ void slotRespellNatural();
+ void slotRespellSharp();
+ void slotRespellDoubleSharp();
+ void slotRespellUp();
+ void slotRespellDown();
+ void slotRespellRestore();
+ void slotShowCautionary();
+ void slotCancelCautionary();
+
+ void slotSetStyleFromAction();
+ void slotInsertNoteFromAction();
+ void slotInsertRest();
+ void slotSwitchFromRestToNote();
+ void slotSwitchFromNoteToRest();
+ void slotToggleDot();
+
+ void slotAddMark();
+ void slotMarksAddTextMark();
+ void slotMarksAddFingeringMark();
+ void slotMarksAddFingeringMarkFromAction();
+ void slotMarksRemoveMarks();
+ void slotMarksRemoveFingeringMarks();
+ void slotMakeOrnament();
+ void slotUseOrnament();
+ void slotRemoveOrnament();
+
+ void slotNoteChangeAction();
+ void slotSetNoteDurations(Note::Type, bool notationOnly);
+ void slotAddDot();
+ void slotAddDotNotationOnly();
+
+ void slotAddSlashes();
+
+ void slotEditAddClef();
+ void slotEditAddKeySignature();
+ void slotEditAddSustainDown();
+ void slotEditAddSustainUp();
+ void slotEditAddSustain(bool down);
+ void slotEditTranspose();
+ void slotEditSwitchPreset();
+ void slotEditElement(NotationStaff *, NotationElement *, bool advanced);
+
+ void slotFinePositionLeft();
+ void slotFinePositionRight();
+ void slotFinePositionUp();
+ void slotFinePositionDown();
+ void slotFinePositionRestore();
+
+ void slotMakeVisible();
+ void slotMakeInvisible();
+
+ void slotDebugDump();
+
+ /// Canvas actions slots
+
+ /**
+ * Called when a mouse press occurred on a notation element
+ * or somewhere on a staff
+ */
+ void slotItemPressed(int height, int staffNo, QMouseEvent*, NotationElement*);
+
+ /**
+ * Called when a mouse press occurred on a non-notation element
+ */
+ void slotNonNotationItemPressed(QMouseEvent *e, QCanvasItem *i);
+
+ /**
+ * Called when a mouse press occurred on a QCanvasText
+ */
+ void slotTextItemPressed(QMouseEvent *e, QCanvasItem *i);
+
+ void slotMouseMoved(QMouseEvent*);
+ void slotMouseReleased(QMouseEvent*);
+
+ /**
+ * Called when the mouse cursor moves over a different height on
+ * the staff
+ *
+ * @see NotationCanvasView#hoveredOverNoteChange()
+ */
+ void slotHoveredOverNoteChanged(const QString&);
+
+ /**
+ * Called when the mouse cursor moves over a note which is at a
+ * different time on the staff
+ *
+ * @see NotationCanvasView#hoveredOverAbsoluteTimeChange()
+ */
+ void slotHoveredOverAbsoluteTimeChanged(unsigned int);
+
+ /**
+ * Set the time pointer position during playback (purely visual,
+ * doesn't affect playback). This is also at liberty to highlight
+ * some notes, if it so desires...
+ */
+ void slotSetPointerPosition(timeT position);
+
+ /**
+ * As above, but with the ability to specify whether to scroll or
+ * not to follow the pointer (above method uses the play tracking
+ * setting to determine that)
+ */
+ void slotSetPointerPosition(timeT position, bool scroll);
+
+ /**
+ * Update the recording segment if it's one of the ones in the
+ * view
+ */
+ void slotUpdateRecordingSegment(Segment *recordingSegment,
+ timeT updatedFrom);
+
+ /// Set the current staff to the one containing the given canvas Y coord
+ void slotSetCurrentStaff(double canvasX, int canvasY);
+
+ /// Set the current staff to that with the given id
+ void slotSetCurrentStaff(int staffNo);
+
+ /**
+ * Set the insert cursor position (from the top LoopRuler).
+ * If the segment has recently been changed and no refresh has
+ * occurred since, pass updateNow false; then the move will
+ * happen on the next update.
+ */
+ void slotSetInsertCursorPosition(timeT position,
+ bool scroll, bool updateNow);
+
+ virtual void slotSetInsertCursorPosition(timeT position) {
+ slotSetInsertCursorPosition(position, true, true);
+ }
+
+ /// Set the insert cursor position from a mouse event location
+ void slotSetInsertCursorPosition(double canvasX, int canvasY,
+ bool scroll, bool updateNow);
+
+ void slotSetInsertCursorPosition(double canvasX, int canvasY) {
+ slotSetInsertCursorPosition(canvasX, canvasY, true, true);
+ }
+
+ /**
+ * Set the insert cursor position and scroll so it's at given point.
+ * If the segment has recently been changed and no refresh has
+ * occurred since, pass updateNow false; then the move will
+ * happen on the next update.
+ */
+ void slotSetInsertCursorAndRecentre(timeT position,
+ double cx, int cy,
+ bool updateNow = true);
+
+ void slotSetInsertCursorAndRecentre(timeT position,
+ double cx, double cy) {
+ slotSetInsertCursorAndRecentre(position, cx, static_cast<int>(cy), true);
+ }
+
+ /// Set insert cursor to playback pointer position
+ void slotJumpCursorToPlayback();
+
+ /// Set playback pointer to insert cursor position (affects playback)
+ void slotJumpPlaybackToCursor();
+
+ /// Toggle tracking with the position pointer during playback
+ void slotToggleTracking();
+
+ /// Change the current staff to the one preceding the current one
+ void slotCurrentStaffUp();
+
+ /// Change the current staff to the one following the current one
+ void slotCurrentStaffDown();
+
+ /// Change the current segment to the one following the current one
+ void slotCurrentSegmentPrior();
+
+ /// Change the current segment to the one preceding the current one
+ void slotCurrentSegmentNext();
+
+ /// Changes the font of the staffs on the view, gets font name from sender
+ void slotChangeFontFromAction();
+
+ /// Changes the font of the staffs on the view
+ void slotChangeFont(std::string newFont);
+
+ /// Changes the font and font size of the staffs on the view
+ void slotChangeFont(std::string newFont, int newSize);
+
+ /// Changes the font of the staffs on the view
+ void slotChangeFont(const QString &newFont);
+
+ /// Changes the font size of the staffs on the view
+ void slotChangeFontSize(int newSize);
+
+ /// Changes the font size of the staffs on the view, gets size from sender
+ void slotChangeFontSizeFromAction();
+
+ /// Changes the font size of the staffs on the view to the nth size in the available size list
+ void slotChangeFontSizeFromStringValue(const QString&);
+
+ /// Changes to the next font size up
+ void slotZoomIn();
+
+ /// Changes to the next font size down
+ void slotZoomOut();
+
+ /// Changes the hlayout spacing of the staffs on the view
+ void slotChangeSpacing(int newSpacing);
+
+ /// Changes the hlayout spacing of the staffs on the view
+ void slotChangeSpacingFromStringValue(const QString&);
+
+ /// Changes the hlayout spacing of the staffs on the view
+ void slotChangeSpacingFromAction();
+
+ /// Changes the hlayout proportion of the staffs on the view
+ void slotChangeProportion(int newProportion);
+
+ /// Changes the hlayout proportion of the staffs on the view
+ void slotChangeProportionFromIndex(int newProportionIndex);
+
+ /// Changes the hlayout proportion of the staffs on the view
+ void slotChangeProportionFromAction();
+
+ /// Note-on received asynchronously -- consider step-by-step editing
+ void slotInsertableNoteOnReceived(int pitch, int velocity);
+
+ /// Note-off received asynchronously -- consider step-by-step editing
+ void slotInsertableNoteOffReceived(int pitch, int velocity);
+
+ /// Note-on or note-off received asynchronously -- as above
+ void slotInsertableNoteEventReceived(int pitch, int velocity, bool noteOn);
+
+ /// A timer set when a note-on event was received has elapsed
+ void slotInsertableTimerElapsed();
+
+ /// The given QObject has originated a step-by-step-editing request
+ void slotStepByStepTargetRequested(QObject *);
+
+ /// Do on-demand rendering for a region.
+ void slotCheckRendered(double cx0, double cx1);
+
+ /// Do some background rendering work.
+ void slotRenderSomething();
+
+ void slotSetOperationNameAndStatus(QString);
+
+ // Update notation view based on track/staff name change
+ void slotUpdateStaffName();
+
+ // LilyPond Directive slots
+ void slotBeginLilyPondRepeat();
+
+signals:
+ /**
+ * Emitted when the note selected in the palette changes
+ */
+ void changeCurrentNote(bool isRest, Note::Type);
+
+ /**
+ * Emitted when a new accidental has been choosen by the user
+ */
+ void changeAccidental(Accidental, bool follow);
+
+ /**
+ * Emitted when the selection has been cut or copied
+ *
+ * @see NotationSelector#hideSelection
+ */
+ void usedSelection();
+
+ void play();
+ void stop();
+ void fastForwardPlayback();
+ void rewindPlayback();
+ void fastForwardPlaybackToEnd();
+ void rewindPlaybackToBeginning();
+ void jumpPlaybackTo(timeT);
+ void panic();
+
+ /// progress Report
+ void setProgress(int);
+ void incrementProgress(int);
+ void setOperationName(QString);
+
+ void stepByStepTargetRequested(QObject *);
+
+ void renderComplete();
+
+ void editTimeSignature(timeT);
+
+ void editMetadata(QString);
+
+ void editTriggerSegment(int);
+
+ void staffLabelChanged(TrackId id, QString label);
+
+protected:
+
+ virtual void paintEvent(QPaintEvent* e);
+
+ /**
+ * init the action maps for notes, marks etc
+ */
+ void initActionDataMaps();
+
+protected slots:
+ /**
+ * save general Options like all bar positions and status as well
+ * as the geometry and the recent file list to the configuration
+ * file
+ */
+ virtual void slotSaveOptions();
+
+protected:
+
+ /**
+ * read general Options again and initialize all variables like the recent file list
+ */
+ virtual void readOptions();
+
+ void setOneToolbar(const char *actionName,
+ const char *toolbarName);
+
+ /**
+ * create menus and toolbars
+ */
+ virtual void setupActions();
+
+ /**
+ * create or re-initialise (after font change) the font size menu
+ */
+ virtual void setupFontSizeMenu(std::string oldFontName = "");
+
+ /**
+ * Set KDE3+ menu states based on the current selection
+ */
+ virtual void setMenuStates();
+
+ /**
+ * setup status bar
+ */
+ virtual void initStatusBar();
+
+ /**
+ * Place the staffs at the correct x & y coordinates (before layout)
+ */
+ void positionStaffs();
+
+ /**
+ * Place the page pixmaps (if any) at the correct x & y
+ * coordinates (after layout)
+ */
+ void positionPages();
+
+ /**
+ * Update the panner thumbnail images. If complete is true,
+ * copy the entire mini-canvas.
+ */
+ void updateThumbnails(bool complete);
+
+ /**
+ * setup the layout/font toolbar
+ */
+ void initLayoutToolbar();
+
+ /**
+ * Helper function to toggle a toolbar given its name
+ * If \a force point to a bool, then the bool's value
+ * is used to show/hide the toolbar.
+ */
+ void toggleNamedToolBar(const QString& toolBarName, bool* force = 0);
+
+ /// Calls all the relevant preparse and layout methods
+ virtual bool applyLayout(int staffNo = -1,
+ timeT startTime = 0,
+ timeT endTime = 0);
+
+ /**
+ * Readjust the size of the canvas after a layout
+ *
+ * Checks the total width computed by the horizontal layout
+ *
+ * @see NotationHLayout#getTotalWidth()
+ */
+ void readjustCanvasSize();
+
+ /**
+ * Override from EditView
+ * @see EditView#getViewSize
+ */
+ virtual QSize getViewSize();
+
+ /**
+ * Override from EditView
+ * @see EditView#setViewSize
+ */
+ virtual void setViewSize(QSize);
+
+ /**
+ * Set the note pixmap factory
+ *
+ * The previous pixmap factory is deleted
+ */
+ void setNotePixmapFactory(NotePixmapFactory*);
+
+ virtual NotationCanvasView* getCanvasView();
+
+ virtual Segment *getCurrentSegment();
+ virtual Staff *getCurrentStaff() { return getCurrentLinedStaff(); }
+ virtual LinedStaff *getCurrentLinedStaff();
+
+ virtual LinedStaff *getStaffAbove();
+ virtual LinedStaff *getStaffBelow();
+
+ virtual bool hasSegment(Segment *segment);
+
+ /**
+ * Return the time at which the insert cursor may be found.
+ */
+ virtual timeT getInsertionTime();
+
+ /**
+ * Return the time at which the insert cursor may be found,
+ * and the time signature, clef and key at that time.
+ */
+ virtual timeT getInsertionTime(Clef &clef,
+ Rosegarden::Key &key);
+
+ void doDeferredCursorMove();
+
+ void removeViewLocalProperties(Event*);
+
+ void setupProgress(KProgress*);
+ void setupProgress(ProgressDialog*);
+ void setupDefaultProgress();
+ void disconnectProgress();
+
+ /**
+ * Test whether we've had too many preview notes recently
+ */
+ bool canPreviewAnotherNote();
+
+ virtual void updateViewCaption();
+
+ void showHeadersGroup();
+ void hideHeadersGroup();
+
+
+ //--------------- Data members ---------------------------------
+
+ NotationProperties m_properties;
+
+ /// Displayed in the status bar, shows number of events selected
+ QLabel *m_selectionCounter;
+
+ /// Displayed in the status bar, shows insertion mode
+ QLabel *m_insertModeLabel;
+
+ /// Displayed in the status bar, shows when annotations are hidden
+ QLabel *m_annotationsLabel;
+
+ /// Displayed in the status bar, shows when LilyPond directives are hidden
+ QLabel *m_lilyPondDirectivesLabel;
+
+ /// Displayed in the status bar, shows progress of current operation
+ ProgressBar *m_progressBar;
+
+ /// Displayed in the status bar, holds the pixmap of the current note
+ QLabel* m_currentNotePixmap;
+
+ /// Displayed in the status bar, shows the pitch the cursor is at
+ QLabel* m_hoveredOverNoteName;
+
+ /// Displayed in the status bar, shows the absolute time the cursor is at
+ QLabel* m_hoveredOverAbsoluteTime;
+
+ std::vector<NotationStaff*> m_staffs;
+ int m_currentStaff;
+ int m_lastFinishingStaff;
+
+ QCanvasItem *m_title;
+ QCanvasItem *m_subtitle;
+ QCanvasItem *m_composer;
+ QCanvasItem *m_copyright;
+ std::vector<QCanvasItem *> m_pages;
+ std::vector<QCanvasItem *> m_pageNumbers;
+
+ timeT m_insertionTime;
+ enum DeferredCursorMoveType {
+ NoCursorMoveNeeded,
+ CursorMoveOnly,
+ CursorMoveAndMakeVisible,
+ CursorMoveAndScrollToPosition
+ };
+ DeferredCursorMoveType m_deferredCursorMove;
+ double m_deferredCursorScrollToX;
+
+ QString m_lastNoteAction;
+
+ std::string m_fontName;
+ int m_fontSize;
+ LinedStaff::PageMode m_pageMode;
+ int m_leftGutter;
+
+ NotePixmapFactory *m_notePixmapFactory;
+
+ NotationHLayout* m_hlayout;
+ NotationVLayout* m_vlayout;
+
+ ChordNameRuler *m_chordNameRuler;
+ QWidget *m_tempoRuler;
+ RawNoteRuler *m_rawNoteRuler;
+ bool m_annotationsVisible;
+ bool m_lilyPondDirectivesVisible;
+
+ KAction* m_selectDefaultNote;
+
+ typedef QMap<QString, NoteActionData *> NoteActionDataMap;
+ static NoteActionDataMap* m_noteActionDataMap;
+
+ typedef QMap<QString, NoteChangeActionData *> NoteChangeActionDataMap;
+ static NoteChangeActionDataMap* m_noteChangeActionDataMap;
+
+ typedef QMap<QString, MarkActionData *> MarkActionDataMap;
+ static MarkActionDataMap *m_markActionDataMap;
+
+ KComboBox *m_fontCombo;
+ KComboBox *m_fontSizeCombo;
+ KComboBox *m_spacingCombo;
+ KActionMenu *m_fontSizeActionMenu;
+ ScrollBoxDialog *m_pannerDialog;
+ QTimer *m_renderTimer;
+
+ bool m_playTracking;
+
+ std::vector<std::pair<int, int> > m_pendingInsertableNotes;
+
+ enum { PROGRESS_NONE,
+ PROGRESS_BAR,
+ PROGRESS_DIALOG } m_progressDisplayer;
+
+ bool m_inhibitRefresh;
+ bool m_ok;
+
+ bool m_printMode;
+ int m_printSize;
+
+ static std::map<KProcess *, KTempFile *> m_lilyTempFileMap;
+
+ int m_showHeadersGroup;
+ QDeferScrollView * m_headersGroupView;
+ HeadersGroup * m_headersGroup;
+ QFrame * m_headersTopFrame;
+
+ KAction * m_showHeadersMenuEntry;
+
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NoteCharacter.cpp b/src/gui/editors/notation/NoteCharacter.cpp
new file mode 100644
index 0000000..fdcb578
--- /dev/null
+++ b/src/gui/editors/notation/NoteCharacter.cpp
@@ -0,0 +1,133 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NoteCharacter.h"
+
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qcanvas.h>
+#include <qbitmap.h>
+
+
+namespace Rosegarden
+{
+
+NoteCharacter::NoteCharacter() :
+ m_hotspot(0, 0),
+ m_pixmap(new QPixmap()),
+ m_rep(0)
+{}
+
+NoteCharacter::NoteCharacter(QPixmap pixmap,
+ QPoint hotspot, NoteCharacterDrawRep *rep) :
+ m_hotspot(hotspot),
+ m_pixmap(new QPixmap(pixmap)),
+ m_rep(rep)
+{}
+
+NoteCharacter::NoteCharacter(const NoteCharacter &c) :
+ m_hotspot(c.m_hotspot),
+ m_pixmap(new QPixmap(*c.m_pixmap)),
+ m_rep(c.m_rep)
+{
+ // nothing else
+}
+
+NoteCharacter &
+NoteCharacter::operator=(const NoteCharacter &c)
+{
+ if (&c == this)
+ return * this;
+ m_hotspot = c.m_hotspot;
+ m_pixmap = new QPixmap(*c.m_pixmap);
+ m_rep = c.m_rep;
+ return *this;
+}
+
+NoteCharacter::~NoteCharacter()
+{
+ delete m_pixmap;
+}
+
+int
+NoteCharacter::getWidth() const
+{
+ return m_pixmap->width();
+}
+
+int
+NoteCharacter::getHeight() const
+{
+ return m_pixmap->height();
+}
+
+QPoint
+NoteCharacter::getHotspot() const
+{
+ return m_hotspot;
+}
+
+QPixmap *
+NoteCharacter::getPixmap() const
+{
+ return m_pixmap;
+}
+
+QCanvasPixmap *
+NoteCharacter::getCanvasPixmap() const
+{
+ return new QCanvasPixmap(*m_pixmap, m_hotspot);
+}
+
+void
+NoteCharacter::draw(QPainter *painter, int x, int y) const
+{
+ if (!m_rep) {
+
+ painter->drawPixmap(x, y, *m_pixmap);
+
+ } else {
+
+ NoteCharacterDrawRep a(m_rep->size());
+
+ for (unsigned int i = 0; i < m_rep->size(); ++i) {
+ QPoint p(m_rep->point(i));
+ a.setPoint(i, p.x() + x, p.y() + y);
+ }
+
+ painter->drawLineSegments(a);
+ }
+}
+
+void
+NoteCharacter::drawMask(QPainter *painter, int x, int y) const
+{
+ if (!m_rep && m_pixmap->mask()) {
+ painter->drawPixmap(x, y, *(m_pixmap->mask()));
+ }
+}
+
+}
diff --git a/src/gui/editors/notation/NoteCharacter.h b/src/gui/editors/notation/NoteCharacter.h
new file mode 100644
index 0000000..bc9359e
--- /dev/null
+++ b/src/gui/editors/notation/NoteCharacter.h
@@ -0,0 +1,93 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTECHARACTER_H_
+#define _RG_NOTECHARACTER_H_
+
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qpointarray.h>
+
+
+class QPainter;
+class QCanvasPixmap;
+
+namespace Rosegarden
+{
+
+class NoteCharacterDrawRep : public QPointArray
+{
+public:
+ NoteCharacterDrawRep(int size = 0) : QPointArray(size) { }
+};
+
+
+/**
+ * NoteCharacter knows how to draw a character from a font. It may be
+ * optimised for screen (using QPixmap underneath to produce
+ * low-resolution colour or greyscale glyphs) or printer (using some
+ * internal representation to draw in high-resolution monochrome on a
+ * print device). You can use screen characters on a printer and vice
+ * versa, but the performance and quality might not be as good.
+ *
+ * NoteCharacter objects are always constructed by the NoteFont, never
+ * directly.
+ */
+
+class NoteCharacter
+{
+public:
+ NoteCharacter();
+ NoteCharacter(const NoteCharacter &);
+ NoteCharacter &operator=(const NoteCharacter &);
+ ~NoteCharacter();
+
+ int getWidth() const;
+ int getHeight() const;
+
+ QPoint getHotspot() const;
+
+ QPixmap *getPixmap() const;
+ QCanvasPixmap *getCanvasPixmap() const;
+
+ void draw(QPainter *painter, int x, int y) const;
+ void drawMask(QPainter *painter, int x, int y) const;
+
+private:
+ friend class NoteFont;
+ NoteCharacter(QPixmap pixmap, QPoint hotspot, NoteCharacterDrawRep *rep);
+
+ QPoint m_hotspot;
+ QPixmap *m_pixmap; // I own this
+ NoteCharacterDrawRep *m_rep; // I don't own this, it's a reference to a static in the NoteFont
+};
+
+
+// Encapsulates NoteFontMap, and loads pixmaps etc on demand
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NoteCharacterNames.cpp b/src/gui/editors/notation/NoteCharacterNames.cpp
new file mode 100644
index 0000000..bcd450c
--- /dev/null
+++ b/src/gui/editors/notation/NoteCharacterNames.cpp
@@ -0,0 +1,123 @@
+// -*- c-basic-offset: 4 -*-
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NoteCharacterNames.h"
+
+namespace Rosegarden
+{
+
+namespace NoteCharacterNames
+{
+
+const CharName SHARP = "MUSIC SHARP SIGN";
+const CharName FLAT = "MUSIC FLAT SIGN";
+const CharName NATURAL = "MUSIC NATURAL SIGN";
+const CharName DOUBLE_SHARP = "MUSICAL SYMBOL DOUBLE SHARP";
+const CharName DOUBLE_FLAT = "MUSICAL SYMBOL DOUBLE FLAT";
+
+const CharName BREVE = "MUSICAL SYMBOL BREVE";
+const CharName WHOLE_NOTE = "MUSICAL SYMBOL WHOLE NOTE";
+const CharName VOID_NOTEHEAD = "MUSICAL SYMBOL VOID NOTEHEAD";
+const CharName NOTEHEAD_BLACK = "MUSICAL SYMBOL NOTEHEAD BLACK";
+
+const CharName X_NOTEHEAD = "MUSICAL SYMBOL X NOTEHEAD";
+const CharName CIRCLE_X_NOTEHEAD = "MUSICAL SYMBOL CIRCLE X NOTEHEAD";
+const CharName BREVIS = "MUSICAL SYMBOL BREVIS";
+const CharName SEMIBREVIS_WHITE = "MUSICAL SYMBOL SEMIBREVIS WHITE";
+const CharName SEMIBREVIS_BLACK = "MUSICAL SYMBOL SEMIBREVIS BLACK";
+const CharName TRIANGLE_NOTEHEAD_UP_WHITE = "MUSICAL SYMBOL TRIANGLE NOTEHEAD UP WHITE";
+const CharName TRIANGLE_NOTEHEAD_UP_BLACK = "MUSICAL SYMBOL TRIANGLE NOTEHEAD UP BLACK";
+const CharName SQUARE_NOTEHEAD_WHITE = "MUSICAL SYMBOL SQUARE NOTEHEAD WHITE";
+const CharName SQUARE_NOTEHEAD_BLACK = "MUSICAL SYMBOL SQUARE NOTEHEAD BLACK";
+
+// These two names are not valid Unicode names. They describe flags
+// that should be used to compose multi-flag notes, rather than used
+// on their own. Unicode has no code point for these, but they're
+// common in real fonts. COMBINING PARTIAL FLAG is a flag that may be
+// drawn several times to make a multi-flag note; COMBINING PARTIAL
+// FLAG FINAL may be used as the flag nearest the note head and may
+// have an additional swash. (In many fonts, the FLAG 1 character may
+// also be suitable for use as PARTIAL FLAG FINAL).
+const CharName FLAG_PARTIAL = "MUSICAL SYMBOL COMBINING PARTIAL FLAG";
+const CharName FLAG_PARTIAL_FINAL = "MUSICAL SYMBOL COMBINING PARTIAL FLAG FINAL";
+
+const CharName FLAG_1 = "MUSICAL SYMBOL COMBINING FLAG-1";
+const CharName FLAG_2 = "MUSICAL SYMBOL COMBINING FLAG-2";
+const CharName FLAG_3 = "MUSICAL SYMBOL COMBINING FLAG-3";
+const CharName FLAG_4 = "MUSICAL SYMBOL COMBINING FLAG-4";
+
+const CharName MULTI_REST = "MUSICAL SYMBOL MULTI REST"; // Unicode-4 glyph 1D13A
+const CharName MULTI_REST_ON_STAFF = "MUSICAL SYMBOL MULTI REST ON STAFF";
+const CharName WHOLE_REST = "MUSICAL SYMBOL WHOLE REST"; // Unicode-4 glyph 1D13B
+const CharName WHOLE_REST_ON_STAFF = "MUSICAL SYMBOL WHOLE REST ON STAFF";
+const CharName HALF_REST = "MUSICAL SYMBOL HALF REST"; // Unicode-4 glyph 1D13C
+const CharName HALF_REST_ON_STAFF = "MUSICAL SYMBOL HALF REST ON STAFF";
+const CharName QUARTER_REST = "MUSICAL SYMBOL QUARTER REST";
+const CharName EIGHTH_REST = "MUSICAL SYMBOL EIGHTH REST";
+const CharName SIXTEENTH_REST = "MUSICAL SYMBOL SIXTEENTH REST";
+const CharName THIRTY_SECOND_REST = "MUSICAL SYMBOL THIRTY-SECOND REST";
+const CharName SIXTY_FOURTH_REST = "MUSICAL SYMBOL SIXTY-FOURTH REST";
+
+const CharName DOT = "MUSICAL SYMBOL COMBINING AUGMENTATION DOT";
+
+const CharName ACCENT = "MUSICAL SYMBOL COMBINING ACCENT";
+const CharName TENUTO = "MUSICAL SYMBOL COMBINING TENUTO";
+const CharName STACCATO = "MUSICAL SYMBOL COMBINING STACCATO";
+const CharName STACCATISSIMO = "MUSICAL SYMBOL COMBINING STACCATISSIMO";
+const CharName MARCATO = "MUSICAL SYMBOL COMBINING MARCATO";
+const CharName FERMATA = "MUSICAL SYMBOL FERMATA";
+const CharName TRILL = "MUSICAL SYMBOL TR";
+const CharName TRILL_LINE = "MUSICAL SYMBOL COMBINING TRILL LINE";
+const CharName TURN = "MUSICAL SYMBOL TURN";
+
+const CharName MORDENT = "MUSICAL SYMBOL MORDENT";
+const CharName MORDENT_INVERTED = "MUSICAL SYMBOL INVERTED MORDENT";
+const CharName MORDENT_LONG = "MUSICAL SYMBOL LONG MORDENT";
+const CharName MORDENT_LONG_INVERTED = "MUSICAL SYMBOL LONG INVERTED MORDENT";
+
+const CharName PEDAL_MARK = "MUSICAL SYMBOL PEDAL MARK";
+const CharName PEDAL_UP_MARK = "MUSICAL SYMBOL PEDAL UP MARK";
+
+const CharName UP_BOW = "MUSICAL SYMBOL COMBINING UP BOW";
+const CharName DOWN_BOW = "MUSICAL SYMBOL COMBINING DOWN BOW";
+
+const CharName C_CLEF = "MUSICAL SYMBOL C CLEF";
+const CharName G_CLEF = "MUSICAL SYMBOL G CLEF";
+const CharName F_CLEF = "MUSICAL SYMBOL F CLEF";
+
+const CharName COMMON_TIME = "MUSICAL SYMBOL COMMON TIME";
+const CharName CUT_TIME = "MUSICAL SYMBOL CUT TIME";
+const CharName DIGIT_ZERO = "DIGIT ZERO";
+const CharName DIGIT_ONE = "DIGIT ONE";
+const CharName DIGIT_TWO = "DIGIT TWO";
+const CharName DIGIT_THREE = "DIGIT THREE";
+const CharName DIGIT_FOUR = "DIGIT FOUR";
+const CharName DIGIT_FIVE = "DIGIT FIVE";
+const CharName DIGIT_SIX = "DIGIT SIX";
+const CharName DIGIT_SEVEN = "DIGIT SEVEN";
+const CharName DIGIT_EIGHT = "DIGIT EIGHT";
+const CharName DIGIT_NINE = "DIGIT NINE";
+
+const CharName UNKNOWN = "__UNKNOWN__";
+
+}
+
+}
diff --git a/src/gui/editors/notation/NoteCharacterNames.h b/src/gui/editors/notation/NoteCharacterNames.h
new file mode 100644
index 0000000..9022ecd
--- /dev/null
+++ b/src/gui/editors/notation/NoteCharacterNames.h
@@ -0,0 +1,120 @@
+// -*- c-basic-offset: 4 -*-
+
+/*
+ Rosegarden
+ A sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 _NOTE_CHAR_NAME_H_
+#define _NOTE_CHAR_NAME_H_
+
+#include "PropertyName.h"
+
+namespace Rosegarden {
+
+typedef PropertyName CharName;
+
+/// A selection of Unicode character names for symbols in a note font
+
+namespace NoteCharacterNames
+{
+extern const CharName SHARP;
+extern const CharName FLAT;
+extern const CharName NATURAL;
+extern const CharName DOUBLE_SHARP;
+extern const CharName DOUBLE_FLAT;
+
+extern const CharName BREVE;
+extern const CharName WHOLE_NOTE;
+extern const CharName VOID_NOTEHEAD;
+extern const CharName NOTEHEAD_BLACK;
+
+extern const CharName X_NOTEHEAD;
+extern const CharName CIRCLE_X_NOTEHEAD;
+extern const CharName SEMIBREVIS_WHITE;
+extern const CharName SEMIBREVIS_BLACK;
+extern const CharName TRIANGLE_NOTEHEAD_UP_WHITE;
+extern const CharName TRIANGLE_NOTEHEAD_UP_BLACK;
+extern const CharName SQUARE_NOTEHEAD_WHITE;
+extern const CharName SQUARE_NOTEHEAD_BLACK;
+
+extern const CharName FLAG_PARTIAL;
+extern const CharName FLAG_PARTIAL_FINAL;
+
+extern const CharName FLAG_1;
+extern const CharName FLAG_2;
+extern const CharName FLAG_3;
+extern const CharName FLAG_4;
+
+extern const CharName MULTI_REST;
+extern const CharName MULTI_REST_ON_STAFF;
+extern const CharName WHOLE_REST;
+extern const CharName WHOLE_REST_ON_STAFF;
+extern const CharName HALF_REST;
+extern const CharName HALF_REST_ON_STAFF;
+extern const CharName QUARTER_REST;
+extern const CharName EIGHTH_REST;
+extern const CharName SIXTEENTH_REST;
+extern const CharName THIRTY_SECOND_REST;
+extern const CharName SIXTY_FOURTH_REST;
+
+extern const CharName DOT;
+
+extern const CharName ACCENT;
+extern const CharName TENUTO;
+extern const CharName STACCATO;
+extern const CharName STACCATISSIMO;
+extern const CharName MARCATO;
+extern const CharName FERMATA;
+extern const CharName TRILL;
+extern const CharName TRILL_LINE;
+extern const CharName TURN;
+extern const CharName UP_BOW;
+extern const CharName DOWN_BOW;
+
+extern const CharName MORDENT;
+extern const CharName MORDENT_INVERTED;
+extern const CharName MORDENT_LONG;
+extern const CharName MORDENT_LONG_INVERTED;
+
+extern const CharName PEDAL_MARK;
+extern const CharName PEDAL_UP_MARK;
+
+extern const CharName C_CLEF;
+extern const CharName G_CLEF;
+extern const CharName F_CLEF;
+
+extern const CharName COMMON_TIME;
+extern const CharName CUT_TIME;
+extern const CharName DIGIT_ZERO;
+extern const CharName DIGIT_ONE;
+extern const CharName DIGIT_TWO;
+extern const CharName DIGIT_THREE;
+extern const CharName DIGIT_FOUR;
+extern const CharName DIGIT_FIVE;
+extern const CharName DIGIT_SIX;
+extern const CharName DIGIT_SEVEN;
+extern const CharName DIGIT_EIGHT;
+extern const CharName DIGIT_NINE;
+
+extern const CharName UNKNOWN;
+}
+
+}
+
+#endif
+
diff --git a/src/gui/editors/notation/NoteFont.cpp b/src/gui/editors/notation/NoteFont.cpp
new file mode 100644
index 0000000..95746c3
--- /dev/null
+++ b/src/gui/editors/notation/NoteFont.cpp
@@ -0,0 +1,650 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NoteFont.h"
+#include "misc/Debug.h"
+
+#include "misc/Strings.h"
+#include "base/Exception.h"
+#include "gui/general/PixmapFunctions.h"
+#include "NoteCharacter.h"
+#include "NoteFontMap.h"
+#include "SystemFont.h"
+#include <qbitmap.h>
+#include <qgarray.h>
+#include <qimage.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+
+namespace Rosegarden
+{
+
+NoteFont::FontPixmapMap *NoteFont::m_fontPixmapMap = 0;
+
+NoteFont::DrawRepMap *NoteFont::m_drawRepMap = 0;
+QPixmap *NoteFont::m_blankPixmap = 0;
+
+
+NoteFont::NoteFont(std::string fontName, int size) :
+ m_fontMap(fontName)
+{
+ // Do the size checks first, to avoid doing the extra work if they fail
+
+ std::set<int> sizes = m_fontMap.getSizes();
+
+ if (sizes.size() > 0) {
+ m_size = *sizes.begin();
+ } else {
+ throw BadNoteFont(std::string("No sizes listed for font ") + fontName);
+ }
+
+ if (size > 0) {
+ if (sizes.find(size) == sizes.end()) {
+ throw BadNoteFont(qstrtostr(QString("Font \"%1\" not available in size %2").arg(strtoqstr(fontName)).arg(size)));
+ } else {
+ m_size = size;
+ }
+ }
+
+ // Create the global font map and blank pixmap if necessary
+
+ if (m_fontPixmapMap == 0) {
+ m_fontPixmapMap = new FontPixmapMap();
+ }
+
+ if (m_blankPixmap == 0) {
+ m_blankPixmap = new QPixmap(10, 10);
+ m_blankPixmap->setMask(QBitmap(10, 10, TRUE));
+ }
+
+ // Locate our font's pixmap map in the font map, create if necessary
+
+ std::string fontKey = qstrtostr(QString("__%1__%2__")
+ .arg(strtoqstr(m_fontMap.getName()))
+ .arg(m_size));
+
+ FontPixmapMap::iterator i = m_fontPixmapMap->find(fontKey);
+ if (i == m_fontPixmapMap->end()) {
+ (*m_fontPixmapMap)[fontKey] = new PixmapMap();
+ }
+
+ m_map = (*m_fontPixmapMap)[fontKey];
+}
+
+NoteFont::~NoteFont()
+{
+ // empty
+}
+
+bool
+NoteFont::getStemThickness(unsigned int &thickness) const
+{
+ thickness = m_size / 9 + 1;
+ return m_fontMap.getStemThickness(m_size, thickness);
+}
+
+bool
+NoteFont::getBeamThickness(unsigned int &thickness) const
+{
+ thickness = m_size / 2;
+ return m_fontMap.getBeamThickness(m_size, thickness);
+}
+
+bool
+NoteFont::getStemLength(unsigned int &length) const
+{
+ getStaffLineThickness(length);
+ length = (m_size + length) * 7 / 2;
+ return m_fontMap.getStemLength(m_size, length);
+}
+
+bool
+NoteFont::getFlagSpacing(unsigned int &spacing) const
+{
+ spacing = m_size;
+ return m_fontMap.getFlagSpacing(m_size, spacing);
+}
+
+bool
+NoteFont::getStaffLineThickness(unsigned int &thickness) const
+{
+ thickness = (m_size < 7 ? 1 : m_size / 7);
+ return m_fontMap.getStaffLineThickness(m_size, thickness);
+}
+
+bool
+NoteFont::getLegerLineThickness(unsigned int &thickness) const
+{
+ thickness = (m_size < 6 ? 1 : m_size / 6);
+ return m_fontMap.getLegerLineThickness(m_size, thickness);
+}
+
+bool
+NoteFont::lookup(CharName charName, bool inverted, QPixmap *&pixmap) const
+{
+ PixmapMap::iterator i = m_map->find(charName);
+ if (i != m_map->end()) {
+ if (inverted) {
+ pixmap = i->second.second;
+ if (!pixmap && i->second.first)
+ return false;
+ } else {
+ pixmap = i->second.first;
+ if (!pixmap && i->second.second)
+ return false;
+ }
+ return true;
+ }
+ pixmap = 0;
+ return false;
+}
+
+void
+NoteFont::add
+(CharName charName, bool inverted, QPixmap *pixmap) const
+{
+ PixmapMap::iterator i = m_map->find(charName);
+ if (i != m_map->end()) {
+ if (inverted) {
+ delete i->second.second;
+ i->second.second = pixmap;
+ } else {
+ delete i->second.first;
+ i->second.first = pixmap;
+ }
+ } else {
+ if (inverted) {
+ (*m_map)[charName] = PixmapPair(0, pixmap);
+ } else {
+ (*m_map)[charName] = PixmapPair(pixmap, 0);
+ }
+ }
+}
+
+NoteCharacterDrawRep *
+NoteFont::lookupDrawRep(QPixmap *pixmap) const
+{
+ if (!m_drawRepMap)
+ m_drawRepMap = new DrawRepMap();
+
+ if (m_drawRepMap->find(pixmap) != m_drawRepMap->end()) {
+
+ return (*m_drawRepMap)[pixmap];
+
+ } else {
+
+ QImage image = pixmap->convertToImage();
+ if (image.isNull())
+ return 0;
+
+ if (image.depth() > 1) {
+ image = image.convertDepth(1, Qt::MonoOnly | Qt::ThresholdDither);
+ }
+
+ NoteCharacterDrawRep *a = new NoteCharacterDrawRep();
+
+ for (int yi = 0; yi < image.height(); ++yi) {
+
+ unsigned char *line = image.scanLine(yi);
+
+ int startx = 0;
+
+ for (int xi = 0; xi <= image.width(); ++xi) {
+
+ bool pixel = false;
+
+ if (xi < image.width()) {
+ if (image.bitOrder() == QImage::LittleEndian) {
+ if (*(line + (xi >> 3)) & 1 << (xi & 7))
+ pixel = true;
+ } else {
+ if (*(line + (xi >> 3)) & 1 << (7 - (xi & 7)))
+ pixel = true;
+ }
+ }
+
+ if (!pixel) {
+ if (startx < xi) {
+ a->resize(a->size() + 2, QGArray::SpeedOptim);
+ a->setPoint(a->size() - 2, startx, yi);
+ a->setPoint(a->size() - 1, xi - 1, yi);
+ }
+ startx = xi + 1;
+ }
+ }
+ }
+
+ (*m_drawRepMap)[pixmap] = a;
+ return a;
+ }
+}
+
+bool
+NoteFont::getPixmap(CharName charName, QPixmap &pixmap, bool inverted) const
+{
+ QPixmap *found = 0;
+ bool ok = lookup(charName, inverted, found);
+ if (ok) {
+ if (found) {
+ pixmap = *found;
+ return true;
+ } else {
+ pixmap = *m_blankPixmap;
+ return false;
+ }
+ }
+
+ if (inverted && !m_fontMap.hasInversion(m_size, charName)) {
+ if (!getPixmap(charName, pixmap, !inverted))
+ return false;
+ found = new QPixmap(PixmapFunctions::flipVertical(pixmap));
+ add(charName, inverted, found);
+ pixmap = *found;
+ return true;
+ }
+
+ std::string src;
+ ok = false;
+
+ if (!inverted)
+ ok = m_fontMap.getSrc(m_size, charName, src);
+ else
+ ok = m_fontMap.getInversionSrc(m_size, charName, src);
+
+ if (ok) {
+ NOTATION_DEBUG
+ << "NoteFont::getPixmap: Loading \"" << src << "\"" << endl;
+
+ found = new QPixmap(strtoqstr(src));
+
+ if (!found->isNull()) {
+
+ if (found->mask() == 0) {
+ std::cerr << "NoteFont::getPixmap: Warning: No automatic mask "
+ << "for character \"" << charName << "\""
+ << (inverted ? " (inverted)" : "") << " in font \""
+ << m_fontMap.getName() << "-" << m_size
+ << "\"; consider making xpm background transparent"
+ << std::endl;
+ found->setMask(PixmapFunctions::generateMask(*found));
+ }
+
+ add(charName, inverted, found);
+ pixmap = *found;
+ return true;
+ }
+
+ std::cerr << "NoteFont::getPixmap: Warning: Unable to read pixmap file " << src << std::endl;
+ } else {
+
+ int code = -1;
+ if (!inverted)
+ ok = m_fontMap.getCode(m_size, charName, code);
+ else
+ ok = m_fontMap.getInversionCode(m_size, charName, code);
+
+ int glyph = -1;
+ if (!inverted)
+ ok = m_fontMap.getGlyph(m_size, charName, glyph);
+ else
+ ok = m_fontMap.getInversionGlyph(m_size, charName, glyph);
+
+ if (code < 0 && glyph < 0) {
+ std::cerr << "NoteFont::getPixmap: Warning: No pixmap, code, or glyph for character \""
+ << charName << "\"" << (inverted ? " (inverted)" : "")
+ << " in font \"" << m_fontMap.getName() << "\"" << std::endl;
+ add(charName, inverted, 0);
+ pixmap = *m_blankPixmap;
+ return false;
+ }
+
+ int charBase = 0;
+ SystemFont *systemFont =
+ m_fontMap.getSystemFont(m_size, charName, charBase);
+
+ if (!systemFont) {
+ if (!inverted && m_fontMap.hasInversion(m_size, charName)) {
+ if (!getPixmap(charName, pixmap, !inverted))
+ return false;
+ found = new QPixmap(PixmapFunctions::flipVertical(pixmap));
+ add(charName, inverted, found);
+ pixmap = *found;
+ return true;
+ }
+
+ std::cerr << "NoteFont::getPixmap: Warning: No system font for character \""
+ << charName << "\"" << (inverted ? " (inverted)" : "")
+ << " in font \"" << m_fontMap.getName() << "\"" << std::endl;
+
+ add(charName, inverted, 0);
+ pixmap = *m_blankPixmap;
+ return false;
+ }
+
+ SystemFont::Strategy strategy =
+ m_fontMap.getStrategy(m_size, charName);
+
+ bool success;
+ found = new QPixmap(systemFont->renderChar(charName,
+ glyph,
+ code + charBase,
+ strategy,
+ success));
+
+ if (success) {
+ add(charName, inverted, found);
+ pixmap = *found;
+ return true;
+ } else {
+ add(charName, inverted, 0);
+ pixmap = *m_blankPixmap;
+ return false;
+ }
+ }
+
+ add(charName, inverted, 0);
+ pixmap = *m_blankPixmap;
+ return false;
+}
+
+bool
+NoteFont::getColouredPixmap(CharName baseCharName, QPixmap &pixmap,
+ int hue, int minValue, bool inverted) const
+{
+ CharName charName(getNameWithColour(baseCharName, hue));
+
+ QPixmap *found = 0;
+ bool ok = lookup(charName, inverted, found);
+ if (ok) {
+ if (found) {
+ pixmap = *found;
+ return true;
+ } else {
+ pixmap = *m_blankPixmap;
+ return false;
+ }
+ }
+
+ QPixmap basePixmap;
+ ok = getPixmap(baseCharName, basePixmap, inverted);
+
+ if (!ok) {
+ add(charName, inverted, 0);
+ pixmap = *m_blankPixmap;
+ return false;
+ }
+
+ found = new QPixmap
+ (PixmapFunctions::colourPixmap(basePixmap, hue, minValue));
+ add(charName, inverted, found);
+ pixmap = *found;
+ return ok;
+}
+
+bool
+NoteFont::getShadedPixmap(CharName baseCharName, QPixmap &pixmap,
+ bool inverted) const
+{
+ CharName charName(getNameShaded(baseCharName));
+
+ QPixmap *found = 0;
+ bool ok = lookup(charName, inverted, found);
+ if (ok) {
+ if (found) {
+ pixmap = *found;
+ return true;
+ } else {
+ pixmap = *m_blankPixmap;
+ return false;
+ }
+ }
+
+ QPixmap basePixmap;
+ ok = getPixmap(baseCharName, basePixmap, inverted);
+
+ if (!ok) {
+ add(charName, inverted, 0);
+ pixmap = *m_blankPixmap;
+ return false;
+ }
+
+ found = new QPixmap(PixmapFunctions::shadePixmap(basePixmap));
+ add(charName, inverted, found);
+ pixmap = *found;
+ return ok;
+}
+
+CharName
+NoteFont::getNameWithColour(CharName base, int hue) const
+{
+ return qstrtostr(QString("%1__%2").arg(hue).arg(strtoqstr(base)));
+}
+
+CharName
+NoteFont::getNameShaded(CharName base) const
+{
+ return qstrtostr(QString("shaded__%1").arg(strtoqstr(base)));
+}
+
+bool
+NoteFont::getDimensions(CharName charName, int &x, int &y, bool inverted) const
+{
+ QPixmap pixmap;
+ bool ok = getPixmap(charName, pixmap, inverted);
+ x = pixmap.width();
+ y = pixmap.height();
+ return ok;
+}
+
+int
+NoteFont::getWidth(CharName charName) const
+{
+ int x, y;
+ getDimensions(charName, x, y);
+ return x;
+}
+
+int
+NoteFont::getHeight(CharName charName) const
+{
+ int x, y;
+ getDimensions(charName, x, y);
+ return y;
+}
+
+bool
+NoteFont::getHotspot(CharName charName, int &x, int &y, bool inverted) const
+{
+ int w, h;
+ getDimensions(charName, w, h, inverted);
+ bool ok = m_fontMap.getHotspot(m_size, charName, w, h, x, y);
+
+ if (!ok) {
+ x = 0;
+ y = h / 2;
+ }
+
+ if (inverted) {
+ y = h - y;
+ }
+
+ return ok;
+}
+
+QPoint
+NoteFont::getHotspot(CharName charName, bool inverted) const
+{
+ int x, y;
+ (void)getHotspot(charName, x, y, inverted);
+ return QPoint(x, y);
+}
+
+bool
+NoteFont::getCharacter(CharName charName,
+ NoteCharacter &character,
+ CharacterType type,
+ bool inverted)
+{
+ QPixmap pixmap;
+ if (!getPixmap(charName, pixmap, inverted))
+ return false;
+
+ if (type == Screen) {
+ character = NoteCharacter(pixmap,
+ getHotspot(charName, inverted),
+ 0);
+ } else {
+
+ // Get the pointer direct from cache (depends on earlier call
+ // to getPixmap to put it in the cache if available)
+
+ QPixmap *pmapptr = 0;
+ bool found = lookup(charName, inverted, pmapptr);
+
+ NoteCharacterDrawRep *rep = 0;
+ if (found && pmapptr)
+ rep = lookupDrawRep(pmapptr);
+
+ character = NoteCharacter(pixmap,
+ getHotspot(charName, inverted),
+ rep);
+ }
+
+ return true;
+}
+
+NoteCharacter
+NoteFont::getCharacter(CharName charName,
+ CharacterType type,
+ bool inverted)
+{
+ NoteCharacter character;
+ getCharacter(charName, character, type, inverted);
+ return character;
+}
+
+bool
+NoteFont::getCharacterColoured(CharName charName,
+ int hue, int minValue,
+ NoteCharacter &character,
+ CharacterType type,
+ bool inverted)
+{
+ QPixmap pixmap;
+ if (!getColouredPixmap(charName, pixmap, hue, minValue, inverted)) {
+ return false;
+ }
+
+ if (type == Screen) {
+
+ character = NoteCharacter(pixmap,
+ getHotspot(charName, inverted),
+ 0);
+
+ } else {
+
+ // Get the pointer direct from cache (depends on earlier call
+ // to getPixmap to put it in the cache if available)
+
+ QPixmap *pmapptr = 0;
+ CharName cCharName(getNameWithColour(charName, hue));
+ bool found = lookup(cCharName, inverted, pmapptr);
+
+ NoteCharacterDrawRep *rep = 0;
+ if (found && pmapptr)
+ rep = lookupDrawRep(pmapptr);
+
+ character = NoteCharacter(pixmap,
+ getHotspot(charName, inverted),
+ rep);
+ }
+
+ return true;
+}
+
+NoteCharacter
+NoteFont::getCharacterColoured(CharName charName,
+ int hue, int minValue,
+ CharacterType type,
+ bool inverted)
+{
+ NoteCharacter character;
+ getCharacterColoured(charName, hue, minValue, character, type, inverted);
+ return character;
+}
+
+bool
+NoteFont::getCharacterShaded(CharName charName,
+ NoteCharacter &character,
+ CharacterType type,
+ bool inverted)
+{
+ QPixmap pixmap;
+ if (!getShadedPixmap(charName, pixmap, inverted)) {
+ return false;
+ }
+
+ if (type == Screen) {
+
+ character = NoteCharacter(pixmap,
+ getHotspot(charName, inverted),
+ 0);
+
+ } else {
+
+ // Get the pointer direct from cache (depends on earlier call
+ // to getPixmap to put it in the cache if available)
+
+ QPixmap *pmapptr = 0;
+ CharName cCharName(getNameShaded(charName));
+ bool found = lookup(cCharName, inverted, pmapptr);
+
+ NoteCharacterDrawRep *rep = 0;
+ if (found && pmapptr)
+ rep = lookupDrawRep(pmapptr);
+
+ character = NoteCharacter(pixmap,
+ getHotspot(charName, inverted),
+ rep);
+ }
+
+ return true;
+}
+
+NoteCharacter
+NoteFont::getCharacterShaded(CharName charName,
+ CharacterType type,
+ bool inverted)
+{
+ NoteCharacter character;
+ getCharacterShaded(charName, character, type, inverted);
+ return character;
+}
+
+}
diff --git a/src/gui/editors/notation/NoteFont.h b/src/gui/editors/notation/NoteFont.h
new file mode 100644
index 0000000..81a3b19
--- /dev/null
+++ b/src/gui/editors/notation/NoteFont.h
@@ -0,0 +1,184 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTEFONT_H_
+#define _RG_NOTEFONT_H_
+
+#include "base/Exception.h"
+#include <map>
+#include "NoteCharacter.h"
+#include "NoteFontMap.h"
+#include <set>
+#include <string>
+#include <qpoint.h>
+#include <utility>
+#include "gui/editors/notation/NoteCharacterNames.h"
+
+
+class QPixmap;
+class PixmapMap;
+class NoteCharacterDrawRep;
+class FontPixmapMap;
+class DrawRepMap;
+
+
+namespace Rosegarden
+{
+
+
+
+class NoteFont
+{
+public:
+ enum CharacterType { Screen, Printer };
+
+ typedef Exception BadNoteFont;
+ ~NoteFont();
+
+ std::string getName() const { return m_fontMap.getName(); }
+ int getSize() const { return m_size; }
+ bool isSmooth() const { return m_fontMap.isSmooth(); }
+ const NoteFontMap &getNoteFontMap() const { return m_fontMap; }
+
+ /// Returns false + thickness=1 if not specified
+ bool getStemThickness(unsigned int &thickness) const;
+
+ /// Returns false + a guess at suitable thickness if not specified
+ bool getBeamThickness(unsigned int &thickness) const;
+
+ /// Returns false + a guess at suitable length if not specified
+ bool getStemLength(unsigned int &length) const;
+
+ /// Returns false + a guess at suitable spacing if not specified
+ bool getFlagSpacing(unsigned int &spacing) const;
+
+ /// Returns false + thickness=1 if not specified
+ bool getStaffLineThickness(unsigned int &thickness) const;
+
+ /// Returns false + thickness=1 if not specified
+ bool getLegerLineThickness(unsigned int &thickness) const;
+
+ /// Returns false if not available
+ bool getCharacter(CharName charName,
+ NoteCharacter &character,
+ CharacterType type = Screen,
+ bool inverted = false);
+
+ /// Returns an empty character if not available
+ NoteCharacter getCharacter(CharName charName,
+ CharacterType type = Screen,
+ bool inverted = false);
+
+ /// Returns false if not available
+ bool getCharacterColoured(CharName charName,
+ int hue, int minValue,
+ NoteCharacter &character,
+ CharacterType type = Screen,
+ bool inverted = false);
+
+ /// Returns an empty character if not available
+ NoteCharacter getCharacterColoured(CharName charName,
+ int hue, int minValue,
+ CharacterType type = Screen,
+ bool inverted = false);
+
+ /// Returns false if not available
+ bool getCharacterShaded(CharName charName,
+ NoteCharacter &character,
+ CharacterType type = Screen,
+ bool inverted = false);
+
+ /// Returns an empty character if not available
+ NoteCharacter getCharacterShaded(CharName charName,
+ CharacterType type = Screen,
+ bool inverted = false);
+
+ /// Returns false + dimensions of blank pixmap if none found
+ bool getDimensions(CharName charName, int &x, int &y,
+ bool inverted = false) const;
+
+ /// Ignores problems, returning dimension of blank pixmap if necessary
+ int getWidth(CharName charName) const;
+
+ /// Ignores problems, returning dimension of blank pixmap if necessary
+ int getHeight(CharName charName) const;
+
+ /// Returns false + centre-left of pixmap if no hotspot specified
+ bool getHotspot(CharName charName, int &x, int &y,
+ bool inverted = false) const;
+
+ /// Ignores problems, returns centre-left of pixmap if necessary
+ QPoint getHotspot(CharName charName, bool inverted = false) const;
+
+private:
+ /// Returns false + blank pixmap if it can't find the right one
+ bool getPixmap(CharName charName, QPixmap &pixmap,
+ bool inverted = false) const;
+
+ /// Returns false + blank pixmap if it can't find the right one
+ bool getColouredPixmap(CharName charName, QPixmap &pixmap,
+ int hue, int minValue,
+ bool inverted = false) const;
+
+ /// Returns false + blank pixmap if it can't find the right one
+ bool getShadedPixmap(CharName charName, QPixmap &pixmap,
+ bool inverted = false) const;
+
+ friend class NoteFontFactory;
+ NoteFont(std::string fontName, int size = 0);
+ std::set<int> getSizes() const { return m_fontMap.getSizes(); }
+
+ bool lookup(CharName charName, bool inverted, QPixmap *&pixmap) const;
+ void add(CharName charName, bool inverted, QPixmap *pixmap) const;
+
+ NoteCharacterDrawRep *lookupDrawRep(QPixmap *pixmap) const;
+
+ CharName getNameWithColour(CharName origName, int hue) const;
+ CharName getNameShaded(CharName origName) const;
+
+ typedef std::pair<QPixmap *, QPixmap *> PixmapPair;
+ typedef std::map<CharName, PixmapPair> PixmapMap;
+ typedef std::map<std::string, PixmapMap *> FontPixmapMap;
+
+ typedef std::map<QPixmap *, NoteCharacterDrawRep *> DrawRepMap;
+
+ //--------------- Data members ---------------------------------
+
+ int m_size;
+ NoteFontMap m_fontMap;
+
+ mutable PixmapMap *m_map; // pointer at a member of m_fontPixmapMap
+
+ static FontPixmapMap *m_fontPixmapMap;
+ static DrawRepMap *m_drawRepMap;
+
+ static QPixmap *m_blankPixmap;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NoteFontFactory.cpp b/src/gui/editors/notation/NoteFontFactory.cpp
new file mode 100644
index 0000000..2decce4
--- /dev/null
+++ b/src/gui/editors/notation/NoteFontFactory.cpp
@@ -0,0 +1,236 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NoteFontFactory.h"
+#include "misc/Debug.h"
+#include <kapplication.h>
+
+#include <klocale.h>
+#include <kstddirs.h>
+#include "misc/Strings.h"
+#include "document/ConfigGroups.h"
+#include "base/Exception.h"
+#include "gui/kdeext/KStartupLogo.h"
+#include "NoteFont.h"
+#include "NoteFontMap.h"
+#include <kconfig.h>
+#include <kglobal.h>
+#include <kmessagebox.h>
+#include <qdir.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <algorithm>
+
+
+namespace Rosegarden
+{
+
+std::set<std::string>
+NoteFontFactory::getFontNames(bool forceRescan)
+{
+ NOTATION_DEBUG << "NoteFontFactory::getFontNames: forceRescan = " << forceRescan << endl;
+
+ if (forceRescan)
+ m_fontNames.clear();
+ if (!m_fontNames.empty())
+ return m_fontNames;
+
+ KConfig *config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+
+ QString fontNameList = "";
+ if (!forceRescan) {
+ fontNameList = config->readEntry("notefontlist");
+ }
+
+ NOTATION_DEBUG << "NoteFontFactory::getFontNames: read from cache: " << fontNameList << endl;
+
+ QStringList names = QStringList::split(",", fontNameList);
+
+ if (names.empty()) {
+
+ NOTATION_DEBUG << "NoteFontFactory::getFontNames: No names available, rescanning..." << endl;
+
+ QString mappingDir =
+ KGlobal::dirs()->findResource("appdata", "fonts/mappings/");
+ QDir dir(mappingDir);
+ if (!dir.exists()) {
+ std::cerr << "NoteFontFactory::getFontNames: mapping directory \""
+ << mappingDir << "\" not found" << std::endl;
+ return m_fontNames;
+ }
+
+ dir.setFilter(QDir::Files | QDir::Readable);
+ QStringList files = dir.entryList();
+ for (QStringList::Iterator i = files.begin(); i != files.end(); ++i) {
+
+ if ((*i).length() > 4 && (*i).right(4).lower() == ".xml") {
+
+ std::string name(qstrtostr((*i).left((*i).length() - 4)));
+
+ try {
+ NoteFontMap map(name);
+ if (map.ok())
+ names.append(strtoqstr(map.getName()));
+ } catch (Exception e) {
+ KStartupLogo::hideIfStillThere();
+ KMessageBox::error(0, strtoqstr(e.getMessage()));
+ throw;
+ }
+ }
+ }
+ }
+
+ QString savedNames = "";
+
+ for (QStringList::Iterator i = names.begin(); i != names.end(); ++i) {
+ m_fontNames.insert(qstrtostr(*i));
+ if (i != names.begin())
+ savedNames += ",";
+ savedNames += *i;
+ }
+
+ config->writeEntry("notefontlist", savedNames);
+
+ return m_fontNames;
+}
+
+std::vector<int>
+NoteFontFactory::getAllSizes(std::string fontName)
+{
+ NoteFont *font = getFont(fontName, 0);
+ if (!font)
+ return std::vector<int>();
+
+ std::set
+ <int> s(font->getSizes());
+ std::vector<int> v;
+ for (std::set
+ <int>::iterator i = s.begin(); i != s.end(); ++i) {
+ v.push_back(*i);
+ }
+
+ std::sort(v.begin(), v.end());
+ return v;
+}
+
+std::vector<int>
+NoteFontFactory::getScreenSizes(std::string fontName)
+{
+ NoteFont *font = getFont(fontName, 0);
+ if (!font)
+ return std::vector<int>();
+
+ std::set
+ <int> s(font->getSizes());
+ std::vector<int> v;
+ for (std::set
+ <int>::iterator i = s.begin(); i != s.end(); ++i) {
+ if (*i <= 16)
+ v.push_back(*i);
+ }
+ std::sort(v.begin(), v.end());
+ return v;
+}
+
+NoteFont *
+NoteFontFactory::getFont(std::string fontName, int size)
+{
+ std::map<std::pair<std::string, int>, NoteFont *>::iterator i =
+ m_fonts.find(std::pair<std::string, int>(fontName, size));
+
+ if (i == m_fonts.end()) {
+ try {
+ NoteFont *font = new NoteFont(fontName, size);
+ m_fonts[std::pair<std::string, int>(fontName, size)] = font;
+ return font;
+ } catch (Exception e) {
+ KStartupLogo::hideIfStillThere();
+ KMessageBox::error(0, strtoqstr(e.getMessage()));
+ throw;
+ }
+ } else {
+ return i->second;
+ }
+}
+
+std::string
+NoteFontFactory::getDefaultFontName()
+{
+ static std::string defaultFont = "";
+ if (defaultFont != "")
+ return defaultFont;
+
+ std::set
+ <std::string> fontNames = getFontNames();
+
+ if (fontNames.find("Feta") != fontNames.end())
+ defaultFont = "Feta";
+ else {
+ fontNames = getFontNames(true);
+ if (fontNames.find("Feta") != fontNames.end())
+ defaultFont = "Feta";
+ else if (fontNames.find("Feta Pixmaps") != fontNames.end())
+ defaultFont = "Feta Pixmaps";
+ else if (fontNames.size() > 0)
+ defaultFont = *fontNames.begin();
+ else {
+ QString message = i18n("Can't obtain a default font -- no fonts found");
+ KStartupLogo::hideIfStillThere();
+ KMessageBox::error(0, message);
+ throw NoFontsAvailable(qstrtostr(message));
+ }
+ }
+
+ return defaultFont;
+}
+
+int
+NoteFontFactory::getDefaultSize(std::string fontName)
+{
+ // always return 8 if it's supported!
+ std::vector<int> sizes(getScreenSizes(fontName));
+ for (unsigned int i = 0; i < sizes.size(); ++i) {
+ if (sizes[i] == 8)
+ return sizes[i];
+ }
+ return sizes[sizes.size() / 2];
+}
+
+bool
+NoteFontFactory::isAvailableInSize(std::string fontName, int size)
+{
+ std::vector<int> sizes(getAllSizes(fontName));
+ for (unsigned int i = 0; i < sizes.size(); ++i) {
+ if (sizes[i] == size)
+ return true;
+ }
+ return false;
+}
+
+std::set<std::string> NoteFontFactory::m_fontNames;
+std::map<std::pair<std::string, int>, NoteFont *> NoteFontFactory::m_fonts;
+
+}
diff --git a/src/gui/editors/notation/NoteFontFactory.h b/src/gui/editors/notation/NoteFontFactory.h
new file mode 100644
index 0000000..33e6e80
--- /dev/null
+++ b/src/gui/editors/notation/NoteFontFactory.h
@@ -0,0 +1,71 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTEFONTFACTORY_H_
+#define _RG_NOTEFONTFACTORY_H_
+
+#include "base/Exception.h"
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+
+
+
+namespace Rosegarden
+{
+
+class NoteFont;
+
+
+class NoteFontFactory
+{
+public:
+ typedef Exception NoFontsAvailable;
+
+ // Any method passed a fontName argument may throw BadFont or
+ // MappingFileReadFailed; any other method may throw NoFontsAvailable
+
+ static NoteFont *getFont(std::string fontName, int size);
+
+ static std::set<std::string> getFontNames(bool forceRescan = false);
+ static std::vector<int> getAllSizes(std::string fontName); // sorted
+ static std::vector<int> getScreenSizes(std::string fontName); // sorted
+
+ static std::string getDefaultFontName();
+ static int getDefaultSize(std::string fontName);
+ static bool isAvailableInSize(std::string fontName, int size);
+
+private:
+ static std::set<std::string> m_fontNames;
+ static std::map<std::pair<std::string, int>, NoteFont *> m_fonts;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NoteFontMap.cpp b/src/gui/editors/notation/NoteFontMap.cpp
new file mode 100644
index 0000000..e11c126
--- /dev/null
+++ b/src/gui/editors/notation/NoteFontMap.cpp
@@ -0,0 +1,1088 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NoteFontMap.h"
+#include "misc/Debug.h"
+
+#include <klocale.h>
+#include <kstddirs.h>
+#include "misc/Strings.h"
+#include "base/Exception.h"
+#include "SystemFont.h"
+#include <kglobal.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qpixmap.h>
+#include <qregexp.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+
+namespace Rosegarden
+{
+
+NoteFontMap::NoteFontMap(std::string name) :
+ m_name(name),
+ m_smooth(false),
+ m_srcDirectory(name),
+ m_characterDestination(0),
+ m_hotspotCharName(""),
+ m_errorString(i18n("unknown error")),
+ m_ok(true)
+{
+ m_fontDirectory = KGlobal::dirs()->findResource("appdata", "fonts/");
+
+ QString mapFileName;
+
+ QString mapFileMixedName = QString("%1/mappings/%2.xml")
+ .arg(m_fontDirectory)
+ .arg(strtoqstr(name));
+
+ QFileInfo mapFileMixedInfo(mapFileMixedName);
+
+ if (!mapFileMixedInfo.isReadable()) {
+
+ QString lowerName = strtoqstr(name).lower();
+ lowerName.replace(QRegExp(" "), "_");
+ QString mapFileLowerName = QString("%1/mappings/%2.xml")
+ .arg(m_fontDirectory)
+ .arg(lowerName);
+
+ QFileInfo mapFileLowerInfo(mapFileLowerName);
+
+ if (!mapFileLowerInfo.isReadable()) {
+ if (mapFileLowerName != mapFileMixedName) {
+ throw MappingFileReadFailed
+ (qstrtostr(i18n("Can't open font mapping file %1 or %2").
+ arg(mapFileMixedName).arg(mapFileLowerName)));
+ } else {
+ throw MappingFileReadFailed
+ (qstrtostr(i18n("Can't open font mapping file %1").
+ arg(mapFileMixedName)));
+ }
+ } else {
+ mapFileName = mapFileLowerName;
+ }
+ } else {
+ mapFileName = mapFileMixedName;
+ }
+
+ QFile mapFile(mapFileName);
+
+ QXmlInputSource source(mapFile);
+ QXmlSimpleReader reader;
+ reader.setContentHandler(this);
+ reader.setErrorHandler(this);
+ bool ok = reader.parse(source);
+ mapFile.close();
+
+ if (!ok) {
+ throw MappingFileReadFailed(qstrtostr(m_errorString));
+ }
+}
+
+NoteFontMap::~NoteFontMap()
+{
+ for (SystemFontMap::iterator i = m_systemFontCache.begin();
+ i != m_systemFontCache.end(); ++i) {
+ delete i->second;
+ }
+}
+
+bool
+NoteFontMap::characters(QString &chars)
+{
+ if (!m_characterDestination)
+ return true;
+ *m_characterDestination += qstrtostr(chars);
+ return true;
+}
+
+int
+NoteFontMap::toSize(int baseSize, double factor, bool limitAtOne)
+{
+ double dsize = factor * baseSize;
+ dsize += 0.5;
+ if (limitAtOne && dsize < 1.0)
+ dsize = 1.0;
+ return int(dsize);
+}
+
+bool
+NoteFontMap::startElement(const QString &, const QString &,
+ const QString &qName,
+ const QXmlAttributes &attributes)
+{
+ QString lcName = qName.lower();
+ m_characterDestination = 0;
+
+ // The element names are actually unique within the whole file;
+ // we don't bother checking we're in the right context. Leave that
+ // to the DTD, when we have one.
+
+ if (lcName == "rosegarden-font-encoding") {
+
+ QString s;
+
+ s = attributes.value("name");
+ if (s) {
+ m_name = qstrtostr(s);
+ m_srcDirectory = m_name;
+ }
+
+ } else if (lcName == "font-information") {
+
+ QString s;
+
+ s = attributes.value("origin");
+ if (s)
+ m_origin = qstrtostr(s);
+
+ s = attributes.value("copyright");
+ if (s)
+ m_copyright = qstrtostr(s);
+
+ s = attributes.value("mapped-by");
+ if (s)
+ m_mappedBy = qstrtostr(s);
+
+ s = attributes.value("type");
+ if (s)
+ m_type = qstrtostr(s);
+
+ s = attributes.value("autocrop");
+ if (s) {
+ std::cerr << "Warning: autocrop attribute in note font mapping file is no longer supported\n(all fonts are now always autocropped)" << std::endl;
+ }
+
+ s = attributes.value("smooth");
+ if (s)
+ m_smooth = (s.lower() == "true");
+
+ } else if (lcName == "font-sizes") {
+ }
+ else if (lcName == "font-size") {
+
+ QString s = attributes.value("note-height");
+ if (!s) {
+ m_errorString = "note-height is a required attribute of font-size";
+ return false;
+ }
+ int noteHeight = s.toInt();
+
+ SizeData &sizeData = m_sizes[noteHeight];
+
+ s = attributes.value("staff-line-thickness");
+ if (s)
+ sizeData.setStaffLineThickness(s.toInt());
+
+ s = attributes.value("leger-line-thickness");
+ if (s)
+ sizeData.setLegerLineThickness(s.toInt());
+
+ s = attributes.value("stem-thickness");
+ if (s)
+ sizeData.setStemThickness(s.toInt());
+
+ s = attributes.value("beam-thickness");
+ if (s)
+ sizeData.setBeamThickness(s.toInt());
+
+ s = attributes.value("stem-length");
+ if (s)
+ sizeData.setStemLength(s.toInt());
+
+ s = attributes.value("flag-spacing");
+ if (s)
+ sizeData.setFlagSpacing(s.toInt());
+
+ s = attributes.value("border-x");
+ if (s) {
+ std::cerr << "Warning: border-x attribute in note font mapping file is no longer supported\n(use hotspot-x for note head or flag)" << std::endl;
+ }
+
+ s = attributes.value("border-y");
+ if (s) {
+ std::cerr << "Warning: border-y attribute in note font mapping file is no longer supported" << std::endl;
+ }
+
+ int fontId = 0;
+ s = attributes.value("font-id");
+ if (s)
+ fontId = s.toInt();
+
+ s = attributes.value("font-height");
+ if (s)
+ sizeData.setFontHeight(fontId, s.toInt());
+
+ } else if (lcName == "font-scale") {
+
+ double fontHeight = -1.0;
+ double beamThickness = -1.0;
+ double stemLength = -1.0;
+ double flagSpacing = -1.0;
+ double staffLineThickness = -1.0;
+ double legerLineThickness = -1.0;
+ double stemThickness = -1.0;
+
+ QString s;
+
+ s = attributes.value("font-height");
+ if (s)
+ fontHeight = qstrtodouble(s);
+ else {
+ m_errorString = "font-height is a required attribute of font-scale";
+ return false;
+ }
+
+ s = attributes.value("staff-line-thickness");
+ if (s)
+ staffLineThickness = qstrtodouble(s);
+
+ s = attributes.value("leger-line-thickness");
+ if (s)
+ legerLineThickness = qstrtodouble(s);
+
+ s = attributes.value("stem-thickness");
+ if (s)
+ stemThickness = qstrtodouble(s);
+
+ s = attributes.value("beam-thickness");
+ if (s)
+ beamThickness = qstrtodouble(s);
+
+ s = attributes.value("stem-length");
+ if (s)
+ stemLength = qstrtodouble(s);
+
+ s = attributes.value("flag-spacing");
+ if (s)
+ flagSpacing = qstrtodouble(s);
+
+ int fontId = 0;
+ s = attributes.value("font-id");
+ if (s)
+ fontId = s.toInt();
+
+ //!!! need to be able to calculate max size -- checkFont needs
+ //to take a size argument; unfortunately Qt doesn't seem to be
+ //able to report to us when a scalable font was loaded in the
+ //wrong size, so large sizes might be significantly inaccurate
+ //as it just stops scaling up any further at somewhere around
+ //120px. We could test whether the metric for the black
+ //notehead is noticeably smaller than the notehead should be,
+ //and reject if so? [update -- no, that doesn't work either,
+ //Qt just returns the correct metric even if drawing the
+ //incorrect size]
+
+ for (int sz = 1; sz <= 30; sz += (sz == 1 ? 1 : 2)) {
+
+ SizeData & sizeData = m_sizes[sz];
+ unsigned int temp;
+
+ if (sizeData.getStaffLineThickness(temp) == false &&
+ staffLineThickness >= 0.0)
+ sizeData.setStaffLineThickness(toSize(sz, staffLineThickness, true));
+
+ if (sizeData.getLegerLineThickness(temp) == false &&
+ legerLineThickness >= 0.0)
+ sizeData.setLegerLineThickness(toSize(sz, legerLineThickness, true));
+
+ if (sizeData.getStemThickness(temp) == false &&
+ stemThickness >= 0.0)
+ sizeData.setStemThickness(toSize(sz, stemThickness, true));
+
+ if (sizeData.getBeamThickness(temp) == false &&
+ beamThickness >= 0.0)
+ sizeData.setBeamThickness(toSize(sz, beamThickness, true));
+
+ if (sizeData.getStemLength(temp) == false &&
+ stemLength >= 0.0)
+ sizeData.setStemLength(toSize(sz, stemLength, true));
+
+ if (sizeData.getFlagSpacing(temp) == false &&
+ flagSpacing >= 0.0)
+ sizeData.setFlagSpacing(toSize(sz, flagSpacing, true));
+
+ if (sizeData.getFontHeight(fontId, temp) == false)
+ sizeData.setFontHeight(fontId, toSize(sz, fontHeight, true));
+ }
+
+ } else if (lcName == "font-symbol-map") {
+ }
+ else if (lcName == "src-directory") {
+
+ QString d = attributes.value("name");
+ if (!d) {
+ m_errorString = "name is a required attribute of src-directory";
+ return false;
+ }
+
+ m_srcDirectory = qstrtostr(d);
+
+ } else if (lcName == "codebase") {
+
+ int bn = 0, fn = 0;
+ bool ok;
+ QString base = attributes.value("base");
+ if (!base) {
+ m_errorString = "base is a required attribute of codebase";
+ return false;
+ }
+ bn = base.toInt(&ok);
+ if (!ok || bn < 0) {
+ m_errorString =
+ QString("invalid base attribute \"%1\" (must be integer >= 0)").
+ arg(base);
+ return false;
+ }
+
+ QString fontId = attributes.value("font-id");
+ if (!fontId) {
+ m_errorString = "font-id is a required attribute of codebase";
+ return false;
+ }
+ fn = fontId.stripWhiteSpace().toInt(&ok);
+ if (!ok || fn < 0) {
+ m_errorString =
+ QString("invalid font-id attribute \"%1\" (must be integer >= 0)").
+ arg(fontId);
+ return false;
+ }
+
+ m_bases[fn] = bn;
+
+ } else if (lcName == "symbol") {
+
+ QString symbolName = attributes.value("name");
+ if (!symbolName) {
+ m_errorString = "name is a required attribute of symbol";
+ return false;
+ }
+ SymbolData symbolData;
+
+ QString src = attributes.value("src");
+ QString code = attributes.value("code");
+ QString glyph = attributes.value("glyph");
+
+ int icode = -1;
+ bool ok = false;
+ if (code) {
+ icode = code.stripWhiteSpace().toInt(&ok);
+ if (!ok || icode < 0) {
+ m_errorString =
+ QString("invalid code attribute \"%1\" (must be integer >= 0)").
+ arg(code);
+ return false;
+ }
+ symbolData.setCode(icode);
+ }
+
+ int iglyph = -1;
+ ok = false;
+ if (glyph) {
+ iglyph = glyph.stripWhiteSpace().toInt(&ok);
+ if (!ok || iglyph < 0) {
+ m_errorString =
+ QString("invalid glyph attribute \"%1\" (must be integer >= 0)").
+ arg(glyph);
+ return false;
+ }
+ symbolData.setGlyph(iglyph);
+ }
+
+ if (!src && icode < 0 && iglyph < 0) {
+ m_errorString = "symbol must have either src, code, or glyph attribute";
+ return false;
+ }
+ if (src)
+ symbolData.setSrc(qstrtostr(src));
+
+ QString inversionSrc = attributes.value("inversion-src");
+ if (inversionSrc)
+ symbolData.setInversionSrc(qstrtostr(inversionSrc));
+
+ QString inversionCode = attributes.value("inversion-code");
+ if (inversionCode) {
+ icode = inversionCode.stripWhiteSpace().toInt(&ok);
+ if (!ok || icode < 0) {
+ m_errorString =
+ QString("invalid inversion code attribute \"%1\" (must be integer >= 0)").
+ arg(inversionCode);
+ return false;
+ }
+ symbolData.setInversionCode(icode);
+ }
+
+ QString inversionGlyph = attributes.value("inversion-glyph");
+ if (inversionGlyph) {
+ iglyph = inversionGlyph.stripWhiteSpace().toInt(&ok);
+ if (!ok || iglyph < 0) {
+ m_errorString =
+ QString("invalid inversion glyph attribute \"%1\" (must be integer >= 0)").
+ arg(inversionGlyph);
+ return false;
+ }
+ symbolData.setInversionGlyph(iglyph);
+ }
+
+ QString fontId = attributes.value("font-id");
+ if (fontId) {
+ int n = fontId.stripWhiteSpace().toInt(&ok);
+ if (!ok || n < 0) {
+ m_errorString =
+ QString("invalid font-id attribute \"%1\" (must be integer >= 0)").
+ arg(fontId);
+ return false;
+ }
+ symbolData.setFontId(n);
+ }
+
+ m_data[qstrtostr(symbolName.upper())] = symbolData;
+
+ } else if (lcName == "font-hotspots") {
+ }
+ else if (lcName == "hotspot") {
+
+ QString s = attributes.value("name");
+ if (!s) {
+ m_errorString = "name is a required attribute of hotspot";
+ return false;
+ }
+ m_hotspotCharName = qstrtostr(s.upper());
+
+ } else if (lcName == "scaled") {
+
+ if (m_hotspotCharName == "") {
+ m_errorString = "scaled-element must be in hotspot-element";
+ return false;
+ }
+
+ QString s = attributes.value("x");
+ double x = -1.0;
+ if (s)
+ x = qstrtodouble(s);
+
+ s = attributes.value("y");
+ if (!s) {
+ m_errorString = "y is a required attribute of scaled";
+ return false;
+ }
+ double y = qstrtodouble(s);
+
+ HotspotDataMap::iterator i = m_hotspots.find(m_hotspotCharName);
+ if (i == m_hotspots.end()) {
+ m_hotspots[m_hotspotCharName] = HotspotData();
+ i = m_hotspots.find(m_hotspotCharName);
+ }
+
+ i->second.setScaledHotspot(x, y);
+
+ } else if (lcName == "fixed") {
+
+ if (m_hotspotCharName == "") {
+ m_errorString = "fixed-element must be in hotspot-element";
+ return false;
+ }
+
+ QString s = attributes.value("x");
+ int x = 0;
+ if (s)
+ x = s.toInt();
+
+ s = attributes.value("y");
+ int y = 0;
+ if (s)
+ y = s.toInt();
+
+ HotspotDataMap::iterator i = m_hotspots.find(m_hotspotCharName);
+ if (i == m_hotspots.end()) {
+ m_hotspots[m_hotspotCharName] = HotspotData();
+ i = m_hotspots.find(m_hotspotCharName);
+ }
+
+ i->second.addHotspot(0, x, y);
+
+ } else if (lcName == "when") {
+
+ if (m_hotspotCharName == "") {
+ m_errorString = "when-element must be in hotspot-element";
+ return false;
+ }
+
+ QString s = attributes.value("note-height");
+ if (!s) {
+ m_errorString = "note-height is a required attribute of when";
+ return false;
+ }
+ int noteHeight = s.toInt();
+
+ s = attributes.value("x");
+ int x = 0;
+ if (s)
+ x = s.toInt();
+
+ s = attributes.value("y");
+ if (!s) {
+ m_errorString = "y is a required attribute of when";
+ return false;
+ }
+ int y = s.toInt();
+
+ HotspotDataMap::iterator i = m_hotspots.find(m_hotspotCharName);
+ if (i == m_hotspots.end()) {
+ m_hotspots[m_hotspotCharName] = HotspotData();
+ i = m_hotspots.find(m_hotspotCharName);
+ }
+
+ i->second.addHotspot(noteHeight, x, y);
+
+ } else if (lcName == "font-requirements") {
+ }
+ else if (lcName == "font-requirement") {
+
+ QString id = attributes.value("font-id");
+ int n = -1;
+ bool ok = false;
+ if (id) {
+ n = id.stripWhiteSpace().toInt(&ok);
+ if (!ok) {
+ m_errorString =
+ QString("invalid font-id attribute \"%1\" (must be integer >= 0)").
+ arg(id);
+ return false;
+ }
+ } else {
+ m_errorString = "font-id is a required attribute of font-requirement";
+ return false;
+ }
+
+ QString name = attributes.value("name");
+ QString names = attributes.value("names");
+
+ if (name) {
+ if (names) {
+ m_errorString = "font-requirement may have name or names attribute, but not both";
+ return false;
+ }
+
+ SystemFont *font = SystemFont::loadSystemFont
+ (SystemFontSpec(name, 12));
+
+ if (font) {
+ m_systemFontNames[n] = name;
+ delete font;
+ } else {
+ std::cerr << QString("Warning: Unable to load font \"%1\"").arg(name) << std::endl;
+ m_ok = false;
+ }
+
+ } else if (names) {
+
+ bool have = false;
+ QStringList list = QStringList::split(",", names, false);
+ for (QStringList::Iterator i = list.begin(); i != list.end(); ++i) {
+ SystemFont *font = SystemFont::loadSystemFont
+ (SystemFontSpec(*i, 12));
+ if (font) {
+ m_systemFontNames[n] = *i;
+ have = true;
+ delete font;
+ break;
+ }
+ }
+ if (!have) {
+ std::cerr << QString("Warning: Unable to load any of the fonts in \"%1\"").
+ arg(names) << std::endl;
+ m_ok = false;
+ }
+
+ } else {
+ m_errorString = "font-requirement must have either name or names attribute";
+ return false;
+ }
+
+ QString s = attributes.value("strategy").lower();
+ SystemFont::Strategy strategy = SystemFont::PreferGlyphs;
+
+ if (s) {
+ if (s == "prefer-glyphs")
+ strategy = SystemFont::PreferGlyphs;
+ else if (s == "prefer-codes")
+ strategy = SystemFont::PreferCodes;
+ else if (s == "only-glyphs")
+ strategy = SystemFont::OnlyGlyphs;
+ else if (s == "only-codes")
+ strategy = SystemFont::OnlyCodes;
+ else {
+ std::cerr << "Warning: Unknown strategy value " << s
+ << " (known values are prefer-glyphs, prefer-codes,"
+ << " only-glyphs, only-codes)" << std::endl;
+ }
+ }
+
+ m_systemFontStrategies[n] = strategy;
+
+ } else {
+ }
+
+ if (m_characterDestination)
+ *m_characterDestination = "";
+ return true;
+}
+
+bool
+NoteFontMap::error(const QXmlParseException& exception)
+{
+ m_errorString = QString("%1 at line %2, column %3: %4")
+ .arg(exception.message())
+ .arg(exception.lineNumber())
+ .arg(exception.columnNumber())
+ .arg(m_errorString);
+ return QXmlDefaultHandler::error(exception);
+}
+
+bool
+NoteFontMap::fatalError(const QXmlParseException& exception)
+{
+ m_errorString = QString("%1 at line %2, column %3: %4")
+ .arg(exception.message())
+ .arg(exception.lineNumber())
+ .arg(exception.columnNumber())
+ .arg(m_errorString);
+ return QXmlDefaultHandler::fatalError(exception);
+}
+
+std::set<int>
+NoteFontMap::getSizes() const
+{
+ std::set<int> sizes;
+
+ for (SizeDataMap::const_iterator i = m_sizes.begin();
+ i != m_sizes.end(); ++i) {
+ sizes.insert(i->first);
+ }
+
+ return sizes;
+}
+
+std::set<CharName>
+NoteFontMap::getCharNames() const
+{
+ std::set<CharName> names;
+
+ for (SymbolDataMap::const_iterator i = m_data.begin();
+ i != m_data.end(); ++i) {
+ names.insert(i->first);
+ }
+
+ return names;
+}
+
+bool
+NoteFontMap::checkFile(int size, std::string &src) const
+{
+ QString pixmapFileMixedName = QString("%1/%2/%3/%4.xpm")
+ .arg(m_fontDirectory)
+ .arg(strtoqstr(m_srcDirectory))
+ .arg(size)
+ .arg(strtoqstr(src));
+
+ QFileInfo pixmapFileMixedInfo(pixmapFileMixedName);
+
+ if (!pixmapFileMixedInfo.isReadable()) {
+
+ QString pixmapFileLowerName = QString("%1/%2/%3/%4.xpm")
+ .arg(m_fontDirectory)
+ .arg(strtoqstr(m_srcDirectory).lower())
+ .arg(size)
+ .arg(strtoqstr(src));
+
+ QFileInfo pixmapFileLowerInfo(pixmapFileLowerName);
+
+ if (!pixmapFileLowerInfo.isReadable()) {
+ if (pixmapFileMixedName != pixmapFileLowerName) {
+ std::cerr << "Warning: Unable to open pixmap file "
+ << pixmapFileMixedName << " or " << pixmapFileLowerName
+ << std::endl;
+ } else {
+ std::cerr << "Warning: Unable to open pixmap file "
+ << pixmapFileMixedName << std::endl;
+ }
+ return false;
+ } else {
+ src = qstrtostr(pixmapFileLowerName);
+ }
+ } else {
+ src = qstrtostr(pixmapFileMixedName);
+ }
+
+ return true;
+}
+
+bool
+NoteFontMap::hasInversion(int, CharName charName) const
+{
+ SymbolDataMap::const_iterator i = m_data.find(charName);
+ if (i == m_data.end())
+ return false;
+ return i->second.hasInversion();
+}
+
+bool
+NoteFontMap::getSrc(int size, CharName charName, std::string &src) const
+{
+ SymbolDataMap::const_iterator i = m_data.find(charName);
+ if (i == m_data.end())
+ return false;
+
+ src = i->second.getSrc();
+ if (src == "")
+ return false;
+ return checkFile(size, src);
+}
+
+bool
+NoteFontMap::getInversionSrc(int size, CharName charName, std::string &src) const
+{
+ SymbolDataMap::const_iterator i = m_data.find(charName);
+ if (i == m_data.end())
+ return false;
+
+ if (!i->second.hasInversion())
+ return false;
+ src = i->second.getInversionSrc();
+ if (src == "")
+ return false;
+ return checkFile(size, src);
+}
+
+SystemFont *
+NoteFontMap::getSystemFont(int size, CharName charName, int &charBase)
+const
+{
+ SymbolDataMap::const_iterator i = m_data.find(charName);
+ if (i == m_data.end())
+ return false;
+
+ SizeDataMap::const_iterator si = m_sizes.find(size);
+ if (si == m_sizes.end())
+ return false;
+
+ int fontId = i->second.getFontId();
+
+ unsigned int fontHeight = 0;
+ if (!si->second.getFontHeight(fontId, fontHeight)) {
+ if (fontId == 0 || !si->second.getFontHeight(0, fontHeight)) {
+ fontHeight = size;
+ }
+ }
+
+ SystemFontNameMap::const_iterator fni = m_systemFontNames.find(fontId);
+ if (fontId < 0 || fni == m_systemFontNames.end())
+ return false;
+ QString fontName = fni->second;
+
+ CharBaseMap::const_iterator bi = m_bases.find(fontId);
+ if (bi == m_bases.end())
+ charBase = 0;
+ else
+ charBase = bi->second;
+
+ SystemFontSpec spec(fontName, fontHeight);
+ SystemFontMap::const_iterator fi = m_systemFontCache.find(spec);
+ if (fi != m_systemFontCache.end()) {
+ return fi->second;
+ }
+
+ SystemFont *font = SystemFont::loadSystemFont(spec);
+ if (!font)
+ return 0;
+ m_systemFontCache[spec] = font;
+
+ NOTATION_DEBUG << "NoteFontMap::getFont: loaded font " << fontName
+ << " at pixel size " << fontHeight << endl;
+
+ return font;
+}
+
+SystemFont::Strategy
+NoteFontMap::getStrategy(int, CharName charName) const
+{
+ SymbolDataMap::const_iterator i = m_data.find(charName);
+ if (i == m_data.end())
+ return SystemFont::PreferGlyphs;
+
+ int fontId = i->second.getFontId();
+ SystemFontStrategyMap::const_iterator si =
+ m_systemFontStrategies.find(fontId);
+
+ if (si != m_systemFontStrategies.end()) {
+ return si->second;
+ }
+
+ return SystemFont::PreferGlyphs;
+}
+
+bool
+NoteFontMap::getCode(int, CharName charName, int &code) const
+{
+ SymbolDataMap::const_iterator i = m_data.find(charName);
+ if (i == m_data.end())
+ return false;
+
+ code = i->second.getCode();
+ return (code >= 0);
+}
+
+bool
+NoteFontMap::getInversionCode(int, CharName charName, int &code) const
+{
+ SymbolDataMap::const_iterator i = m_data.find(charName);
+ if (i == m_data.end())
+ return false;
+
+ code = i->second.getInversionCode();
+ return (code >= 0);
+}
+
+bool
+NoteFontMap::getGlyph(int, CharName charName, int &glyph) const
+{
+ SymbolDataMap::const_iterator i = m_data.find(charName);
+ if (i == m_data.end())
+ return false;
+
+ glyph = i->second.getGlyph();
+ return (glyph >= 0);
+}
+
+bool
+NoteFontMap::getInversionGlyph(int, CharName charName, int &glyph) const
+{
+ SymbolDataMap::const_iterator i = m_data.find(charName);
+ if (i == m_data.end())
+ return false;
+
+ glyph = i->second.getInversionGlyph();
+ return (glyph >= 0);
+}
+
+bool
+NoteFontMap::getStaffLineThickness(int size, unsigned int &thickness) const
+{
+ SizeDataMap::const_iterator i = m_sizes.find(size);
+ if (i == m_sizes.end())
+ return false;
+
+ return i->second.getStaffLineThickness(thickness);
+}
+
+bool
+NoteFontMap::getLegerLineThickness(int size, unsigned int &thickness) const
+{
+ SizeDataMap::const_iterator i = m_sizes.find(size);
+ if (i == m_sizes.end())
+ return false;
+
+ return i->second.getLegerLineThickness(thickness);
+}
+
+bool
+NoteFontMap::getStemThickness(int size, unsigned int &thickness) const
+{
+ SizeDataMap::const_iterator i = m_sizes.find(size);
+ if (i == m_sizes.end())
+ return false;
+
+ return i->second.getStemThickness(thickness);
+}
+
+bool
+NoteFontMap::getBeamThickness(int size, unsigned int &thickness) const
+{
+ SizeDataMap::const_iterator i = m_sizes.find(size);
+ if (i == m_sizes.end())
+ return false;
+
+ return i->second.getBeamThickness(thickness);
+}
+
+bool
+NoteFontMap::getStemLength(int size, unsigned int &length) const
+{
+ SizeDataMap::const_iterator i = m_sizes.find(size);
+ if (i == m_sizes.end())
+ return false;
+
+ return i->second.getStemLength(length);
+}
+
+bool
+NoteFontMap::getFlagSpacing(int size, unsigned int &spacing) const
+{
+ SizeDataMap::const_iterator i = m_sizes.find(size);
+ if (i == m_sizes.end())
+ return false;
+
+ return i->second.getFlagSpacing(spacing);
+}
+
+bool
+NoteFontMap::getHotspot(int size, CharName charName, int width, int height,
+ int &x, int &y) const
+{
+ HotspotDataMap::const_iterator i = m_hotspots.find(charName);
+ if (i == m_hotspots.end())
+ return false;
+ return i->second.getHotspot(size, width, height, x, y);
+}
+
+bool
+NoteFontMap::HotspotData::getHotspot(int size, int width, int height,
+ int &x, int &y) const
+{
+ DataMap::const_iterator i = m_data.find(size);
+ if (i == m_data.end()) {
+ i = m_data.find(0); // fixed-pixel hotspot
+ x = 0;
+ if (m_scaled.first >= 0) {
+ x = toSize(width, m_scaled.first, false);
+ } else {
+ if (i != m_data.end()) {
+ x = i->second.first;
+ }
+ }
+ if (m_scaled.second >= 0) {
+ y = toSize(height, m_scaled.second, false);
+ return true;
+ } else {
+ if (i != m_data.end()) {
+ y = i->second.second;
+ return true;
+ }
+ return false;
+ }
+ }
+ x = i->second.first;
+ y = i->second.second;
+ return true;
+}
+
+QStringList
+NoteFontMap::getSystemFontNames() const
+{
+ QStringList names;
+ for (SystemFontNameMap::const_iterator i = m_systemFontNames.begin();
+ i != m_systemFontNames.end(); ++i) {
+ names.append(i->second);
+ }
+ return names;
+}
+
+void
+NoteFontMap::dump() const
+{
+ // debug code
+
+ std::cout << "Font data:\nName: " << getName() << "\nOrigin: " << getOrigin()
+ << "\nCopyright: " << getCopyright() << "\nMapped by: "
+ << getMappedBy() << "\nType: " << getType()
+ << "\nSmooth: " << isSmooth() << std::endl;
+
+ std::set<int> sizes = getSizes();
+ std::set<CharName> names = getCharNames();
+
+ for (std::set<int>::iterator sizei = sizes.begin(); sizei != sizes.end();
+ ++sizei) {
+
+ std::cout << "\nSize: " << *sizei << "\n" << std::endl;
+
+ unsigned int t = 0;
+
+ if (getStaffLineThickness(*sizei, t)) {
+ std::cout << "Staff line thickness: " << t << std::endl;
+ }
+
+ if (getLegerLineThickness(*sizei, t)) {
+ std::cout << "Leger line thickness: " << t << std::endl;
+ }
+
+ if (getStemThickness(*sizei, t)) {
+ std::cout << "Stem thickness: " << t << std::endl;
+ }
+
+ if (getBeamThickness(*sizei, t)) {
+ std::cout << "Beam thickness: " << t << std::endl;
+ }
+
+ if (getStemLength(*sizei, t)) {
+ std::cout << "Stem length: " << t << std::endl;
+ }
+
+ if (getFlagSpacing(*sizei, t)) {
+ std::cout << "Flag spacing: " << t << std::endl;
+ }
+
+ for (std::set<CharName>::iterator namei = names.begin();
+ namei != names.end(); ++namei) {
+
+ std::cout << "\nCharacter: " << namei->c_str() << std::endl;
+
+ std::string s;
+ int x, y, c;
+
+ if (getSrc(*sizei, *namei, s)) {
+ std::cout << "Src: " << s << std::endl;
+ }
+
+ if (getInversionSrc(*sizei, *namei, s)) {
+ std::cout << "Inversion src: " << s << std::endl;
+ }
+
+ if (getCode(*sizei, *namei, c)) {
+ std::cout << "Code: " << c << std::endl;
+ }
+
+ if (getInversionCode(*sizei, *namei, c)) {
+ std::cout << "Inversion code: " << c << std::endl;
+ }
+
+ if (getGlyph(*sizei, *namei, c)) {
+ std::cout << "Glyph: " << c << std::endl;
+ }
+
+ if (getInversionGlyph(*sizei, *namei, c)) {
+ std::cout << "Inversion glyph: " << c << std::endl;
+ }
+
+ if (getHotspot(*sizei, *namei, 1, 1, x, y)) {
+ std::cout << "Hot spot: (" << x << "," << y << ")" << std::endl;
+ }
+ }
+ }
+}
+
+}
diff --git a/src/gui/editors/notation/NoteFontMap.h b/src/gui/editors/notation/NoteFontMap.h
new file mode 100644
index 0000000..119db76
--- /dev/null
+++ b/src/gui/editors/notation/NoteFontMap.h
@@ -0,0 +1,333 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTEFONTMAP_H_
+#define _RG_NOTEFONTMAP_H_
+
+#include "base/Exception.h"
+#include <map>
+#include <set>
+#include <string>
+#include "SystemFont.h"
+#include <qstring.h>
+#include <qstringlist.h>
+#include <utility>
+#include <qxml.h>
+#include "gui/editors/notation/NoteCharacterNames.h"
+
+
+class QXmlParseException;
+class QXmlAttributes;
+
+
+namespace Rosegarden
+{
+
+
+
+class NoteFontMap : public QXmlDefaultHandler
+{
+public:
+ typedef Exception MappingFileReadFailed;
+
+ NoteFontMap(std::string name); // load and parse the XML mapping file
+ ~NoteFontMap();
+
+ /**
+ * ok() returns false if the file read succeeded but the font
+ * relies on system fonts that are not available. (If the file
+ * read fails, the constructor throws MappingFileReadFailed.)
+ */
+ bool ok() const { return m_ok; }
+
+ std::string getName() const { return m_name; }
+ std::string getOrigin() const { return m_origin; }
+ std::string getCopyright() const { return m_copyright; }
+ std::string getMappedBy() const { return m_mappedBy; }
+ std::string getType() const { return m_type; }
+ bool isSmooth() const { return m_smooth; }
+
+ std::set<int> getSizes() const;
+ std::set<CharName> getCharNames() const;
+
+ bool getStaffLineThickness(int size, unsigned int &thickness) const;
+ bool getLegerLineThickness(int size, unsigned int &thickness) const;
+ bool getStemThickness(int size, unsigned int &thickness) const;
+ bool getBeamThickness(int size, unsigned int &thickness) const;
+ bool getStemLength(int size, unsigned int &length) const;
+ bool getFlagSpacing(int size, unsigned int &spacing) const;
+
+ bool hasInversion(int size, CharName charName) const;
+
+ bool getSrc(int size, CharName charName, std::string &src) const;
+ bool getInversionSrc(int size, CharName charName, std::string &src) const;
+
+ SystemFont *getSystemFont(int size, CharName charName, int &charBase) const;
+ SystemFont::Strategy getStrategy(int size, CharName charName) const;
+ bool getCode(int size, CharName charName, int &code) const;
+ bool getInversionCode(int size, CharName charName, int &code) const;
+ bool getGlyph(int size, CharName charName, int &glyph) const;
+ bool getInversionGlyph(int size, CharName charName, int &glyph) const;
+
+ bool getHotspot(int size, CharName charName, int width, int height,
+ int &x, int &y) const;
+
+ // Xml handler methods:
+
+ virtual bool startElement
+ (const QString& namespaceURI, const QString& localName,
+ const QString& qName, const QXmlAttributes& atts);
+
+ virtual bool characters(QString &);
+
+ bool error(const QXmlParseException& exception);
+ bool fatalError(const QXmlParseException& exception);
+
+ void dump() const;
+
+ // Not for general use, but very handy for diagnostic display
+ QStringList getSystemFontNames() const;
+
+ // want this to be private, but need access from HotspotData
+ static int toSize(int baseSize, double factor, bool limitAtOne);
+
+private:
+ class SymbolData
+ {
+ public:
+ SymbolData() : m_fontId(0),
+ m_src(""), m_inversionSrc(""),
+ m_code(-1), m_inversionCode(-1),
+ m_glyph(-1), m_inversionGlyph(-1) { }
+ ~SymbolData() { }
+
+ void setFontId(int id) { m_fontId = id; }
+ int getFontId() const { return m_fontId; }
+
+ void setSrc(std::string src) { m_src = src; }
+ std::string getSrc() const { return m_src; }
+
+ void setCode(int code) { m_code = code; }
+ int getCode() const { return m_code; }
+
+ void setGlyph(int glyph) { m_glyph = glyph; }
+ int getGlyph() const { return m_glyph; }
+
+ void setInversionSrc(std::string inversion) { m_inversionSrc = inversion; }
+ std::string getInversionSrc() const { return m_inversionSrc; }
+
+ void setInversionCode(int code) { m_inversionCode = code; }
+ int getInversionCode() const { return m_inversionCode; }
+
+ void setInversionGlyph(int glyph) { m_inversionGlyph = glyph; }
+ int getInversionGlyph() const { return m_inversionGlyph; }
+
+ bool hasInversion() const {
+ return m_inversionGlyph >= 0 ||
+ m_inversionCode >= 0 ||
+ m_inversionSrc != "";
+ }
+
+ private:
+ int m_fontId;
+ std::string m_src;
+ std::string m_inversionSrc;
+ int m_code;
+ int m_inversionCode;
+ int m_glyph;
+ int m_inversionGlyph;
+ };
+
+ class HotspotData
+ {
+ private:
+ typedef std::pair<int, int> Point;
+ typedef std::pair<double, double> ScaledPoint;
+ typedef std::map<int, Point> DataMap;
+
+ public:
+ HotspotData() : m_scaled(-1.0, -1.0) { }
+ ~HotspotData() { }
+
+ void addHotspot(int size, int x, int y) {
+ m_data[size] = Point(x, y);
+ }
+
+ void setScaledHotspot(double x, double y) {
+ m_scaled = ScaledPoint(x, y);
+ }
+
+ bool getHotspot(int size, int width, int height, int &x, int &y) const;
+
+ private:
+ DataMap m_data;
+ ScaledPoint m_scaled;
+ };
+
+ class SizeData
+ {
+ public:
+ SizeData() : m_stemThickness(-1),
+ m_beamThickness(-1),
+ m_stemLength(-1),
+ m_flagSpacing(-1),
+ m_staffLineThickness(-1),
+ m_legerLineThickness(-1) { }
+ ~SizeData() { }
+
+ void setStemThickness(unsigned int i) {
+ m_stemThickness = (int)i;
+ }
+ void setBeamThickness(unsigned int i) {
+ m_beamThickness = (int)i;
+ }
+ void setStemLength(unsigned int i) {
+ m_stemLength = (int)i;
+ }
+ void setFlagSpacing(unsigned int i) {
+ m_flagSpacing = (int)i;
+ }
+ void setStaffLineThickness(unsigned int i) {
+ m_staffLineThickness = (int)i;
+ }
+ void setLegerLineThickness(unsigned int i) {
+ m_legerLineThickness = (int)i;
+ }
+ void setFontHeight(int fontId, unsigned int h) {
+ m_fontHeights[fontId] = (int)h;
+ }
+
+ bool getStemThickness(unsigned int &i) const {
+ if (m_stemThickness >= 0) {
+ i = (unsigned int)m_stemThickness;
+ return true;
+ } else return false;
+ }
+
+ bool getBeamThickness(unsigned int &i) const {
+ if (m_beamThickness >= 0) {
+ i = (unsigned int)m_beamThickness;
+ return true;
+ } else return false;
+ }
+
+ bool getStemLength(unsigned int &i) const {
+ if (m_stemLength >= 0) {
+ i = (unsigned int)m_stemLength;
+ return true;
+ } else return false;
+ }
+
+ bool getFlagSpacing(unsigned int &i) const {
+ if (m_flagSpacing >= 0) {
+ i = (unsigned int)m_flagSpacing;
+ return true;
+ } else return false;
+ }
+
+ bool getStaffLineThickness(unsigned int &i) const {
+ if (m_staffLineThickness >= 0) {
+ i = (unsigned int)m_staffLineThickness;
+ return true;
+ } else return false;
+ }
+
+ bool getLegerLineThickness(unsigned int &i) const {
+ if (m_legerLineThickness >= 0) {
+ i = (unsigned int)m_legerLineThickness;
+ return true;
+ } else return false;
+ }
+
+ bool getFontHeight(int fontId, unsigned int &h) const {
+ std::map<int, int>::const_iterator fhi = m_fontHeights.find(fontId);
+ if (fhi != m_fontHeights.end()) {
+ h = (unsigned int)fhi->second;
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ int m_stemThickness;
+ int m_beamThickness;
+ int m_stemLength;
+ int m_flagSpacing;
+ int m_staffLineThickness;
+ int m_legerLineThickness;
+ std::map<int, int> m_fontHeights; // per-font-id
+ };
+
+ //--------------- Data members ---------------------------------
+
+ std::string m_name;
+ std::string m_origin;
+ std::string m_copyright;
+ std::string m_mappedBy;
+ std::string m_type;
+ bool m_smooth;
+
+ std::string m_srcDirectory;
+
+ typedef std::map<CharName, SymbolData> SymbolDataMap;
+ SymbolDataMap m_data;
+
+ typedef std::map<CharName, HotspotData> HotspotDataMap;
+ HotspotDataMap m_hotspots;
+
+ typedef std::map<int, SizeData> SizeDataMap;
+ SizeDataMap m_sizes;
+
+ typedef std::map<int, QString> SystemFontNameMap;
+ SystemFontNameMap m_systemFontNames;
+
+ typedef std::map<int, SystemFont::Strategy> SystemFontStrategyMap;
+ SystemFontStrategyMap m_systemFontStrategies;
+
+ typedef std::map<SystemFontSpec, SystemFont *> SystemFontMap;
+ mutable SystemFontMap m_systemFontCache;
+
+ typedef std::map<int, int> CharBaseMap;
+ CharBaseMap m_bases;
+
+ // For use when reading the XML file:
+ bool m_expectingCharacters;
+ std::string *m_characterDestination;
+ std::string m_hotspotCharName;
+ QString m_errorString;
+
+ bool checkFile(int size, std::string &src) const;
+ QString m_fontDirectory;
+
+ bool m_ok;
+};
+
+
+class NoteCharacterDrawRep;
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NoteFontViewer.cpp b/src/gui/editors/notation/NoteFontViewer.cpp
new file mode 100644
index 0000000..81f07e9
--- /dev/null
+++ b/src/gui/editors/notation/NoteFontViewer.cpp
@@ -0,0 +1,125 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NoteFontViewer.h"
+
+#include <klocale.h>
+#include "FontViewFrame.h"
+#include <kcombobox.h>
+#include <kdialogbase.h>
+#include <ktoolbar.h>
+#include <qlabel.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qvbox.h>
+#include <qwidget.h>
+
+
+namespace Rosegarden
+{
+
+void
+NoteFontViewer::slotViewChanged(int i)
+{
+ m_frame->setGlyphs(i == 0);
+
+ m_rows->clear();
+ int firstRow = -1;
+
+ for (int r = 0; r < 256; ++r) {
+ if (m_frame->hasRow(r)) {
+ m_rows->insertItem(QString("%1").arg(r));
+ if (firstRow < 0)
+ firstRow = r;
+ }
+ }
+
+ if (firstRow < 0) {
+ m_rows->setEnabled(false);
+ m_frame->setRow(0);
+ } else {
+ m_rows->setEnabled(true);
+ m_frame->setRow(firstRow);
+ }
+}
+
+void
+NoteFontViewer::slotRowChanged(const QString &s)
+{
+ bool ok;
+ int i = s.toInt(&ok);
+ if (ok)
+ m_frame->setRow(i);
+}
+
+void
+NoteFontViewer::slotFontChanged(const QString &s)
+{
+ m_frame->setFont(s);
+ slotViewChanged(m_view->currentItem());
+}
+
+NoteFontViewer::NoteFontViewer(QWidget *parent, QString noteFontName,
+ QStringList fontNames, int pixelSize) :
+ KDialogBase(parent, 0, true,
+ i18n("Note Font Viewer: %1").arg(noteFontName), Close)
+{
+ QVBox *box = makeVBoxMainWidget();
+ KToolBar* controls = new KToolBar(box);
+ controls->setMargin(3);
+
+ (void) new QLabel(i18n(" Component: "), controls);
+ m_font = new KComboBox(controls);
+
+ for (QStringList::iterator i = fontNames.begin(); i != fontNames.end();
+ ++i) {
+ m_font->insertItem(*i);
+ }
+
+ (void) new QLabel(i18n(" View: "), controls);
+ m_view = new KComboBox(controls);
+
+ m_view->insertItem(i18n("Glyphs"));
+ m_view->insertItem(i18n("Codes"));
+
+ (void) new QLabel(i18n(" Page: "), controls);
+ m_rows = new KComboBox(controls);
+
+ m_frame = new FontViewFrame(pixelSize, box);
+
+ connect(m_font, SIGNAL(activated(const QString &)),
+ this, SLOT(slotFontChanged(const QString &)));
+
+ connect(m_view, SIGNAL(activated(int)),
+ this, SLOT(slotViewChanged(int)));
+
+ connect(m_rows, SIGNAL(activated(const QString &)),
+ this, SLOT(slotRowChanged(const QString &)));
+
+ slotFontChanged(m_font->currentText());
+}
+
+}
+#include "NoteFontViewer.moc"
diff --git a/src/gui/editors/notation/NoteFontViewer.h b/src/gui/editors/notation/NoteFontViewer.h
new file mode 100644
index 0000000..31c8613
--- /dev/null
+++ b/src/gui/editors/notation/NoteFontViewer.h
@@ -0,0 +1,68 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTEFONTVIEWER_H_
+#define _RG_NOTEFONTVIEWER_H_
+
+#include <kdialogbase.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+
+class QWidget;
+class KComboBox;
+
+
+namespace Rosegarden
+{
+
+class FontViewFrame;
+
+
+class NoteFontViewer : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ NoteFontViewer(QWidget *parent, QString noteFontName,
+ QStringList systemFontNames, int pixelSize);
+
+protected slots:
+ void slotFontChanged(const QString &);
+ void slotViewChanged(int);
+ void slotRowChanged(const QString &);
+
+private:
+ KComboBox *m_font;
+ KComboBox *m_view;
+ KComboBox *m_rows;
+ FontViewFrame *m_frame;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NoteInserter.cpp b/src/gui/editors/notation/NoteInserter.cpp
new file mode 100644
index 0000000..66adafe
--- /dev/null
+++ b/src/gui/editors/notation/NoteInserter.cpp
@@ -0,0 +1,722 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NoteInserter.h"
+#include "misc/Debug.h"
+#include <kapplication.h>
+
+#include "base/BaseProperties.h"
+#include <klocale.h>
+#include "misc/Strings.h"
+#include "document/ConfigGroups.h"
+#include "base/Event.h"
+#include "base/NotationTypes.h"
+#include "base/Segment.h"
+#include "base/Staff.h"
+#include "base/ViewElement.h"
+#include "commands/notation/NoteInsertionCommand.h"
+#include "commands/notation/RestInsertionCommand.h"
+#include "commands/notation/TupletCommand.h"
+#include "gui/general/EditTool.h"
+#include "gui/general/LinedStaff.h"
+#include "gui/general/RosegardenCanvasView.h"
+#include "NotationProperties.h"
+#include "NotationStrings.h"
+#include "NotationTool.h"
+#include "NotationView.h"
+#include "NotationStaff.h"
+#include "NotePixmapFactory.h"
+#include "NoteStyleFactory.h"
+#include <kaction.h>
+#include <kcommand.h>
+#include <kconfig.h>
+#include <qiconset.h>
+#include <qregexp.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+NoteInserter::NoteInserter(NotationView* view)
+ : NotationTool("NoteInserter", view),
+ m_noteType(Note::Quaver),
+ m_noteDots(0),
+ m_autoBeam(true),
+ m_accidental(Accidentals::NoAccidental),
+ m_lastAccidental(Accidentals::NoAccidental),
+ m_followAccidental(false)
+{
+ QIconSet icon;
+
+ KConfig *config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+ m_autoBeam = config->readBoolEntry("autobeam", true);
+ m_matrixInsertType = (config->readNumEntry("inserttype", 0) > 0);
+ m_defaultStyle = qstrtostr(config->readEntry
+ ("style", strtoqstr(NoteStyleFactory::DefaultStyle)));
+
+ KToggleAction *autoBeamAction =
+ new KToggleAction(i18n("Auto-Beam when appropriate"), 0, this,
+ SLOT(slotToggleAutoBeam()), actionCollection(),
+ "toggle_auto_beam");
+ autoBeamAction->setChecked(m_autoBeam);
+
+ for (unsigned int i = 0; i < 6; ++i) {
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap(m_actionsAccidental[i][3])));
+ KRadioAction* noteAction = new KRadioAction(i18n(m_actionsAccidental[i][0]),
+ icon, 0, this,
+ m_actionsAccidental[i][1],
+ actionCollection(),
+ m_actionsAccidental[i][2]);
+ noteAction->setExclusiveGroup("accidentals");
+ }
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("dotted-crotchet")));
+ new KToggleAction(i18n("Dotted note"), icon, 0, this,
+ SLOT(slotToggleDot()), actionCollection(),
+ "toggle_dot");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("select")));
+ new KAction(i18n("Switch to Select Tool"), icon, 0, this,
+ SLOT(slotSelectSelected()), actionCollection(),
+ "select");
+
+ new KAction(i18n("Switch to Erase Tool"), "eraser", 0, this,
+ SLOT(slotEraseSelected()), actionCollection(),
+ "erase");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("rest-crotchet")));
+ new KAction(i18n("Switch to Inserting Rests"), icon, 0, this,
+ SLOT(slotRestsSelected()), actionCollection(),
+ "rests");
+
+ createMenu("noteinserter.rc");
+
+ connect(m_parentView, SIGNAL(changeAccidental(Accidental, bool)),
+ this, SLOT(slotSetAccidental(Accidental, bool)));
+}
+
+NoteInserter::NoteInserter(const QString& menuName, NotationView* view)
+ : NotationTool(menuName, view),
+ m_noteType(Note::Quaver),
+ m_noteDots(0),
+ m_autoBeam(false),
+ m_clickHappened(false),
+ m_accidental(Accidentals::NoAccidental),
+ m_lastAccidental(Accidentals::NoAccidental),
+ m_followAccidental(false)
+{
+ connect(m_parentView, SIGNAL(changeAccidental(Accidental, bool)),
+ this, SLOT(slotSetAccidental(Accidental, bool)));
+}
+
+NoteInserter::~NoteInserter()
+{}
+
+void NoteInserter::ready()
+{
+ m_clickHappened = false;
+ m_nParentView->setCanvasCursor(Qt::crossCursor);
+ m_nParentView->setHeightTracking(true);
+}
+
+void
+NoteInserter::handleLeftButtonPress(timeT,
+ int,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement*)
+{
+ if (staffNo < 0)
+ return ;
+ computeLocationAndPreview(e);
+}
+
+int
+NoteInserter::handleMouseMove(timeT,
+ int,
+ QMouseEvent *e)
+{
+ if (m_clickHappened) {
+ computeLocationAndPreview(e);
+ }
+
+ return RosegardenCanvasView::NoFollow;
+}
+
+void
+NoteInserter::handleMouseRelease(timeT,
+ int,
+ QMouseEvent *e)
+{
+ if (!m_clickHappened)
+ return ;
+ bool okay = computeLocationAndPreview(e);
+ m_clickHappened = false;
+ if (!okay)
+ return ;
+ clearPreview();
+
+ Note note(m_noteType, m_noteDots);
+ timeT endTime = m_clickTime + note.getDuration();
+ Segment &segment = m_nParentView->getStaff(m_clickStaffNo)->getSegment();
+
+ Segment::iterator realEnd = segment.findTime(endTime);
+ if (!segment.isBeforeEndMarker( realEnd) ||
+ !segment.isBeforeEndMarker(++realEnd)) {
+ endTime = segment.getEndMarkerTime();
+ } else {
+ endTime = std::max(endTime, (*realEnd)->getNotationAbsoluteTime());
+ }
+
+ Event *lastInsertedEvent = doAddCommand
+ (segment, m_clickTime, endTime, note, m_clickPitch,
+ (m_accidental == Accidentals::NoAccidental &&
+ m_followAccidental) ?
+ m_lastAccidental : m_accidental);
+
+ if (lastInsertedEvent) {
+
+ m_nParentView->setSingleSelectedEvent
+ (m_clickStaffNo, lastInsertedEvent);
+
+ if (m_nParentView->isInChordMode()) {
+ m_nParentView->slotSetInsertCursorAndRecentre
+ (lastInsertedEvent->getAbsoluteTime(), e->x(), (int)e->y(),
+ false);
+ } else {
+ m_nParentView->slotSetInsertCursorAndRecentre
+ (lastInsertedEvent->getAbsoluteTime() +
+ lastInsertedEvent->getDuration(), e->x(), (int)e->y(),
+ false);
+ }
+ }
+}
+
+void
+NoteInserter::insertNote(Segment &segment, timeT insertionTime,
+ int pitch, Accidental accidental,
+ bool suppressPreview)
+{
+ Note note(m_noteType, m_noteDots);
+ timeT endTime = insertionTime + note.getDuration();
+
+ Segment::iterator realEnd = segment.findTime(endTime);
+ if (!segment.isBeforeEndMarker( realEnd) ||
+ !segment.isBeforeEndMarker(++realEnd)) {
+ endTime = segment.getEndMarkerTime();
+ } else {
+ endTime = std::max(endTime, (*realEnd)->getNotationAbsoluteTime());
+ }
+
+ Event *lastInsertedEvent = doAddCommand
+ (segment, insertionTime, endTime, note, pitch, accidental);
+
+ if (lastInsertedEvent) {
+
+ m_nParentView->setSingleSelectedEvent(segment, lastInsertedEvent);
+
+ if (m_nParentView->isInChordMode()) {
+ m_nParentView->slotSetInsertCursorPosition
+ (lastInsertedEvent->getAbsoluteTime(), true, false);
+ } else {
+ m_nParentView->slotSetInsertCursorPosition
+ (lastInsertedEvent->getAbsoluteTime() +
+ lastInsertedEvent->getDuration(), true, false);
+ }
+ }
+
+ if (!suppressPreview)
+ m_nParentView->playNote(segment, pitch);
+}
+
+bool
+NoteInserter::computeLocationAndPreview(QMouseEvent *e)
+{
+ double x = e->x();
+ int y = (int)e->y();
+
+ LinedStaff *staff = m_nParentView->getStaffForCanvasCoords(e->x(), y);
+ if (!staff) {
+ clearPreview();
+ return false;
+ }
+
+ int staffNo = staff->getId();
+ if (m_clickHappened && staffNo != m_clickStaffNo) {
+ // abandon
+ clearPreview();
+ return false;
+ }
+
+ // If we're inserting grace notes, then we need to "dress to the
+ // right", as it were
+ bool grace = m_nParentView->isInGraceMode();
+
+ int height = staff->getHeightAtCanvasCoords(x, y);
+
+ Event *clefEvt = 0, *keyEvt = 0;
+ Clef clef;
+ Rosegarden::Key key;
+
+ NotationElementList::iterator itr =
+ staff->getElementUnderCanvasCoords(x, y, clefEvt, keyEvt);
+ if (itr == staff->getViewElementList()->end()) {
+ clearPreview();
+ return false;
+ }
+
+ NotationElement* el = static_cast<NotationElement*>(*itr);
+
+ timeT time = el->event()->getAbsoluteTime(); // not getViewAbsoluteTime()
+ m_clickInsertX = el->getLayoutX();
+ if (clefEvt)
+ clef = Clef(*clefEvt);
+ if (keyEvt)
+ key = Rosegarden::Key(*keyEvt);
+
+ int subordering = el->event()->getSubOrdering();
+ float targetSubordering = subordering;
+
+ if (grace && el->getCanvasItem()) {
+
+ NotationStaff *ns = dynamic_cast<NotationStaff *>(staff);
+ if (!ns) {
+ std::cerr << "WARNING: NoteInserter: Staff is not a NotationStaff"
+ << std::endl;
+ } else {
+ std::cerr << "x=" << x << ", el->getCanvasX()=" << el->getCanvasX() << std::endl;
+ if (el->isRest()) std::cerr << "elt is a rest" << std::endl;
+ if (x - el->getCanvasX() >
+ ns->getNotePixmapFactory(false).getNoteBodyWidth()) {
+ NotationElementList::iterator j(itr);
+ while (++j != staff->getViewElementList()->end()) {
+ NotationElement *candidate = static_cast<NotationElement *>(*j);
+ if ((candidate->isNote() || candidate->isRest()) &&
+ (candidate->getViewAbsoluteTime()
+ > el->getViewAbsoluteTime() ||
+ candidate->event()->getSubOrdering()
+ > el->event()->getSubOrdering())) {
+ itr = j;
+ el = candidate;
+ m_clickInsertX = el->getLayoutX();
+ time = el->event()->getAbsoluteTime();
+ subordering = el->event()->getSubOrdering();
+ targetSubordering = subordering;
+ break;
+ }
+ }
+ }
+ }
+
+ if (x - el->getCanvasX() < 1) {
+ targetSubordering -= 0.5;
+ }
+ }
+
+ if (el->isRest() && el->getCanvasItem()) {
+ time += getOffsetWithinRest(staffNo, itr, x);
+ m_clickInsertX += (x - el->getCanvasX());
+ }
+
+ Pitch p(height, clef, key, m_accidental);
+ int pitch = p.getPerformancePitch();
+
+ // [RFE 987960] When inserting via mouse, if no accidental is
+ // selected, we use the same accidental (and thus the same pitch)
+ // as of the previous note found at this height -- iff such a note
+ // is found more recently than the last key signature.
+
+ if (m_accidental == Accidentals::NoAccidental &&
+ m_followAccidental) {
+ Segment &segment = staff->getSegment();
+ m_lastAccidental = m_accidental;
+ Segment::iterator i = segment.findNearestTime(time);
+ while (i != segment.end()) {
+ if ((*i)->isa(Rosegarden::Key::EventType)) break;
+ if ((*i)->isa(Note::EventType)) {
+ if ((*i)->has(NotationProperties::HEIGHT_ON_STAFF) &&
+ (*i)->has(BaseProperties::PITCH)) {
+ int h = (*i)->get<Int>(NotationProperties::HEIGHT_ON_STAFF);
+ if (h == height) {
+ pitch = (*i)->get<Int>(BaseProperties::PITCH);
+ (*i)->get<String>(BaseProperties::ACCIDENTAL,
+ m_lastAccidental);
+ break;
+ }
+ }
+ }
+ if (i == segment.begin()) break;
+ --i;
+ }
+ }
+
+ bool changed = false;
+
+ if (m_clickHappened) {
+ if (time != m_clickTime ||
+ subordering != m_clickSubordering ||
+ pitch != m_clickPitch ||
+ height != m_clickHeight ||
+ staffNo != m_clickStaffNo) {
+ changed = true;
+ }
+ } else {
+ m_clickHappened = true;
+ changed = true;
+ }
+
+ if (changed) {
+ m_clickTime = time;
+ m_clickSubordering = subordering;
+ m_clickPitch = pitch;
+ m_clickHeight = height;
+ m_clickStaffNo = staffNo;
+ m_targetSubordering = targetSubordering;
+
+ showPreview();
+ }
+
+ return true;
+}
+
+void NoteInserter::showPreview()
+{
+ Segment &segment = m_nParentView->getStaff(m_clickStaffNo)->getSegment();
+
+ int pitch = m_clickPitch;
+ pitch += getOttavaShift(segment, m_clickTime) * 12;
+
+ m_nParentView->showPreviewNote(m_clickStaffNo, m_clickInsertX,
+ pitch, m_clickHeight,
+ Note(m_noteType, m_noteDots),
+ m_nParentView->isInGraceMode());
+}
+
+void NoteInserter::clearPreview()
+{
+ m_nParentView->clearPreviewNote();
+}
+
+timeT
+NoteInserter::getOffsetWithinRest(int staffNo,
+ const NotationElementList::iterator &i,
+ double &canvasX) // will be snapped
+{
+ //!!! To make this work correctly in tuplet mode, our divisor would
+ // have to be the tupletified duration of the tuplet unit -- we can
+ // do that, we just haven't yet
+ if (m_nParentView->isInTripletMode())
+ return 0;
+
+ Staff *staff = m_nParentView->getStaff(staffNo);
+ NotationElement* el = static_cast<NotationElement*>(*i);
+ if (!el->getCanvasItem())
+ return 0;
+ double offset = canvasX - el->getCanvasX();
+
+ if (offset < 0)
+ return 0;
+
+ double airX, airWidth;
+ el->getLayoutAirspace(airX, airWidth);
+ double origin = ((*i)->getLayoutX() - airX) / 2;
+ double width = airWidth - origin;
+
+ timeT duration = (*i)->getViewDuration();
+
+ TimeSignature timeSig =
+ staff->getSegment().getComposition()->getTimeSignatureAt
+ ((*i)->event()->getAbsoluteTime());
+ timeT unit = timeSig.getUnitDuration();
+
+ int unitCount = duration / unit;
+ if (unitCount > 1) {
+
+ timeT result = (int)((offset / width) * unitCount);
+ if (result > unitCount - 1)
+ result = unitCount - 1;
+
+ double visibleWidth(airWidth);
+ NotationElementList::iterator j(i);
+ if (++j != staff->getViewElementList()->end()) {
+ visibleWidth = (*j)->getLayoutX() - (*i)->getLayoutX();
+ }
+ offset = (visibleWidth * result) / unitCount;
+ canvasX = el->getCanvasX() + offset;
+
+ result *= unit;
+ return result;
+ }
+
+ return 0;
+}
+
+int
+NoteInserter::getOttavaShift(Segment &segment, timeT time)
+{
+ // Find out whether we're in an ottava section.
+
+ int ottavaShift = 0;
+
+ for (Segment::iterator i = segment.findTime(time); ; --i) {
+
+ if (!segment.isBeforeEndMarker(i)) {
+ break;
+ }
+
+ if ((*i)->isa(Indication::EventType)) {
+ try {
+ Indication ind(**i);
+ if (ind.isOttavaType()) {
+ timeT endTime =
+ (*i)->getNotationAbsoluteTime() +
+ (*i)->getNotationDuration();
+ if (time < endTime) {
+ ottavaShift = ind.getOttavaShift();
+ }
+ break;
+ }
+ } catch (...) { }
+ }
+
+ if (i == segment.begin()) {
+ break;
+ }
+ }
+
+ return ottavaShift;
+}
+
+Event *
+NoteInserter::doAddCommand(Segment &segment, timeT time, timeT endTime,
+ const Note &note, int pitch, Accidental accidental)
+{
+ timeT noteEnd = time + note.getDuration();
+
+ // #1046934: make it possible to insert triplet at end of segment!
+ if (m_nParentView->isInTripletMode()) {
+ noteEnd = time + (note.getDuration() * 2 / 3);
+ }
+
+ if (time < segment.getStartTime() ||
+ endTime > segment.getEndMarkerTime() ||
+ noteEnd > segment.getEndMarkerTime()) {
+ return 0;
+ }
+
+ pitch += getOttavaShift(segment, time) * 12;
+
+ float targetSubordering = 0;
+ if (m_nParentView->isInGraceMode()) {
+ targetSubordering = m_targetSubordering;
+ }
+
+ NoteInsertionCommand *insertionCommand =
+ new NoteInsertionCommand
+ (segment, time, endTime, note, pitch, accidental,
+ (m_autoBeam && !m_nParentView->isInTripletMode() && !m_nParentView->isInGraceMode()) ?
+ NoteInsertionCommand::AutoBeamOn : NoteInsertionCommand::AutoBeamOff,
+ m_matrixInsertType && !m_nParentView->isInGraceMode() ?
+ NoteInsertionCommand::MatrixModeOn : NoteInsertionCommand::MatrixModeOff,
+ m_nParentView->isInGraceMode() ?
+ (m_nParentView->isInTripletMode() ?
+ NoteInsertionCommand::GraceAndTripletModesOn :
+ NoteInsertionCommand::GraceModeOn)
+ : NoteInsertionCommand::GraceModeOff,
+ targetSubordering,
+ m_defaultStyle);
+
+ KCommand *activeCommand = insertionCommand;
+
+ if (m_nParentView->isInTripletMode() && !m_nParentView->isInGraceMode()) {
+ Segment::iterator i(segment.findTime(time));
+ if (i != segment.end() &&
+ !(*i)->has(BaseProperties::BEAMED_GROUP_TUPLET_BASE)) {
+
+ KMacroCommand *command = new KMacroCommand(insertionCommand->name());
+
+ //## Attempted fix to bug reported on rg-user by SlowPic
+ //## <[email protected]> 28/02/2005 22:32:56 UTC: Triplet input error
+ //# HJJ: Comment out this attempt. It breaks the splitting of
+ //# the first bars into rests.
+ //## if ((*i)->isa(Note::EventRestType) &&
+ //## (*i)->getNotationDuration() > (note.getDuration() * 3)) {
+ // split the rest
+ command->addCommand(new RestInsertionCommand
+ (segment, time,
+ time + note.getDuration() * 2,
+ Note::getNearestNote(note.getDuration() * 2)));
+ //## }
+ //# These comments should probably be deleted.
+
+ command->addCommand(new TupletCommand
+ (segment, time, note.getDuration(),
+ 3, 2, true)); // #1046934: "has timing already"
+ command->addCommand(insertionCommand);
+ activeCommand = command;
+ }
+ }
+
+ m_nParentView->addCommandToHistory(activeCommand);
+
+ NOTATION_DEBUG << "NoteInserter::doAddCommand: accidental is "
+ << accidental << endl;
+
+ return insertionCommand->getLastInsertedEvent();
+}
+
+void NoteInserter::slotSetNote(Note::Type nt)
+{
+ m_noteType = nt;
+}
+
+void NoteInserter::slotSetDots(unsigned int dots)
+{
+ m_noteDots = dots;
+
+ KToggleAction *dotsAction = dynamic_cast<KToggleAction *>
+ (actionCollection()->action("toggle_dot"));
+ if (dotsAction)
+ dotsAction->setChecked(dots > 0);
+}
+
+void NoteInserter::slotSetAccidental(Accidental accidental,
+ bool follow)
+{
+ NOTATION_DEBUG << "NoteInserter::setAccidental: accidental is "
+ << accidental << endl;
+ m_accidental = accidental;
+ m_followAccidental = follow;
+}
+
+void NoteInserter::slotNoAccidental()
+{
+ m_parentView->actionCollection()->action("no_accidental")->activate();
+}
+
+void NoteInserter::slotFollowAccidental()
+{
+ m_parentView->actionCollection()->action("follow_accidental")->activate();
+}
+
+void NoteInserter::slotSharp()
+{
+ m_parentView->actionCollection()->action("sharp_accidental")->activate();
+}
+
+void NoteInserter::slotFlat()
+{
+ m_parentView->actionCollection()->action("flat_accidental")->activate();
+}
+
+void NoteInserter::slotNatural()
+{
+ m_parentView->actionCollection()->action("natural_accidental")->activate();
+}
+
+void NoteInserter::slotDoubleSharp()
+{
+ m_parentView->actionCollection()->action("double_sharp_accidental")->activate();
+}
+
+void NoteInserter::slotDoubleFlat()
+{
+ m_parentView->actionCollection()->action("double_flat_accidental")->activate();
+}
+
+void NoteInserter::slotToggleDot()
+{
+ m_noteDots = (m_noteDots) ? 0 : 1;
+ Note note(m_noteType, m_noteDots);
+ QString actionName(NotationStrings::getReferenceName(note));
+ actionName.replace(QRegExp("-"), "_");
+ KAction *action = m_parentView->actionCollection()->action(actionName);
+ if (!action) {
+ std::cerr << "WARNING: No such action as " << actionName << std::endl;
+ } else {
+ action->activate();
+ }
+}
+
+void NoteInserter::slotToggleAutoBeam()
+{
+ m_autoBeam = !m_autoBeam;
+}
+
+void NoteInserter::slotEraseSelected()
+{
+ m_parentView->actionCollection()->action("erase")->activate();
+}
+
+void NoteInserter::slotSelectSelected()
+{
+ m_parentView->actionCollection()->action("select")->activate();
+}
+
+void NoteInserter::slotRestsSelected()
+{
+ Note note(m_noteType, m_noteDots);
+ QString actionName(NotationStrings::getReferenceName(note, true));
+ actionName.replace(QRegExp("-"), "_");
+ KAction *action = m_parentView->actionCollection()->action(actionName);
+ if (!action) {
+ std::cerr << "WARNING: No such action as " << actionName << std::endl;
+ } else {
+ action->activate();
+ }
+}
+
+const char* NoteInserter::m_actionsAccidental[][4] =
+{
+ { "No accidental", "1slotNoAccidental()", "no_accidental",
+ "accidental-none" },
+ { "Follow accidental", "1slotFollowAccidental()", "follow_accidental",
+ "accidental-follow" },
+ { "Sharp", "1slotSharp()", "sharp_accidental",
+ "accidental-sharp" },
+ { "Flat", "1slotFlat()", "flat_accidental",
+ "accidental-flat" },
+ { "Natural", "1slotNatural()", "natural_accidental",
+ "accidental-natural" },
+ { "Double sharp", "1slotDoubleSharp()", "double_sharp_accidental",
+ "accidental-doublesharp" },
+ { "Double flat", "1slotDoubleFlat()", "double_flat_accidental",
+ "accidental-doubleflat" }
+};
+
+const QString NoteInserter::ToolName = "noteinserter";
+
+}
+#include "NoteInserter.moc"
diff --git a/src/gui/editors/notation/NoteInserter.h b/src/gui/editors/notation/NoteInserter.h
new file mode 100644
index 0000000..cb46b38
--- /dev/null
+++ b/src/gui/editors/notation/NoteInserter.h
@@ -0,0 +1,166 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTEINSERTER_H_
+#define _RG_NOTEINSERTER_H_
+
+#include "base/NotationTypes.h"
+#include "NotationTool.h"
+#include "NotationElement.h"
+#include "NoteStyle.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class Segment;
+class NotationView;
+class Event;
+
+
+/**
+ * This tool will insert notes on mouse click events
+ */
+class NoteInserter : public NotationTool
+{
+ Q_OBJECT
+
+ friend class NotationToolBox;
+
+public:
+ ~NoteInserter();
+
+ virtual void handleLeftButtonPress(timeT,
+ int height,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement* el);
+
+ virtual int handleMouseMove(timeT time,
+ int height,
+ QMouseEvent*);
+
+ virtual void handleMouseRelease(timeT time,
+ int height,
+ QMouseEvent*);
+
+ virtual void ready();
+
+ Note getCurrentNote() {
+ return Note(m_noteType, m_noteDots);
+ }
+
+ /// Insert a note as if the user has clicked at the given time & pitch
+ void insertNote(Segment &segment,
+ timeT insertionTime,
+ int pitch,
+ Accidental accidental,
+ bool suppressPreview = false);
+
+ static const QString ToolName;
+
+public slots:
+ /// Set the type of note (quaver, breve...) which will be inserted
+ void slotSetNote(Note::Type);
+
+ /// Set the nb of dots the inserted note will have
+ void slotSetDots(unsigned int dots);
+
+ /// Set the accidental for the notes which will be inserted
+ void slotSetAccidental(Accidental, bool follow);
+
+protected:
+ NoteInserter(NotationView*);
+
+ /// this ctor is used by RestInserter
+ NoteInserter(const QString& menuName, NotationView*);
+
+ timeT getOffsetWithinRest(int staffNo,
+ const NotationElementList::iterator&,
+ double &canvasX);
+
+ int getOttavaShift(Segment &segment, timeT time);
+
+ virtual Event *doAddCommand(Segment &,
+ timeT time,
+ timeT endTime,
+ const Note &,
+ int pitch, Accidental);
+
+ virtual bool computeLocationAndPreview(QMouseEvent *e);
+ virtual void showPreview();
+ virtual void clearPreview();
+
+protected slots:
+ // RMB menu slots
+ void slotNoAccidental();
+ void slotFollowAccidental();
+ void slotSharp();
+ void slotFlat();
+ void slotNatural();
+ void slotDoubleSharp();
+ void slotDoubleFlat();
+ void slotToggleDot();
+ void slotToggleAutoBeam();
+
+ void slotEraseSelected();
+ void slotSelectSelected();
+ void slotRestsSelected();
+
+protected:
+ //--------------- Data members ---------------------------------
+
+ Note::Type m_noteType;
+ unsigned int m_noteDots;
+ bool m_autoBeam;
+ bool m_matrixInsertType;
+ NoteStyleName m_defaultStyle;
+
+ bool m_clickHappened;
+ timeT m_clickTime;
+ int m_clickSubordering;
+ int m_clickPitch;
+ int m_clickHeight;
+ int m_clickStaffNo;
+ double m_clickInsertX;
+ float m_targetSubordering;
+
+ Accidental m_accidental;
+ Accidental m_lastAccidental;
+ bool m_followAccidental;
+
+ static const char* m_actionsAccidental[][4];
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotePixmapFactory.cpp b/src/gui/editors/notation/NotePixmapFactory.cpp
new file mode 100644
index 0000000..c2a99ee
--- /dev/null
+++ b/src/gui/editors/notation/NotePixmapFactory.cpp
@@ -0,0 +1,3689 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 <cmath>
+#include "NotePixmapFactory.h"
+#include "misc/Debug.h"
+#include "base/NotationRules.h"
+#include <kapplication.h>
+
+#include <klocale.h>
+#include <kstddirs.h>
+#include <kconfig.h>
+#include "misc/Strings.h"
+#include "document/ConfigGroups.h"
+#include "base/Exception.h"
+#include "base/NotationTypes.h"
+#include "base/Profiler.h"
+#include "gui/editors/guitar/Fingering.h"
+#include "gui/editors/guitar/FingeringBox.h"
+#include "gui/editors/guitar/NoteSymbols.h"
+#include "gui/editors/notation/TrackHeader.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/PixmapFunctions.h"
+#include "gui/general/Spline.h"
+#include "gui/kdeext/KStartupLogo.h"
+#include "NotationStrings.h"
+#include "NotationView.h"
+#include "NoteCharacter.h"
+#include "NoteCharacterNames.h"
+#include "NoteFontFactory.h"
+#include "NoteFont.h"
+#include "NotePixmapParameters.h"
+#include "NotePixmapPainter.h"
+#include "NoteStyleFactory.h"
+#include "NoteStyle.h"
+#include <kglobal.h>
+#include <kmessagebox.h>
+#include <qbitmap.h>
+#include <qcolor.h>
+#include <qfile.h>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qimage.h>
+#include <qpainter.h>
+#include <qpen.h>
+#include <qpixmap.h>
+#include <qpointarray.h>
+#include <qpoint.h>
+#include <qrect.h>
+#include <qstring.h>
+#include <qwmatrix.h>
+
+
+namespace Rosegarden
+{
+
+using namespace Accidentals;
+
+static clock_t drawBeamsTime = 0;
+static clock_t makeNotesTime = 0;
+static int drawBeamsCount = 0;
+static int drawBeamsBeamCount = 0;
+
+class NotePixmapCache : public std::map<CharName, QCanvasPixmap*>
+{
+ // nothing to add -- just so we can predeclare it in the header
+};
+
+const char* const NotePixmapFactory::defaultSerifFontFamily = "Bitstream Vera Serif";
+const char* const NotePixmapFactory::defaultSansSerifFontFamily = "Bitstream Vera Sans";
+const char* const NotePixmapFactory::defaultTimeSigFontFamily = "Bitstream Vera Serif";
+
+NotePixmapFactory::NotePixmapFactory(std::string fontName, int size) :
+ m_selected(false),
+ m_shaded(false),
+ m_tupletCountFont(defaultSerifFontFamily, 8, QFont::Bold),
+ m_tupletCountFontMetrics(m_tupletCountFont),
+ m_textMarkFont(defaultSerifFontFamily, 8, QFont::Bold, true),
+ m_textMarkFontMetrics(m_textMarkFont),
+ m_fingeringFont(defaultSerifFontFamily, 8, QFont::Bold),
+ m_fingeringFontMetrics(m_fingeringFont),
+ m_timeSigFont(defaultTimeSigFontFamily, 8, QFont::Bold),
+ m_timeSigFontMetrics(m_timeSigFont),
+ m_bigTimeSigFont(defaultTimeSigFontFamily, 12, QFont::Normal),
+ m_bigTimeSigFontMetrics(m_bigTimeSigFont),
+ m_ottavaFont(defaultSerifFontFamily, 8, QFont::Normal, true),
+ m_ottavaFontMetrics(m_ottavaFont),
+ m_clefOttavaFont(defaultSerifFontFamily, 8, QFont::Normal),
+ m_clefOttavaFontMetrics(m_ottavaFont),
+ m_trackHeaderFont(defaultSansSerifFontFamily, 10, QFont::Normal),
+ m_trackHeaderFontMetrics(m_trackHeaderFont),
+ m_trackHeaderBoldFont(defaultSansSerifFontFamily, 10, QFont::Bold),
+ m_trackHeaderBoldFontMetrics(m_trackHeaderBoldFont),
+ m_generatedPixmap(0),
+ m_generatedMask(0),
+ m_generatedWidth( -1),
+ m_generatedHeight( -1),
+ m_inPrinterMethod(false),
+ m_p(new NotePixmapPainter()),
+ m_dottedRestCache(new NotePixmapCache)
+{
+ init(fontName, size);
+}
+
+NotePixmapFactory::NotePixmapFactory(const NotePixmapFactory &npf) :
+ m_selected(false),
+ m_shaded(false),
+ m_tupletCountFont(npf.m_tupletCountFont),
+ m_tupletCountFontMetrics(m_tupletCountFont),
+ m_textMarkFont(npf.m_textMarkFont),
+ m_textMarkFontMetrics(m_textMarkFont),
+ m_fingeringFont(npf.m_fingeringFont),
+ m_fingeringFontMetrics(m_fingeringFont),
+ m_timeSigFont(npf.m_timeSigFont),
+ m_timeSigFontMetrics(m_timeSigFont),
+ m_bigTimeSigFont(npf.m_bigTimeSigFont),
+ m_bigTimeSigFontMetrics(m_bigTimeSigFont),
+ m_ottavaFont(defaultSerifFontFamily, 8, QFont::Normal, true),
+ m_ottavaFontMetrics(m_ottavaFont),
+ m_clefOttavaFont(defaultSerifFontFamily, 8, QFont::Normal),
+ m_clefOttavaFontMetrics(m_ottavaFont),
+ m_trackHeaderFont(defaultSansSerifFontFamily, 10, QFont::Normal),
+ m_trackHeaderFontMetrics(m_trackHeaderFont),
+ m_trackHeaderBoldFont(defaultSansSerifFontFamily, 10, QFont::Bold),
+ m_trackHeaderBoldFontMetrics(m_trackHeaderBoldFont),
+ m_generatedPixmap(0),
+ m_generatedMask(0),
+ m_generatedWidth( -1),
+ m_generatedHeight( -1),
+ m_inPrinterMethod(false),
+ m_p(new NotePixmapPainter()),
+ m_dottedRestCache(new NotePixmapCache)
+{
+ init(npf.m_font->getName(), npf.m_font->getSize());
+}
+
+NotePixmapFactory &
+NotePixmapFactory::operator=(const NotePixmapFactory &npf)
+{
+ if (&npf != this) {
+ m_selected = npf.m_selected;
+ m_shaded = npf.m_shaded;
+ m_timeSigFont = npf.m_timeSigFont;
+ m_timeSigFontMetrics = QFontMetrics(m_timeSigFont);
+ m_bigTimeSigFont = npf.m_bigTimeSigFont;
+ m_bigTimeSigFontMetrics = QFontMetrics(m_bigTimeSigFont);
+ m_tupletCountFont = npf.m_tupletCountFont;
+ m_tupletCountFontMetrics = QFontMetrics(m_tupletCountFont);
+ m_textMarkFont = npf.m_textMarkFont;
+ m_textMarkFontMetrics = QFontMetrics(m_textMarkFont);
+ m_fingeringFont = npf.m_fingeringFont;
+ m_fingeringFontMetrics = QFontMetrics(m_fingeringFont);
+ m_ottavaFont = npf.m_ottavaFont;
+ m_ottavaFontMetrics = QFontMetrics(m_ottavaFont);
+ m_clefOttavaFont = npf.m_clefOttavaFont;
+ m_clefOttavaFontMetrics = QFontMetrics(m_clefOttavaFont);
+ m_trackHeaderFont = npf.m_trackHeaderFont;
+ m_trackHeaderFontMetrics = QFontMetrics(m_trackHeaderFont);
+ m_trackHeaderBoldFont = npf.m_trackHeaderBoldFont;
+ m_trackHeaderBoldFontMetrics = QFontMetrics(m_trackHeaderBoldFont);
+ init(npf.m_font->getName(), npf.m_font->getSize());
+ m_dottedRestCache->clear();
+ m_textFontCache.clear();
+ }
+ return *this;
+}
+
+void
+NotePixmapFactory::init(std::string fontName, int size)
+{
+ try {
+ m_style = NoteStyleFactory::getStyle(NoteStyleFactory::DefaultStyle);
+ } catch (NoteStyleFactory::StyleUnavailable u) {
+ KStartupLogo::hideIfStillThere();
+ KMessageBox::error(0, i18n(strtoqstr(u.getMessage())));
+ throw;
+ }
+
+ int origSize = size;
+
+ if (fontName != "") {
+ try {
+ if (size < 0)
+ size = NoteFontFactory::getDefaultSize(fontName);
+ m_font = NoteFontFactory::getFont(fontName, size);
+ } catch (Exception f) {
+ fontName = "";
+ // fall through
+ }
+ }
+
+ if (fontName == "") { // either because it was passed in or because read failed
+ try {
+ fontName = NoteFontFactory::getDefaultFontName();
+ size = origSize;
+ if (size < 0)
+ size = NoteFontFactory::getDefaultSize(fontName);
+ m_font = NoteFontFactory::getFont(fontName, size);
+ } catch (Exception f) { // already reported
+ throw;
+ }
+ }
+
+ // Resize the fonts, because the original constructor used point
+ // sizes only and we want pixels
+ QFont timeSigFont(defaultTimeSigFontFamily),
+ textFont(defaultSerifFontFamily);
+ KConfig* config = kapp->config();
+ config->setGroup(NotationViewConfigGroup);
+
+ m_timeSigFont = config->readFontEntry("timesigfont", &timeSigFont);
+ m_timeSigFont.setBold(true);
+ m_timeSigFont.setPixelSize(size * 5 / 2);
+ m_timeSigFontMetrics = QFontMetrics(m_timeSigFont);
+
+ m_bigTimeSigFont = config->readFontEntry("timesigfont", &timeSigFont);
+ m_bigTimeSigFont.setPixelSize(size * 4 + 2);
+ m_bigTimeSigFontMetrics = QFontMetrics(m_bigTimeSigFont);
+
+ m_tupletCountFont = config->readFontEntry("textfont", &textFont);
+ m_tupletCountFont.setBold(true);
+ m_tupletCountFont.setPixelSize(size * 2);
+ m_tupletCountFontMetrics = QFontMetrics(m_tupletCountFont);
+
+ m_textMarkFont = config->readFontEntry("textfont", &textFont);
+ m_textMarkFont.setBold(true);
+ m_textMarkFont.setItalic(true);
+ m_textMarkFont.setPixelSize(size * 2);
+ m_textMarkFontMetrics = QFontMetrics(m_textMarkFont);
+
+ m_fingeringFont = config->readFontEntry("textfont", &textFont);
+ m_fingeringFont.setBold(true);
+ m_fingeringFont.setPixelSize(size * 5 / 3);
+ m_fingeringFontMetrics = QFontMetrics(m_fingeringFont);
+
+ m_ottavaFont = config->readFontEntry("textfont", &textFont);
+ m_ottavaFont.setPixelSize(size * 2);
+ m_ottavaFontMetrics = QFontMetrics(m_ottavaFont);
+
+ m_clefOttavaFont = config->readFontEntry("textfont", &textFont);
+ m_clefOttavaFont.setPixelSize(getLineSpacing() * 3 / 2);
+ m_clefOttavaFontMetrics = QFontMetrics(m_clefOttavaFont);
+
+ m_trackHeaderFont = config->readFontEntry("sansfont", &m_trackHeaderFont);
+ m_trackHeaderFont.setPixelSize(12);
+ m_trackHeaderFontMetrics = QFontMetrics(m_trackHeaderFont);
+
+ m_trackHeaderBoldFont = m_trackHeaderFont;
+ m_trackHeaderBoldFont.setBold(true);
+ m_trackHeaderBoldFontMetrics = QFontMetrics(m_trackHeaderBoldFont);
+}
+
+NotePixmapFactory::~NotePixmapFactory()
+{
+ delete m_p;
+ delete m_dottedRestCache;
+}
+
+std::string
+NotePixmapFactory::getFontName() const
+{
+ return m_font->getName();
+}
+
+int
+NotePixmapFactory::getSize() const
+{
+ return m_font->getSize();
+}
+
+QPixmap
+NotePixmapFactory::toQPixmap(QCanvasPixmap* cp)
+{
+ QPixmap p = *cp;
+ delete cp;
+ return p;
+}
+
+void
+NotePixmapFactory::dumpStats(std::ostream &s)
+{
+#ifdef DUMP_STATS
+ s << "NotePixmapFactory: total times since last stats dump:\n"
+ << "makeNotePixmap: "
+ << (makeNotesTime * 1000 / CLOCKS_PER_SEC) << "ms\n"
+ << "drawBeams: "
+ << (drawBeamsTime * 1000 / CLOCKS_PER_SEC) << "ms"
+ << " (drew " << drawBeamsCount << " individual points in " << drawBeamsBeamCount << " beams)"
+ << endl;
+ makeNotesTime = 0;
+ drawBeamsTime = 0;
+ drawBeamsCount = 0;
+ drawBeamsBeamCount = 0;
+#endif
+
+ (void)s; // avoid warnings
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeNotePixmap(const NotePixmapParameters &params)
+{
+ Profiler profiler("NotePixmapFactory::makeNotePixmap");
+ clock_t startTime = clock();
+
+ drawNoteAux(params, 0, 0, 0);
+
+ QPoint hotspot(m_left, m_above + m_noteBodyHeight / 2);
+
+ //#define ROSE_DEBUG_NOTE_PIXMAP_FACTORY
+#ifdef ROSE_DEBUG_NOTE_PIXMAP_FACTORY
+
+ m_p->painter().setPen(Qt::red);
+ m_p->painter().setBrush(Qt::red);
+
+ m_p->drawLine(0, 0, 0, m_generatedHeight - 1);
+ m_p->drawLine(m_generatedWidth - 1, 0,
+ m_generatedWidth - 1,
+ m_generatedHeight - 1);
+
+ {
+ int hsx = hotspot.x();
+ int hsy = hotspot.y();
+ m_p->drawLine(hsx - 2, hsy - 2, hsx + 2, hsy + 2);
+ m_p->drawLine(hsx - 2, hsy + 2, hsx + 2, hsy - 2);
+ }
+#endif
+
+ clock_t endTime = clock();
+ makeNotesTime += (endTime - startTime);
+
+ return makeCanvasPixmap(hotspot);
+}
+
+void
+NotePixmapFactory::drawNote(const NotePixmapParameters &params,
+ QPainter &painter, int x, int y)
+{
+ Profiler profiler("NotePixmapFactory::drawNote");
+ m_inPrinterMethod = true;
+ drawNoteAux(params, &painter, x, y);
+ m_inPrinterMethod = false;
+}
+
+void
+NotePixmapFactory::drawNoteAux(const NotePixmapParameters &params,
+ QPainter *painter, int x, int y)
+{
+ NoteFont::CharacterType charType = m_inPrinterMethod ? NoteFont::Printer : NoteFont::Screen;
+
+ bool drawFlag = params.m_drawFlag;
+
+ if (params.m_beamed)
+ drawFlag = false;
+
+ // A note pixmap is formed of note head, stem, flags,
+ // accidentals, dots and beams. Assume the note head first, then
+ // do the rest of the calculations left to right, ie accidentals,
+ // stem, flags, dots, beams
+
+ m_noteBodyWidth = getNoteBodyWidth(params.m_noteType);
+ m_noteBodyHeight = getNoteBodyHeight(params.m_noteType);
+
+ // Spacing surrounding the note head. For top and bottom, we
+ // adjust this according to the discrepancy between the nominal
+ // and actual heights of the note head pixmap. For left and
+ // right, we use the hotspot x coordinate of the head.
+ int temp;
+ if (!m_font->getHotspot(m_style->getNoteHeadCharName(params.m_noteType).first,
+ m_borderX, temp))
+ m_borderX = 0;
+
+ if (params.m_noteType == Note::Minim && params.m_stemGoesUp)
+ m_borderX++;
+ int actualNoteBodyHeight =
+ m_font->getHeight(m_style->getNoteHeadCharName(params.m_noteType).first);
+
+ m_left = m_right = m_borderX;
+ m_above = m_borderY = (actualNoteBodyHeight - m_noteBodyHeight) / 2;
+ m_below = (actualNoteBodyHeight - m_noteBodyHeight) - m_above;
+
+ // NOTATION_DEBUG << "actualNoteBodyHeight: " << actualNoteBodyHeight
+ // << ", noteBodyHeight: " << m_noteBodyHeight << ", borderX: "
+ // << m_borderX << ", borderY: "
+ // << m_borderY << endl;
+
+ bool isStemmed = m_style->hasStem(params.m_noteType);
+ int flagCount = m_style->getFlagCount(params.m_noteType);
+ int slashCount = params.m_slashes;
+ if (!slashCount)
+ slashCount = m_style->getSlashCount(params.m_noteType);
+
+ if (params.m_accidental != NoAccidental) {
+ makeRoomForAccidental(params.m_accidental,
+ params.m_cautionary,
+ params.m_accidentalShift,
+ params.m_accidentalExtra);
+ }
+
+ NoteCharacter dot(getCharacter(NoteCharacterNames::DOT, PlainColour, charType));
+ int dotWidth = dot.getWidth();
+ if (dotWidth < getNoteBodyWidth() / 2)
+ dotWidth = getNoteBodyWidth() / 2;
+
+ int stemLength = getStemLength(params);
+
+ if (params.m_marks.size() > 0) {
+ makeRoomForMarks(isStemmed, params, stemLength);
+ }
+
+ if (params.m_legerLines != 0) {
+ makeRoomForLegerLines(params);
+ }
+
+ if (slashCount > 0) {
+ m_left = std::max(m_left, m_noteBodyWidth / 2);
+ m_right = std::max(m_right, m_noteBodyWidth / 2);
+ }
+
+ if (params.m_tupletCount > 0) {
+ makeRoomForTuplingLine(params);
+ }
+
+ m_right = std::max(m_right, params.m_dots * dotWidth + dotWidth / 2);
+ if (params.m_dotShifted) {
+ m_right += m_noteBodyWidth;
+ }
+ if (params.m_onLine) {
+ m_above = std::max(m_above, dot.getHeight() / 2);
+ }
+
+ if (params.m_shifted) {
+ if (params.m_stemGoesUp) {
+ m_right += m_noteBodyWidth;
+ } else {
+ m_left = std::max(m_left, m_noteBodyWidth);
+ }
+ }
+
+ bool tieAbove = params.m_tieAbove;
+ if (!params.m_tiePositionExplicit) {
+ tieAbove = !params.m_stemGoesUp;
+ }
+
+ if (params.m_tied) {
+ m_right = std::max(m_right, params.m_tieLength);
+ if (!tieAbove) {
+ m_below = std::max(m_below, m_noteBodyHeight * 2);
+ } else {
+ m_above = std::max(m_above, m_noteBodyHeight * 2);
+ }
+ }
+
+ QPoint startPoint, endPoint;
+ if (isStemmed && params.m_drawStem) {
+ makeRoomForStemAndFlags(drawFlag ? flagCount : 0, stemLength, params,
+ startPoint, endPoint);
+ }
+
+ if (isStemmed && params.m_drawStem && params.m_beamed) {
+ makeRoomForBeams(params);
+ }
+
+ // for all other calculations we use the nominal note-body height
+ // (same as the gap between staff lines), but here we want to know
+ // if the pixmap itself is taller than that
+ /*!!!
+ int actualNoteBodyHeight = m_font->getHeight
+ (m_style->getNoteHeadCharName(params.m_noteType).first);
+ // - 2*m_origin.y();
+ if (actualNoteBodyHeight > m_noteBodyHeight) {
+ m_below = std::max(m_below, actualNoteBodyHeight - m_noteBodyHeight);
+ }
+ */
+ if (painter) {
+ painter->save();
+ m_p->beginExternal(painter);
+ // NOTATION_DEBUG << "Translate: (" << x << "," << y << ")" << endl;
+ painter->translate(x - m_left, y - m_above - m_noteBodyHeight / 2);
+ } else {
+ createPixmapAndMask(m_noteBodyWidth + m_left + m_right,
+ m_noteBodyHeight + m_above + m_below);
+ }
+
+ if (params.m_tupletCount > 0) {
+ drawTuplingLine(params);
+ }
+
+ if (isStemmed && params.m_drawStem && drawFlag) {
+ drawFlags(flagCount, params, startPoint, endPoint);
+ }
+
+ if (params.m_accidental != NoAccidental) {
+ drawAccidental(params.m_accidental, params.m_cautionary);
+ }
+
+ NoteStyle::CharNameRec charNameRec
+ (m_style->getNoteHeadCharName(params.m_noteType));
+ CharName charName = charNameRec.first;
+ bool inverted = charNameRec.second;
+ NoteCharacter body = getCharacter
+ (charName,
+ params.m_highlighted ? HighlightedColour :
+ params.m_quantized ? QuantizedColour :
+ params.m_trigger ? TriggerColour :
+ params.m_inRange ? PlainColour : OutRangeColour,
+ inverted);
+
+ QPoint bodyLocation(m_left - m_borderX,
+ m_above - m_borderY + getStaffLineThickness() / 2);
+ if (params.m_shifted) {
+ if (params.m_stemGoesUp) {
+ bodyLocation.rx() += m_noteBodyWidth;
+ } else {
+ bodyLocation.rx() -= m_noteBodyWidth - 1;
+ }
+ }
+
+ m_p->drawNoteCharacter(bodyLocation.x(), bodyLocation.y(), body);
+
+ if (params.m_dots > 0) {
+
+ int x = m_left + m_noteBodyWidth + dotWidth / 2;
+ int y = m_above + m_noteBodyHeight / 2 - dot.getHeight() / 2;
+
+ if (params.m_onLine)
+ y -= m_noteBodyHeight / 2;
+
+ if (params.m_shifted)
+ x += m_noteBodyWidth;
+ else if (params.m_dotShifted)
+ x += m_noteBodyWidth;
+
+ for (int i = 0; i < params.m_dots; ++i) {
+ m_p->drawNoteCharacter(x, y, dot);
+ x += dotWidth;
+ }
+ }
+
+ if (isStemmed && params.m_drawStem) {
+
+ if (flagCount > 0 && !drawFlag && params.m_beamed) {
+ drawBeams(endPoint, params, flagCount);
+ }
+
+ if (slashCount > 0) {
+ drawSlashes(startPoint, params, slashCount);
+ }
+
+ if (m_selected)
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ else
+ m_p->painter().setPen(Qt::black);
+
+ // If we draw stems after beams, instead of beams after stems,
+ // beam anti-aliasing won't damage stems but we have to shorten the
+ // stems slightly first so that the stems don't extend all the way
+ // through the beam into the anti-aliased region on the
+ // other side of the beam that faces away from the note-heads.
+ int shortening;
+ if (flagCount > 0 && !drawFlag && params.m_beamed)
+ shortening = 2;
+ else
+ shortening = 0;
+ drawStem(params, startPoint, endPoint, shortening);
+ }
+
+ if (params.m_marks.size() > 0) {
+ drawMarks(isStemmed, params, stemLength);
+ }
+
+ if (params.m_legerLines != 0) {
+ drawLegerLines(params);
+ }
+
+ if (params.m_tied) {
+ drawTie(tieAbove, params.m_tieLength, dotWidth * params.m_dots);
+ }
+
+ if (painter) {
+ painter->restore();
+ }
+}
+
+
+QCanvasPixmap*
+NotePixmapFactory::makeNoteHaloPixmap(const NotePixmapParameters &params)
+{
+ int nbh0 = getNoteBodyHeight();
+ int nbh = getNoteBodyHeight(params.m_noteType);
+ int nbw0 = getNoteBodyHeight();
+ int nbw = getNoteBodyWidth(params.m_noteType);
+
+ createPixmapAndMask(nbw + nbw0, nbh + nbh0);
+ drawNoteHalo(0, 0, nbw + nbw0, nbh + nbh0);
+
+ return makeCanvasPixmap(QPoint(nbw0 / 2, nbh0));
+}
+
+
+void
+NotePixmapFactory::drawNoteHalo(int x, int y, int w, int h) {
+
+ m_p->painter().setPen(QPen(QColor(GUIPalette::CollisionHaloHue,
+ GUIPalette::CollisionHaloSaturation,
+ 255, QColor::Hsv), 1));
+ m_p->painter().setBrush(QColor(GUIPalette::CollisionHaloHue,
+ GUIPalette::CollisionHaloSaturation,
+ 255, QColor::Hsv));
+ m_p->drawEllipse(x, y, w, h);
+}
+
+
+
+int
+NotePixmapFactory::getStemLength(const NotePixmapParameters &params) const
+{
+ if (params.m_beamed && params.m_stemLength >= 0) {
+ return params.m_stemLength;
+ }
+
+ int stemLength = getStemLength();
+
+ int flagCount = m_style->getFlagCount(params.m_noteType);
+ int slashCount = params.m_slashes;
+ bool stemUp = params.m_stemGoesUp;
+ int nbh = m_noteBodyHeight;
+
+ if (flagCount > 2) {
+ stemLength += getLineSpacing() * (flagCount - 2);
+ }
+
+ int width = 0, height = 0;
+
+ if (flagCount > 0) {
+
+ if (!stemUp)
+ stemLength += nbh / 2;
+
+ if (m_font->getDimensions(m_style->getFlagCharName(flagCount),
+ width, height)) {
+
+ stemLength = std::max(stemLength, height);
+
+ } else if (m_font->getDimensions(m_style->getPartialFlagCharName(true),
+ width, height) ||
+ m_font->getDimensions(m_style->getPartialFlagCharName(false),
+ width, height)) {
+
+ unsigned int flagSpace = m_noteBodyHeight;
+ (void)m_font->getFlagSpacing(flagSpace);
+
+ stemLength = std::max(stemLength,
+ height + (flagCount - 1) * (int)flagSpace);
+ }
+ }
+
+ if (slashCount > 3 && flagCount < 3) {
+ stemLength += (slashCount - 3) * (nbh / 2);
+ }
+
+ if (params.m_stemLength >= 0) {
+ if (flagCount == 0)
+ return params.m_stemLength;
+ stemLength = std::max(stemLength, params.m_stemLength);
+ }
+
+ return stemLength;
+}
+
+void
+NotePixmapFactory::makeRoomForAccidental(Accidental a,
+ bool cautionary, int shift, bool extra)
+{
+ // General observation: where we're only using a character to
+ // determine its dimensions, we should (for the moment) just
+ // request it in screen mode, because it may be quicker and we
+ // don't need to render it, and the dimensions are the same.
+ NoteCharacter ac
+ (m_font->getCharacter(m_style->getAccidentalCharName(a)));
+
+ QPoint ah(m_font->getHotspot(m_style->getAccidentalCharName(a)));
+
+ m_left += ac.getWidth() + (m_noteBodyWidth / 4 - m_borderX);
+
+ if (shift > 0) {
+ if (extra) {
+ // The extra flag indicates that the first shift is to get
+ // out of the way of a note head, thus has to move
+ // possibly further, or at least a different amount. So
+ // replace the first shift with a different one.
+ --shift;
+ m_left += m_noteBodyWidth - m_noteBodyWidth / 5;
+ }
+ if (shift > 0) {
+ // The amount we shift for each accidental is the greater
+ // of the probable shift for that accidental and the
+ // probable shift for a sharp, on the assumption (usually
+ // true in classical notation) that the sharp is the
+ // widest accidental and that we may have other
+ // accidentals possibly including sharps on other notes in
+ // this chord that we can't know about here.
+ int step = ac.getWidth() - ah.x();
+ if (a != Accidentals::Sharp) {
+ NoteCharacter acSharp
+ (m_font->getCharacter(m_style->getAccidentalCharName
+ (Accidentals::Sharp)));
+ QPoint ahSharp
+ (m_font->getHotspot(m_style->getAccidentalCharName
+ (Accidentals::Sharp)));
+ step = std::max(step, acSharp.getWidth() - ahSharp.x());
+ }
+ m_left += shift * step;
+ }
+ }
+
+ if (cautionary)
+ m_left += m_noteBodyWidth;
+
+ int above = ah.y() - m_noteBodyHeight / 2;
+ int below = (ac.getHeight() - ah.y()) -
+ (m_noteBodyHeight - m_noteBodyHeight / 2); // subtract in case it's odd
+
+ if (above > 0)
+ m_above = std::max(m_above, above);
+ if (below > 0)
+ m_below = std::max(m_below, below);
+}
+
+void
+NotePixmapFactory::drawAccidental(Accidental a, bool cautionary)
+{
+ NoteCharacter ac = getCharacter
+ (m_style->getAccidentalCharName(a), PlainColour, false);
+
+ QPoint ah(m_font->getHotspot(m_style->getAccidentalCharName(a)));
+
+ int ax = 0;
+
+ if (cautionary) {
+ ax += m_noteBodyWidth / 2;
+ int bl = ac.getHeight() * 2 / 3;
+ int by = m_above + m_noteBodyHeight / 2 - bl / 2;
+ drawBracket(bl, true, false, m_noteBodyWidth*3 / 8, by);
+ drawBracket(bl, false, false, ac.getWidth() + m_noteBodyWidth*5 / 8, by);
+ }
+
+ m_p->drawNoteCharacter(ax, m_above + m_noteBodyHeight / 2 - ah.y(), ac);
+}
+
+void
+NotePixmapFactory::makeRoomForMarks(bool isStemmed,
+ const NotePixmapParameters &params,
+ int stemLength)
+{
+ int height = 0, width = 0;
+ int gap = m_noteBodyHeight / 5 + 1;
+
+ std::vector<Mark> normalMarks = params.getNormalMarks();
+ std::vector<Mark> aboveMarks = params.getAboveMarks();
+
+ for (std::vector<Mark>::iterator i = normalMarks.begin();
+ i != normalMarks.end(); ++i) {
+
+ if (!Marks::isTextMark(*i)) {
+
+ NoteCharacter character(m_font->getCharacter(m_style->getMarkCharName(*i)));
+ height += character.getHeight() + gap;
+ if (character.getWidth() > width)
+ width = character.getWidth();
+
+ } else {
+ // Inefficient to do this here _and_ in drawMarks, but
+ // text marks are not all that common
+ QString text = strtoqstr(Marks::getTextFromMark(*i));
+ QRect bounds = m_textMarkFontMetrics.boundingRect(text);
+ height += bounds.height() + gap;
+ if (bounds.width() > width)
+ width = bounds.width();
+ }
+ }
+
+ if (height > 0) {
+ if (isStemmed && params.m_stemGoesUp) {
+ m_below += height + 1;
+ } else {
+ m_above += height + 1;
+ }
+ }
+
+ height = 0;
+
+ if (params.m_safeVertDistance > 0 && !aboveMarks.empty()) {
+ m_above = std::max(m_above, params.m_safeVertDistance);
+ }
+
+ for (std::vector<Mark>::iterator i = aboveMarks.begin();
+ i != aboveMarks.end(); ++i) {
+
+ if (!Marks::isFingeringMark(*i)) {
+
+ Mark m(*i);
+
+ if (m == Marks::TrillLine)
+ m = Marks::LongTrill;
+
+ if (m == Marks::LongTrill) {
+ m_right = std::max(m_right, params.m_width);
+ }
+
+ NoteCharacter character(m_font->getCharacter(m_style->getMarkCharName(m)));
+ height += character.getHeight() + gap;
+ if (character.getWidth() > width)
+ width = character.getWidth();
+
+ } else {
+
+ // Inefficient to do this here _and_ in drawMarks
+ QString text = strtoqstr(Marks::getFingeringFromMark(*i));
+ QRect bounds = m_fingeringFontMetrics.boundingRect(text);
+ height += bounds.height() + gap + 3;
+ if (bounds.width() > width)
+ width = bounds.width();
+ }
+ }
+
+ if (height > 0) {
+ if (isStemmed && params.m_stemGoesUp && params.m_safeVertDistance == 0) {
+ m_above += stemLength + height + 1;
+ } else {
+ m_above += height + 1;
+ }
+ }
+
+ m_left = std::max(m_left, width / 2 - m_noteBodyWidth / 2);
+ m_right = std::max(m_right, width / 2 - m_noteBodyWidth / 2);
+}
+
+void
+NotePixmapFactory::drawMarks(bool isStemmed,
+ const NotePixmapParameters &params,
+ int stemLength)
+{
+ int gap = m_noteBodyHeight / 5 + 1;
+ int dy = gap;
+
+ std::vector<Mark> normalMarks = params.getNormalMarks();
+ std::vector<Mark> aboveMarks = params.getAboveMarks();
+
+ bool normalMarksAreAbove = !(isStemmed && params.m_stemGoesUp);
+
+ for (std::vector<Mark>::iterator i = normalMarks.begin();
+ i != normalMarks.end(); ++i) {
+
+ if (!Marks::isTextMark(*i)) {
+
+ NoteCharacter character = getCharacter
+ (m_style->getMarkCharName(*i), PlainColour,
+ !normalMarksAreAbove);
+
+ int x = m_left + m_noteBodyWidth / 2 - character.getWidth() / 2;
+ int y = (normalMarksAreAbove ?
+ (m_above - dy - character.getHeight() - 1) :
+ (m_above + m_noteBodyHeight + m_borderY * 2 + dy));
+
+ m_p->drawNoteCharacter(x, y, character);
+ dy += character.getHeight() + gap;
+
+ } else {
+
+ QString text = strtoqstr(Marks::getTextFromMark(*i));
+ QRect bounds = m_textMarkFontMetrics.boundingRect(text);
+
+ m_p->painter().setFont(m_textMarkFont);
+ if (!m_inPrinterMethod)
+ m_p->maskPainter().setFont(m_textMarkFont);
+
+ int x = m_left + m_noteBodyWidth / 2 - bounds.width() / 2;
+ int y = (normalMarksAreAbove ?
+ (m_above - dy - 3) :
+ (m_above + m_noteBodyHeight + m_borderY * 2 + dy + bounds.height() + 1));
+
+ m_p->drawText(x, y, text);
+ dy += bounds.height() + gap;
+ }
+ }
+
+ if (!normalMarksAreAbove)
+ dy = gap;
+ if (params.m_safeVertDistance > 0) {
+ if (normalMarksAreAbove) {
+ dy = std::max(dy, params.m_safeVertDistance);
+ } else {
+ dy = params.m_safeVertDistance;
+ }
+ } else if (isStemmed && params.m_stemGoesUp) {
+ dy += stemLength;
+ }
+
+ for (std::vector<Mark>::iterator i = aboveMarks.begin();
+ i != aboveMarks.end(); ++i) {
+
+ if (m_selected)
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ else
+ m_p->painter().setPen(Qt::black);
+ if (!Marks::isFingeringMark(*i)) {
+
+ int x = m_left + m_noteBodyWidth / 2;
+ int y = m_above - dy - 1;
+
+ if (*i != Marks::TrillLine) {
+
+ NoteCharacter character
+ (getCharacter
+ (m_style->getMarkCharName(*i), PlainColour,
+ false));
+
+ x -= character.getWidth() / 2;
+ y -= character.getHeight();
+
+ m_p->drawNoteCharacter(x, y, character);
+
+ y += character.getHeight() / 2;
+ x += character.getWidth();
+
+ dy += character.getHeight() + gap;
+
+ } else {
+
+ NoteCharacter character
+ (getCharacter
+ (m_style->getMarkCharName(Marks::Trill), PlainColour,
+ false));
+ y -= character.getHeight() / 2;
+ dy += character.getHeight() + gap;
+ }
+
+ if (*i == Marks::LongTrill ||
+ *i == Marks::TrillLine) {
+ NoteCharacter extension;
+ if (getCharacter(NoteCharacterNames::TRILL_LINE, extension,
+ PlainColour, false)) {
+ x += extension.getHotspot().x();
+ while (x < m_left + params.m_width - extension.getWidth()) {
+ x -= extension.getHotspot().x();
+ m_p->drawNoteCharacter(x, y, extension);
+ x += extension.getWidth();
+ }
+ }
+ if (*i == Marks::TrillLine)
+ dy += extension.getHeight() + gap;
+ }
+
+ } else {
+ QString text = strtoqstr(Marks::getFingeringFromMark(*i));
+ QRect bounds = m_fingeringFontMetrics.boundingRect(text);
+
+ m_p->painter().setFont(m_fingeringFont);
+ if (!m_inPrinterMethod)
+ m_p->maskPainter().setFont(m_fingeringFont);
+
+ int x = m_left + m_noteBodyWidth / 2 - bounds.width() / 2;
+ int y = m_above - dy - 3;
+
+ m_p->drawText(x, y, text);
+ dy += bounds.height() + gap;
+ }
+ }
+}
+
+void
+NotePixmapFactory::makeRoomForLegerLines(const NotePixmapParameters &params)
+{
+ if (params.m_legerLines < 0 || params.m_restOutsideStave) {
+ m_above = std::max(m_above,
+ (m_noteBodyHeight + 1) *
+ ( -params.m_legerLines / 2));
+ }
+ if (params.m_legerLines > 0 || params.m_restOutsideStave) {
+ m_below = std::max(m_below,
+ (m_noteBodyHeight + 1) *
+ (params.m_legerLines / 2));
+ }
+ if (params.m_legerLines != 0) {
+ m_left = std::max(m_left, m_noteBodyWidth / 5 + 1);
+ m_right = std::max(m_right, m_noteBodyWidth / 5 + 1);
+ }
+ if (params.m_restOutsideStave) {
+ m_above += 1;
+ m_left = std::max(m_left, m_noteBodyWidth * 3 + 1);
+ m_right = std::max(m_right, m_noteBodyWidth * 3 + 1);
+ }
+}
+
+void
+NotePixmapFactory::drawLegerLines(const NotePixmapParameters &params)
+{
+ int x0, x1, y;
+
+ if (params.m_legerLines == 0)
+ return ;
+
+ if (params.m_restOutsideStave) {
+ if (m_selected)
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ else
+ m_p->painter().setPen(Qt::black);
+ }
+ x0 = m_left - m_noteBodyWidth / 5 - 1;
+ x1 = m_left + m_noteBodyWidth + m_noteBodyWidth / 5 /* + 1 */;
+
+ if (params.m_shifted) {
+ if (params.m_stemGoesUp) {
+ x0 += m_noteBodyWidth;
+ x1 += m_noteBodyWidth;
+ } else {
+ x0 -= m_noteBodyWidth;
+ x1 -= m_noteBodyWidth;
+ }
+ }
+
+ int offset = m_noteBodyHeight + getStaffLineThickness();
+ int legerLines = params.m_legerLines;
+ bool below = (legerLines < 0);
+
+ if (below) {
+ legerLines = -legerLines;
+ offset = -offset;
+ }
+
+ if (params.m_restOutsideStave)
+ y = m_above;
+ else {
+ if (!below) { // note above staff
+ if (legerLines % 2) { // note is between lines
+ y = m_above + m_noteBodyHeight;
+ } else { // note is on a line
+ y = m_above + m_noteBodyHeight / 2 - getStaffLineThickness() / 2;
+ }
+ } else { // note below staff
+ if (legerLines % 2) { // note is between lines
+ y = m_above - getStaffLineThickness();
+ } else { // note is on a line
+ y = m_above + m_noteBodyHeight / 2;
+ }
+ }
+ }
+ if (params.m_restOutsideStave) {
+ NOTATION_DEBUG << "draw leger lines: " << legerLines << " lines, below "
+ << below
+ << ", note body height " << m_noteBodyHeight
+ << ", thickness " << getLegerLineThickness()
+ << " (staff line " << getStaffLineThickness() << ")"
+ << ", offset " << offset << endl;
+ }
+
+ // NOTATION_DEBUG << "draw leger lines: " << legerLines << " lines, below "
+ // << below
+ // << ", note body height " << m_noteBodyHeight
+ // << ", thickness " << getLegerLineThickness()
+ // << " (staff line " << getStaffLineThickness() << ")"
+ // << ", offset " << offset << endl;
+
+ // bool first = true;
+
+ if (getLegerLineThickness() > getStaffLineThickness()) {
+ y -= (getLegerLineThickness() - getStaffLineThickness() + 1) / 2;
+ }
+
+ for (int i = legerLines - 1; i >= 0; --i) {
+ if (i % 2) {
+ // NOTATION_DEBUG << "drawing leger line at y = " << y << endl;
+ for (int j = 0; j < getLegerLineThickness(); ++j) {
+ m_p->drawLine(x0, y + j, x1, y + j);
+ }
+ y += offset;
+ // if (first) {
+ // x0 += getStemThickness();
+ // x1 -= getStemThickness();
+ // first = false;
+ // }
+ }
+ }
+}
+
+void
+NotePixmapFactory::makeRoomForStemAndFlags(int flagCount, int stemLength,
+ const NotePixmapParameters &params,
+ QPoint &s0, QPoint &s1)
+{
+ // The coordinates we set in s0 and s1 are relative to (m_above, m_left)
+
+ if (params.m_stemGoesUp) {
+ m_above = std::max
+ (m_above, stemLength - m_noteBodyHeight / 2);
+ } else {
+ m_below = std::max
+ (m_below, stemLength - m_noteBodyHeight / 2 + 1);
+ }
+
+ if (flagCount > 0) {
+ if (params.m_stemGoesUp) {
+ int width = 0, height = 0;
+ if (!m_font->getDimensions
+ (m_style->getFlagCharName(flagCount), width, height)) {
+ width = m_font->getWidth(m_style->getPartialFlagCharName(false));
+ }
+ m_right += width;
+ }
+ }
+
+ unsigned int stemThickness = getStemThickness();
+
+ NoteStyle::HFixPoint hfix;
+ NoteStyle::VFixPoint vfix;
+ m_style->getStemFixPoints(params.m_noteType, hfix, vfix);
+
+ switch (hfix) {
+
+ case NoteStyle::Normal:
+ case NoteStyle::Reversed:
+ if (params.m_stemGoesUp ^ (hfix == NoteStyle::Reversed)) {
+ s0.setX(m_noteBodyWidth - stemThickness);
+ } else {
+ s0.setX(0);
+ }
+ break;
+
+ case NoteStyle::Central:
+ if (params.m_stemGoesUp ^ (hfix == NoteStyle::Reversed)) {
+ s0.setX(m_noteBodyWidth / 2 + 1);
+ } else {
+ s0.setX(m_noteBodyWidth / 2);
+ }
+ break;
+ }
+
+ switch (vfix) {
+
+ case NoteStyle::Near:
+ case NoteStyle::Far:
+ if (params.m_stemGoesUp ^ (vfix == NoteStyle::Far)) {
+ s0.setY(0);
+ } else {
+ s0.setY(m_noteBodyHeight);
+ }
+ if (vfix == NoteStyle::Near) {
+ stemLength -= m_noteBodyHeight / 2;
+ } else {
+ stemLength += m_noteBodyHeight / 2;
+ }
+ break;
+
+ case NoteStyle::Middle:
+ if (params.m_stemGoesUp) {
+ s0.setY(m_noteBodyHeight * 3 / 8);
+ } else {
+ s0.setY(m_noteBodyHeight * 5 / 8);
+ }
+ stemLength -= m_noteBodyHeight / 8;
+ break;
+ }
+
+ if (params.m_stemGoesUp) {
+ s1.setY(s0.y() - stemLength + getStaffLineThickness());
+ } else {
+ s1.setY(s0.y() + stemLength);
+ }
+
+ s1.setX(s0.x());
+}
+
+void
+NotePixmapFactory::drawFlags(int flagCount,
+ const NotePixmapParameters &params,
+ const QPoint &, const QPoint &s1)
+{
+ if (flagCount < 1)
+ return ;
+
+ NoteCharacter flagChar;
+ bool found = getCharacter(m_style->getFlagCharName(flagCount),
+ flagChar,
+ PlainColour,
+ !params.m_stemGoesUp);
+
+ if (!found) {
+
+ // Handle fonts that don't have all the flags in separate characters
+
+ found = getCharacter(m_style->getPartialFlagCharName(false),
+ flagChar,
+ PlainColour,
+ !params.m_stemGoesUp);
+
+ if (!found) {
+ std::cerr << "Warning: NotePixmapFactory::drawFlags: No way to draw note with " << flagCount << " flags in this font!?" << std::endl;
+ return ;
+ }
+
+ QPoint hotspot = flagChar.getHotspot();
+
+ NoteCharacter oneFlagChar;
+ bool foundOne =
+ (flagCount > 1 ?
+ getCharacter(m_style->getPartialFlagCharName(true),
+ oneFlagChar,
+ PlainColour,
+ !params.m_stemGoesUp) : false);
+
+ unsigned int flagSpace = m_noteBodyHeight;
+ (void)m_font->getFlagSpacing(flagSpace);
+
+ for (int flag = 0; flag < flagCount; ++flag) {
+
+ // use flag_1 in preference to flag_0 for the final flag, so
+ // as to end with a flourish
+ if (flag == flagCount - 1 && foundOne)
+ flagChar = oneFlagChar;
+
+ int y = m_above + s1.y();
+ if (params.m_stemGoesUp)
+ y += flag * flagSpace;
+ else
+ y -= (flag * flagSpace) + flagChar.getHeight();
+
+ if (!m_inPrinterMethod) {
+
+ m_p->end();
+
+ // Super-slow
+
+ PixmapFunctions::drawPixmapMasked(*m_generatedPixmap,
+ *m_generatedMask,
+ m_left + s1.x() - hotspot.x(),
+ y,
+ *flagChar.getPixmap());
+
+ m_p->begin(m_generatedPixmap, m_generatedMask);
+
+ } else {
+
+ // No problem with mask here
+ m_p->drawNoteCharacter(m_left + s1.x() - hotspot.x(),
+ y,
+ flagChar);
+ }
+ }
+
+ } else { // the normal case
+
+ QPoint hotspot = flagChar.getHotspot();
+
+ int y = m_above + s1.y();
+ if (!params.m_stemGoesUp)
+ y -= flagChar.getHeight();
+
+ m_p->drawNoteCharacter(m_left + s1.x() - hotspot.x(), y, flagChar);
+ }
+}
+
+void
+NotePixmapFactory::drawStem(const NotePixmapParameters &params,
+ const QPoint &s0, const QPoint &s1,
+ int shortening)
+{
+ if (params.m_stemGoesUp)
+ shortening = -shortening;
+ for (int i = 0; i < getStemThickness(); ++i) {
+ m_p->drawLine(m_left + s0.x() + i, m_above + s0.y(),
+ m_left + s1.x() + i, m_above + s1.y() - shortening);
+ }
+}
+
+void
+NotePixmapFactory::makeRoomForBeams(const NotePixmapParameters &params)
+{
+ int beamSpacing = (int)(params.m_width * params.m_gradient);
+
+ if (params.m_stemGoesUp) {
+
+ beamSpacing = -beamSpacing;
+ if (beamSpacing < 0)
+ beamSpacing = 0;
+ m_above += beamSpacing + 2;
+
+ // allow a bit extra in case the h fixpoint is non-normal
+ m_right = std::max(m_right, params.m_width + m_noteBodyWidth);
+
+ } else {
+
+ if (beamSpacing < 0)
+ beamSpacing = 0;
+ m_below += beamSpacing + 2;
+
+ m_right = std::max(m_right, params.m_width);
+ }
+}
+
+void
+NotePixmapFactory::drawShallowLine(int x0, int y0, int x1, int y1,
+ int thickness, bool smooth)
+{
+ if (!smooth || m_inPrinterMethod || (y0 == y1)) {
+
+ if (!m_inPrinterMethod) {
+ if (m_selected)
+ m_p->painter().setBrush(GUIPalette::getColour(GUIPalette::SelectedElement));
+ else
+ m_p->painter().setBrush(Qt::black);
+ }
+ if (thickness < 4) {
+ for (int i = 0; i < thickness; ++i) {
+ m_p->drawLine(x0, y0 + i, x1, y1 + i);
+ }
+ } else {
+ Profiler profiler("NotePixmapFactory::drawShallowLine(polygon)");
+ QPointArray qp(4);
+ qp.setPoint(0, x0, y0);
+ qp.setPoint(1, x0, y0 + thickness);
+ qp.setPoint(2, x1, y1 + thickness);
+ qp.setPoint(3, x1, y1);
+ m_p->drawPolygon(qp);
+ }
+
+ return ;
+ }
+
+ Profiler profiler("NotePixmapFactory::drawShallowLine(points)");
+
+ int dv = y1 - y0;
+ int dh = x1 - x0;
+
+ static std::vector<QColor> colours, selectedColours;
+ if (colours.size() == 0) {
+ int h, s, v;
+ QColor c = GUIPalette::getColour(GUIPalette::SelectedElement);
+ c.hsv(&h, &s, &v);
+ for (int step = 0; step < 256; step += (step == 0 ? 63 : 64)) {
+ colours.push_back(QColor( -1, 0, step, QColor::Hsv));
+ selectedColours.push_back(QColor(h, 255 - step, v, QColor::Hsv));
+ }
+ }
+
+ int cx = x0, cy = y0;
+
+ int inc = 1;
+
+ if (dv < 0) {
+ dv = -dv;
+ inc = -1;
+ }
+
+ int g = 2 * dv - dh;
+ int dg1 = 2 * (dv - dh);
+ int dg2 = 2 * dv;
+
+ int segment = (dg2 - dg1) / 4;
+
+ while (cx < x1) {
+
+ if (g > 0) {
+ g += dg1;
+ cy += inc;
+ } else {
+ g += dg2;
+ }
+
+ int quartile = segment ? ((dg2 - g) / segment) : 0;
+ if (quartile < 0)
+ quartile = 0;
+ if (quartile > 3)
+ quartile = 3;
+ if (inc > 0)
+ quartile = 4 - quartile;
+ /*
+ NOTATION_DEBUG
+ << "x = " << cx << ", y = " << cy
+ << ", g = " << g << ", dg1 = " << dg1 << ", dg2 = " << dg2
+ << ", seg = " << segment << ", q = " << quartile << endl;
+ */
+ // I don't know enough about Qt to be sure of this, but I
+ // suspect this may be some of the most inefficient code ever
+ // written:
+
+ int off = 0;
+
+ if (m_selected) {
+ m_p->painter().setPen(selectedColours[quartile]);
+ } else {
+ m_p->painter().setPen(colours[quartile]);
+ }
+
+ m_p->drawPoint(cx, cy);
+ drawBeamsCount ++;
+
+ if (thickness > 1) {
+ if (m_selected) {
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ } else {
+ m_p->painter().setPen(Qt::black);
+ }
+ }
+
+ while (++off < thickness) {
+ m_p->drawPoint(cx, cy + off);
+ drawBeamsCount ++;
+ }
+
+ if (m_selected) {
+ m_p->painter().setPen(selectedColours[4 - quartile]);
+ } else {
+ m_p->painter().setPen(colours[4 - quartile]);
+ }
+
+ m_p->drawPoint(cx, cy + off);
+ drawBeamsCount ++;
+
+ ++cx;
+ }
+
+ m_p->painter().setPen(Qt::black);
+}
+
+void
+NotePixmapFactory::drawBeams(const QPoint &s1,
+ const NotePixmapParameters &params,
+ int beamCount)
+{
+ clock_t startTime = clock();
+
+ // draw beams: first we draw all the beams common to both ends of
+ // the section, then we draw beams for those that appear at the
+ // end only
+
+ int startY = m_above + s1.y(), startX = m_left + s1.x();
+ int commonBeamCount = std::min(beamCount, params.m_nextBeamCount);
+
+ unsigned int thickness;
+ (void)m_font->getBeamThickness(thickness);
+
+ int width = params.m_width;
+ double grad = params.m_gradient;
+ bool smooth = m_font->isSmooth();
+ int spacing = getLineSpacing();
+
+ int sign = (params.m_stemGoesUp ? 1 : -1);
+
+ if (!params.m_stemGoesUp)
+ startY -= thickness;
+
+ if (!smooth)
+ startY -= sign;
+ else if (grad > -0.01 && grad < 0.01)
+ startY -= sign;
+
+ if (m_inPrinterMethod) {
+ startX += getStemThickness() / 2;
+ }
+
+ for (int j = 0; j < commonBeamCount; ++j) {
+ int y = sign * j * spacing;
+ drawShallowLine(startX, startY + y, startX + width,
+ startY + (int)(width*grad) + y,
+ thickness, smooth);
+ drawBeamsBeamCount ++;
+ }
+
+ int partWidth = width / 3;
+ if (partWidth < 2)
+ partWidth = 2;
+ else if (partWidth > m_noteBodyWidth)
+ partWidth = m_noteBodyWidth;
+
+ if (params.m_thisPartialBeams) {
+ for (int j = commonBeamCount; j < beamCount; ++j) {
+ int y = sign * j * spacing;
+ drawShallowLine(startX, startY + y, startX + partWidth,
+ startY + (int)(partWidth*grad) + y,
+ thickness, smooth);
+ drawBeamsBeamCount ++;
+ }
+ }
+
+ if (params.m_nextPartialBeams) {
+ startX += width - partWidth;
+ startY += (int)((width - partWidth) * grad);
+
+ for (int j = commonBeamCount; j < params.m_nextBeamCount; ++j) {
+ int y = sign * j * spacing;
+ drawShallowLine(startX, startY + y, startX + partWidth,
+ startY + (int)(partWidth*grad) + y,
+ thickness, smooth);
+ drawBeamsBeamCount ++;
+ }
+ }
+
+ clock_t endTime = clock();
+ drawBeamsTime += (endTime - startTime);
+}
+
+void
+NotePixmapFactory::drawSlashes(const QPoint &s0,
+ const NotePixmapParameters &params,
+ int slashCount)
+{
+ unsigned int thickness;
+ (void)m_font->getBeamThickness(thickness);
+ thickness = thickness * 3 / 4;
+ if (thickness < 1)
+ thickness = 1;
+
+ int gap = thickness - 1;
+ if (gap < 1)
+ gap = 1;
+
+ bool smooth = m_font->isSmooth();
+
+ int width = m_noteBodyWidth * 4 / 5;
+ int sign = (params.m_stemGoesUp ? -1 : 1);
+
+ int offset =
+ (slashCount == 1 ? m_noteBodyHeight * 2 :
+ slashCount == 2 ? m_noteBodyHeight * 3 / 2 :
+ m_noteBodyHeight);
+ int y = m_above + s0.y() + sign * (offset + thickness / 2);
+
+ for (int i = 0; i < slashCount; ++i) {
+ int yoff = width / 2;
+ drawShallowLine(m_left + s0.x() - width / 2, y + yoff / 2,
+ m_left + s0.x() + width / 2 + getStemThickness(), y - yoff / 2,
+ thickness, smooth);
+ y += sign * (thickness + gap);
+ }
+}
+
+void
+NotePixmapFactory::makeRoomForTuplingLine(const NotePixmapParameters &params)
+{
+ int lineSpacing =
+ (int)(params.m_tuplingLineWidth * params.m_tuplingLineGradient);
+ int th = m_tupletCountFontMetrics.height();
+
+ if (params.m_tuplingLineY < 0) {
+
+ lineSpacing = -lineSpacing;
+ if (lineSpacing < 0)
+ lineSpacing = 0;
+ m_above = std::max(m_above, -params.m_tuplingLineY + th / 2);
+ m_above += lineSpacing + 1;
+
+ } else {
+
+ if (lineSpacing < 0)
+ lineSpacing = 0;
+ m_below = std::max(m_below, params.m_tuplingLineY + th / 2);
+ m_below += lineSpacing + 1;
+ }
+
+ m_right = std::max(m_right, params.m_tuplingLineWidth);
+}
+
+void
+NotePixmapFactory::drawTuplingLine(const NotePixmapParameters &params)
+{
+ int thickness = getStaffLineThickness() * 3 / 2;
+ int countSpace = thickness * 2;
+
+ QString count;
+ count.setNum(params.m_tupletCount);
+ QRect cr = m_tupletCountFontMetrics.boundingRect(count);
+
+ int tlw = params.m_tuplingLineWidth;
+ int indent = m_noteBodyWidth / 2;
+
+ if (tlw < (cr.width() + countSpace * 2 + m_noteBodyWidth * 2)) {
+ tlw += m_noteBodyWidth - 1;
+ indent = 0;
+ }
+
+ int w = (tlw - cr.width()) / 2 - countSpace;
+
+ int startX = m_left + indent;
+ int endX = startX + w;
+
+ int startY = params.m_tuplingLineY + m_above + getLineSpacing() / 2;
+ int endY = startY + (int)(params.m_tuplingLineGradient * w);
+
+ if (startY == endY)
+ ++thickness;
+
+ int tickOffset = getLineSpacing() / 2;
+ if (params.m_tuplingLineY >= 0)
+ tickOffset = -tickOffset;
+
+ // NOTATION_DEBUG << "adjusted params.m_tuplingLineWidth = "
+ // << tlw
+ // << ", cr.width = " << cr.width()
+ // << ", tickOffset = " << tickOffset << endl;
+ // NOTATION_DEBUG << "line: (" << startX << "," << startY << ") -> ("
+ // << endX << "," << endY << ")" << endl;
+
+ bool smooth = m_font->isSmooth();
+
+ if (!params.m_tuplingLineFollowsBeam) {
+ m_p->drawLine(startX, startY, startX, startY + tickOffset);
+ drawShallowLine(startX, startY, endX, endY, thickness, smooth);
+ }
+
+ m_p->painter().setFont(m_tupletCountFont);
+ if (!m_inPrinterMethod)
+ m_p->maskPainter().setFont(m_tupletCountFont);
+
+ int textX = endX + countSpace;
+ int textY = endY + cr.height() / 2;
+ // NOTATION_DEBUG << "text: (" << textX << "," << textY << ")" << endl;
+
+ m_p->drawText(textX, textY, count);
+
+ startX += tlw - w;
+ endX = startX + w;
+
+ startY += (int)(params.m_tuplingLineGradient * (tlw - w));
+ endY = startY + (int)(params.m_tuplingLineGradient * w);
+
+ // NOTATION_DEBUG << "line: (" << startX << "," << startY << ") -> ("
+ // << endX << "," << endY << ")" << endl;
+
+ if (!params.m_tuplingLineFollowsBeam) {
+ drawShallowLine(startX, startY, endX, endY, thickness, smooth);
+ m_p->drawLine(endX, endY, endX, endY + tickOffset);
+ }
+}
+
+void
+NotePixmapFactory::drawTie(bool above, int length, int shift)
+{
+#ifdef NASTY_OLD_FLAT_TIE_CODE
+
+ int tieThickness = getStaffLineThickness() * 2;
+ int tieCurve = m_font->getSize() * 2 / 3;
+ int height = tieCurve + tieThickness;
+ int x = m_left + m_noteBodyWidth;
+ int y = (above ? m_above - height - tieCurve / 2 :
+ m_above + m_noteBodyHeight + tieCurve / 2 + 1);
+ int i;
+
+ length -= m_noteBodyWidth;
+ if (length < tieCurve * 2)
+ length = tieCurve * 2;
+ if (length < m_noteBodyWidth * 3) {
+ length += m_noteBodyWidth - 2;
+ x -= m_noteBodyWidth / 2 - 1;
+ }
+
+ for (i = 0; i < tieThickness; ++i) {
+
+ if (above) {
+
+ m_p->drawArc
+ (x, y + i, tieCurve*2, tieCurve*2, 90*16, 70*16);
+
+ m_p->drawLine
+ (x + tieCurve, y + i, x + length - tieCurve - 2, y + i);
+
+ m_p->drawArc
+ (x + length - 2*tieCurve - 1, y + i,
+ tieCurve*2, tieCurve*2, 20*16, 70*16);
+
+ } else {
+
+ m_p->drawArc
+ (x, y + i - tieCurve, tieCurve*2, tieCurve*2, 200*16, 70*16);
+
+ m_p->drawLine
+ (x + tieCurve, y + height - i - 1,
+ x + length - tieCurve - 2, y + height - i - 1);
+
+ m_p->drawArc
+ (x + length - 2*tieCurve - 1, y + i - tieCurve,
+ tieCurve*2, tieCurve*2, 270*16, 70*16);
+ }
+ }
+#else
+
+ int origLength = length;
+
+ int x = m_left + m_noteBodyWidth + m_noteBodyWidth / 4 + shift;
+ length = origLength - m_noteBodyWidth - m_noteBodyWidth / 3 - shift;
+
+ // if the length is short, move the tie a bit closer to both notes
+ if (length < m_noteBodyWidth*2) {
+ x = m_left + m_noteBodyWidth + shift;
+ length = origLength - m_noteBodyWidth - shift;
+ }
+
+ if (length < m_noteBodyWidth) {
+ length = m_noteBodyWidth;
+ }
+
+ // We can't request a smooth slur here, because that always involves
+ // creating a new pixmap
+
+ QPoint hotspot;
+ drawSlurAux(length, 0, above, false, true, false, hotspot,
+ &m_p->painter(),
+ x,
+ above ? m_above : m_above + m_noteBodyHeight);
+ // above ? m_above - m_noteBodyHeight/2 :
+ // m_above + m_noteBodyHeight + m_noteBodyHeight/2);
+
+#endif
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeRestPixmap(const NotePixmapParameters &params)
+{
+ Profiler profiler("NotePixmapFactory::makeRestPixmap");
+
+ CharName charName(m_style->getRestCharName(params.m_noteType,
+ params.m_restOutsideStave));
+ // Check whether the font has the glyph for this charName;
+ // if not, substitute a rest-on-stave glyph for a rest-outside-stave glyph,
+ // and vice-versa.
+ NoteCharacter character;
+ if (!getCharacter(charName, character, PlainColour, false))
+ charName = m_style->getRestCharName(params.m_noteType,
+ !params.m_restOutsideStave);
+
+ bool encache = false;
+
+ if (params.m_tupletCount == 0 && !m_selected && !m_shaded &&
+ !params.m_restOutsideStave) {
+
+ if (params.m_dots == 0) {
+ return getCharacter(charName, PlainColour, false).getCanvasPixmap();
+ } else {
+ NotePixmapCache::iterator ci(m_dottedRestCache->find(charName));
+ if (ci != m_dottedRestCache->end())
+ return new QCanvasPixmap
+ (*ci->second, QPoint(ci->second->offsetX(),
+ ci->second->offsetY()));
+ else
+ encache = true;
+ }
+ }
+
+ QPoint hotspot(m_font->getHotspot(charName));
+ drawRestAux(params, hotspot, 0, 0, 0);
+
+ QCanvasPixmap* canvasMap = makeCanvasPixmap(hotspot);
+ if (encache) {
+ m_dottedRestCache->insert(std::pair<CharName, QCanvasPixmap*>
+ (charName, new QCanvasPixmap
+ (*canvasMap, hotspot)));
+ }
+ return canvasMap;
+}
+
+void
+NotePixmapFactory::drawRest(const NotePixmapParameters &params,
+ QPainter &painter, int x, int y)
+{
+ Profiler profiler("NotePixmapFactory::drawRest");
+ m_inPrinterMethod = true;
+ QPoint hotspot; // unused
+ drawRestAux(params, hotspot, &painter, x, y);
+ m_inPrinterMethod = false;
+}
+
+void
+NotePixmapFactory::drawRestAux(const NotePixmapParameters &params,
+ QPoint &hotspot, QPainter *painter, int x, int y)
+{
+ CharName charName(m_style->getRestCharName(params.m_noteType,
+ params.m_restOutsideStave));
+ NoteCharacter character = getCharacter(charName,
+ params.m_quantized ? QuantizedColour :
+ PlainColour,
+ false);
+
+ NoteCharacter dot = getCharacter(NoteCharacterNames::DOT, PlainColour, false);
+
+ int dotWidth = dot.getWidth();
+ if (dotWidth < getNoteBodyWidth() / 2)
+ dotWidth = getNoteBodyWidth() / 2;
+
+ m_above = m_left = 0;
+ m_below = dot.getHeight() / 2; // for dotted shallow rests like semibreve
+ m_right = dotWidth / 2 + dotWidth * params.m_dots;
+ m_noteBodyWidth = character.getWidth();
+ m_noteBodyHeight = character.getHeight();
+
+ if (params.m_tupletCount)
+ makeRoomForTuplingLine(params);
+
+ // we'll adjust this for tupling line after drawing rest character:
+ hotspot = m_font->getHotspot(charName);
+
+ if (params.m_restOutsideStave &&
+ (charName == NoteCharacterNames::MULTI_REST ||
+ charName == NoteCharacterNames::MULTI_REST_ON_STAFF)) {
+ makeRoomForLegerLines(params);
+ }
+ if (painter) {
+ painter->save();
+ m_p->beginExternal(painter);
+ painter->translate(x - m_left, y - m_above - hotspot.y());
+ } else {
+ createPixmapAndMask(m_noteBodyWidth + m_left + m_right,
+ m_noteBodyHeight + m_above + m_below);
+ }
+
+ m_p->drawNoteCharacter(m_left, m_above, character);
+
+ if (params.m_tupletCount)
+ drawTuplingLine(params);
+
+ hotspot.setX(m_left);
+ hotspot.setY(m_above + hotspot.y());
+
+ int restY = hotspot.y() - dot.getHeight() - getStaffLineThickness();
+ if (params.m_noteType == Note::Semibreve ||
+ params.m_noteType == Note::Breve) {
+ restY += getLineSpacing();
+ }
+
+ for (int i = 0; i < params.m_dots; ++i) {
+ int x = m_left + m_noteBodyWidth + i * dotWidth + dotWidth / 2;
+ m_p->drawNoteCharacter(x, restY, dot);
+ }
+
+ if (params.m_restOutsideStave &&
+ (charName == NoteCharacterNames::MULTI_REST ||
+ charName == NoteCharacterNames::MULTI_REST_ON_STAFF)) {
+ drawLegerLines(params);
+ }
+
+ if (painter) {
+ painter->restore();
+ }
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeClefPixmap(const Clef &clef)
+{
+ Profiler profiler("NotePixmapFactory::makeClefPixmap");
+ NoteCharacter plain = getCharacter(m_style->getClefCharName(clef),
+ PlainColour, false);
+
+ int oct = clef.getOctaveOffset();
+ if (oct == 0)
+ return plain.getCanvasPixmap();
+
+ // fix #1522784 and use 15 rather than 16 for double octave offset
+ int adjustedOctave = (8 * (oct < 0 ? -oct : oct));
+ if (adjustedOctave > 8)
+ adjustedOctave--;
+ else if (adjustedOctave < 8)
+ adjustedOctave++;
+
+ QString text = QString("%1").arg(adjustedOctave);
+ QRect rect = m_clefOttavaFontMetrics.boundingRect(text);
+
+ createPixmapAndMask(plain.getWidth(),
+ plain.getHeight() + rect.height());
+
+ if (m_selected) {
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ }
+
+ m_p->drawNoteCharacter(0, oct < 0 ? 0 : rect.height(), plain);
+
+ m_p->painter().setFont(m_clefOttavaFont);
+ if (!m_inPrinterMethod)
+ m_p->maskPainter().setFont(m_clefOttavaFont);
+
+ m_p->drawText(plain.getWidth() / 2 - rect.width() / 2,
+ oct < 0 ? plain.getHeight() + rect.height() - 1 :
+ rect.height(), text);
+
+ m_p->painter().setPen(Qt::black);
+ QPoint hotspot(plain.getHotspot());
+ if (oct > 0) hotspot.setY(hotspot.y() + rect.height());
+ return makeCanvasPixmap(hotspot, true);
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makePedalDownPixmap()
+{
+ return getCharacter(NoteCharacterNames::PEDAL_MARK, PlainColour, false)
+ .getCanvasPixmap();
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makePedalUpPixmap()
+{
+ return getCharacter(NoteCharacterNames::PEDAL_UP_MARK, PlainColour, false)
+ .getCanvasPixmap();
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeUnknownPixmap()
+{
+ Profiler profiler("NotePixmapFactory::makeUnknownPixmap");
+ return getCharacter(NoteCharacterNames::UNKNOWN, PlainColour, false)
+ .getCanvasPixmap();
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeToolbarPixmap(const char *name, bool menuSize)
+{
+ QString pixmapDir = KGlobal::dirs()->findResource("appdata", "pixmaps/");
+ QString fileBase = pixmapDir + "/toolbar/";
+ if (menuSize) fileBase += "menu-";
+ fileBase += name;
+ if (QFile(fileBase + ".png").exists()) {
+ return new QCanvasPixmap(fileBase + ".png");
+ } else if (QFile(fileBase + ".xpm").exists()) {
+ return new QCanvasPixmap(fileBase + ".xpm");
+ } else if (menuSize) {
+ return makeToolbarPixmap(name, false);
+ } else {
+ // this will fail, but we don't want to return a null pointer
+ return new QCanvasPixmap(fileBase + ".png");
+ }
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeNoteMenuPixmap(timeT duration,
+ timeT &errorReturn)
+{
+ Note nearestNote = Note::getNearestNote(duration);
+ bool triplet = false;
+ errorReturn = 0;
+
+ if (nearestNote.getDuration() != duration) {
+ Note tripletNote = Note::getNearestNote(duration * 3 / 2);
+ if (tripletNote.getDuration() == duration * 3 / 2) {
+ nearestNote = tripletNote;
+ triplet = true;
+ } else {
+ errorReturn = duration - nearestNote.getDuration();
+ }
+ }
+
+ QString noteName = NotationStrings::getReferenceName(nearestNote);
+ if (triplet)
+ noteName = "3-" + noteName;
+ noteName = "menu-" + noteName;
+ return makeToolbarPixmap(noteName);
+}
+
+QCanvasPixmap *
+NotePixmapFactory::makeMarkMenuPixmap(Mark mark)
+{
+ if (mark == Marks::Sforzando ||
+ mark == Marks::Rinforzando) {
+ return makeToolbarPixmap(mark.c_str());
+ } else {
+ NoteFont *font = 0;
+ try {
+ font = NoteFontFactory::getFont
+ (NoteFontFactory::getDefaultFontName(), 6);
+ } catch (Exception) {
+ font = NoteFontFactory::getFont
+ (NoteFontFactory::getDefaultFontName(),
+ NoteFontFactory::getDefaultSize(NoteFontFactory::getDefaultFontName()));
+ }
+ NoteCharacter character = font->getCharacter
+ (NoteStyleFactory::getStyle(NoteStyleFactory::DefaultStyle)->
+ getMarkCharName(mark));
+ return character.getCanvasPixmap();
+ }
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeKeyPixmap(const Key &key,
+ const Clef &clef,
+ Key previousKey)
+{
+ Profiler profiler("NotePixmapFactory::makeKeyPixmap");
+
+ std::vector<int> ah0 = previousKey.getAccidentalHeights(clef);
+ std::vector<int> ah1 = key.getAccidentalHeights(clef);
+
+ int cancelCount = 0;
+ if (key.isSharp() != previousKey.isSharp())
+ cancelCount = ah0.size();
+ else if (ah1.size() < ah0.size())
+ cancelCount = ah0.size() - ah1.size();
+
+ CharName keyCharName;
+ if (key.isSharp())
+ keyCharName = NoteCharacterNames::SHARP;
+ else
+ keyCharName = NoteCharacterNames::FLAT;
+
+ NoteCharacter keyCharacter;
+ NoteCharacter cancelCharacter;
+
+ keyCharacter = getCharacter(keyCharName, PlainColour, false);
+ if (cancelCount > 0) {
+ cancelCharacter = getCharacter(NoteCharacterNames::NATURAL, PlainColour, false);
+ }
+
+ int x = 0;
+ int lw = getLineSpacing();
+ int keyDelta = keyCharacter.getWidth() - keyCharacter.getHotspot().x();
+
+ int cancelDelta = 0;
+ int between = 0;
+ if (cancelCount > 0) {
+ cancelDelta = cancelCharacter.getWidth() + cancelCharacter.getWidth() / 3;
+ between = cancelCharacter.getWidth();
+ }
+
+ createPixmapAndMask(keyDelta * ah1.size() + cancelDelta * cancelCount + between +
+ keyCharacter.getWidth() / 4, lw * 8 + 1);
+
+ if (key.isSharp() != previousKey.isSharp()) {
+
+ // cancellation first
+
+ for (int i = 0; i < cancelCount; ++i) {
+
+ int h = ah0[ah0.size() - cancelCount + i];
+ int y = (lw * 2) + ((8 - h) * lw) / 2 - cancelCharacter.getHotspot().y();
+
+ m_p->drawNoteCharacter(x, y, cancelCharacter);
+
+ x += cancelDelta;
+ }
+
+ if (cancelCount > 0) {
+ x += between;
+ }
+ }
+
+ for (unsigned int i = 0; i < ah1.size(); ++i) {
+
+ int h = ah1[i];
+ int y = (lw * 2) + ((8 - h) * lw) / 2 - keyCharacter.getHotspot().y();
+
+ m_p->drawNoteCharacter(x, y, keyCharacter);
+
+ x += keyDelta;
+ }
+
+ if (key.isSharp() == previousKey.isSharp()) {
+
+ // cancellation afterwards
+
+ if (cancelCount > 0) {
+ x += between;
+ }
+
+ for (int i = 0; i < cancelCount; ++i) {
+
+ int h = ah0[ah0.size() - cancelCount + i];
+ int y = (lw * 2) + ((8 - h) * lw) / 2 - cancelCharacter.getHotspot().y();
+
+ m_p->drawNoteCharacter(x, y, cancelCharacter);
+
+ x += cancelDelta;
+ }
+ }
+
+ return makeCanvasPixmap(m_pointZero);
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeClefDisplayPixmap(const Clef &clef)
+{
+ QCanvasPixmap* clefPixmap = makeClefPixmap(clef);
+
+ int lw = getLineSpacing();
+ int width = clefPixmap->width() + 6 * getNoteBodyWidth();
+
+ createPixmapAndMask(width, lw * 10 + 1);
+
+ int h = clef.getAxisHeight();
+ int y = (lw * 3) + ((8 - h) * lw) / 2;
+ int x = 3 * getNoteBodyWidth();
+ m_p->drawPixmap(x, y - clefPixmap->offsetY(), *clefPixmap);
+
+ for (h = 0; h <= 8; h += 2) {
+ y = (lw * 3) + ((8 - h) * lw) / 2;
+ m_p->drawLine(x / 2, y, m_generatedWidth - x / 2 - 1, y);
+ }
+
+ delete clefPixmap;
+
+ return makeCanvasPixmap(m_pointZero);
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeKeyDisplayPixmap(const Key &key, const Clef &clef)
+{
+ std::vector<int> ah = key.getAccidentalHeights(clef);
+
+ CharName charName = (key.isSharp() ?
+ NoteCharacterNames::SHARP :
+ NoteCharacterNames::FLAT);
+
+ QCanvasPixmap* clefPixmap = makeClefPixmap(clef);
+ QPixmap accidentalPixmap(*m_font->getCharacter(charName).getPixmap());
+ QPoint hotspot(m_font->getHotspot(charName));
+
+ int lw = getLineSpacing();
+ int delta = accidentalPixmap.width() - hotspot.x();
+ int maxDelta = getAccidentalWidth(Sharp);
+ int width = clefPixmap->width() + 5 * maxDelta + 7 * maxDelta;
+ int x = clefPixmap->width() + 5 * maxDelta / 2;
+
+ createPixmapAndMask(width, lw * 10 + 1);
+
+ int h = clef.getAxisHeight();
+ int y = (lw * 3) + ((8 - h) * lw) / 2;
+ m_p->drawPixmap(2 * maxDelta, y - clefPixmap->offsetY(), *clefPixmap);
+
+ for (unsigned int i = 0; i < ah.size(); ++i) {
+
+ h = ah[i];
+ y = (lw * 3) + ((8 - h) * lw) / 2 - hotspot.y();
+
+ m_p->drawPixmap(x, y, accidentalPixmap);
+
+ x += delta;
+ }
+
+ for (h = 0; h <= 8; h += 2) {
+ y = (lw * 3) + ((8 - h) * lw) / 2;
+ m_p->drawLine(maxDelta, y, m_generatedWidth - 2*maxDelta - 1, y);
+ }
+
+ delete clefPixmap;
+ return makeCanvasPixmap(m_pointZero);
+}
+
+int
+NotePixmapFactory::getClefAndKeyWidth(const Key &key, const Clef &clef)
+{
+ std::vector<int> ah = key.getAccidentalHeights(clef);
+ Accidental accidental = key.isSharp() ? Sharp : Flat;
+ NoteCharacter plain = getCharacter(m_style->getClefCharName(clef),
+ PlainColour, false);
+
+ int clefWidth = plain.getWidth();
+ int accWidth = getAccidentalWidth(accidental);
+ int maxDelta = getAccidentalWidth(Sharp);
+
+ int width = clefWidth + 2 * maxDelta + ah.size() * accWidth;
+
+ return width;
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeTrackHeaderPixmap(
+ int width, int height, TrackHeader *header)
+{
+
+ height -= 4; // Make room to the label frame :
+ // 4 = 2 * (margin + lineWidth)
+
+ createPixmapAndMask(width, height);
+
+ int lw = getLineSpacing();
+ int h;
+ QColor colour;
+ int maxDelta = getAccidentalWidth(Sharp);
+
+ // Staff Y position inside the whole header
+ int offset = (height - 10 * lw -1) / 2;
+
+ // Draw staff lines
+ m_p->painter().setPen(QPen(Qt::black, getStaffLineThickness()));
+ for (h = 0; h <= 8; h += 2) {
+ int y = (lw * 3) + ((8 - h) * lw) / 2;
+ m_p->drawLine(maxDelta/2, y + offset, m_generatedWidth - maxDelta/2, y + offset);
+ }
+
+ if (header->isAClefToDraw()) {
+ const Clef &clef = header->getClef();
+ // TODO : use colours from GUIPalette
+ colour = header->isClefInconsistent() ? Qt::red : Qt::black;
+
+ int hue, sat, val;
+ colour.getHsv(&hue, &sat, &val);
+ NoteCharacter clefChar = m_font->getCharacterColoured
+ (m_style->getClefCharName(clef),
+ hue, val, NoteFont::Screen, false);
+
+ // Draw clef
+ h = clef.getAxisHeight();
+ int y = (lw * 3) + ((8 - h) * lw) / 2;
+ m_p->drawNoteCharacter(maxDelta,
+ y - clefChar.getHotspot().y() + offset, clefChar);
+
+ // If necessary, write 8 or 15 above or under the clef
+ int oct = clef.getOctaveOffset();
+ if (oct != 0) {
+
+ int adjustedOctave = (8 * (oct < 0 ? -oct : oct));
+ if (adjustedOctave > 8)
+ adjustedOctave--;
+ else if (adjustedOctave < 8)
+ adjustedOctave++;
+
+ QString text = QString("%1").arg(adjustedOctave);
+ QRect rect = m_clefOttavaFontMetrics.boundingRect(text);
+
+ m_p->painter().setPen(colour);
+
+ m_p->painter().setFont(m_clefOttavaFont);
+ // m_p->maskPainter().setFont(m_clefOttavaFont);
+ int xpos = maxDelta + clefChar.getWidth() / 2 - rect.width() / 2;
+ int ypos = y - clefChar.getHotspot().y() + offset
+ + (oct < 0 ? clefChar.getHeight() + rect.height() - 1 : - rect.height() / 3);
+ m_p->drawText(xpos, ypos, text);
+ }
+
+ // TODO : use colours from GUIPalette
+ colour = header->isKeyInconsistent() ? Qt::red : Qt::black;
+
+
+ // Draw the key signature if any
+
+ const Key &key = header->getKey();
+ std::vector<int> ah = key.getAccidentalHeights(clef);
+
+ CharName charName = key.isSharp() ?
+ NoteCharacterNames::SHARP :
+ NoteCharacterNames::FLAT;
+
+ colour.getHsv(&hue, &sat, &val);
+ NoteCharacter accident = m_font->getCharacterColoured(charName,
+ hue, val, NoteFont::Screen, false);
+
+ QPoint hotspot(m_font->getHotspot(charName));
+ int delta = accident.getWidth() - hotspot.x();
+
+ int x = clefChar.getWidth() + maxDelta;
+ for (unsigned int i = 0; i < ah.size(); ++i) {
+ h = ah[i];
+ y = (lw * 3) + ((8 - h) * lw) / 2 - hotspot.y() + offset;
+ m_p->drawNoteCharacter(x, y, accident);
+
+ x += delta;
+ }
+
+ }
+
+ m_p->painter().setFont(m_trackHeaderFont);
+ // m_p->maskPainter().setFont(m_trackHeaderFont);
+
+ QString text;
+ QString textLine;
+
+ int charHeight = m_trackHeaderFontMetrics.height();
+ int charWidth = m_trackHeaderFontMetrics.maxWidth();
+
+ const QString transposeText = header->getTransposeText();
+ QRect bounds = m_trackHeaderBoldFontMetrics.boundingRect(transposeText);
+ int transposeWidth = bounds.width();
+
+
+ // Write upper text (track name and track label)
+
+ m_p->painter().setPen(Qt::black);
+ text = header->getUpperText();
+ int numberOfTextLines = header->getNumberOfTextLines();
+
+ for (int l=1; l<=numberOfTextLines; l++) {
+ int upperTextY = charHeight + (l - 1) * getTrackHeaderTextLineSpacing();
+ if (l == numberOfTextLines) {
+ int transposeSpace = transposeWidth ? transposeWidth + charWidth / 4 : 0;
+ textLine = getOneLine(text, width - transposeSpace - charWidth / 2);
+ if (!text.isEmpty()) {
+ // String too long : cut it and replace last character by dots
+ int len = textLine.length();
+ if (len > 1) textLine.replace(len - 1, 1, i18n("..."));
+ }
+ } else {
+ textLine = getOneLine(text, width - charWidth / 2);
+ }
+ if (textLine.isEmpty()) break;
+ m_p->drawText(charWidth / 4, upperTextY, textLine);
+ }
+
+
+ // Write transposition text
+
+ // TODO : use colours from GUIPalette
+ colour = header->isTransposeInconsistent() ? Qt::red : Qt::black;
+ m_p->painter().setFont(m_trackHeaderBoldFont);
+ // m_p->maskPainter().setFont(m_trackHeaderBoldFont);
+ m_p->painter().setPen(colour);
+
+ m_p->drawText(width - transposeWidth - charWidth / 4,
+ charHeight
+ + (numberOfTextLines - 1) * getTrackHeaderTextLineSpacing(),
+ transposeText);
+
+
+ // Write lower text (segment label)
+
+ // TODO : use colours from GUIPalette
+ colour = header->isLabelInconsistent() ? Qt::red : Qt::black;
+ m_p->painter().setFont(m_trackHeaderFont);
+ // m_p->maskPainter().setFont(m_trackHeaderFont);
+
+ m_p->painter().setPen(colour);
+ text = header->getLowerText();
+
+ for (int l=1; l<=numberOfTextLines; l++) {
+ int lowerTextY = m_generatedHeight - 4 // -4 : adjust
+ - (numberOfTextLines - l) * getTrackHeaderTextLineSpacing();
+
+ QString textLine = getOneLine(text, width - charWidth / 2);
+ if (textLine.isEmpty()) break;
+
+ if ((l == numberOfTextLines) && !text.isEmpty()) {
+ // String too long : cut it and replace last character by dots
+ int len = textLine.length();
+ if (len > 1) textLine.replace(len - 1, 1, i18n("..."));
+ }
+
+ m_p->drawText(charWidth / 4, lowerTextY, textLine);
+ }
+
+ return makeCanvasPixmap(m_pointZero, true);
+}
+
+int
+NotePixmapFactory::getTrackHeaderNTL(int height)
+{
+ int clefMaxHeight = 12 * getLineSpacing();
+ int textLineHeight = getTrackHeaderTextLineSpacing();
+ int numberOfLines = ((height - clefMaxHeight) / 2) / textLineHeight;
+ return (numberOfLines > 0) ? numberOfLines : 1;
+}
+
+int
+NotePixmapFactory::getTrackHeaderTextWidth(QString str)
+{
+ QRect bounds = m_trackHeaderFontMetrics.boundingRect(str);
+ return bounds.width();
+}
+
+int
+NotePixmapFactory::getTrackHeaderTextLineSpacing()
+{
+ // 3/2 is some arbitrary line spacing
+ return m_trackHeaderFont.pixelSize() * 3 / 2;
+}
+
+QString
+NotePixmapFactory::getOneLine(QString &text, int width)
+{
+ QString str;
+ int n;
+
+ // Immediately stop if string is empty or only contains white spaces ...
+ if (text.stripWhiteSpace().isEmpty()) return QString("");
+
+ // ... or if width is too small.
+ if (width < m_trackHeaderFontMetrics.boundingRect(text.left(1)).width())
+ return QString("");
+
+ // Get a first approx. string length
+ int totalLength = text.length();
+ n = totalLength * width / getTrackHeaderTextWidth(text) + 1;
+ if (n > totalLength) n = totalLength;
+
+ // Verify string size is less than width then correct it if necessary
+ while (((getTrackHeaderTextWidth(text.left(n))) > width) && n) n--;
+
+ if (n == 0) {
+ str = text;
+ text = QString("");
+ } else {
+ str = text.left(n);
+ text.remove(0, n);
+ }
+
+ return str;
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makePitchDisplayPixmap(int p, const Clef &clef,
+ bool useSharps)
+{
+ NotationRules rules;
+
+ Pitch pitch(p);
+ Accidental accidental(pitch.getAccidental(useSharps));
+ NotePixmapParameters params(Note::Crotchet, 0, accidental);
+
+ QCanvasPixmap* clefPixmap = makeClefPixmap(clef);
+
+ int lw = getLineSpacing();
+ int width = getClefWidth(Clef::Bass) + 10 * getNoteBodyWidth();
+
+ int h = pitch.getHeightOnStaff(clef, useSharps);
+ params.setStemGoesUp(rules.isStemUp(h));
+
+ if (h < -1)
+ params.setStemLength(lw * (4 - h) / 2);
+ else if (h > 9)
+ params.setStemLength(lw * (h - 4) / 2);
+ if (h > 8)
+ params.setLegerLines(h - 8);
+ else if (h < 0)
+ params.setLegerLines(h);
+
+ params.setIsOnLine(h % 2 == 0);
+ params.setSelected(m_selected);
+
+ QCanvasPixmap *notePixmap = makeNotePixmap(params);
+
+ int pixmapHeight = lw * 12 + 1;
+ int yoffset = lw * 3;
+ if (h > 12) {
+ pixmapHeight += 6 * lw;
+ yoffset += 6 * lw;
+ } else if (h < -4) {
+ pixmapHeight += 6 * lw;
+ }
+
+ createPixmapAndMask(width, pixmapHeight);
+
+ int x =
+ getClefWidth(Clef::Bass) + 5 * getNoteBodyWidth() -
+ getAccidentalWidth(accidental);
+ int y = yoffset + ((8 - h) * lw) / 2 - notePixmap->offsetY();
+ m_p->drawPixmap(x, y, *notePixmap);
+
+ h = clef.getAxisHeight();
+ x = 3 * getNoteBodyWidth();
+ y = yoffset + ((8 - h) * lw) / 2;
+ m_p->drawPixmap(x, y - clefPixmap->offsetY(), *clefPixmap);
+
+ for (h = 0; h <= 8; h += 2) {
+ y = yoffset + ((8 - h) * lw) / 2;
+ m_p->drawLine(x / 2, y, m_generatedWidth - x / 2, y);
+ }
+
+ delete clefPixmap;
+ delete notePixmap;
+
+ return makeCanvasPixmap(m_pointZero);
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makePitchDisplayPixmap(int p, const Clef &clef,
+ int octave, int step)
+{
+ NotationRules rules;
+
+ Pitch pitch(step, octave, p, 0);
+ Accidental accidental = pitch.getDisplayAccidental(Key("C major"));
+ NotePixmapParameters params(Note::Crotchet, 0, accidental);
+
+ QCanvasPixmap* clefPixmap = makeClefPixmap(clef);
+
+ int lw = getLineSpacing();
+ int width = getClefWidth(Clef::Bass) + 10 * getNoteBodyWidth();
+
+ int h = pitch.getHeightOnStaff
+ (clef,
+ Key("C major"));
+ params.setStemGoesUp(rules.isStemUp(h));
+
+ if (h < -1)
+ params.setStemLength(lw * (4 - h) / 2);
+ else if (h > 9)
+ params.setStemLength(lw * (h - 4) / 2);
+ if (h > 8)
+ params.setLegerLines(h - 8);
+ else if (h < 0)
+ params.setLegerLines(h);
+
+ params.setIsOnLine(h % 2 == 0);
+ params.setSelected(m_selected);
+
+ QCanvasPixmap *notePixmap = makeNotePixmap(params);
+
+ int pixmapHeight = lw * 12 + 1;
+ int yoffset = lw * 3;
+ if (h > 12) {
+ pixmapHeight += 6 * lw;
+ yoffset += 6 * lw;
+ } else if (h < -4) {
+ pixmapHeight += 6 * lw;
+ }
+
+ createPixmapAndMask(width, pixmapHeight);
+
+ int x =
+ getClefWidth(Clef::Bass) + 5 * getNoteBodyWidth() -
+ getAccidentalWidth(accidental);
+ int y = yoffset + ((8 - h) * lw) / 2 - notePixmap->offsetY();
+ m_p->drawPixmap(x, y, *notePixmap);
+
+ h = clef.getAxisHeight();
+ x = 3 * getNoteBodyWidth();
+ y = yoffset + ((8 - h) * lw) / 2;
+ m_p->drawPixmap(x, y - clefPixmap->offsetY(), *clefPixmap);
+
+ for (h = 0; h <= 8; h += 2) {
+ y = yoffset + ((8 - h) * lw) / 2;
+ m_p->drawLine(x / 2, y, m_generatedWidth - x / 2, y);
+ }
+
+ delete clefPixmap;
+ delete notePixmap;
+
+ return makeCanvasPixmap(m_pointZero);
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeHairpinPixmap(int length, bool isCrescendo)
+{
+ Profiler profiler("NotePixmapFactory::makeHairpinPixmap");
+ drawHairpinAux(length, isCrescendo, 0, 0, 0);
+ return makeCanvasPixmap(QPoint(0, m_generatedHeight / 2));
+}
+
+void
+NotePixmapFactory::drawHairpin(int length, bool isCrescendo,
+ QPainter &painter, int x, int y)
+{
+ Profiler profiler("NotePixmapFactory::drawHairpin");
+ m_inPrinterMethod = true;
+ drawHairpinAux(length, isCrescendo, &painter, x, y);
+ m_inPrinterMethod = false;
+}
+
+void
+NotePixmapFactory::drawHairpinAux(int length, bool isCrescendo,
+ QPainter *painter, int x, int y)
+{
+ int nbh = getNoteBodyHeight();
+ int nbw = getNoteBodyWidth();
+
+ int height = (int)(((double)nbh / (double)(nbw * 40)) * length) + nbh;
+ int thickness = getStaffLineThickness() * 3 / 2;
+
+ // NOTATION_DEBUG << "NotePixmapFactory::makeHairpinPixmap: mapped length " << length << " to height " << height << " (nbh = " << nbh << ", nbw = " << nbw << ")" << endl;
+
+ if (height < nbh)
+ height = nbh;
+ if (height > nbh*2)
+ height = nbh * 2;
+
+ height += thickness - 1;
+
+ if (painter) {
+ painter->save();
+ m_p->beginExternal(painter);
+ painter->translate(x, y - height / 2);
+ } else {
+ createPixmapAndMask(length, height);
+ }
+
+ if (m_selected) {
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ }
+
+ int left = 1, right = length - 2 * nbw / 3 + 1;
+
+ bool smooth = m_font->isSmooth();
+
+ if (isCrescendo) {
+ drawShallowLine(left, height / 2 - 1,
+ right, height - thickness - 1, thickness, smooth);
+ drawShallowLine(left, height / 2 - 1, right, 0, thickness, smooth);
+ } else {
+ drawShallowLine(left, 0, right, height / 2 - 1, thickness, smooth);
+ drawShallowLine(left, height - thickness - 1,
+ right, height / 2 - 1, thickness, smooth);
+ }
+
+ m_p->painter().setPen(Qt::black);
+
+ if (painter) {
+ painter->restore();
+ }
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeSlurPixmap(int length, int dy, bool above, bool phrasing)
+{
+ Profiler profiler("NotePixmapFactory::makeSlurPixmap");
+
+ //!!! could remove "height > 5" requirement if we did a better job of
+ // sizing so that any horizontal part was rescaled down to exactly
+ // 1 pixel wide instead of blurring
+ bool smooth = m_font->isSmooth() && getNoteBodyHeight() > 5;
+ QPoint hotspot;
+ if (length < getNoteBodyWidth()*2)
+ length = getNoteBodyWidth() * 2;
+ drawSlurAux(length, dy, above, smooth, false, phrasing, hotspot, 0, 0, 0);
+
+ m_p->end();
+
+ if (smooth) {
+
+ QImage i = m_generatedPixmap->convertToImage();
+ if (i.depth() == 1)
+ i = i.convertDepth(32);
+ i = i.smoothScale(i.width() / 2, i.height() / 2);
+
+ delete m_generatedPixmap;
+ delete m_generatedMask;
+ QPixmap newPixmap(i);
+ QCanvasPixmap *p = new QCanvasPixmap(newPixmap, hotspot);
+ p->setMask(PixmapFunctions::generateMask(newPixmap,
+ Qt::white.rgb()));
+ return p;
+
+ } else {
+
+ QCanvasPixmap *p = new QCanvasPixmap(*m_generatedPixmap, hotspot);
+ p->setMask(PixmapFunctions::generateMask(*m_generatedPixmap,
+ Qt::white.rgb()));
+ delete m_generatedPixmap;
+ delete m_generatedMask;
+ return p;
+ }
+}
+
+void
+NotePixmapFactory::drawSlur(int length, int dy, bool above, bool phrasing,
+ QPainter &painter, int x, int y)
+{
+ Profiler profiler("NotePixmapFactory::drawSlur");
+ QPoint hotspot;
+ m_inPrinterMethod = true;
+ if (length < getNoteBodyWidth()*2)
+ length = getNoteBodyWidth() * 2;
+ drawSlurAux(length, dy, above, false, false, phrasing, hotspot, &painter, x, y);
+ m_inPrinterMethod = false;
+}
+
+void
+NotePixmapFactory::drawSlurAux(int length, int dy, bool above,
+ bool smooth, bool flat, bool phrasing,
+ QPoint &hotspot, QPainter *painter, int x, int y)
+{
+ QWMatrix::TransformationMode mode = QWMatrix::transformationMode();
+ QWMatrix::setTransformationMode(QWMatrix::Points);
+
+ int thickness = getStaffLineThickness() * 2;
+ if (phrasing)
+ thickness = thickness * 3 / 4;
+ int nbh = getNoteBodyHeight(), nbw = getNoteBodyWidth();
+
+ // Experiment with rotating the painter rather than the control points.
+ double theta = 0;
+ bool rotate = false;
+ if (dy != 0) {
+ // We have opposite (dy) and adjacent (length).
+ theta = atan(double(dy) / double(length)) * 180.0 / M_PI;
+ // NOTATION_DEBUG << "slur: dy is " << dy << ", length " << length << ", rotating through " << theta << endl;
+ rotate = true;
+ }
+
+ // draw normal slur for very slopey phrasing slur:
+ if (theta < -5 || theta > 5)
+ phrasing = false;
+
+ int y0 = 0, my = 0;
+
+ float noteLengths = float(length) / nbw;
+ if (noteLengths < 1)
+ noteLengths = 1;
+
+ my = int(0 - nbh * sqrt(noteLengths) / 2);
+ if (flat)
+ my = my * 2 / 3;
+ else if (phrasing)
+ my = my * 3 / 4;
+ if (!above)
+ my = -my;
+
+ bool havePixmap = false;
+ QPoint topLeft, bottomRight;
+
+ if (smooth)
+ thickness += 2;
+
+ for (int i = 0; i < thickness; ++i) {
+
+ Spline::PointList pl;
+
+ if (!phrasing) {
+ pl.push_back(QPoint(length / 6, my));
+ pl.push_back(QPoint(length - length / 6, my));
+ } else {
+ pl.push_back(QPoint(abs(my) / 4, my / 3));
+ pl.push_back(QPoint(length / 6, my));
+
+ if (theta > 1) {
+ pl.push_back(QPoint(length * 3 / 8, my * 3 / 2));
+ } else if (theta < -1) {
+ pl.push_back(QPoint(length * 5 / 8, my * 3 / 2));
+ } else {
+ pl.push_back(QPoint(length / 2, my * 4 / 3));
+ }
+
+ pl.push_back(QPoint(length - length / 6, my));
+ pl.push_back(QPoint(length - abs(my) / 4, my / 3));
+ }
+
+ Spline::PointList *polyPoints = Spline::calculate
+ (QPoint(0, y0), QPoint(length - 1, y0), pl, topLeft, bottomRight);
+
+ if (!havePixmap) {
+ int width = bottomRight.x() - topLeft.x();
+ int height = bottomRight.y() - topLeft.y() + thickness - 1 + abs(dy);
+ hotspot = QPoint(0, -topLeft.y() + (dy < 0 ? -dy : 0));
+
+ // NOTATION_DEBUG << "slur: bottomRight (" << bottomRight.x() << "," << bottomRight.y() << "), topLeft (" << topLeft.x() << "," << topLeft.y() << "), width " << width << ", height " << height << ", hotspot (" << hotspot.x() << "," << hotspot.y() << "), dy " << dy << ", thickness " << thickness << endl;
+
+ if (painter) {
+
+ // This conditional is because we're also called with
+ // a painter arg from non-printer drawTie. It's a big
+ // hack.
+
+ if (m_inPrinterMethod) {
+ painter->save();
+ m_p->beginExternal(painter);
+ painter->translate(x, y);
+ if (rotate)
+ painter->rotate(theta);
+ } else {
+ m_p->painter().save();
+ m_p->maskPainter().save();
+ m_p->painter().translate(x, y);
+ m_p->maskPainter().translate(x, y);
+ if (rotate) {
+ m_p->painter().rotate(theta);
+ m_p->maskPainter().rotate(theta);
+ }
+ }
+
+ } else {
+ createPixmapAndMask(smooth ? width*2 + 1 : width,
+ smooth ? height*2 + thickness*2 : height + thickness,
+ width, height);
+
+ QWMatrix m;
+ if (smooth)
+ m.translate(2 * hotspot.x(), 2 * hotspot.y());
+ else
+ m.translate(hotspot.x(), hotspot.y());
+ m.rotate(theta);
+ m_p->painter().setWorldMatrix(m);
+ m_p->maskPainter().setWorldMatrix(m);
+ }
+
+ if (m_selected)
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ else if (m_shaded) {
+ m_p->painter().setPen(Qt::gray);
+ }
+ havePixmap = true;
+ }
+ /*
+ for (int j = 0; j < pl.size(); ++j) {
+ if (smooth) {
+ m_p->drawPoint(pl[j].x()*2, pl[j].y()*2);
+ } else {
+ m_p->drawPoint(pl[j].x(), pl[j].y());
+ }
+ }
+ */
+ int ppc = polyPoints->size();
+ QPointArray qp(ppc);
+
+ for (int j = 0; j < ppc; ++j) {
+ qp.setPoint(j, (*polyPoints)[j].x(), (*polyPoints)[j].y());
+ }
+
+ delete polyPoints;
+
+ if (!smooth || (i > 0 && i < thickness - 1)) {
+ if (smooth) {
+ for (int j = 0; j < ppc; ++j) {
+ qp.setPoint(j, qp.point(j).x()*2, qp.point(j).y()*2);
+ }
+ m_p->drawPolyline(qp);
+ for (int j = 0; j < ppc; ++j) {
+ qp.setPoint(j, qp.point(j).x(), qp.point(j).y() + 1);
+ }
+ m_p->drawPolyline(qp);
+ } else {
+ m_p->drawPolyline(qp);
+ }
+ }
+
+ if (above) {
+ ++my;
+ if (i % 2)
+ ++y0;
+ } else {
+ --my;
+ if (i % 2)
+ --y0;
+ }
+ }
+
+ if (m_selected) {
+ m_p->painter().setPen(Qt::black);
+ }
+
+ QWMatrix::setTransformationMode(mode);
+
+ if (painter) {
+ painter->restore();
+ if (!m_inPrinterMethod)
+ m_p->maskPainter().restore();
+ }
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeOttavaPixmap(int length, int octavesUp)
+{
+ Profiler profiler("NotePixmapFactory::makeOttavaPixmap");
+ m_inPrinterMethod = false;
+ drawOttavaAux(length, octavesUp, 0, 0, 0);
+ return makeCanvasPixmap(QPoint(0, m_generatedHeight - 1));
+}
+
+void
+NotePixmapFactory::drawOttava(int length, int octavesUp,
+ QPainter &painter, int x, int y)
+{
+ Profiler profiler("NotePixmapFactory::drawOttava");
+ m_inPrinterMethod = true;
+ drawOttavaAux(length, octavesUp, &painter, x, y);
+ m_inPrinterMethod = false;
+}
+
+void
+NotePixmapFactory::drawOttavaAux(int length, int octavesUp,
+ QPainter *painter, int x, int y)
+{
+ int height = m_ottavaFontMetrics.height();
+ int backpedal = 0;
+ QString label;
+ QRect r;
+
+ if (octavesUp == 2 || octavesUp == -2) {
+ label = "15ma ";
+ backpedal = m_ottavaFontMetrics.width("15") / 2;
+ } else {
+ label = "8va ";
+ backpedal = m_ottavaFontMetrics.width("8") / 2;
+ }
+
+ int width = length + backpedal;
+
+ if (painter) {
+ painter->save();
+ m_p->beginExternal(painter);
+ painter->translate(x - backpedal, y - height);
+ } else {
+ NOTATION_DEBUG << "NotePixmapFactory::drawOttavaAux: making pixmap and mask " << width << "x" << height << endl;
+ createPixmapAndMask(width, height);
+ }
+
+ int thickness = getStemThickness();
+ QPen pen(Qt::black, thickness, Qt::DotLine);
+
+ if (m_selected) {
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ pen.setColor(GUIPalette::getColour(GUIPalette::SelectedElement));
+ } else if (m_shaded) {
+ m_p->painter().setPen(Qt::gray);
+ pen.setColor(Qt::gray);
+ }
+
+ m_p->painter().setFont(m_ottavaFont);
+ if (!m_inPrinterMethod)
+ m_p->maskPainter().setFont(m_ottavaFont);
+
+ m_p->drawText(0, m_ottavaFontMetrics.ascent(), label);
+
+ m_p->painter().setPen(pen);
+ // if (!m_inPrinterMethod) m_p->maskPainter().setPen(pen);
+
+ int x0 = m_ottavaFontMetrics.width(label) + thickness;
+ int x1 = width - thickness;
+ int y0 = m_ottavaFontMetrics.ascent() * 2 / 3 - thickness / 2;
+ int y1 = (octavesUp < 0 ? 0 : m_ottavaFontMetrics.ascent());
+
+ NOTATION_DEBUG << "NotePixmapFactory::drawOttavaAux: drawing " << x0 << "," << y0 << " to " << x1 << "," << y0 << ", thickness " << thickness << endl;
+
+ m_p->drawLine(x0, y0, x1, y0);
+
+ pen.setStyle(Qt::SolidLine);
+ m_p->painter().setPen(pen);
+ // if (!m_inPrinterMethod) m_p->maskPainter().setPen(pen);
+
+ NOTATION_DEBUG << "NotePixmapFactory::drawOttavaAux: drawing " << x1 << "," << y0 << " to " << x1 << "," << y1 << ", thickness " << thickness << endl;
+
+ m_p->drawLine(x1, y0, x1, y1);
+
+ m_p->painter().setPen(QPen());
+ if (!m_inPrinterMethod)
+ m_p->maskPainter().setPen(QPen());
+
+ if (painter) {
+ painter->restore();
+ }
+}
+
+void
+NotePixmapFactory::drawBracket(int length, bool left, bool curly, int x, int y)
+{
+ // curly mode not yet implemented
+
+ int thickness = getStemThickness() * 2;
+
+ int m1 = length / 6;
+ int m2 = length - length / 6 - 1;
+
+ int off0 = 0, moff = 0;
+
+ int nbh = getNoteBodyHeight(), nbw = getNoteBodyWidth();
+ float noteLengths = float(length) / nbw;
+ if (noteLengths < 1)
+ noteLengths = 1;
+ moff = int(nbh * sqrt(noteLengths) / 2);
+ moff = moff * 2 / 3;
+
+ if (left)
+ moff = -moff;
+
+ QPoint topLeft, bottomRight;
+
+ for (int i = 0; i < thickness; ++i) {
+
+ Spline::PointList pl;
+ pl.push_back(QPoint((int)moff, m1));
+ pl.push_back(QPoint((int)moff, m2));
+ /*
+ NOTATION_DEBUG << "bracket spline controls: " << moff << "," << m1
+ << ", " << moff << "," << m2 << "; end points "
+ << off0 << ",0, " << off0 << "," << length-1
+ << endl;
+ */
+ Spline::PointList *polyPoints = Spline::calculate
+ (QPoint(off0, 0), QPoint(off0, length - 1), pl, topLeft, bottomRight);
+
+ int ppc = polyPoints->size();
+ QPointArray qp(ppc);
+ /*
+ NOTATION_DEBUG << "bracket spline polypoints: " << endl;
+ for (int j = 0; j < ppc; ++j) {
+ NOTATION_DEBUG << (*polyPoints)[j].x() << "," << (*polyPoints)[j].y() << endl;
+ }
+ */
+
+ for (int j = 0; j < ppc; ++j) {
+ qp.setPoint(j, x + (*polyPoints)[j].x(), y + (*polyPoints)[j].y());
+ }
+
+ delete polyPoints;
+
+ m_p->drawPolyline(qp);
+
+ if (!left) {
+ ++moff;
+ if (i % 2)
+ ++off0;
+ } else {
+ --moff;
+ if (i % 2)
+ --off0;
+ }
+ }
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeTimeSigPixmap(const TimeSignature& sig)
+{
+ Profiler profiler("NotePixmapFactory::makeTimeSigPixmap");
+
+ if (sig.isCommon()) {
+
+ NoteCharacter character;
+
+ CharName charName;
+ if (sig.getNumerator() == 2) {
+ charName = NoteCharacterNames::CUT_TIME;
+ } else {
+ charName = NoteCharacterNames::COMMON_TIME;
+ }
+
+ if (getCharacter(charName, character, PlainColour, false)) {
+ createPixmapAndMask(character.getWidth(), character.getHeight());
+ m_p->drawNoteCharacter(0, 0, character);
+ return makeCanvasPixmap(QPoint(0, character.getHeight() / 2));
+ }
+
+ QString c("c");
+ QRect r = m_bigTimeSigFontMetrics.boundingRect(c);
+
+ int dy = getLineSpacing() / 4;
+ createPixmapAndMask(r.width(), r.height() + dy*2);
+
+ if (m_selected) {
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ } else if (m_shaded) {
+ m_p->painter().setPen(Qt::gray);
+ }
+
+ m_p->painter().setFont(m_bigTimeSigFont);
+ if (!m_inPrinterMethod)
+ m_p->maskPainter().setFont(m_bigTimeSigFont);
+
+ m_p->drawText(0, r.height() + dy, c);
+
+ if (sig.getNumerator() == 2) { // cut common
+
+ int x = r.width() * 3 / 5 - getStemThickness();
+
+ for (int i = 0; i < getStemThickness() * 2; ++i, ++x) {
+ m_p->drawLine(x, 0, x, r.height() + dy*2 - 1);
+ }
+ }
+
+ m_p->painter().setPen(Qt::black);
+ return makeCanvasPixmap(QPoint(0, r.height() / 2 + dy));
+
+ } else {
+
+ int numerator = sig.getNumerator(),
+ denominator = sig.getDenominator();
+
+ QString numS, denomS;
+
+ numS.setNum(numerator);
+ denomS.setNum(denominator);
+
+ NoteCharacter character;
+ if (getCharacter(m_style->getTimeSignatureDigitName(0), character,
+ PlainColour, false)) {
+
+ // if the 0 digit exists, we assume 1-9 also all exist
+ // and all have the same width
+
+ int numW = character.getWidth() * numS.length();
+ int denomW = character.getWidth() * denomS.length();
+
+ int width = std::max(numW, denomW);
+ int height = getLineSpacing() * 4 - getStaffLineThickness();
+
+ createPixmapAndMask(width, height);
+
+ for (unsigned int i = 0; i < numS.length(); ++i) {
+ int x = width - (width - numW) / 2 - (i + 1) * character.getWidth();
+ int y = height / 4 - (character.getHeight() / 2);
+ NoteCharacter charCharacter = getCharacter
+ (m_style->getTimeSignatureDigitName(numerator % 10),
+ PlainColour, false);
+ m_p->drawNoteCharacter(x, y, charCharacter);
+ numerator /= 10;
+ }
+
+ for (unsigned int i = 0; i < denomS.length(); ++i) {
+ int x = width - (width - denomW) / 2 - (i + 1) * character.getWidth();
+ int y = height - height / 4 - (character.getHeight() / 2);
+ NoteCharacter charCharacter = getCharacter
+ (m_style->getTimeSignatureDigitName(denominator % 10),
+ PlainColour, false);
+ m_p->drawNoteCharacter(x, y, charCharacter);
+ denominator /= 10;
+ }
+
+ return makeCanvasPixmap(QPoint(0, height / 2));
+ }
+
+ QRect numR = m_timeSigFontMetrics.boundingRect(numS);
+ QRect denomR = m_timeSigFontMetrics.boundingRect(denomS);
+ int width = std::max(numR.width(), denomR.width()) + 2;
+ int x;
+
+ createPixmapAndMask(width, denomR.height() * 2 + getNoteBodyHeight());
+
+ if (m_selected) {
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ } else if (m_shaded) {
+ m_p->painter().setPen(Qt::gray);
+ }
+
+ m_p->painter().setFont(m_timeSigFont);
+ if (!m_inPrinterMethod)
+ m_p->maskPainter().setFont(m_timeSigFont);
+
+ x = (width - numR.width()) / 2 - 1;
+ m_p->drawText(x, denomR.height(), numS);
+
+ x = (width - denomR.width()) / 2 - 1;
+ m_p->drawText(x, denomR.height() * 2 + (getNoteBodyHeight() / 2) - 1, denomS);
+
+ m_p->painter().setPen(Qt::black);
+
+ return makeCanvasPixmap(QPoint(0, denomR.height() +
+ (getNoteBodyHeight() / 4) - 1),
+ true);
+ }
+}
+
+int NotePixmapFactory::getTimeSigWidth(const TimeSignature &sig) const
+{
+ if (sig.isCommon()) {
+
+ QRect r(m_bigTimeSigFontMetrics.boundingRect("c"));
+ return r.width() + 2;
+
+ } else {
+
+ int numerator = sig.getNumerator(),
+ denominator = sig.getDenominator();
+
+ QString numS, denomS;
+
+ numS.setNum(numerator);
+ denomS.setNum(denominator);
+
+ QRect numR = m_timeSigFontMetrics.boundingRect(numS);
+ QRect denomR = m_timeSigFontMetrics.boundingRect(denomS);
+ int width = std::max(numR.width(), denomR.width()) + 2;
+
+ return width;
+ }
+}
+
+QFont
+NotePixmapFactory::getTextFont(const Text &text) const
+{
+ std::string type(text.getTextType());
+ TextFontCache::iterator i = m_textFontCache.find(type.c_str());
+ if (i != m_textFontCache.end())
+ return i->second;
+
+ /*
+ * Text types:
+ *
+ * UnspecifiedType: Nothing known, use small roman
+ * StaffName: Large roman, to left of start of staff
+ * ChordName: Not normally shown in score, use small roman
+ * KeyName: Not normally shown in score, use small roman
+ * Lyric: Small roman, below staff and dynamic texts
+ * Chord: Small bold roman, above staff
+ * Dynamic: Small italic, below staff
+ * Direction: Large roman, above staff (by barline?)
+ * LocalDirection: Small bold italic, below staff (by barline?)
+ * Tempo: Large bold roman, above staff
+ * LocalTempo: Small bold roman, above staff
+ * Annotation: Very small sans-serif, in a yellow box
+ * LilyPondDirective: Very small sans-serif, in a green box
+ */
+
+ int weight = QFont::Normal;
+ bool italic = false;
+ bool large = false;
+ bool tiny = false;
+ bool serif = true;
+
+ if (type == Text::Tempo ||
+ type == Text::LocalTempo ||
+ type == Text::LocalDirection ||
+ type == Text::Chord) {
+ weight = QFont::Bold;
+ }
+
+ if (type == Text::Dynamic ||
+ type == Text::LocalDirection) {
+ italic = true;
+ }
+
+ if (type == Text::StaffName ||
+ type == Text::Direction ||
+ type == Text::Tempo) {
+ large = true;
+ }
+
+ if (type == Text::Annotation ||
+ type == Text::LilyPondDirective) {
+ serif = false;
+ tiny = true;
+ }
+
+ KConfig* config = kapp->config();
+
+ QFont textFont;
+
+ if (serif) {
+ textFont = QFont(defaultSerifFontFamily);
+ textFont = config->readFontEntry("textfont", &textFont);
+ } else {
+ textFont = QFont(defaultSansSerifFontFamily);
+ textFont = config->readFontEntry("sansfont", &textFont);
+ }
+
+ textFont.setStyleStrategy(QFont::StyleStrategy(QFont::PreferDefault |
+ QFont::PreferMatch));
+
+ int size;
+ if (large)
+ size = (getLineSpacing() * 7) / 2;
+ else if (tiny)
+ size = (getLineSpacing() * 4) / 3;
+ else if (serif)
+ size = (getLineSpacing() * 2);
+ else
+ size = (getLineSpacing() * 3) / 2;
+
+ textFont.setPixelSize(size);
+ textFont.setStyleHint(serif ? QFont::Serif : QFont::SansSerif);
+ textFont.setWeight(weight);
+ textFont.setItalic(italic);
+
+ NOTATION_DEBUG << "NotePixmapFactory::getTextFont: requested size " << size
+ << " for type " << type << endl;
+
+ NOTATION_DEBUG << "NotePixmapFactory::getTextFont: returning font '"
+ << textFont.toString() << "' for type " << type.c_str()
+ << " text : " << text.getText().c_str() << endl;
+
+ m_textFontCache[type.c_str()] = textFont;
+ return textFont;
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeTextPixmap(const Text &text)
+{
+ Profiler profiler("NotePixmapFactory::makeTextPixmap");
+
+ std::string type(text.getTextType());
+
+ if (type == Text::Annotation ||
+ type == Text::LilyPondDirective) {
+ return makeAnnotationPixmap(text, (type == Text::LilyPondDirective));
+ }
+
+ drawTextAux(text, 0, 0, 0);
+ return makeCanvasPixmap(QPoint(2, 2), true);
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeGuitarChordPixmap(const Guitar::Fingering &fingering,
+ int x,
+ int y)
+{
+ using namespace Guitar;
+ Profiler profiler("NotePixmapFactory::makeGuitarChordPixmap");
+
+ int guitarChordWidth = getLineSpacing() * 6;
+ int guitarChordHeight = getLineSpacing() * 6;
+
+ createPixmapAndMask(guitarChordWidth, guitarChordHeight);
+
+ if (m_selected) {
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ m_p->painter().setBrush(GUIPalette::getColour(GUIPalette::SelectedElement));
+ } else {
+ m_p->painter().setPen(Qt::black);
+ m_p->painter().setBrush(Qt::black);
+ }
+
+ Guitar::NoteSymbols ns(Guitar::Fingering::DEFAULT_NB_STRINGS, FingeringBox::DEFAULT_NB_DISPLAYED_FRETS);
+ Guitar::NoteSymbols::drawFingeringPixmap(fingering, ns, &(m_p->painter()));
+
+ return makeCanvasPixmap(QPoint (x, y), true);
+}
+
+void
+NotePixmapFactory::drawText(const Text &text,
+ QPainter &painter, int x, int y)
+{
+ Profiler profiler("NotePixmapFactory::drawText");
+
+ // NOTATION_DEBUG << "NotePixmapFactory::drawText() " << text.getText().c_str()
+ // << " - type : " << text.getTextType().c_str() << endl;
+
+ std::string type(text.getTextType());
+
+ if (type == Text::Annotation ||
+ type == Text::LilyPondDirective) {
+ QCanvasPixmap *map = makeAnnotationPixmap(text, (type == Text::LilyPondDirective));
+ painter.drawPixmap(x, y, *map);
+ return ;
+ }
+
+ m_inPrinterMethod = true;
+ drawTextAux(text, &painter, x, y);
+ m_inPrinterMethod = false;
+}
+
+void
+NotePixmapFactory::drawTextAux(const Text &text,
+ QPainter *painter, int x, int y)
+{
+ QString s(strtoqstr(text.getText()));
+ QFont textFont(getTextFont(text));
+ QFontMetrics textMetrics(textFont);
+
+ int offset = 2;
+ int width = textMetrics.width(s) + 2 * offset;
+ int height = textMetrics.height() + 2 * offset;
+
+ if (painter) {
+ painter->save();
+ m_p->beginExternal(painter);
+ painter->translate(x - offset, y - offset);
+ } else {
+ createPixmapAndMask(width, height);
+ }
+
+ if (m_selected)
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ else if (m_shaded)
+ m_p->painter().setPen(Qt::gray);
+
+ m_p->painter().setFont(textFont);
+ if (!m_inPrinterMethod)
+ m_p->maskPainter().setFont(textFont);
+
+ m_p->drawText(offset, textMetrics.ascent() + offset, s);
+
+ m_p->painter().setPen(Qt::black);
+
+ if (painter) {
+ painter->restore();
+ }
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeAnnotationPixmap(const Text &text)
+{
+ return makeAnnotationPixmap(text, false);
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeAnnotationPixmap(const Text &text, const bool isLilyPondDirective)
+{
+ QString s(strtoqstr(text.getText()));
+
+ QFont textFont(getTextFont(text));
+ QFontMetrics textMetrics(textFont);
+
+ int annotationWidth = getLineSpacing() * 16;
+ int annotationHeight = getLineSpacing() * 6;
+
+ int topGap = getLineSpacing() / 4 + 1;
+ int bottomGap = getLineSpacing() / 3 + 1;
+ int sideGap = getLineSpacing() / 4 + 1;
+
+ QRect r = textMetrics.boundingRect
+ (0, 0, annotationWidth, annotationHeight, Qt::WordBreak, s);
+
+ int pixmapWidth = r.width() + sideGap * 2;
+ int pixmapHeight = r.height() + topGap + bottomGap;
+
+ createPixmapAndMask(pixmapWidth, pixmapHeight);
+
+ if (m_selected)
+ m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
+ else if (m_shaded)
+ m_p->painter().setPen(Qt::gray);
+
+ m_p->painter().setFont(textFont);
+ if (!m_inPrinterMethod)
+ m_p->maskPainter().setFont(textFont);
+
+ if (isLilyPondDirective) {
+ m_p->painter().setBrush(GUIPalette::getColour(GUIPalette::TextLilyPondDirectiveBackground));
+ } else {
+ m_p->painter().setBrush(GUIPalette::getColour(GUIPalette::TextAnnotationBackground));
+ }
+
+ m_p->drawRect(0, 0, pixmapWidth, pixmapHeight);
+
+ m_p->painter().setBrush(Qt::black);
+ m_p->painter().drawText(QRect(sideGap, topGap,
+ annotationWidth + sideGap,
+ pixmapHeight - bottomGap),
+ Qt::WordBreak, s);
+
+ /* unnecessary following the rectangle draw
+ m_pm.drawText(QRect(sideGap, topGap,
+ annotationWidth + sideGap, annotationHeight + topGap),
+ Qt::WordBreak, s);
+ */
+
+ return makeCanvasPixmap(QPoint(0, 0));
+}
+
+void
+NotePixmapFactory::createPixmapAndMask(int width, int height,
+ int maskWidth, int maskHeight)
+{
+ if (maskWidth < 0)
+ maskWidth = width;
+ if (maskHeight < 0)
+ maskHeight = height;
+
+ m_generatedWidth = width;
+ m_generatedHeight = height;
+ m_generatedPixmap = new QPixmap(width, height);
+ m_generatedMask = new QBitmap(maskWidth, maskHeight);
+
+ static unsigned long total = 0;
+ total += width * height;
+// NOTATION_DEBUG << "createPixmapAndMask: " << width << "x" << height << " (" << (width*height) << " px, " << total << " total)" << endl;
+
+ // clear up pixmap and mask
+ m_generatedPixmap->fill();
+ m_generatedMask->fill(Qt::color0);
+
+ // initiate painting
+ m_p->begin(m_generatedPixmap, m_generatedMask);
+
+ m_p->painter().setPen(Qt::black);
+ m_p->painter().setBrush(Qt::black);
+ m_p->maskPainter().setPen(Qt::white);
+ m_p->maskPainter().setBrush(Qt::white);
+}
+
+QCanvasPixmap*
+NotePixmapFactory::makeCanvasPixmap(QPoint hotspot, bool generateMask)
+{
+ m_p->end();
+
+ QCanvasPixmap* p = new QCanvasPixmap(*m_generatedPixmap, hotspot);
+
+ if (generateMask) {
+ p->setMask(PixmapFunctions::generateMask(*p));
+ } else {
+ p->setMask(*m_generatedMask);
+ }
+
+ delete m_generatedPixmap;
+ delete m_generatedMask;
+ return p;
+}
+
+NoteCharacter
+NotePixmapFactory::getCharacter(CharName name, ColourType type, bool inverted)
+{
+ NoteCharacter ch;
+ getCharacter(name, ch, type, inverted);
+ return ch;
+}
+
+bool
+NotePixmapFactory::getCharacter(CharName name, NoteCharacter &ch,
+ ColourType type, bool inverted)
+{
+ NoteFont::CharacterType charType =
+ m_inPrinterMethod ? NoteFont::Printer : NoteFont::Screen;
+
+ if (m_selected) {
+ return m_font->getCharacterColoured
+ (name,
+ GUIPalette::SelectedElementHue,
+ GUIPalette::SelectedElementMinValue,
+ ch, charType, inverted);
+ }
+
+ if (m_shaded) {
+ return m_font->getCharacterShaded(name, ch, charType, inverted);
+ }
+
+ switch (type) {
+
+ case PlainColour:
+ return m_font->getCharacter(name, ch, charType, inverted);
+
+ case QuantizedColour:
+ return m_font->getCharacterColoured
+ (name,
+ GUIPalette::QuantizedNoteHue,
+ GUIPalette::QuantizedNoteMinValue,
+ ch, charType, inverted);
+
+ case HighlightedColour:
+ return m_font->getCharacterColoured
+ (name,
+ GUIPalette::HighlightedElementHue,
+ GUIPalette::HighlightedElementMinValue,
+ ch, charType, inverted);
+
+ case TriggerColour:
+ return m_font->getCharacterColoured
+ (name,
+ GUIPalette::TriggerNoteHue,
+ GUIPalette::TriggerNoteMinValue,
+ ch, charType, inverted);
+
+ case OutRangeColour:
+ return m_font->getCharacterColoured
+ (name,
+ GUIPalette::OutRangeNoteHue,
+ GUIPalette::OutRangeNoteMinValue,
+ ch, charType, inverted);
+ }
+
+ return m_font->getCharacter(name, ch, charType, inverted);
+}
+
+QPoint
+NotePixmapFactory::m_pointZero;
+
+
+int NotePixmapFactory::getNoteBodyWidth(Note::Type type)
+const
+{
+ CharName charName(m_style->getNoteHeadCharName(type).first);
+ int hx, hy;
+ if (!m_font->getHotspot(charName, hx, hy))
+ hx = 0;
+ return m_font->getWidth(charName) - hx * 2;
+}
+
+int NotePixmapFactory::getNoteBodyHeight(Note::Type )
+const
+{
+ // this is by definition
+ return m_font->getSize();
+}
+
+int NotePixmapFactory::getLineSpacing() const
+{
+ return m_font->getSize() + getStaffLineThickness();
+}
+
+int NotePixmapFactory::getAccidentalWidth(const Accidental &a,
+ int shift, bool extraShift) const
+{
+ if (a == Accidentals::NoAccidental)
+ return 0;
+ int w = m_font->getWidth(m_style->getAccidentalCharName(a));
+ if (!shift)
+ return w;
+ else {
+ int sw = w;
+ if (extraShift) {
+ --shift;
+ w += getNoteBodyWidth() + getStemThickness();
+ }
+ w += shift *
+ (sw - m_font->getHotspot(m_style->getAccidentalCharName(a)).x());
+ }
+ return w;
+}
+
+int NotePixmapFactory::getAccidentalHeight(const Accidental &a) const
+{
+ return m_font->getHeight(m_style->getAccidentalCharName(a));
+}
+
+int NotePixmapFactory::getStemLength() const
+{
+ unsigned int l = 1;
+ (void)m_font->getStemLength(l);
+ return l;
+}
+
+int NotePixmapFactory::getStemThickness() const
+{
+ unsigned int i = 1;
+ (void)m_font->getStemThickness(i);
+ return i;
+}
+
+int NotePixmapFactory::getStaffLineThickness() const
+{
+ unsigned int i;
+ (void)m_font->getStaffLineThickness(i);
+ return i;
+}
+
+int NotePixmapFactory::getLegerLineThickness() const
+{
+ unsigned int i;
+ (void)m_font->getLegerLineThickness(i);
+ return i;
+}
+
+int NotePixmapFactory::getDotWidth() const
+{
+ return m_font->getWidth(NoteCharacterNames::DOT);
+}
+
+int NotePixmapFactory::getClefWidth(const Clef &clef) const
+{
+ return m_font->getWidth(m_style->getClefCharName(clef.getClefType()));
+}
+
+int NotePixmapFactory::getBarMargin() const
+{
+ return getNoteBodyWidth() * 2;
+}
+
+int NotePixmapFactory::getRestWidth(const Note &restType) const
+{
+ return m_font->getWidth(m_style->getRestCharName(restType.getNoteType(),
+ false)) // small inaccuracy!
+ + (restType.getDots() * getDotWidth());
+}
+
+int NotePixmapFactory::getKeyWidth(const Key &key,
+ Key previousKey) const
+{
+ std::vector<int> ah0 = previousKey.getAccidentalHeights(Clef());
+ std::vector<int> ah1 = key.getAccidentalHeights(Clef());
+
+ int cancelCount = 0;
+ if (key.isSharp() != previousKey.isSharp())
+ cancelCount = ah0.size();
+ else if (ah1.size() < ah0.size())
+ cancelCount = ah0.size() - ah1.size();
+
+ CharName keyCharName;
+ if (key.isSharp())
+ keyCharName = NoteCharacterNames::SHARP;
+ else
+ keyCharName = NoteCharacterNames::FLAT;
+
+ NoteCharacter keyCharacter;
+ NoteCharacter cancelCharacter;
+
+ keyCharacter = m_font->getCharacter(keyCharName);
+ if (cancelCount > 0) {
+ cancelCharacter = m_font->getCharacter(NoteCharacterNames::NATURAL);
+ }
+
+ //int x = 0;
+ //int lw = getLineSpacing();
+ int keyDelta = keyCharacter.getWidth() - keyCharacter.getHotspot().x();
+
+ int cancelDelta = 0;
+ int between = 0;
+ if (cancelCount > 0) {
+ cancelDelta = cancelCharacter.getWidth() + cancelCharacter.getWidth() / 3;
+ between = cancelCharacter.getWidth();
+ }
+
+ return (keyDelta * ah1.size() + cancelDelta * cancelCount + between +
+ keyCharacter.getWidth() / 4);
+}
+
+int NotePixmapFactory::getTextWidth(const Text &text) const
+{
+ QFontMetrics metrics(getTextFont(text));
+ return metrics.boundingRect(strtoqstr(text.getText())).width() + 4;
+}
+
+}
diff --git a/src/gui/editors/notation/NotePixmapFactory.h b/src/gui/editors/notation/NotePixmapFactory.h
new file mode 100644
index 0000000..14b4773
--- /dev/null
+++ b/src/gui/editors/notation/NotePixmapFactory.h
@@ -0,0 +1,358 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTEPIXMAPFACTORY_H_
+#define _RG_NOTEPIXMAPFACTORY_H_
+
+#include "base/NotationTypes.h"
+#include <map>
+#include "NoteCharacter.h"
+#include <string>
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qpixmap.h>
+#include <qpoint.h>
+#include "base/Event.h"
+#include "gui/editors/notation/NoteCharacterNames.h"
+
+
+class QPainter;
+class QCanvasPixmap;
+class QBitmap;
+
+
+namespace Rosegarden
+{
+
+namespace Guitar { class Fingering; }
+
+class TimeSignature;
+class Text;
+class NoteStyle;
+class NotePixmapParameters;
+class NoteFont;
+class NotePixmapPainter;
+class NotePixmapCache;
+class Clef;
+class TrackHeader;
+
+/**
+ * Generates QCanvasPixmaps for various notation items.
+ */
+
+class NotePixmapFactory
+{
+public:
+ NotePixmapFactory(std::string fontName = "", int size = -1);
+ NotePixmapFactory(const NotePixmapFactory &);
+ NotePixmapFactory &operator=(const NotePixmapFactory &);
+ ~NotePixmapFactory();
+
+ std::string getFontName() const;
+ int getSize() const;
+
+ void setSelected(bool selected) { m_selected = selected; }
+ bool isSelected() const { return m_selected; }
+
+ void setShaded(bool shaded) { m_shaded = shaded; }
+ bool isShaded() const { return m_shaded; }
+
+ void setNoteStyle(NoteStyle *style) { m_style = style; }
+ const NoteStyle *getNoteStyle() const { return m_style; }
+
+ // Display methods -- create canvas pixmaps:
+
+ QCanvasPixmap* makeNotePixmap(const NotePixmapParameters &parameters);
+ QCanvasPixmap* makeRestPixmap(const NotePixmapParameters &parameters);
+ QCanvasPixmap* makeClefPixmap(const Clef &clef);
+ QCanvasPixmap* makeKeyPixmap(const Key &key,
+ const Clef &clef,
+ Key previousKey =
+ Key::DefaultKey);
+ QCanvasPixmap* makeTimeSigPixmap(const TimeSignature& sig);
+ QCanvasPixmap* makeHairpinPixmap(int length, bool isCrescendo);
+ QCanvasPixmap* makeSlurPixmap(int length, int dy, bool above, bool phrasing);
+ QCanvasPixmap* makeOttavaPixmap(int length, int octavesUp);
+ QCanvasPixmap* makePedalDownPixmap();
+ QCanvasPixmap* makePedalUpPixmap();
+ QCanvasPixmap* makeUnknownPixmap();
+ QCanvasPixmap* makeTextPixmap(const Text &text);
+ QCanvasPixmap* makeGuitarChordPixmap(const Guitar::Fingering &fingering,
+ int x, int y);
+
+ QCanvasPixmap* makeNoteHaloPixmap(const NotePixmapParameters &parameters);
+
+ // Printing methods -- draw direct to a paint device:
+
+ void drawNote(const NotePixmapParameters &parameters,
+ QPainter &painter, int x, int y);
+ void drawRest(const NotePixmapParameters &parameters,
+ QPainter &painter, int x, int y);
+ void drawHairpin(int length, bool isCrescendo,
+ QPainter &painter, int x, int y);
+ void drawSlur(int length, int dy, bool above, bool phrasing,
+ QPainter &painter, int x, int y);
+ void drawOttava(int length, int octavesUp,
+ QPainter &painter, int x, int y);
+ void drawText(const Text &text,
+ QPainter &painter, int x, int y);
+
+ // Other support methods for producing pixmaps for other contexts:
+
+ static QCanvasPixmap *makeToolbarPixmap(const char *name,
+ bool menuSize = false);
+ static QCanvasPixmap *makeNoteMenuPixmap(timeT duration,
+ timeT &errorReturn);
+ static QCanvasPixmap *makeMarkMenuPixmap(Mark);
+
+ QCanvasPixmap* makePitchDisplayPixmap(int pitch,
+ const Clef &clef,
+ bool useSharps);
+ QCanvasPixmap* makePitchDisplayPixmap(int pitch,
+ const Clef &clef,
+ int octave,
+ int step);
+ QCanvasPixmap* makeClefDisplayPixmap(const Clef &clef);
+ QCanvasPixmap* makeKeyDisplayPixmap(const Key &key,
+ const Clef &clef);
+
+ QCanvasPixmap* makeTrackHeaderPixmap(int width, int height,
+ TrackHeader *header);
+
+ // Bounding box and other geometry methods:
+
+ int getNoteBodyWidth (Note::Type =
+ Note::Crotchet) const;
+
+ int getNoteBodyHeight(Note::Type =
+ Note::Crotchet) const;
+
+ int getAccidentalWidth (const Accidental &,
+ int shift = 0, bool extra = false) const;
+ int getAccidentalHeight(const Accidental &) const;
+
+ int getLineSpacing() const;
+ int getStemLength() const;
+ int getStemThickness() const;
+ int getStaffLineThickness() const;
+ int getLegerLineThickness() const;
+ int getDotWidth() const;
+ int getBarMargin() const;
+
+ int getClefWidth(const Clef &clef) const;
+ int getTimeSigWidth(const TimeSignature &timesig) const;
+ int getRestWidth(const Note &restType) const;
+ int getKeyWidth(const Key &key,
+ Key previousKey = Key::DefaultKey) const;
+ int getTextWidth(const Text &text) const;
+
+ /**
+ * Returns the width of clef and key signature drawn in a track header.
+ */
+ int getClefAndKeyWidth(const Key &key, const Clef &clef);
+
+ /**
+ * Returns the Number of Text Lines that can be written at top and bottom
+ * of a track header.
+ * The parameter is the track header height.
+ * Always returns a value >= 1.
+ */
+ int getTrackHeaderNTL(int height);
+
+ /**
+ * Returns the width of a text string written in a track header.
+ */
+ int getTrackHeaderTextWidth(QString str);
+
+ /**
+ * Returns the spacing of a text lines written in a track header.
+ */
+ int getTrackHeaderTextLineSpacing();
+
+ /**
+ * Returns from the beginning of "text" a string of horizontal size
+ * "width" (when written with m_trackHeaderFont) and removes it
+ * from "text".
+ */
+ QString getOneLine(QString &text, int width);
+
+
+ /**
+ * We need this function because as of Qt 3.1, QCanvasPixmap
+ * is no longer copyable by value, while QPixmap still is.
+ *
+ * So all the makeXXPixmap are now returning QCanvasPixmap*
+ * instead of QCanvasPixmap, but we need an easy way to
+ * convert them to QPixmap, since we use them that
+ * way quite often (to generate toolbar button icons for instance).
+ */
+ static QPixmap toQPixmap(QCanvasPixmap*);
+ static void dumpStats(std::ostream &);
+
+
+ static const char* const defaultSerifFontFamily;
+ static const char* const defaultSansSerifFontFamily;
+ static const char* const defaultTimeSigFontFamily;
+
+
+protected:
+ void init(std::string fontName, int size);
+ void initMaybe() { if (!m_font) init("", -1); }
+
+ void drawNoteAux(const NotePixmapParameters &parameters,
+ QPainter *painter, int x, int y);
+ void drawRestAux(const NotePixmapParameters &parameters, QPoint &hotspot,
+ QPainter *painter, int x, int y);
+ void drawHairpinAux(int length, bool isCrescendo,
+ QPainter *painter, int x, int y);
+ void drawSlurAux(int length, int dy, bool above, bool smooth, bool tie, bool phrasing,
+ QPoint &hotspot,
+ QPainter *painter, int x, int y);
+ void drawOttavaAux(int length, int octavesUp,
+ QPainter *painter, int x, int y);
+ void drawTextAux(const Text &text,
+ QPainter *painter, int x, int y);
+
+ int getStemLength(const NotePixmapParameters &) const;
+
+ void makeRoomForAccidental(Accidental, bool cautionary, int shift, bool extra);
+ void drawAccidental(Accidental, bool cautionary);
+
+ void makeRoomForMarks(bool isStemmed, const NotePixmapParameters &params, int stemLength);
+ void drawMarks(bool isStemmed, const NotePixmapParameters &params, int stemLength);
+
+ void makeRoomForLegerLines(const NotePixmapParameters &params);
+ void drawLegerLines(const NotePixmapParameters &params);
+
+ void makeRoomForStemAndFlags(int flagCount, int stemLength,
+ const NotePixmapParameters &params,
+ QPoint &startPoint, QPoint &endPoint);
+ void drawFlags(int flagCount, const NotePixmapParameters &params,
+ const QPoint &startPoint, const QPoint &endPoint);
+ void drawStem(const NotePixmapParameters &params,
+ const QPoint &startPoint, const QPoint &endPoint,
+ int shortening);
+
+ void makeRoomForBeams(const NotePixmapParameters &params);
+ void drawBeams(const QPoint &, const NotePixmapParameters &params,
+ int beamCount);
+
+ void drawSlashes(const QPoint &, const NotePixmapParameters &params,
+ int slashCount);
+
+ void makeRoomForTuplingLine(const NotePixmapParameters &params);
+ void drawTuplingLine(const NotePixmapParameters &params);
+
+ void drawShallowLine(int x0, int y0, int x1, int y1, int thickness,
+ bool smooth);
+ void drawTie(bool above, int length, int shift);
+
+ void drawBracket(int length, bool left, bool curly, int x, int y);
+
+ QFont getTextFont(const Text &text) const;
+
+ QCanvasPixmap* makeAnnotationPixmap(const Text &text);
+ QCanvasPixmap* makeAnnotationPixmap(const Text &text, const bool isLilyPondDirective);
+
+ void createPixmapAndMask(int width, int height,
+ int maskWidth = -1,
+ int maskHeight = -1);
+ QCanvasPixmap* makeCanvasPixmap(QPoint hotspot, bool generateMask = false);
+
+ enum ColourType {
+ PlainColour,
+ QuantizedColour,
+ HighlightedColour,
+ TriggerColour,
+ OutRangeColour
+ };
+
+ /// draws selected/shaded status from m_selected/m_shaded:
+ NoteCharacter getCharacter(CharName name, ColourType type, bool inverted);
+
+ /// draws selected/shaded status from m_selected/m_shaded:
+ bool getCharacter(CharName name, NoteCharacter &ch, ColourType type, bool inverted);
+
+ void drawNoteHalo(int x, int y, int w, int h);
+
+ //--------------- Data members ---------------------------------
+
+ NoteFont *m_font;
+ NoteStyle *m_style;
+ bool m_selected;
+ bool m_shaded;
+
+ int m_noteBodyWidth, m_noteBodyHeight;
+ int m_left, m_right, m_above, m_below;
+ int m_borderX, m_borderY;
+
+ QFont m_tupletCountFont;
+ QFontMetrics m_tupletCountFontMetrics;
+
+ QFont m_textMarkFont;
+ QFontMetrics m_textMarkFontMetrics;
+
+ QFont m_fingeringFont;
+ QFontMetrics m_fingeringFontMetrics;
+
+ QFont m_timeSigFont;
+ QFontMetrics m_timeSigFontMetrics;
+
+ QFont m_bigTimeSigFont;
+ QFontMetrics m_bigTimeSigFontMetrics;
+
+ QFont m_ottavaFont;
+ QFontMetrics m_ottavaFontMetrics;
+
+ QFont m_clefOttavaFont;
+ QFontMetrics m_clefOttavaFontMetrics;
+
+ QFont m_trackHeaderFont;
+ QFontMetrics m_trackHeaderFontMetrics;
+
+ QFont m_trackHeaderBoldFont;
+ QFontMetrics m_trackHeaderBoldFontMetrics;
+
+ QPixmap *m_generatedPixmap;
+ QBitmap *m_generatedMask;
+
+ int m_generatedWidth;
+ int m_generatedHeight;
+ bool m_inPrinterMethod;
+
+ NotePixmapPainter *m_p;
+
+ mutable NotePixmapCache *m_dottedRestCache;
+
+ typedef std::map<const char *, QFont> TextFontCache;
+ mutable TextFontCache m_textFontCache;
+
+ static QPoint m_pointZero;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotePixmapPainter.h b/src/gui/editors/notation/NotePixmapPainter.h
new file mode 100644
index 0000000..ed9d541
--- /dev/null
+++ b/src/gui/editors/notation/NotePixmapPainter.h
@@ -0,0 +1,148 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTEPIXMAPPAINTER_H_
+#define _RG_NOTEPIXMAPPAINTER_H_
+
+#include <qpainter.h>
+
+namespace Rosegarden {
+
+class NotePixmapPainter
+{
+ // Just a trivial class that instructs two painters to do the
+ // same thing (one for the pixmap, one for the mask). We only
+ // duplicate those methods we actually use in NotePixmapFactory
+
+public:
+ NotePixmapPainter() :
+ m_painter(&m_myPainter) { }
+
+ void beginExternal(QPainter *painter) {
+
+ m_externalPainter = painter;
+ m_useMask = false;
+
+ painter->setPen(QPen(Qt::black, 1, Qt::SolidLine,
+ Qt::RoundCap, Qt::RoundJoin));
+
+ if (m_externalPainter) {
+ m_painter = m_externalPainter;
+ } else {
+ m_painter = &m_myPainter;
+ }
+ }
+
+ bool begin(QPaintDevice *device, QPaintDevice *mask = 0, bool unclipped = false) {
+
+ m_externalPainter = 0;
+
+ if (mask) {
+ m_useMask = true;
+ m_maskPainter.begin(mask, unclipped);
+ } else {
+ m_useMask = false;
+ }
+
+ m_painter = &m_myPainter;
+ return m_painter->begin(device, unclipped);
+ }
+
+ bool end() {
+ if (m_useMask) m_maskPainter.end();
+ return m_painter->end();
+ }
+
+ QPainter &painter() {
+ return *m_painter;
+ }
+
+ QPainter &maskPainter() {
+ return m_maskPainter;
+ }
+
+ void drawPoint(int x, int y) {
+ m_painter->drawPoint(x, y);
+ if (m_useMask) m_maskPainter.drawPoint(x, y);
+ }
+
+ void drawLine(int x1, int y1, int x2, int y2) {
+ m_painter->drawLine(x1, y1, x2, y2);
+ if (m_useMask) m_maskPainter.drawLine(x1, y1, x2, y2);
+ }
+
+ void drawRect(int x, int y, int w, int h) {
+ m_painter->drawRect(x, y, w, h);
+ if (m_useMask) m_maskPainter.drawRect(x, y, w, h);
+ }
+
+ void drawArc(int x, int y, int w, int h, int a, int alen) {
+ m_painter->drawArc(x, y, w, h, a, alen);
+ if (m_useMask) m_maskPainter.drawArc(x, y, w, h, a, alen);
+ }
+
+ void drawPolygon(const QPointArray &a, bool winding = false,
+ int index = 0, int n = -1) {
+ m_painter->drawPolygon(a, winding, index, n);
+ if (m_useMask) m_maskPainter.drawPolygon(a, winding, index, n);
+ }
+
+ void drawPolyline(const QPointArray &a, int index = 0, int n = -1) {
+ m_painter->drawPolyline(a, index, n);
+ if (m_useMask) m_maskPainter.drawPolyline(a, index, n);
+ }
+
+ void drawPixmap(int x, int y, const QPixmap &pm,
+ int sx = 0, int sy = 0, int sw = -1, int sh = -1) {
+ m_painter->drawPixmap(x, y, pm, sx, sy, sw, sh);
+ if (m_useMask) m_maskPainter.drawPixmap(x, y, *(pm.mask()), sx, sy, sw, sh);
+ }
+
+ void drawText(int x, int y, const QString &string) {
+ m_painter->drawText(x, y, string);
+ if (m_useMask) m_maskPainter.drawText(x, y, string);
+ }
+
+ void drawNoteCharacter(int x, int y, const NoteCharacter &character) {
+ character.draw(m_painter, x, y);
+ if (m_useMask) character.drawMask(&m_maskPainter, x, y);
+ }
+
+ void drawEllipse(int x, int y, int w, int h) {
+ m_painter->drawEllipse(x, y, w, h);
+ if (m_useMask) m_maskPainter.drawEllipse(x, y, w, h);
+ }
+
+private:
+ bool m_useMask;
+ QPainter m_myPainter;
+ QPainter m_maskPainter;
+ QPainter *m_externalPainter;
+ QPainter *m_painter;
+};
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NotePixmapParameters.cpp b/src/gui/editors/notation/NotePixmapParameters.cpp
new file mode 100644
index 0000000..b6dd7fb
--- /dev/null
+++ b/src/gui/editors/notation/NotePixmapParameters.cpp
@@ -0,0 +1,151 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NotePixmapParameters.h"
+
+#include "base/NotationTypes.h"
+
+
+namespace Rosegarden
+{
+
+NotePixmapParameters::NotePixmapParameters(Note::Type noteType,
+ int dots,
+ Accidental accidental) :
+ m_noteType(noteType),
+ m_dots(dots),
+ m_accidental(accidental),
+ m_cautionary(false),
+ m_shifted(false),
+ m_dotShifted(false),
+ m_accidentalShift(0),
+ m_drawFlag(true),
+ m_drawStem(true),
+ m_stemGoesUp(true),
+ m_stemLength( -1),
+ m_legerLines(0),
+ m_slashes(0),
+ m_selected(false),
+ m_highlighted(false),
+ m_quantized(false),
+ m_trigger(false),
+ m_onLine(false),
+ m_safeVertDistance(0),
+ m_restOutsideStave(false),
+ m_beamed(false),
+ m_nextBeamCount(0),
+ m_thisPartialBeams(false),
+ m_nextPartialBeams(false),
+ m_width(1),
+ m_gradient(0.0),
+ m_tupletCount(0),
+ m_tuplingLineY(0),
+ m_tuplingLineWidth(0),
+ m_tuplingLineGradient(0.0),
+ m_tied(false),
+ m_tieLength(0),
+ m_tiePositionExplicit(false),
+ m_tieAbove(false),
+ m_inRange(true)
+{
+ // nothing else
+}
+
+NotePixmapParameters::~NotePixmapParameters()
+{
+ // nothing to see here
+}
+
+void
+NotePixmapParameters::setMarks(const std::vector<Mark> &marks)
+{
+ m_marks.clear();
+ for (unsigned int i = 0; i < marks.size(); ++i)
+ m_marks.push_back(marks[i]);
+}
+
+void
+NotePixmapParameters::removeMarks()
+{
+ m_marks.clear();
+}
+
+std::vector<Rosegarden::Mark>
+NotePixmapParameters::getNormalMarks() const
+{
+ std::vector<Mark> marks;
+
+ for (std::vector<Mark>::const_iterator mi = m_marks.begin();
+ mi != m_marks.end(); ++mi) {
+
+ if (*mi == Marks::Pause ||
+ *mi == Marks::UpBow ||
+ *mi == Marks::DownBow ||
+ *mi == Marks::Trill ||
+ *mi == Marks::LongTrill ||
+ *mi == Marks::TrillLine ||
+ *mi == Marks::Turn ||
+ Marks::isFingeringMark(*mi))
+ continue;
+
+ marks.push_back(*mi);
+ }
+
+ return marks;
+}
+
+std::vector<Rosegarden::Mark>
+NotePixmapParameters::getAboveMarks() const
+{
+ std::vector<Mark> marks;
+
+ // fingerings before other marks
+
+ for (std::vector<Mark>::const_iterator mi = m_marks.begin();
+ mi != m_marks.end(); ++mi) {
+
+ if (Marks::isFingeringMark(*mi)) {
+ marks.push_back(*mi);
+ }
+ }
+
+ for (std::vector<Mark>::const_iterator mi = m_marks.begin();
+ mi != m_marks.end(); ++mi) {
+
+ if (*mi == Marks::Pause ||
+ *mi == Marks::UpBow ||
+ *mi == Marks::DownBow ||
+ *mi == Marks::Trill ||
+ *mi == Marks::LongTrill ||
+ *mi == Marks::TrillLine ||
+ *mi == Marks::Turn) {
+ marks.push_back(*mi);
+ }
+ }
+
+ return marks;
+}
+
+}
diff --git a/src/gui/editors/notation/NotePixmapParameters.h b/src/gui/editors/notation/NotePixmapParameters.h
new file mode 100644
index 0000000..f7bfee7
--- /dev/null
+++ b/src/gui/editors/notation/NotePixmapParameters.h
@@ -0,0 +1,161 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTEPIXMAPPARAMETERS_H_
+#define _RG_NOTEPIXMAPPARAMETERS_H_
+
+#include "base/NotationTypes.h"
+#include <vector>
+
+
+
+
+namespace Rosegarden
+{
+
+
+
+class NotePixmapParameters
+{
+public:
+ NotePixmapParameters(Note::Type noteType,
+ int dots,
+ Accidental accidental =
+ Accidentals::NoAccidental);
+ ~NotePixmapParameters();
+
+ void setNoteType(Note::Type type) { m_noteType = type; }
+ void setDots(int dots) { m_dots = dots; }
+ void setAccidental(Accidental acc) { m_accidental = acc; }
+
+ void setAccidentalCautionary(bool cautionary) { m_cautionary = cautionary; }
+ void setNoteHeadShifted(bool shifted) { m_shifted = shifted; }
+ void setNoteDotShifted(bool shifted) { m_dotShifted = shifted; }
+ void setAccidentalShift(int shift) { m_accidentalShift = shift; }
+ void setAccExtraShift(bool extra) { m_accidentalExtra = extra; }
+
+ void setDrawFlag(bool df) { m_drawFlag = df; }
+ void setDrawStem(bool ds) { m_drawStem = ds; }
+ void setStemGoesUp(bool up) { m_stemGoesUp = up; }
+ void setStemLength(int length) { m_stemLength = length; }
+ void setLegerLines(int lines) { m_legerLines = lines; }
+ void setSlashes(int slashes) { m_slashes = slashes; }
+ void setRestOutside(bool os) { m_restOutsideStave = os; }
+
+ void setSelected(bool selected) { m_selected = selected; }
+ void setHighlighted(bool highlighted) { m_highlighted = highlighted;}
+ void setQuantized(bool quantized) { m_quantized = quantized; }
+ void setTrigger(bool trigger) { m_trigger = trigger; }
+ void setIsOnLine(bool isOnLine) { m_onLine = isOnLine; }
+ void setSafeVertDistance(int safe) { m_safeVertDistance = safe; }
+
+ void setBeamed(bool beamed) { m_beamed = beamed; }
+ void setNextBeamCount(int tc) { m_nextBeamCount = tc; }
+ void setThisPartialBeams(bool pt) { m_thisPartialBeams = pt; }
+ void setNextPartialBeams(bool pt) { m_nextPartialBeams = pt; }
+ void setWidth(int width) { m_width = width; }
+ void setGradient(double gradient) { m_gradient = gradient; }
+
+ void setTupletCount(int count) { m_tupletCount = count; }
+ void setTuplingLineY(int y) { m_tuplingLineY = y; }
+ void setTuplingLineWidth(int width) { m_tuplingLineWidth = width; }
+ void setTuplingLineGradient(double g) { m_tuplingLineGradient = g; }
+ void setTuplingLineFollowsBeam(bool b){ m_tuplingLineFollowsBeam = b; }
+
+ void setTied(bool tied) { m_tied = tied; }
+ void setTieLength(int tieLength) { m_tieLength = tieLength; }
+
+ void setTiePosition(bool expl, bool above) {
+ m_tiePositionExplicit = expl;
+ m_tieAbove = above;
+ }
+
+ void setMarks(const std::vector<Mark> &marks);
+ void removeMarks();
+
+ void setInRange(bool inRange) { m_inRange = inRange; }
+
+ std::vector<Mark> getNormalMarks() const;
+ std::vector<Mark> getAboveMarks() const; // bowings, pause etc
+
+
+private:
+ friend class NotePixmapFactory;
+
+ //--------------- Data members ---------------------------------
+
+ Note::Type m_noteType;
+ int m_dots;
+ Accidental m_accidental;
+
+ bool m_cautionary;
+ bool m_shifted;
+ bool m_dotShifted;
+ int m_accidentalShift;
+ bool m_accidentalExtra;
+ bool m_drawFlag;
+ bool m_drawStem;
+ bool m_stemGoesUp;
+ int m_stemLength;
+ int m_legerLines;
+ int m_slashes;
+ bool m_selected;
+ bool m_highlighted;
+ bool m_quantized;
+ bool m_trigger;
+ bool m_onLine;
+ int m_safeVertDistance;
+ bool m_restOutsideStave;
+
+ bool m_beamed;
+ int m_nextBeamCount;
+ bool m_thisPartialBeams;
+ bool m_nextPartialBeams;
+ int m_width;
+ double m_gradient;
+
+ int m_tupletCount;
+ int m_tuplingLineY;
+ int m_tuplingLineWidth;
+ double m_tuplingLineGradient;
+ bool m_tuplingLineFollowsBeam;
+
+ bool m_tied;
+ int m_tieLength;
+ bool m_tiePositionExplicit;
+ bool m_tieAbove;
+
+ bool m_inRange;
+
+ std::vector<Mark> m_marks;
+};
+
+
+class NotePixmapPainter;
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NoteStyle.cpp b/src/gui/editors/notation/NoteStyle.cpp
new file mode 100644
index 0000000..0b3332d
--- /dev/null
+++ b/src/gui/editors/notation/NoteStyle.cpp
@@ -0,0 +1,485 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NoteStyle.h"
+
+#include "base/NotationTypes.h"
+#include "base/PropertyName.h"
+#include "NoteCharacterNames.h"
+#include "NoteStyleFactory.h"
+
+
+namespace Rosegarden
+{
+
+NoteStyle::~NoteStyle()
+{
+ // nothing
+}
+
+const NoteStyle::NoteHeadShape NoteStyle::AngledOval = "angled oval";
+
+const NoteStyle::NoteHeadShape NoteStyle::LevelOval = "level oval";
+
+const NoteStyle::NoteHeadShape NoteStyle::Breve = "breve";
+
+const NoteStyle::NoteHeadShape NoteStyle::Cross = "cross";
+
+const NoteStyle::NoteHeadShape NoteStyle::TriangleUp = "triangle up";
+
+const NoteStyle::NoteHeadShape NoteStyle::TriangleDown = "triangle down";
+
+const NoteStyle::NoteHeadShape NoteStyle::Diamond = "diamond";
+
+const NoteStyle::NoteHeadShape NoteStyle::Rectangle = "rectangle";
+
+const NoteStyle::NoteHeadShape NoteStyle::Number = "number";
+
+const NoteStyle::NoteHeadShape NoteStyle::CustomCharName = "custom character";
+
+
+
+NoteStyle::NoteHeadShape
+
+NoteStyle::getShape(Note::Type type)
+{
+ NoteDescriptionMap::iterator i = m_notes.find(type);
+ if (i == m_notes.end()) {
+ if (m_baseStyle)
+ return m_baseStyle->getShape(type);
+ std::cerr
+ << "WARNING: NoteStyle::getShape: No shape defined for note type "
+ << type << ", defaulting to AngledOval" << std::endl;
+ return AngledOval;
+ }
+
+ return i->second.shape;
+}
+
+bool
+NoteStyle::isFilled(Note::Type type)
+{
+ NoteDescriptionMap::iterator i = m_notes.find(type);
+ if (i == m_notes.end()) {
+ if (m_baseStyle)
+ return m_baseStyle->isFilled(type);
+ std::cerr
+ << "WARNING: NoteStyle::isFilled: No definition for note type "
+ << type << ", defaulting to true" << std::endl;
+ return true;
+ }
+
+ return i->second.filled;
+}
+
+bool
+NoteStyle::hasStem(Note::Type type)
+{
+ NoteDescriptionMap::iterator i = m_notes.find(type);
+ if (i == m_notes.end()) {
+ if (m_baseStyle)
+ return m_baseStyle->hasStem(type);
+ std::cerr
+ << "WARNING: NoteStyle::hasStem: No definition for note type "
+ << type << ", defaulting to true" << std::endl;
+ return true;
+ }
+
+ return i->second.stem;
+}
+
+int
+NoteStyle::getFlagCount(Note::Type type)
+{
+ NoteDescriptionMap::iterator i = m_notes.find(type);
+ if (i == m_notes.end()) {
+ if (m_baseStyle)
+ return m_baseStyle->getFlagCount(type);
+ std::cerr
+ << "WARNING: NoteStyle::getFlagCount: No definition for note type "
+ << type << ", defaulting to 0" << std::endl;
+ return 0;
+ }
+
+ return i->second.flags;
+}
+
+int
+NoteStyle::getSlashCount(Note::Type type)
+{
+ NoteDescriptionMap::iterator i = m_notes.find(type);
+ if (i == m_notes.end()) {
+ if (m_baseStyle)
+ return m_baseStyle->getSlashCount(type);
+ std::cerr
+ << "WARNING: NoteStyle::getSlashCount: No definition for note type "
+ << type << ", defaulting to 0" << std::endl;
+ return 0;
+ }
+
+ return i->second.slashes;
+}
+
+void
+NoteStyle::getStemFixPoints(Note::Type type,
+ HFixPoint &hfix, VFixPoint &vfix)
+{
+ NoteDescriptionMap::iterator i = m_notes.find(type);
+ if (i == m_notes.end()) {
+ if (m_baseStyle) {
+ m_baseStyle->getStemFixPoints(type, hfix, vfix);
+ return ;
+ }
+ std::cerr
+ << "WARNING: NoteStyle::getStemFixPoints: "
+ << "No definition for note type " << type
+ << ", defaulting to (Normal,Middle)" << std::endl;
+ hfix = Normal;
+ vfix = Middle;
+ return ;
+ }
+
+ hfix = i->second.hfix;
+ vfix = i->second.vfix;
+}
+
+NoteStyle::CharNameRec
+
+NoteStyle::getNoteHeadCharName(Note::Type type)
+{
+ NoteDescriptionMap::iterator i = m_notes.find(type);
+ if (i == m_notes.end()) {
+ if (m_baseStyle)
+ return m_baseStyle->getNoteHeadCharName(type);
+ std::cerr
+ << "WARNING: NoteStyle::getNoteHeadCharName: No definition for note type "
+ << type << ", defaulting to NOTEHEAD_BLACK" << std::endl;
+ return CharNameRec(NoteCharacterNames::NOTEHEAD_BLACK, false);
+ }
+
+ const NoteDescription &desc(i->second);
+ CharName name = NoteCharacterNames::UNKNOWN;
+ bool inverted = false;
+
+ if (desc.shape == AngledOval) {
+
+ name = desc.filled ? NoteCharacterNames::NOTEHEAD_BLACK
+ : NoteCharacterNames::VOID_NOTEHEAD;
+
+ } else if (desc.shape == LevelOval) {
+
+ if (desc.filled) {
+ std::cerr << "WARNING: NoteStyle::getNoteHeadCharName: No filled level oval head" << std::endl;
+ }
+ name = NoteCharacterNames::WHOLE_NOTE;
+
+ } else if (desc.shape == Breve) {
+
+ if (desc.filled) {
+ std::cerr << "WARNING: NoteStyle::getNoteHeadCharName: No filled breve head" << std::endl;
+ }
+ name = NoteCharacterNames::BREVE;
+
+ } else if (desc.shape == Cross) {
+
+ name = desc.filled ? NoteCharacterNames::X_NOTEHEAD
+ : NoteCharacterNames::CIRCLE_X_NOTEHEAD;
+
+ } else if (desc.shape == TriangleUp) {
+
+ name = desc.filled ? NoteCharacterNames::TRIANGLE_NOTEHEAD_UP_BLACK
+ : NoteCharacterNames::TRIANGLE_NOTEHEAD_UP_WHITE;
+
+ } else if (desc.shape == TriangleDown) {
+
+ name = desc.filled ? NoteCharacterNames::TRIANGLE_NOTEHEAD_UP_BLACK
+ : NoteCharacterNames::TRIANGLE_NOTEHEAD_UP_WHITE;
+ inverted = true;
+
+ } else if (desc.shape == Diamond) {
+
+ name = desc.filled ? NoteCharacterNames::SEMIBREVIS_BLACK
+ : NoteCharacterNames::SEMIBREVIS_WHITE;
+
+ } else if (desc.shape == Rectangle) {
+
+ name = desc.filled ? NoteCharacterNames::SQUARE_NOTEHEAD_BLACK
+ : NoteCharacterNames::SQUARE_NOTEHEAD_WHITE;
+
+ } else if (desc.shape == Number) {
+
+ std::cerr << "WARNING: NoteStyle::getNoteHeadCharName: Number not yet implemented" << std::endl;
+ name = NoteCharacterNames::UNKNOWN; //!!!
+
+ } else if (desc.shape == CustomCharName) {
+
+ name = desc.charName;
+
+ } else {
+
+ name = NoteCharacterNames::UNKNOWN;
+ }
+
+ return CharNameRec(name, inverted);
+}
+
+CharName
+NoteStyle::getAccidentalCharName(const Accidental &a)
+{
+ if (a == Accidentals::Sharp)
+ return NoteCharacterNames::SHARP;
+ else if (a == Accidentals::Flat)
+ return NoteCharacterNames::FLAT;
+ else if (a == Accidentals::Natural)
+ return NoteCharacterNames::NATURAL;
+ else if (a == Accidentals::DoubleSharp)
+ return NoteCharacterNames::DOUBLE_SHARP;
+ else if (a == Accidentals::DoubleFlat)
+ return NoteCharacterNames::DOUBLE_FLAT;
+ return NoteCharacterNames::UNKNOWN;
+}
+
+CharName
+NoteStyle::getMarkCharName(const Mark &mark)
+{
+ if (mark == Marks::Accent)
+ return NoteCharacterNames::ACCENT;
+ else if (mark == Marks::Tenuto)
+ return NoteCharacterNames::TENUTO;
+ else if (mark == Marks::Staccato)
+ return NoteCharacterNames::STACCATO;
+ else if (mark == Marks::Staccatissimo)
+ return NoteCharacterNames::STACCATISSIMO;
+ else if (mark == Marks::Marcato)
+ return NoteCharacterNames::MARCATO;
+ else if (mark == Marks::Trill)
+ return NoteCharacterNames::TRILL;
+ else if (mark == Marks::LongTrill)
+ return NoteCharacterNames::TRILL;
+ else if (mark == Marks::TrillLine)
+ return NoteCharacterNames::TRILL_LINE;
+ else if (mark == Marks::Turn)
+ return NoteCharacterNames::TURN;
+ else if (mark == Marks::Pause)
+ return NoteCharacterNames::FERMATA;
+ else if (mark == Marks::UpBow)
+ return NoteCharacterNames::UP_BOW;
+ else if (mark == Marks::DownBow)
+ return NoteCharacterNames::DOWN_BOW;
+ else if (mark == Marks::Mordent)
+ return NoteCharacterNames::MORDENT;
+ else if (mark == Marks::MordentInverted)
+ return NoteCharacterNames::MORDENT_INVERTED;
+ else if (mark == Marks::MordentLong)
+ return NoteCharacterNames::MORDENT_LONG;
+ else if (mark == Marks::MordentLongInverted)
+ return NoteCharacterNames::MORDENT_LONG_INVERTED;
+ // Things like "sf" and "rf" are generated from text fonts
+ return NoteCharacterNames::UNKNOWN;
+}
+
+CharName
+NoteStyle::getClefCharName(const Clef &clef)
+{
+ std::string clefType(clef.getClefType());
+
+ if (clefType == Clef::Bass || clefType == Clef::Varbaritone || clefType == Clef::Subbass) {
+ return NoteCharacterNames::F_CLEF;
+ } else if (clefType == Clef::Treble || clefType == Clef::French) {
+ return NoteCharacterNames::G_CLEF;
+ } else {
+ return NoteCharacterNames::C_CLEF;
+ }
+}
+
+CharName
+NoteStyle::getRestCharName(Note::Type type, bool restOutsideStave)
+{
+ switch (type) {
+ case Note::Hemidemisemiquaver:
+ return NoteCharacterNames::SIXTY_FOURTH_REST;
+ case Note::Demisemiquaver:
+ return NoteCharacterNames::THIRTY_SECOND_REST;
+ case Note::Semiquaver:
+ return NoteCharacterNames::SIXTEENTH_REST;
+ case Note::Quaver:
+ return NoteCharacterNames::EIGHTH_REST;
+ case Note::Crotchet:
+ return NoteCharacterNames::QUARTER_REST;
+ case Note::Minim:
+ return restOutsideStave ?
+ NoteCharacterNames::HALF_REST
+ : NoteCharacterNames::HALF_REST_ON_STAFF;
+ case Note::Semibreve:
+ return restOutsideStave ?
+ NoteCharacterNames::WHOLE_REST
+ : NoteCharacterNames::WHOLE_REST_ON_STAFF;
+ case Note::Breve:
+ return restOutsideStave ?
+ NoteCharacterNames::MULTI_REST
+ : NoteCharacterNames::MULTI_REST_ON_STAFF;
+ default:
+ return NoteCharacterNames::UNKNOWN;
+ }
+}
+
+CharName
+NoteStyle::getPartialFlagCharName(bool final)
+{
+ if (final)
+ return NoteCharacterNames::FLAG_PARTIAL_FINAL;
+ else
+ return NoteCharacterNames::FLAG_PARTIAL;
+}
+
+CharName
+NoteStyle::getFlagCharName(int flagCount)
+{
+ switch (flagCount) {
+ case 1:
+ return NoteCharacterNames::FLAG_1;
+ case 2:
+ return NoteCharacterNames::FLAG_2;
+ case 3:
+ return NoteCharacterNames::FLAG_3;
+ case 4:
+ return NoteCharacterNames::FLAG_4;
+ default:
+ return NoteCharacterNames::UNKNOWN;
+ }
+}
+
+CharName
+NoteStyle::getTimeSignatureDigitName(int digit)
+{
+ switch (digit) {
+ case 0:
+ return NoteCharacterNames::DIGIT_ZERO;
+ case 1:
+ return NoteCharacterNames::DIGIT_ONE;
+ case 2:
+ return NoteCharacterNames::DIGIT_TWO;
+ case 3:
+ return NoteCharacterNames::DIGIT_THREE;
+ case 4:
+ return NoteCharacterNames::DIGIT_FOUR;
+ case 5:
+ return NoteCharacterNames::DIGIT_FIVE;
+ case 6:
+ return NoteCharacterNames::DIGIT_SIX;
+ case 7:
+ return NoteCharacterNames::DIGIT_SEVEN;
+ case 8:
+ return NoteCharacterNames::DIGIT_EIGHT;
+ case 9:
+ return NoteCharacterNames::DIGIT_NINE;
+ default:
+ return NoteCharacterNames::UNKNOWN;
+ }
+}
+
+void
+NoteStyle::setBaseStyle(NoteStyleName name)
+{
+ try {
+ m_baseStyle = NoteStyleFactory::getStyle(name);
+ if (m_baseStyle == this)
+ m_baseStyle = 0;
+ } catch (NoteStyleFactory::StyleUnavailable u) {
+ if (name != NoteStyleFactory::DefaultStyle) {
+ std::cerr
+ << "NoteStyle::setBaseStyle: Base style "
+ << name << " not available, defaulting to "
+ << NoteStyleFactory::DefaultStyle << std::endl;
+ setBaseStyle(NoteStyleFactory::DefaultStyle);
+ } else {
+ std::cerr
+ << "NoteStyle::setBaseStyle: Base style "
+ << name << " not available" << std::endl;
+ m_baseStyle = 0;
+ }
+ }
+}
+
+void
+NoteStyle::checkDescription(Note::Type note)
+{
+ if (m_baseStyle && (m_notes.find(note) == m_notes.end())) {
+ m_baseStyle->checkDescription(note);
+ m_notes[note] = m_baseStyle->m_notes[note];
+ }
+}
+
+void
+NoteStyle::setShape(Note::Type note, NoteHeadShape shape)
+{
+ checkDescription(note);
+ m_notes[note].shape = shape;
+}
+
+void
+NoteStyle::setCharName(Note::Type note, CharName charName)
+{
+ checkDescription(note);
+ m_notes[note].charName = charName;
+}
+
+void
+NoteStyle::setFilled(Note::Type note, bool filled)
+{
+ checkDescription(note);
+ m_notes[note].filled = filled;
+}
+
+void
+NoteStyle::setStem(Note::Type note, bool stem)
+{
+ checkDescription(note);
+ m_notes[note].stem = stem;
+}
+
+void
+NoteStyle::setFlagCount(Note::Type note, int flags)
+{
+ checkDescription(note);
+ m_notes[note].flags = flags;
+}
+
+void
+NoteStyle::setSlashCount(Note::Type note, int slashes)
+{
+ checkDescription(note);
+ m_notes[note].slashes = slashes;
+}
+
+void
+NoteStyle::setStemFixPoints(Note::Type note, HFixPoint hfix, VFixPoint vfix)
+{
+ checkDescription(note);
+ m_notes[note].hfix = hfix;
+ m_notes[note].vfix = vfix;
+}
+
+}
diff --git a/src/gui/editors/notation/NoteStyle.h b/src/gui/editors/notation/NoteStyle.h
new file mode 100644
index 0000000..3959e01
--- /dev/null
+++ b/src/gui/editors/notation/NoteStyle.h
@@ -0,0 +1,142 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTESTYLE_H_
+#define _RG_NOTESTYLE_H_
+
+#include "base/NotationTypes.h"
+#include <map>
+#include "NoteCharacterNames.h"
+#include <string>
+#include <utility>
+#include "gui/editors/notation/NoteCharacterNames.h"
+
+
+class Mark;
+class Accidental;
+
+
+namespace Rosegarden
+{
+
+class Clef;
+
+typedef std::string NoteStyleName;
+
+
+class NoteStyle
+{
+public:
+ virtual ~NoteStyle();
+
+ typedef std::string NoteHeadShape;
+
+ static const NoteHeadShape AngledOval;
+ static const NoteHeadShape LevelOval;
+ static const NoteHeadShape Breve;
+ static const NoteHeadShape Cross;
+ static const NoteHeadShape TriangleUp;
+ static const NoteHeadShape TriangleDown;
+ static const NoteHeadShape Diamond;
+ static const NoteHeadShape Rectangle;
+ static const NoteHeadShape CustomCharName;
+ static const NoteHeadShape Number;
+
+ enum HFixPoint { Normal, Central, Reversed };
+ enum VFixPoint { Near, Middle, Far };
+
+ NoteStyleName getName() const { return m_name; }
+
+ NoteHeadShape getShape (Note::Type);
+ bool isFilled (Note::Type);
+ bool hasStem (Note::Type);
+ int getFlagCount (Note::Type);
+ int getSlashCount(Note::Type);
+
+ typedef std::pair<CharName, bool> CharNameRec; // bool is "inverted"
+ CharNameRec getNoteHeadCharName(Note::Type);
+
+ CharName getRestCharName(Note::Type, bool restOutsideStave);
+ CharName getPartialFlagCharName(bool final);
+ CharName getFlagCharName(int flagCount);
+ CharName getAccidentalCharName(const Accidental &);
+ CharName getMarkCharName(const Mark &);
+ CharName getClefCharName(const Clef &);
+ CharName getTimeSignatureDigitName(int digit);
+
+ void setBaseStyle (NoteStyleName name);
+ void setShape (Note::Type, NoteHeadShape);
+ void setCharName (Note::Type, CharName);
+ void setFilled (Note::Type, bool);
+ void setStem (Note::Type, bool);
+ void setFlagCount (Note::Type, int);
+ void setSlashCount(Note::Type, int);
+
+ void getStemFixPoints(Note::Type, HFixPoint &, VFixPoint &);
+ void setStemFixPoints(Note::Type, HFixPoint, VFixPoint);
+
+protected:
+ struct NoteDescription {
+ NoteHeadShape shape; // if CustomCharName, use charName
+ CharName charName; // only used if shape == CustomCharName
+ bool filled;
+ bool stem;
+ int flags;
+ int slashes;
+ HFixPoint hfix;
+ VFixPoint vfix;
+
+ NoteDescription() :
+ shape(AngledOval), charName(NoteCharacterNames::UNKNOWN),
+ filled(true), stem(true), flags(0), slashes(0),
+ hfix(Normal), vfix(Middle) { }
+
+ NoteDescription(NoteHeadShape _shape, CharName _charName,
+ bool _filled, bool _stem, int _flags, int _slashes,
+ HFixPoint _hfix, VFixPoint _vfix) :
+ shape(_shape), charName(_charName),
+ filled(_filled), stem(_stem), flags(_flags), slashes(_slashes),
+ hfix(_hfix), vfix(_vfix) { }
+ };
+
+ typedef std::map<Note::Type, NoteDescription> NoteDescriptionMap;
+
+ NoteDescriptionMap m_notes;
+ NoteStyle *m_baseStyle;
+ NoteStyleName m_name;
+
+ void checkDescription(Note::Type type);
+
+protected: // for use by NoteStyleFileReader
+ NoteStyle(NoteStyleName name) : m_baseStyle(0), m_name(name) { }
+ friend class NoteStyleFileReader;
+};
+
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NoteStyleFactory.cpp b/src/gui/editors/notation/NoteStyleFactory.cpp
new file mode 100644
index 0000000..d4a8be8
--- /dev/null
+++ b/src/gui/editors/notation/NoteStyleFactory.cpp
@@ -0,0 +1,124 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NoteStyleFactory.h"
+
+#include <kstddirs.h>
+#include "misc/Strings.h"
+#include "base/Event.h"
+#include "base/Exception.h"
+#include "NotationProperties.h"
+#include "NoteStyle.h"
+#include "NoteStyleFileReader.h"
+#include <kglobal.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+
+namespace Rosegarden
+{
+
+const NoteStyleName NoteStyleFactory::DefaultStyle = "Classical";
+
+std::vector<NoteStyleName>
+NoteStyleFactory::getAvailableStyleNames()
+{
+ std::vector<NoteStyleName> names;
+
+ QString styleDir = KGlobal::dirs()->findResource("appdata", "styles/");
+ QDir dir(styleDir);
+ if (!dir.exists()) {
+ std::cerr << "NoteStyle::getAvailableStyleNames: directory \"" << styleDir
+ << "\" not found" << std::endl;
+ return names;
+ }
+
+ dir.setFilter(QDir::Files | QDir::Readable);
+ QStringList files = dir.entryList();
+ bool foundDefault = false;
+
+ for (QStringList::Iterator i = files.begin(); i != files.end(); ++i) {
+ if ((*i).length() > 4 && (*i).right(4) == ".xml") {
+ QFileInfo fileInfo(QString("%1/%2").arg(styleDir).arg(*i));
+ if (fileInfo.exists() && fileInfo.isReadable()) {
+ std::string styleName = qstrtostr((*i).left((*i).length() - 4));
+ if (styleName == DefaultStyle)
+ foundDefault = true;
+ names.push_back(styleName);
+ }
+ }
+ }
+
+ if (!foundDefault) {
+ std::cerr << "NoteStyleFactory::getAvailableStyleNames: WARNING: Default style name \"" << DefaultStyle << "\" not found" << std::endl;
+ }
+
+ return names;
+}
+
+NoteStyle *
+NoteStyleFactory::getStyle(NoteStyleName name)
+{
+ StyleMap::iterator i = m_styles.find(name);
+
+ if (i == m_styles.end()) {
+
+ try {
+ NoteStyle *newStyle = NoteStyleFileReader(name).getStyle();
+ m_styles[name] = newStyle;
+ return newStyle;
+
+ } catch (NoteStyleFileReader::StyleFileReadFailed f) {
+ std::cerr
+ << "NoteStyleFactory::getStyle: Style file read failed: "
+ << f.getMessage() << std::endl;
+ throw StyleUnavailable("Style file read failed: " +
+ f.getMessage());
+ }
+
+ } else {
+ return i->second;
+ }
+}
+
+NoteStyle *
+NoteStyleFactory::getStyleForEvent(Event *event)
+{
+ NoteStyleName styleName;
+ if (event->get
+ <String>(NotationProperties::NOTE_STYLE, styleName)) {
+ return getStyle(styleName);
+ }
+ else {
+ return getStyle(DefaultStyle);
+ }
+}
+
+NoteStyleFactory::StyleMap NoteStyleFactory::m_styles;
+
+
+}
diff --git a/src/gui/editors/notation/NoteStyleFactory.h b/src/gui/editors/notation/NoteStyleFactory.h
new file mode 100644
index 0000000..795537d
--- /dev/null
+++ b/src/gui/editors/notation/NoteStyleFactory.h
@@ -0,0 +1,61 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTESTYLEFACTORY_H_
+#define _RG_NOTESTYLEFACTORY_H_
+
+#include "base/Exception.h"
+#include <map>
+#include <vector>
+#include "NoteStyle.h"
+
+
+namespace Rosegarden
+{
+
+class NoteStyle;
+class Event;
+
+class NoteStyleFactory
+{
+public:
+ static std::vector<NoteStyleName> getAvailableStyleNames();
+ static const NoteStyleName DefaultStyle;
+
+ static NoteStyle *getStyle(NoteStyleName name);
+ static NoteStyle *getStyleForEvent(Event *event);
+
+ typedef Exception StyleUnavailable;
+
+private:
+ typedef std::map<std::string, NoteStyle *> StyleMap;
+ static StyleMap m_styles;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/NoteStyleFileReader.cpp b/src/gui/editors/notation/NoteStyleFileReader.cpp
new file mode 100644
index 0000000..b3f3464
--- /dev/null
+++ b/src/gui/editors/notation/NoteStyleFileReader.cpp
@@ -0,0 +1,193 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "NoteStyleFileReader.h"
+
+#include <string>
+#include "NoteStyle.h"
+#include <qfileinfo.h>
+#include <qdir.h>
+
+#include <kglobal.h>
+#include <kstddirs.h>
+#include <klocale.h>
+
+#include "misc/Strings.h"
+#include "NotationStrings.h"
+#include "misc/Debug.h"
+
+namespace Rosegarden {
+
+
+NoteStyleFileReader::NoteStyleFileReader(std::string name) :
+ m_style(new NoteStyle(name)),
+ m_haveNote(false)
+{
+ QString styleDirectory =
+ KGlobal::dirs()->findResource("appdata", "styles/");
+
+ QString styleFileName =
+ QString("%1/%2.xml").arg(styleDirectory).arg(strtoqstr(name));
+
+ QFileInfo fileInfo(styleFileName);
+
+ if (!fileInfo.isReadable()) {
+ throw StyleFileReadFailed
+ (qstrtostr(i18n("Can't open style file %1").arg(styleFileName)));
+ }
+
+ QFile styleFile(styleFileName);
+
+ QXmlInputSource source(styleFile);
+ QXmlSimpleReader reader;
+ reader.setContentHandler(this);
+ reader.setErrorHandler(this);
+ bool ok = reader.parse(source);
+ styleFile.close();
+
+ if (!ok) {
+ throw StyleFileReadFailed(qstrtostr(m_errorString));
+ }
+}
+
+bool
+NoteStyleFileReader::startElement(const QString &, const QString &,
+ const QString &qName,
+ const QXmlAttributes &attributes)
+{
+ QString lcName = qName.lower();
+
+ if (lcName == "rosegarden-note-style") {
+
+ QString s = attributes.value("base-style");
+ if (s) m_style->setBaseStyle(qstrtostr(s));
+
+ } else if (lcName == "note") {
+
+ m_haveNote = true;
+
+ QString s = attributes.value("type");
+ if (!s) {
+ m_errorString = i18n("type is a required attribute of note");
+ return false;
+ }
+
+ try {
+ Note::Type type = NotationStrings::getNoteForName(s).getNoteType();
+ if (!setFromAttributes(type, attributes)) return false;
+
+ } catch (NotationStrings::MalformedNoteName n) {
+ m_errorString = i18n("Unrecognised note name %1").arg(s);
+ return false;
+ }
+
+ } else if (lcName == "global") {
+
+ if (m_haveNote) {
+ m_errorString = i18n("global element must precede note elements");
+ return false;
+ }
+
+ for (Note::Type type = Note::Shortest; type <= Note::Longest; ++type) {
+ if (!setFromAttributes(type, attributes)) return false;
+ }
+ }
+
+ return true;
+}
+
+
+bool
+NoteStyleFileReader::setFromAttributes(Note::Type type,
+ const QXmlAttributes &attributes)
+{
+ QString s;
+ bool haveShape = false;
+
+ s = attributes.value("shape");
+ if (s) {
+ m_style->setShape(type, qstrtostr(s.lower()));
+ haveShape = true;
+ }
+
+ s = attributes.value("charname");
+ if (s) {
+ if (haveShape) {
+ m_errorString = i18n("global and note elements may have shape "
+ "or charname attribute, but not both");
+ return false;
+ }
+ m_style->setShape(type, NoteStyle::CustomCharName);
+ m_style->setCharName(type, qstrtostr(s));
+ }
+
+ s = attributes.value("filled");
+ if (s) m_style->setFilled(type, s.lower() == "true");
+
+ s = attributes.value("stem");
+ if (s) m_style->setStem(type, s.lower() == "true");
+
+ s = attributes.value("flags");
+ if (s) m_style->setFlagCount(type, s.toInt());
+
+ s = attributes.value("slashes");
+ if (s) m_style->setSlashCount(type, s.toInt());
+
+ NoteStyle::HFixPoint hfix;
+ NoteStyle::VFixPoint vfix;
+ m_style->getStemFixPoints(type, hfix, vfix);
+ bool haveHFix = false;
+ bool haveVFix = false;
+
+ s = attributes.value("hfixpoint");
+ if (s) {
+ s = s.lower();
+ haveHFix = true;
+ if (s == "normal") hfix = NoteStyle::Normal;
+ else if (s == "central") hfix = NoteStyle::Central;
+ else if (s == "reversed") hfix = NoteStyle::Reversed;
+ else haveHFix = false;
+ }
+
+ s = attributes.value("vfixpoint");
+ if (s) {
+ s = s.lower();
+ haveVFix = true;
+ if (s == "near") vfix = NoteStyle::Near;
+ else if (s == "middle") vfix = NoteStyle::Middle;
+ else if (s == "far") vfix = NoteStyle::Far;
+ else haveVFix = false;
+ }
+
+ if (haveHFix || haveVFix) {
+ m_style->setStemFixPoints(type, hfix, vfix);
+ // otherwise they inherit from base style, so avoid setting here
+ }
+
+ return true;
+}
+
+
+}
+
diff --git a/src/gui/editors/notation/NoteStyleFileReader.h b/src/gui/editors/notation/NoteStyleFileReader.h
new file mode 100644
index 0000000..d3dfbbe
--- /dev/null
+++ b/src/gui/editors/notation/NoteStyleFileReader.h
@@ -0,0 +1,59 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_NOTESTYLEFILEREADER_H_
+#define _RG_NOTESTYLEFILEREADER_H_
+
+#include <qxml.h>
+
+#include "NoteStyle.h"
+
+namespace Rosegarden {
+
+class NoteStyleFileReader : public QXmlDefaultHandler
+{
+public:
+ NoteStyleFileReader(NoteStyleName name);
+
+ typedef Rosegarden::Exception StyleFileReadFailed;
+
+ NoteStyle *getStyle() { return m_style; }
+
+ // Xml handler methods:
+
+ virtual bool startElement
+ (const QString& namespaceURI, const QString& localName,
+ const QString& qName, const QXmlAttributes& atts);
+
+private:
+ bool setFromAttributes(Note::Type type, const QXmlAttributes &attributes);
+
+ QString m_errorString;
+ NoteStyle *m_style;
+ bool m_haveNote;
+};
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/RestInserter.cpp b/src/gui/editors/notation/RestInserter.cpp
new file mode 100644
index 0000000..399cf2d
--- /dev/null
+++ b/src/gui/editors/notation/RestInserter.cpp
@@ -0,0 +1,150 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "RestInserter.h"
+
+#include <klocale.h>
+#include "base/Event.h"
+#include "base/NotationTypes.h"
+#include "base/BaseProperties.h"
+#include "base/Segment.h"
+#include "commands/notation/NoteInsertionCommand.h"
+#include "commands/notation/RestInsertionCommand.h"
+#include "commands/notation/TupletCommand.h"
+#include "gui/general/EditTool.h"
+#include "NotationStrings.h"
+#include "NotationTool.h"
+#include "NotationView.h"
+#include "NoteInserter.h"
+#include "NotePixmapFactory.h"
+#include <kaction.h>
+#include <kcommand.h>
+#include <qiconset.h>
+#include <qregexp.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+using namespace BaseProperties;
+
+RestInserter::RestInserter(NotationView* view)
+ : NoteInserter("RestInserter", view)
+{
+ QIconSet icon;
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("dotted-rest-crotchet")));
+ new KToggleAction(i18n("Dotted rest"), icon, 0, this,
+ SLOT(slotToggleDot()), actionCollection(),
+ "toggle_dot");
+
+ icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("select")));
+ new KAction(i18n("Switch to Select Tool"), icon, 0, this,
+ SLOT(slotSelectSelected()), actionCollection(),
+ "select");
+
+ new KAction(i18n("Switch to Erase Tool"), "eraser", 0, this,
+ SLOT(slotEraseSelected()), actionCollection(),
+ "erase");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("crotchet")));
+ new KAction(i18n("Switch to Inserting Notes"), icon, 0, this,
+ SLOT(slotNotesSelected()), actionCollection(),
+ "notes");
+
+ createMenu("restinserter.rc");
+}
+
+void
+RestInserter::showPreview()
+{
+ // no preview available for now
+}
+
+Event *
+RestInserter::doAddCommand(Segment &segment, timeT time, timeT endTime,
+ const Note &note, int, Accidental)
+{
+ if (time < segment.getStartTime() ||
+ endTime > segment.getEndMarkerTime() ||
+ time + note.getDuration() > segment.getEndMarkerTime()) {
+ return 0;
+ }
+
+ NoteInsertionCommand *insertionCommand =
+ new RestInsertionCommand(segment, time, endTime, note);
+
+ KCommand *activeCommand = insertionCommand;
+
+ if (m_nParentView->isInTripletMode()) {
+ Segment::iterator i(segment.findTime(time));
+ if (i != segment.end() &&
+ !(*i)->has(BEAMED_GROUP_TUPLET_BASE)) {
+
+ KMacroCommand *command = new KMacroCommand(insertionCommand->name());
+ command->addCommand(new TupletCommand
+ (segment, time, note.getDuration()));
+ command->addCommand(insertionCommand);
+ activeCommand = command;
+ }
+ }
+
+ m_nParentView->addCommandToHistory(activeCommand);
+
+ return insertionCommand->getLastInsertedEvent();
+}
+
+void RestInserter::slotToggleDot()
+{
+ m_noteDots = (m_noteDots) ? 0 : 1;
+ Note note(m_noteType, m_noteDots);
+ QString actionName(NotationStrings::getReferenceName(note, true));
+ actionName.replace(QRegExp("-"), "_");
+ KAction *action = m_parentView->actionCollection()->action(actionName);
+ if (!action) {
+ std::cerr << "WARNING: No such action as " << actionName << std::endl;
+ } else {
+ action->activate();
+ }
+}
+
+void RestInserter::slotNotesSelected()
+{
+ Note note(m_noteType, m_noteDots);
+ QString actionName(NotationStrings::getReferenceName(note));
+ actionName.replace(QRegExp(" "), "_");
+ m_parentView->actionCollection()->action(actionName)->activate();
+}
+
+const QString RestInserter::ToolName = "restinserter";
+
+}
+#include "RestInserter.moc"
diff --git a/src/gui/editors/notation/RestInserter.h b/src/gui/editors/notation/RestInserter.h
new file mode 100644
index 0000000..90239fb
--- /dev/null
+++ b/src/gui/editors/notation/RestInserter.h
@@ -0,0 +1,76 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_RESTINSERTER_H_
+#define _RG_RESTINSERTER_H_
+
+#include "NoteInserter.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+
+
+namespace Rosegarden
+{
+
+class Segment;
+class Note;
+class NotationView;
+class Event;
+
+
+/**
+ * This tool will insert rests on mouse click events
+ */
+class RestInserter : public NoteInserter
+{
+ Q_OBJECT
+
+ friend class NotationToolBox;
+
+public:
+
+ static const QString ToolName;
+
+protected:
+ RestInserter(NotationView*);
+
+ virtual Event *doAddCommand(Segment &,
+ timeT time,
+ timeT endTime,
+ const Note &,
+ int pitch, Accidental);
+ virtual void showPreview();
+
+protected slots:
+ void slotToggleDot();
+ void slotNotesSelected();
+};
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/SystemFont.cpp b/src/gui/editors/notation/SystemFont.cpp
new file mode 100644
index 0000000..71f0ce7
--- /dev/null
+++ b/src/gui/editors/notation/SystemFont.cpp
@@ -0,0 +1,165 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "SystemFont.h"
+#include "SystemFontQt.h"
+#include "SystemFontXft.h"
+
+#include "misc/Debug.h"
+
+#include <kstddirs.h>
+#include "NoteFontMap.h"
+#include <qfont.h>
+#include <qfontinfo.h>
+#include <qpixmap.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+SystemFont *
+SystemFont::loadSystemFont(const SystemFontSpec &spec)
+{
+ QString name = spec.first;
+ int size = spec.second;
+
+ NOTATION_DEBUG << "SystemFont::loadSystemFont: name is " << name << ", size " << size << endl;
+
+ if (name == "DEFAULT") {
+ QFont font;
+ font.setPixelSize(size);
+ return new SystemFontQt(font);
+ }
+
+#ifdef HAVE_XFT
+
+ FcPattern *pattern, *match;
+ FcResult result;
+ FcChar8 *matchFamily;
+ XftFont *xfont = 0;
+
+ Display *dpy = QPaintDevice::x11AppDisplay();
+ static bool haveFcDirectory = false;
+
+ if (!dpy) {
+ std::cerr << "SystemFont::loadSystemFont[Xft]: Xft support requested but no X11 display available!" << std::endl;
+ goto qfont;
+ }
+
+ if (!haveFcDirectory) {
+ QString fontDir = KGlobal::dirs()->findResource("appdata", "fonts/");
+ if (!FcConfigAppFontAddDir(FcConfigGetCurrent(),
+ (const FcChar8 *)fontDir.latin1())) {
+ NOTATION_DEBUG << "SystemFont::loadSystemFont[Xft]: Failed to add font directory " << fontDir << " to fontconfig, continuing without it" << endl;
+ }
+ haveFcDirectory = true;
+ }
+
+ pattern = FcPatternCreate();
+ FcPatternAddString(pattern, FC_FAMILY, (FcChar8 *)name.latin1());
+ FcPatternAddInteger(pattern, FC_PIXEL_SIZE, size);
+ FcConfigSubstitute(FcConfigGetCurrent(), pattern, FcMatchPattern);
+
+ result = FcResultMatch;
+ match = FcFontMatch(FcConfigGetCurrent(), pattern, &result);
+ FcPatternDestroy(pattern);
+
+ if (!match || result != FcResultMatch) {
+ NOTATION_DEBUG << "SystemFont::loadSystemFont[Xft]: No match for font "
+ << name << " (result is " << result
+ << "), falling back on QFont" << endl;
+ if (match)
+ FcPatternDestroy(match);
+ goto qfont;
+ }
+
+ FcPatternGetString(match, FC_FAMILY, 0, &matchFamily);
+ NOTATION_DEBUG << "SystemFont::loadSystemFont[Xft]: match family is "
+ << (char *)matchFamily << endl;
+
+ if (QString((char *)matchFamily).lower() != name.lower()) {
+ NOTATION_DEBUG << "SystemFont::loadSystemFont[Xft]: Wrong family returned, falling back on QFont" << endl;
+ FcPatternDestroy(match);
+ goto qfont;
+ }
+
+ xfont = XftFontOpenPattern(dpy, match);
+ if (!xfont) {
+ FcPatternDestroy(match);
+ NOTATION_DEBUG << "SystemFont::loadSystemFont[Xft]: Unable to load font "
+ << name << " via Xft, falling back on QFont" << endl;
+ goto qfont;
+ }
+
+ NOTATION_DEBUG << "SystemFont::loadSystemFont[Xft]: successfully loaded font "
+ << name << " through Xft" << endl;
+
+ return new SystemFontXft(dpy, xfont);
+
+
+qfont:
+
+#endif
+
+ QFont qfont(name, size, QFont::Normal);
+ qfont.setPixelSize(size);
+
+ QFontInfo info(qfont);
+
+ NOTATION_DEBUG << "SystemFont::loadSystemFont[Qt]: have family " << info.family() << " (exactMatch " << info.exactMatch() << ")" << endl;
+
+ // return info.exactMatch();
+
+ // The Qt documentation says:
+ //
+ // bool QFontInfo::exactMatch() const
+ // Returns TRUE if the matched window system font is exactly the
+ // same as the one specified by the font; otherwise returns FALSE.
+ //
+ // My arse. I specify "feta", I get "Verdana", and exactMatch
+ // returns true. Uh huh.
+ //
+ // UPDATE: in newer versions of Qt, I specify "fughetta", I get
+ // "Fughetta [macromedia]", and exactMatch returns false. Just as
+ // useless, but in a different way.
+
+ QString family = info.family().lower();
+
+ if (family == name.lower())
+ return new SystemFontQt(qfont);
+ else {
+ int bracket = family.find(" [");
+ if (bracket > 1)
+ family = family.left(bracket);
+ if (family == name.lower())
+ return new SystemFontQt(qfont);
+ }
+
+ NOTATION_DEBUG << "SystemFont::loadSystemFont[Qt]: Wrong family returned, failing" << endl;
+ return 0;
+}
+
+}
diff --git a/src/gui/editors/notation/SystemFont.h b/src/gui/editors/notation/SystemFont.h
new file mode 100644
index 0000000..0acc2dd
--- /dev/null
+++ b/src/gui/editors/notation/SystemFont.h
@@ -0,0 +1,63 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_SYSTEMFONT_H_
+#define _RG_SYSTEMFONT_H_
+
+#include <qpixmap.h>
+#include "gui/editors/notation/NoteCharacterNames.h"
+
+
+class SystemFontSpec;
+
+
+namespace Rosegarden
+{
+
+typedef std::pair<QString, int> SystemFontSpec;
+
+
+class SystemFont
+{
+public:
+ enum Strategy {
+ PreferGlyphs, PreferCodes, OnlyGlyphs, OnlyCodes
+ };
+
+ virtual QPixmap renderChar(CharName charName,
+ int glyph, int code,
+ Strategy strategy,
+ bool &success) = 0;
+
+ static SystemFont *loadSystemFont(const SystemFontSpec &spec);
+};
+
+
+// Helper class for looking up information about a font
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/SystemFontQt.cpp b/src/gui/editors/notation/SystemFontQt.cpp
new file mode 100644
index 0000000..f9c99b1
--- /dev/null
+++ b/src/gui/editors/notation/SystemFontQt.cpp
@@ -0,0 +1,78 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "SystemFontQt.h"
+
+#include "misc/Debug.h"
+#include "gui/general/PixmapFunctions.h"
+
+#include <qfont.h>
+#include <qfontmetrics.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+
+namespace Rosegarden {
+
+QPixmap
+SystemFontQt::renderChar(CharName charName, int glyph, int code,
+ Strategy strategy, bool &success)
+{
+ success = false;
+
+ if (strategy == OnlyGlyphs) {
+ NOTATION_DEBUG << "SystemFontQt::renderChar: OnlyGlyphs strategy not supported by Qt renderer, can't render character " << charName.getName() << " (glyph " << glyph << ")" << endl;
+ return QPixmap();
+ }
+
+ if (code < 0) {
+ NOTATION_DEBUG << "SystemFontQt::renderChar: Can't render using Qt with only glyph value (" << glyph << ") for character " << charName.getName() << ", need a code point" << endl;
+ return QPixmap();
+ }
+
+ QFontMetrics metrics(m_font);
+ QChar qc(code);
+
+ QPixmap map;
+ map = QPixmap(metrics.width(qc), metrics.height());
+
+ map.fill();
+ QPainter painter;
+ painter.begin(&map);
+ painter.setFont(m_font);
+ painter.setPen(Qt::black);
+
+ NOTATION_DEBUG << "NoteFont: Drawing character code "
+ << code << " for " << charName.getName()
+ << " using QFont" << endl;
+
+ painter.drawText(0, metrics.ascent(), qc);
+
+ painter.end();
+ map.setMask(PixmapFunctions::generateMask(map, Qt::white.rgb()));
+
+ success = true;
+ return map;
+}
+
+}
diff --git a/src/gui/editors/notation/SystemFontQt.h b/src/gui/editors/notation/SystemFontQt.h
new file mode 100644
index 0000000..ea8f5f2
--- /dev/null
+++ b/src/gui/editors/notation/SystemFontQt.h
@@ -0,0 +1,49 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_SYSTEMFONTQT_H_
+#define _RG_SYSTEMFONTQT_H_
+
+#include "SystemFont.h"
+
+#include <qfont.h>
+
+namespace Rosegarden {
+
+class SystemFontQt : public SystemFont
+{
+public:
+ SystemFontQt(QFont &font) : m_font(font) { }
+ virtual ~SystemFontQt() { }
+
+ virtual QPixmap renderChar(CharName charName, int glyph, int code,
+ Strategy strategy, bool &success);
+
+private:
+ QFont m_font;
+};
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/SystemFontXft.cpp b/src/gui/editors/notation/SystemFontXft.cpp
new file mode 100644
index 0000000..ce42f61
--- /dev/null
+++ b/src/gui/editors/notation/SystemFontXft.cpp
@@ -0,0 +1,193 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "SystemFontXft.h"
+
+#ifdef HAVE_XFT
+
+#include "misc/Debug.h"
+#include "gui/general/PixmapFunctions.h"
+
+namespace Rosegarden {
+
+/*!!! Just test code.
+
+int
+staticMoveTo(FT_Vector *to, void *)
+{
+ NOTATION_DEBUG << "moveTo: (" << to->x << "," << to->y << ")" << endl;
+ return 0;
+}
+
+int
+staticLineTo(FT_Vector *to, void *)
+{
+ NOTATION_DEBUG << "lineTo: (" << to->x << "," << to->y << ")" << endl;
+ return 0;
+}
+
+int
+staticConicTo(FT_Vector *control, FT_Vector *to, void *)
+{
+ NOTATION_DEBUG << "conicTo: (" << to->x << "," << to->y << ") control (" << control->x << "," << control->y << ")" << endl;
+ return 0;
+}
+
+int
+staticCubicTo(FT_Vector *control1, FT_Vector *control2, FT_Vector *to, void *)
+{
+ NOTATION_DEBUG << "cubicTo: (" << to->x << "," << to->y << ") control1 (" << control1->x << "," << control1->y << ") control2 (" << control2->x << "," << control2->y << ")" << endl;
+ return 0;
+}
+
+*/
+
+QPixmap
+SystemFontXft::renderChar(CharName charName, int glyph, int code,
+ Strategy strategy, bool &success)
+{
+ success = false;
+
+ if (glyph < 0 && code < 0) {
+ NOTATION_DEBUG << "SystemFontXft::renderChar: Have neither glyph nor code point for character " << charName.getName() << ", can't render" << endl;
+ return QPixmap();
+ }
+
+ if (code < 0 && strategy == OnlyCodes) {
+ NOTATION_DEBUG << "SystemFontXft::renderChar: strategy is OnlyCodes but no code point provided for character " << charName.getName() << " (glyph is " << glyph << ")" << endl;
+ return QPixmap();
+ }
+
+ if (glyph < 0 && strategy == OnlyGlyphs) {
+ NOTATION_DEBUG << "SystemFontXft::renderChar: strategy is OnlyGlyphs but no glyph index provided for character " << charName.getName() << " (code is " << code << ")" << endl;
+ return QPixmap();
+ }
+
+ XGlyphInfo extents;
+
+ bool useGlyph = true;
+ if (glyph < 0 || (strategy == PreferCodes && code >= 0)) useGlyph = false;
+ if (glyph >= 0 && useGlyph == false && !XftCharExists(m_dpy, m_font, code)) {
+ NOTATION_DEBUG << "SystemFontXft::renderChar: code " << code << " is preferred for character " << charName.getName() << ", but it doesn't exist in font! Falling back to glyph " << glyph << endl;
+ useGlyph = true;
+ }
+
+ if (useGlyph) {
+ FT_UInt ui(glyph);
+ XftGlyphExtents(m_dpy, m_font, &ui, 1, &extents);
+ if (extents.width == 0 || extents.height == 0) {
+ NOTATION_DEBUG
+ << "SystemFontXft::renderChar: zero extents for character "
+ << charName.getName() << " (glyph " << glyph << ")" << endl;
+ return QPixmap();
+ }
+ } else {
+ FcChar32 char32(code);
+ XftTextExtents32(m_dpy, m_font, &char32, 1, &extents);
+ if (extents.width == 0 || extents.height == 0) {
+ NOTATION_DEBUG
+ << "SystemFontXft::renderChar: zero extents for character "
+ << charName.getName() << " (code " << code << ")" << endl;
+ return QPixmap();
+ }
+ }
+
+ QPixmap map(extents.width, extents.height);
+ map.fill();
+
+ Drawable drawable = (Drawable)map.handle();
+ if (!drawable) {
+ std::cerr << "ERROR: SystemFontXft::renderChar: No drawable in QPixmap!" << std::endl;
+ return map;
+ }
+
+ XftDraw *draw = XftDrawCreate(m_dpy,
+ drawable,
+ (Visual *)map.x11Visual(),
+ map.x11Colormap());
+
+ QColor pen(Qt::black);
+ XftColor col;
+ col.color.red = pen.red () | pen.red() << 8;
+ col.color.green = pen.green () | pen.green() << 8;
+ col.color.blue = pen.blue () | pen.blue() << 8;
+ col.color.alpha = 0xffff;
+ col.pixel = pen.pixel();
+
+ if (useGlyph) {
+ NOTATION_DEBUG << "NoteFont: drawing raw character glyph "
+ << glyph << " for " << charName.getName()
+ << " using Xft" << endl;
+ FT_UInt ui(glyph);
+ XftDrawGlyphs(draw, &col, m_font, extents.x, extents.y, &ui, 1);
+ } else {
+ NOTATION_DEBUG << "NoteFont: drawing character code "
+ << code << " for " << charName.getName()
+ << " using Xft" << endl;
+ FcChar32 char32(code);
+ XftDrawString32(draw, &col, m_font, extents.x, extents.y, &char32, 1);
+ }
+
+ XftDrawDestroy(draw);
+
+ map.setMask(PixmapFunctions::generateMask(map, Qt::white.rgb()));
+ success = true;
+
+
+ //!!! experimental stuff
+/*!!!
+ FT_Face face = XftLockFace(m_font);
+ if (!face) {
+ NOTATION_DEBUG << "Couldn't lock face" << endl;
+ return map;
+ }
+ // not checking return value here
+ FT_Load_Glyph(face, glyph, 0);
+ if (face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) {
+ NOTATION_DEBUG << "Glyph " << glyph << " isn't an outline" << endl;
+ XftUnlockFace(m_font);
+ return map;
+ }
+ FT_Glyph ftglyph;
+ FT_Get_Glyph(face->glyph, &ftglyph);
+ FT_Outline &outline = ((FT_OutlineGlyph)ftglyph)->outline;
+ NOTATION_DEBUG << "Outline: " << outline.n_contours << " contours, "
+ << outline.n_points << " points" << endl;
+
+
+ FT_Outline_Funcs funcs = {
+ staticMoveTo, staticLineTo, staticConicTo, staticCubicTo, 0, 0
+ };
+ FT_Outline_Decompose(&outline, &funcs, 0);
+ FT_Done_Glyph(ftglyph);
+ XftUnlockFace(m_font);
+*/
+
+ return map;
+}
+
+}
+
+#endif /* HAVE_XFT */
+
diff --git a/src/gui/editors/notation/SystemFontXft.h b/src/gui/editors/notation/SystemFontXft.h
new file mode 100644
index 0000000..b1487c4
--- /dev/null
+++ b/src/gui/editors/notation/SystemFontXft.h
@@ -0,0 +1,58 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_SYSTEMFONTXFT_H_
+#define _RG_SYSTEMFONTXFT_H_
+
+#ifdef HAVE_XFT
+
+#include "SystemFont.h"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_OUTLINE_H
+#include FT_GLYPH_H
+#include <X11/Xft/Xft.h>
+
+namespace Rosegarden {
+
+class SystemFontXft : public SystemFont
+{
+public:
+ SystemFontXft(Display *dpy, XftFont *font) : m_dpy(dpy), m_font(font) { }
+ virtual ~SystemFontXft() { if (m_font) XftFontClose(m_dpy, m_font); }
+
+ virtual QPixmap renderChar(CharName charName, int glyph, int code,
+ Strategy strategy, bool &success);
+
+private:
+ Display *m_dpy;
+ XftFont *m_font;
+};
+
+}
+
+#endif
+
+#endif
diff --git a/src/gui/editors/notation/TextInserter.cpp b/src/gui/editors/notation/TextInserter.cpp
new file mode 100644
index 0000000..aa8e1ff
--- /dev/null
+++ b/src/gui/editors/notation/TextInserter.cpp
@@ -0,0 +1,169 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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 "TextInserter.h"
+
+#include <klocale.h>
+#include "base/Event.h"
+#include "base/Exception.h"
+#include "base/NotationTypes.h"
+#include "base/ViewElement.h"
+#include "commands/notation/EraseEventCommand.h"
+#include "commands/notation/TextInsertionCommand.h"
+#include "gui/dialogs/TextEventDialog.h"
+#include "gui/general/EditTool.h"
+#include "gui/general/LinedStaff.h"
+#include "NotationTool.h"
+#include "NotationView.h"
+#include "NotePixmapFactory.h"
+#include "NotationElement.h"
+#include <kaction.h>
+#include <qdialog.h>
+#include <qiconset.h>
+#include <qstring.h>
+
+
+namespace Rosegarden
+{
+
+TextInserter::TextInserter(NotationView* view)
+ : NotationTool("TextInserter", view),
+ m_text("", Text::Dynamic)
+{
+ QIconSet icon = QIconSet(NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("select")));
+ new KAction(i18n("Switch to Select Tool"), icon, 0, this,
+ SLOT(slotSelectSelected()), actionCollection(),
+ "select");
+
+ new KAction(i18n("Switch to Erase Tool"), "eraser", 0, this,
+ SLOT(slotEraseSelected()), actionCollection(),
+ "erase");
+
+ icon = QIconSet
+ (NotePixmapFactory::toQPixmap(NotePixmapFactory::
+ makeToolbarPixmap("crotchet")));
+ new KAction(i18n("Switch to Inserting Notes"), icon, 0, this,
+ SLOT(slotNotesSelected()), actionCollection(),
+ "notes");
+
+ createMenu("textinserter.rc");
+}
+
+void TextInserter::slotNotesSelected()
+{
+ m_nParentView->slotLastNoteAction();
+}
+
+void TextInserter::slotEraseSelected()
+{
+ m_parentView->actionCollection()->action("erase")->activate();
+}
+
+void TextInserter::slotSelectSelected()
+{
+ m_parentView->actionCollection()->action("select")->activate();
+}
+
+void TextInserter::ready()
+{
+ m_nParentView->setCanvasCursor(Qt::crossCursor);
+ m_nParentView->setHeightTracking(false);
+}
+
+void TextInserter::handleLeftButtonPress(timeT,
+ int,
+ int staffNo,
+ QMouseEvent* e,
+ ViewElement *element)
+{
+ if (staffNo < 0)
+ return ;
+ LinedStaff *staff = m_nParentView->getLinedStaff(staffNo);
+
+ Text defaultText(m_text);
+ timeT insertionTime;
+ Event *eraseEvent = 0;
+
+ if (element && element->event()->isa(Text::EventType)) {
+
+ // edit an existing text, if that's what we clicked on
+
+ try {
+ defaultText = Text(*element->event());
+ } catch (Exception e) {}
+
+ insertionTime = element->event()->getAbsoluteTime(); // not getViewAbsoluteTime()
+
+ eraseEvent = element->event();
+
+ } else {
+
+ Event *clef = 0, *key = 0;
+
+ NotationElementList::iterator closestElement =
+ staff->getClosestElementToCanvasCoords(e->x(), (int)e->y(),
+ clef, key, false, -1);
+
+ if (closestElement == staff->getViewElementList()->end())
+ return ;
+
+ insertionTime = (*closestElement)->event()->getAbsoluteTime(); // not getViewAbsoluteTime()
+
+ }
+
+ TextEventDialog *dialog = new TextEventDialog
+ (m_nParentView, m_nParentView->getNotePixmapFactory(), defaultText);
+
+ if (dialog->exec() == QDialog::Accepted) {
+
+ m_text = dialog->getText();
+
+ TextInsertionCommand *command =
+ new TextInsertionCommand
+ (staff->getSegment(), insertionTime, m_text);
+
+ if (eraseEvent) {
+ KMacroCommand *macroCommand = new KMacroCommand(command->name());
+ macroCommand->addCommand(new EraseEventCommand(staff->getSegment(),
+ eraseEvent, false));
+ macroCommand->addCommand(command);
+ m_nParentView->addCommandToHistory(macroCommand);
+ } else {
+ m_nParentView->addCommandToHistory(command);
+ }
+
+ Event *event = command->getLastInsertedEvent();
+ if (event)
+ m_nParentView->setSingleSelectedEvent(staffNo, event);
+ }
+
+ delete dialog;
+}
+
+const QString TextInserter::ToolName = "textinserter";
+
+}
+#include "TextInserter.moc"
diff --git a/src/gui/editors/notation/TextInserter.h b/src/gui/editors/notation/TextInserter.h
new file mode 100644
index 0000000..3b4821b
--- /dev/null
+++ b/src/gui/editors/notation/TextInserter.h
@@ -0,0 +1,78 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[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_TEXTINSERTER_H_
+#define _RG_TEXTINSERTER_H_
+
+#include "base/NotationTypes.h"
+#include "NotationTool.h"
+#include <qstring.h>
+#include "base/Event.h"
+
+
+class QMouseEvent;
+
+
+namespace Rosegarden
+{
+
+class ViewElement;
+class NotationView;
+
+
+/**
+ * This tool will request and insert text on mouse click events
+ */
+class TextInserter : public NotationTool
+{
+ Q_OBJECT
+
+ friend class NotationToolBox;
+
+public:
+ virtual void ready();
+
+ virtual void handleLeftButtonPress(timeT,
+ int height,
+ int staffNo,
+ QMouseEvent*,
+ ViewElement* el);
+ static const QString ToolName;
+
+protected slots:
+ void slotNotesSelected();
+ void slotEraseSelected();
+ void slotSelectSelected();
+
+protected:
+ TextInserter(NotationView*);
+ Text m_text;
+};
+
+
+
+}
+
+#endif
diff --git a/src/gui/editors/notation/TrackHeader.cpp b/src/gui/editors/notation/TrackHeader.cpp
new file mode 100644
index 0000000..32fab2f
--- /dev/null
+++ b/src/gui/editors/notation/TrackHeader.cpp
@@ -0,0 +1,450 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ This file is Copyright 2007-2008
+ Yves Guillemot <[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 "TrackHeader.h"
+#include "HeadersGroup.h"
+#include "base/Composition.h"
+#include "base/NotationTypes.h"
+#include "base/StaffExportTypes.h"
+#include "base/Colour.h"
+#include "base/ColourMap.h"
+#include "base/Track.h"
+#include "gui/general/GUIPalette.h"
+#include "gui/general/LinedStaff.h"
+#include "document/RosegardenGUIDoc.h"
+#include "misc/Strings.h"
+#include "NotePixmapFactory.h"
+#include "NotationView.h"
+#include "NotationStaff.h"
+
+#include <map>
+#include <set>
+#include <string>
+#include <utility>
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <qsize.h>
+#include <qwidget.h>
+#include <qhbox.h>
+#include <qvbox.h>
+#include <qpushbutton.h>
+#include <qlabel.h>
+#include <qframe.h>
+#include <qstring.h>
+#include <qtooltip.h>
+
+
+namespace Rosegarden
+{
+
+
+// Status bits
+const int TrackHeader::SEGMENT_HERE = 1 << 0;
+const int TrackHeader::SUPERIMPOSED_SEGMENTS = 1 << 1;
+const int TrackHeader::INCONSISTENT_CLEFS = 1 << 2;
+const int TrackHeader::INCONSISTENT_KEYS = 1 << 3;
+const int TrackHeader::INCONSISTENT_LABELS = 1 << 4;
+const int TrackHeader::INCONSISTENT_TRANSPOSITIONS = 1 << 5;
+const int TrackHeader::BEFORE_FIRST_SEGMENT = 1 << 6;
+
+
+TrackHeader::TrackHeader(QWidget *parent, TrackId trackId, int height, int ypos) :
+ QLabel(parent),
+ m_track(trackId),
+ m_height(height),
+ m_ypos(ypos),
+ m_lastClef(Clef()),
+ m_lastKey(Rosegarden::Key()),
+ m_lastLabel(QString("")),
+ m_lastTranspose(0),
+ m_lastUpperText(QString("")),
+ m_neverUpdated(true),
+ m_isCurrent(true),
+ m_lastStatusPart(0),
+ m_lastWidth(0),
+ m_key(Rosegarden::Key()),
+ m_label(QString("")),
+ m_transpose(0),
+ m_status(0),
+ m_current(false)
+{
+
+ m_notationView = static_cast<HeadersGroup *>(parent)->getNotationView();
+
+ setFrameStyle(QFrame::Box | QFrame::Plain);
+ setCurrent(false);
+
+
+ //
+ // Tooltip text creation
+
+ Composition *comp =
+ static_cast<HeadersGroup *>(parent)->getComposition();
+ Track *track = comp->getTrackById(m_track);
+ int trackPos = comp->getTrackPositionById(m_track);
+
+ QString toolTipText = QString(i18n("Track %1 : \"%2\"")
+ .arg(trackPos + 1)
+ .arg(strtoqstr(track->getLabel())));
+
+ QString preset = track->getPresetLabel();
+ if (preset != QString(""))
+ toolTipText += QString(i18n("\nNotate for: %1").arg(preset));
+
+ QString notationSize = i18n("normal");
+ switch (track->getStaffSize()) {
+ case StaffTypes::Small:
+ notationSize = i18n("small");
+ break;
+ case StaffTypes::Tiny:
+ notationSize = i18n("tiny");
+ break;
+ }
+
+ QString bracketText = i18n("--");
+ switch (track->getStaffBracket()) {
+ case Brackets::SquareOn:
+ bracketText = "[-";
+ break;
+ case Brackets::SquareOff:
+ bracketText = "-]";
+ break;
+ case Brackets::SquareOnOff:
+ bracketText = "[-]";
+ break;
+ case Brackets::CurlyOn:
+ bracketText = "{-";
+ break;
+ case Brackets::CurlyOff:
+ bracketText = "-}";
+ break;
+ case Brackets::CurlySquareOn:
+ bracketText = "{[-";
+ break;
+ case Brackets::CurlySquareOff:
+ bracketText = "-]}";
+ break;
+ }
+
+ toolTipText += QString(i18n("\nSize: %1, Bracket: %2 "))
+ .arg(notationSize)
+ .arg(bracketText);
+
+ // Sort segments by position on the track
+ SortedSegments segments;
+ for (int i=0; i<m_notationView->getStaffCount(); i++) {
+
+ NotationStaff * notationStaff = m_notationView->getNotationStaff(i);
+ Segment &segment = notationStaff->getSegment();
+ TrackId trackId = segment.getTrack();
+
+ if (trackId == m_track) {
+ segments.insert(&segment);
+ }
+ }
+
+ for (SortedSegments::iterator i=segments.begin(); i!=segments.end(); ++i) {
+ timeT segStart = (*i)->getStartTime();
+ timeT segEnd = (*i)->getEndMarkerTime();
+ int barStart = comp->getBarNumber(segStart) + 1;
+ int barEnd = comp->getBarNumber(segEnd) + 1;
+
+ int transpose = (*i)->getTranspose();
+ if (transpose) {
+ QString transposeName;
+ transposeValueToName(transpose, transposeName);
+ toolTipText += QString(i18n("\nbars [%1-%2] in %3 (tr=%4) : \"%5\""))
+ .arg(barStart)
+ .arg(barEnd)
+ .arg(transposeName)
+ .arg(transpose)
+ .arg(strtoqstr((*i)->getLabel()));
+ } else {
+ toolTipText += QString(i18n("\nbars [%1-%2] (tr=%3) : \"%4\""))
+ .arg(barStart)
+ .arg(barEnd)
+ .arg(transpose)
+ .arg(strtoqstr((*i)->getLabel()));
+ }
+ }
+
+ QToolTip::add(this, toolTipText);
+
+ m_firstSeg = *segments.begin();
+ m_firstSegStartTime = m_firstSeg->getStartTime();
+
+ /// This may not work if two segments are superimposed
+ /// at the beginning of the track (inconsistencies are
+ /// not detected).
+ /// TODO : Look for the first segment(s) in
+ /// lookAtStaff() and not here.
+}
+
+void
+TrackHeader::setCurrent(bool current)
+{
+ /// TODO : use colours from GUIPalette
+
+ if (current != m_isCurrent) {
+ m_isCurrent = current;
+ if (current) {
+ setLineWidth(2);
+ setMargin(0);
+ setPaletteForegroundColor(QColor(0, 0, 255));
+ } else {
+ setLineWidth(1);
+ setMargin(1);
+ setPaletteForegroundColor(QColor(0, 0, 0));
+ }
+ }
+}
+
+void
+TrackHeader::transposeValueToName(int transpose, QString &transposeName)
+{
+
+ /// TODO : Should be rewrited using methods from Pitch class
+
+ int noteIndex = transpose % 12;
+ if (noteIndex < 0) noteIndex += 12;
+
+ switch(noteIndex) {
+ case 0 : transposeName = i18n("C"); break;
+ case 1 : transposeName = i18n("C#"); break;
+ case 2 : transposeName = i18n("D"); break;
+ case 3 : transposeName = i18n("Eb"); break;
+ case 4 : transposeName = i18n("E"); break;
+ case 5 : transposeName = i18n("F"); break;
+ case 6 : transposeName = i18n("F#"); break;
+ case 7 : transposeName = i18n("G"); break;
+ case 8 : transposeName = i18n("G#"); break;
+ case 9 : transposeName = i18n("A"); break;
+ case 10 : transposeName = i18n("Bb"); break;
+ case 11 : transposeName = i18n("B"); break;
+ }
+}
+
+int
+TrackHeader::lookAtStaff(double x, int maxWidth)
+{
+ // Read Clef and Key on canvas at (x, m_ypos + m_height / 2)
+ // then guess the header needed width and return it
+
+ // When walking through the segments :
+ // clef, key, label and transpose are current values
+ // clef0, key0, label0 and transpose0 are preceding values used to look
+ // for inconsistencies
+ // key1, label1 and transpose1 are "visible" (opposed at invisible as are
+ // key=<C major>, label="" or transpose=0)
+ // preceding or current values which may be
+ // displayed with a red colour if some
+ // inconsistency occurs.
+ Clef clef, clef0;
+ Rosegarden::Key key, key0, key1 = Rosegarden::Key("C major");
+ QString label = QString(""), label0, label1 = QString("");
+ int transpose = 0, transpose0, transpose1 = 0;
+
+ int staff;
+
+ Composition *comp =
+ static_cast<HeadersGroup *>(parent())->getComposition();
+ Track *track = comp->getTrackById(m_track);
+ int trackPos = comp->getTrackPositionById(m_track);
+
+ int status = 0;
+ bool current = false;
+ for (int i=0; i<m_notationView->getStaffCount(); i++) {
+ NotationStaff * notationStaff = m_notationView->getNotationStaff(i);
+ Segment &segment = notationStaff->getSegment();
+ TrackId trackId = segment.getTrack();
+ if (trackId == m_track) {
+
+ /// TODO : What if a segment has been moved ???
+ timeT xTime = notationStaff->getTimeAtCanvasCoords(x, m_ypos);
+ if (xTime < m_firstSegStartTime) {
+ status |= BEFORE_FIRST_SEGMENT;
+ /// TODO : What if superimposed segments ???
+ m_firstSeg->getFirstClefAndKey(clef, key);
+ label = strtoqstr(m_firstSeg->getLabel());
+ transpose = m_firstSeg->getTranspose();
+ current = current || m_notationView->isCurrentStaff(i);
+ break;
+ }
+ timeT segStart = segment.getStartTime();
+ timeT segEnd = segment.getEndMarkerTime();
+ current = current || m_notationView->isCurrentStaff(i);
+
+ if ((xTime >= segStart) && (xTime < segEnd)) {
+
+ notationStaff->getClefAndKeyAtCanvasCoords(x,
+ m_ypos + m_height / 2, clef, key);
+ label = strtoqstr(segment.getLabel());
+ transpose = segment.getTranspose();
+
+ if (status & SEGMENT_HERE) {
+ status |= SUPERIMPOSED_SEGMENTS;
+ if (clef != clef0)
+ status |= INCONSISTENT_CLEFS;
+ if (key != key0)
+ status |= INCONSISTENT_KEYS;
+ if (label != label0)
+ status |= INCONSISTENT_LABELS;
+ if (transpose != transpose0)
+ status |= INCONSISTENT_TRANSPOSITIONS;
+ } else {
+ status |= SEGMENT_HERE;
+ }
+
+ staff = i;
+
+ // If current value is visible, remember it
+ if (key.getAccidentalCount()) key1 = key;
+ if (label.stripWhiteSpace().length()) label1 = label;
+ if (transpose) transpose1 = transpose;
+
+ // Current values become last values
+ clef0 = clef;
+ key0 = key;
+ label0 = label;
+ transpose0 = transpose;
+ } // if(xTime...)
+ } // if(trackId...)
+ }
+
+ // Remember current data (but only visible data if inconsistency)
+ m_clef = clef;
+ m_key = (status & INCONSISTENT_KEYS) ? key1 : key;
+ m_label = (status & INCONSISTENT_LABELS) ? label1 : label;
+ m_transpose = (status & INCONSISTENT_TRANSPOSITIONS) ? transpose1 : transpose;
+ m_current = current;
+ m_status = status;
+
+ QString noteName;
+ transposeValueToName(m_transpose, noteName);
+
+ m_upperText = QString(i18n("%1: %2")
+ .arg(trackPos + 1)
+ .arg(strtoqstr(track->getLabel())));
+ if (m_transpose) m_transposeText = i18n(" in %1").arg(noteName);
+ else m_transposeText = QString("");
+
+ NotePixmapFactory * npf = m_notationView->getNotePixmapFactory();
+ int clefAndKeyWidth = npf->getClefAndKeyWidth(m_key, m_clef);
+
+ // How many text lines may be written above or under the clef
+ // in track header ?
+ m_numberOfTextLines = npf->getTrackHeaderNTL(m_height);
+
+ int trackLabelWidth =
+ npf->getTrackHeaderTextWidth(m_upperText + m_transposeText)
+ / m_numberOfTextLines;
+ int segmentNameWidth =
+ npf->getTrackHeaderTextWidth(m_label) / m_numberOfTextLines;
+
+ // Get the max. width from upper text and lower text
+ int width = (segmentNameWidth > trackLabelWidth)
+ ? segmentNameWidth : trackLabelWidth;
+
+ // Text width is limited by max header Width
+ if (width > maxWidth) width = maxWidth;
+
+ // But clef and key width may override max header width
+ if (width < clefAndKeyWidth) width = clefAndKeyWidth;
+
+ return width;
+}
+
+
+
+void
+TrackHeader::updateHeader(int width)
+{
+
+ // Update the header (using given width) if necessary
+
+ // Filter out bits whose display doesn't depend from
+ int statusPart = m_status & ~(SUPERIMPOSED_SEGMENTS);
+
+ // Header should be updated only if necessary
+ if ( m_neverUpdated
+ || (width != m_lastWidth)
+ || (statusPart != m_lastStatusPart)
+ || (m_key != m_lastKey)
+ || (m_clef != m_lastClef)
+ || (m_label != m_lastLabel)
+ || (m_upperText != m_lastUpperText)
+ || (m_transpose != m_lastTranspose)) {
+
+ m_neverUpdated = false;
+ m_lastStatusPart = statusPart;
+ m_lastKey = m_key;
+ m_lastClef = m_clef;
+ m_lastLabel = m_label;
+ m_lastTranspose = m_transpose;
+ m_lastUpperText = m_upperText;
+
+ bool drawClef = true;
+ QColor clefColour;
+ if (m_status & (SEGMENT_HERE | BEFORE_FIRST_SEGMENT)) {
+ if (m_status & (INCONSISTENT_CLEFS | INCONSISTENT_KEYS))
+ clefColour = Qt::red;
+ else
+ clefColour = Qt::black;
+ } else {
+ drawClef = false;
+ }
+
+ NotePixmapFactory * npf = m_notationView->getNotePixmapFactory();
+ QPixmap pmap = NotePixmapFactory::toQPixmap(
+ npf->makeTrackHeaderPixmap(width, m_height, this));
+
+ setPixmap(pmap);
+ setFixedWidth(width);
+
+ // Forced width may differ from localy computed width
+ m_lastWidth = width;
+ }
+
+ // Highlight header if track is the current one
+ setCurrent(m_current);
+}
+
+bool
+TrackHeader::SegmentCmp::operator()(const Segment * s1, const Segment * s2) const
+{
+ // Sort segments by start time, then by end time
+ if (s1->getStartTime() < s2->getStartTime()) return true;
+ if (s1->getStartTime() > s2->getStartTime()) return false;
+ if (s1->getEndMarkerTime() < s2->getEndMarkerTime()) return true;
+ return false;
+}
+
+}
+#include "TrackHeader.moc"
diff --git a/src/gui/editors/notation/TrackHeader.h b/src/gui/editors/notation/TrackHeader.h
new file mode 100644
index 0000000..0104430
--- /dev/null
+++ b/src/gui/editors/notation/TrackHeader.h
@@ -0,0 +1,219 @@
+
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ This file is Copyright 2007-2008
+ Yves Guillemot <[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_TRACKHEADER_H_
+#define _RG_TRACKHEADER_H_
+
+#include "base/NotationTypes.h"
+#include "base/Track.h"
+
+#include <qsize.h>
+#include <qwidget.h>
+#include <qlabel.h>
+
+#include <set>
+
+class QLabel;
+
+
+namespace Rosegarden
+{
+
+class NotePixmapFactory;
+class NotationView;
+class ColourMap;
+class Segment;
+
+class TrackHeader : public QLabel
+{
+ Q_OBJECT
+public:
+ /**
+ * Create a new track header for track of id trackId.
+ * *parent is the parent widget, height the height of staff and
+ * ypos is the staff y position on canvas.
+ */
+ TrackHeader(QWidget *parent, TrackId trackId, int height, int ypos);
+
+ /**
+ * Draw a blue line around header when current is true
+ * (intended to highlight the "current" track).
+ */
+ void setCurrent(bool current);
+
+ /**
+ * Examine staff at x position and gather data needed to draw
+ * the track header.
+ * Return the minimum width required to display the track header.
+ * maxWidth is the maximum width allowed to show text. Return width
+ * may be greater than maxWidth if needed to show clef and key signature.
+ * (Header have always to show complete clef and key signature).
+ */
+ int lookAtStaff(double x, int maxWidth);
+
+ /**
+ * (Re)draw the header on the notation view using the data gathered
+ * by lookAtStaff() last call and the specified width.
+ */
+ void updateHeader(int width);
+
+ /**
+ * Return the Id of the associated track.
+ */
+ TrackId getId()
+ { return m_track;
+ }
+
+ /**
+ * Return how many text lines may be written in the header (above
+ * the clef and under the clef).
+ * This data is coming from the last call of lookAtStaff().
+ */
+ int getNumberOfTextLines() { return m_numberOfTextLines; }
+
+ /**
+ * Return the Clef to draw in the header
+ */
+ Clef & getClef() { return m_clef; }
+
+ /**
+ * Get which key signature should be drawn in the header
+ * from the last call of lookAtStaff().
+ */
+ Rosegarden::Key & getKey() { return m_key; }
+
+ /**
+ * Return true if a Clef (and a key signature) have to be drawn in the header
+ */
+ bool isAClefToDraw()
+ {
+ return (m_status & SEGMENT_HERE) || (m_status & BEFORE_FIRST_SEGMENT);
+ }
+
+ /**
+ * Return the text to write in the header top
+ */
+ QString getUpperText() { return m_upperText; }
+
+ /**
+ * Return the transposition text
+ * (to be written at the end of the upper text)
+ */
+ QString getTransposeText() { return m_transposeText; }
+
+ /**
+ * Return the text to write in the header bottom
+ */
+ QString getLowerText() { return m_label; }
+
+ /**
+ * Return true if two segments or more are superimposed and are
+ * not using the same clef
+ */
+ bool isClefInconsistent() { return m_status & INCONSISTENT_CLEFS; }
+
+ /**
+ * Return true if two segments or more are superimposed and are
+ * not using the same key signature
+ */
+ bool isKeyInconsistent() { return m_status & INCONSISTENT_KEYS; }
+
+ /**
+ * Return true if two segments or more are superimposed and are
+ * not using the same label
+ */
+ bool isLabelInconsistent() { return m_status & INCONSISTENT_LABELS; }
+
+ /**
+ * Return true if two segments or more are superimposed and are
+ * not using the same transposition
+ */
+ bool isTransposeInconsistent()
+ {
+ return m_status & INCONSISTENT_TRANSPOSITIONS;
+ }
+
+
+private :
+ /**
+ * Convert the transpose value to the instrument tune and
+ * return it in a printable string.
+ */
+ void transposeValueToName(int transpose, QString &transposeName);
+
+
+ // Status bits
+ static const int SEGMENT_HERE;
+ static const int SUPERIMPOSED_SEGMENTS;
+ static const int INCONSISTENT_CLEFS;
+ static const int INCONSISTENT_KEYS;
+ static const int INCONSISTENT_LABELS;
+ static const int INCONSISTENT_TRANSPOSITIONS;
+ static const int BEFORE_FIRST_SEGMENT;
+
+ TrackId m_track;
+ int m_height;
+ int m_ypos;
+ NotationView * m_notationView;
+
+ Clef m_lastClef;
+ Rosegarden::Key m_lastKey;
+ QString m_lastLabel;
+ int m_lastTranspose;
+ QString m_lastUpperText;
+ bool m_neverUpdated;
+ bool m_isCurrent;
+ int m_lastStatusPart;
+ int m_lastWidth;
+
+ Clef m_clef;
+ Rosegarden::Key m_key;
+ QString m_label;
+ int m_transpose;
+ int m_status;
+ bool m_current;
+
+ QString m_upperText;
+ QString m_transposeText;
+ int m_numberOfTextLines;
+
+ // Used to sort the segments listed in the header toolTipText
+ struct SegmentCmp {
+ bool operator()(const Segment *s1, const Segment *s2) const;
+ };
+ typedef std::multiset<Segment *, SegmentCmp> SortedSegments;
+
+ // First segment on the track.
+ Segment * m_firstSeg;
+ timeT m_firstSegStartTime;
+};
+
+}
+
+#endif