summaryrefslogtreecommitdiffstats
path: root/src/gui/editors/notation/NotePixmapFactory.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/editors/notation/NotePixmapFactory.cpp')
-rw-r--r--src/gui/editors/notation/NotePixmapFactory.cpp3689
1 files changed, 3689 insertions, 0 deletions
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;
+}
+
+}