1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4     Rosegarden
5     A MIDI and audio sequencer and musical notation editor.
6     Copyright 2000-2021 the Rosegarden development team.
7 
8     Other copyrights also apply to some parts of this work.  Please
9     see the AUTHORS file and individual file headers for details.
10 
11     This program is free software; you can redistribute it and/or
12     modify it under the terms of the GNU General Public License as
13     published by the Free Software Foundation; either version 2 of the
14     License, or (at your option) any later version.  See the file
15     COPYING included with this distribution for more information.
16 */
17 
18 #define RG_NO_DEBUG_PRINT 1
19 
20 #ifdef __GNUG__
21 #pragma GCC diagnostic ignored "-Wswitch-enum"
22 #endif
23 
24 #include "NotePixmapFactory.h"
25 
26 #include "misc/Debug.h"
27 #include "base/NotationRules.h"
28 #include "misc/Strings.h"
29 #include "misc/ConfigGroups.h"
30 #include "base/Exception.h"
31 #include "base/NotationTypes.h"
32 #include "base/Profiler.h"
33 #include "gui/editors/guitar/Fingering.h"
34 #include "gui/editors/guitar/FingeringBox.h"
35 #include "gui/editors/guitar/NoteSymbols.h"
36 #include "gui/editors/notation/StaffHeader.h"
37 #include "gui/general/GUIPalette.h"
38 #include "gui/general/PixmapFunctions.h"
39 #include "gui/general/Spline.h"
40 #include "gui/general/ResourceFinder.h"
41 #include "gui/general/IconLoader.h"
42 #include "gui/widgets/StartupLogo.h"
43 #include "NotationStrings.h"
44 #include "NotationView.h"
45 #include "NoteCharacter.h"
46 #include "NoteCharacterNames.h"
47 #include "NoteFontFactory.h"
48 #include "NoteFont.h"
49 #include "NotePixmapParameters.h"
50 #include "NotePixmapPainter.h"
51 #include "NoteStyleFactory.h"
52 #include "NoteStyle.h"
53 
54 #include <QApplication>
55 #include <QSettings>
56 #include <QMessageBox>
57 #include <QBitmap>
58 #include <QColor>
59 #include <QFile>
60 #include <QFont>
61 #include <QFontMetrics>
62 #include <QImage>
63 #include <QPainter>
64 #include <QPen>
65 #include <QPixmap>
66 #include <QPolygon>
67 #include <QPoint>
68 #include <QRect>
69 #include <QString>
70 
71 #include <cmath>
72 
73 
74 namespace Rosegarden
75 {
76 
77 using namespace Accidentals;
78 
79 //static clock_t drawBeamsTime = 0;
80 //static clock_t makeNotesTime = 0;
81 static int makeNotesCount = 0;
82 static int makeRestsCount = 0;
83 //static int drawBeamsCount = 0;
84 static int drawBeamsBeamCount = 0;
85 
86 const char* const NotePixmapFactory::defaultSerifFontFamily = "Bitstream Vera Serif";
87 const char* const NotePixmapFactory::defaultSansSerifFontFamily = "Bitstream Vera Sans";
88 const char* const NotePixmapFactory::defaultTimeSigFontFamily = "Bitstream Vera Serif";
89 
NotePixmapFactory(QString fontName,int size,int graceSize)90 NotePixmapFactory::NotePixmapFactory(QString fontName, int size, int graceSize) :
91     m_selected(false),
92     m_shaded(false),
93     m_haveGrace(graceSize != NO_GRACE_SIZE),
94     m_graceSize(graceSize),
95     m_tupletCountFont(defaultSerifFontFamily, 8, QFont::Bold),
96     m_tupletCountFontMetrics(m_tupletCountFont),
97     m_textMarkFont(defaultSerifFontFamily, 8, QFont::Bold, true),
98     m_textMarkFontMetrics(m_textMarkFont),
99     m_fingeringFont(defaultSerifFontFamily, 8, QFont::Bold),
100     m_fingeringFontMetrics(m_fingeringFont),
101     m_timeSigFont(defaultTimeSigFontFamily, 8, QFont::Bold),
102     m_timeSigFontMetrics(m_timeSigFont),
103     m_bigTimeSigFont(defaultTimeSigFontFamily, 12, QFont::Normal),
104     m_bigTimeSigFontMetrics(m_bigTimeSigFont),
105     m_ottavaFont(defaultSerifFontFamily, 8, QFont::Normal, true),
106     m_ottavaFontMetrics(m_ottavaFont),
107     m_clefOttavaFont(defaultSerifFontFamily, 8, QFont::Normal),
108     m_clefOttavaFontMetrics(m_ottavaFont),
109     m_trackHeaderFont(defaultSansSerifFontFamily, 9, QFont::Normal),
110     m_trackHeaderFontMetrics(m_trackHeaderFont),
111     m_trackHeaderBoldFont(defaultSansSerifFontFamily, 9, QFont::Bold),
112     m_trackHeaderBoldFontMetrics(m_trackHeaderBoldFont),
113     m_generatedPixmap(nullptr),
114     m_generatedWidth( -1),
115     m_generatedHeight( -1),
116     m_inPrinterMethod(false),
117     m_p(new NotePixmapPainter())
118 {
119     init(fontName, size);
120 }
121 
NotePixmapFactory(const NotePixmapFactory & npf)122 NotePixmapFactory::NotePixmapFactory(const NotePixmapFactory &npf) :
123     m_selected(false),
124     m_shaded(false),
125     m_graceSize(npf.m_graceSize),
126     m_tupletCountFont(npf.m_tupletCountFont),
127     m_tupletCountFontMetrics(m_tupletCountFont),
128     m_textMarkFont(npf.m_textMarkFont),
129     m_textMarkFontMetrics(m_textMarkFont),
130     m_fingeringFont(npf.m_fingeringFont),
131     m_fingeringFontMetrics(m_fingeringFont),
132     m_timeSigFont(npf.m_timeSigFont),
133     m_timeSigFontMetrics(m_timeSigFont),
134     m_bigTimeSigFont(npf.m_bigTimeSigFont),
135     m_bigTimeSigFontMetrics(m_bigTimeSigFont),
136     m_ottavaFont(defaultSerifFontFamily, 8, QFont::Normal, true),
137     m_ottavaFontMetrics(m_ottavaFont),
138     m_clefOttavaFont(defaultSerifFontFamily, 8, QFont::Normal),
139     m_clefOttavaFontMetrics(m_ottavaFont),
140     m_trackHeaderFont(defaultSansSerifFontFamily, 9, QFont::Normal),
141     m_trackHeaderFontMetrics(m_trackHeaderFont),
142     m_trackHeaderBoldFont(defaultSansSerifFontFamily, 9, QFont::Bold),
143     m_trackHeaderBoldFontMetrics(m_trackHeaderBoldFont),
144     m_generatedPixmap(nullptr),
145     m_generatedWidth( -1),
146     m_generatedHeight( -1),
147     m_inPrinterMethod(false),
148     m_p(new NotePixmapPainter())
149 {
150     init(npf.m_font->getName(), npf.m_font->getSize());
151 }
152 
153 NotePixmapFactory &
operator =(const NotePixmapFactory & npf)154 NotePixmapFactory::operator=(const NotePixmapFactory &npf)
155 {
156     if (&npf != this) {
157         m_selected = npf.m_selected;
158         m_shaded = npf.m_shaded;
159         m_timeSigFont = npf.m_timeSigFont;
160         m_timeSigFontMetrics = QFontMetrics(m_timeSigFont);
161         m_bigTimeSigFont = npf.m_bigTimeSigFont;
162         m_bigTimeSigFontMetrics = QFontMetrics(m_bigTimeSigFont);
163         m_tupletCountFont = npf.m_tupletCountFont;
164         m_tupletCountFontMetrics = QFontMetrics(m_tupletCountFont);
165         m_textMarkFont = npf.m_textMarkFont;
166         m_textMarkFontMetrics = QFontMetrics(m_textMarkFont);
167         m_fingeringFont = npf.m_fingeringFont;
168         m_fingeringFontMetrics = QFontMetrics(m_fingeringFont);
169         m_ottavaFont = npf.m_ottavaFont;
170         m_ottavaFontMetrics = QFontMetrics(m_ottavaFont);
171         m_clefOttavaFont = npf.m_clefOttavaFont;
172         m_clefOttavaFontMetrics = QFontMetrics(m_clefOttavaFont);
173         m_trackHeaderFont = npf.m_trackHeaderFont;
174         m_trackHeaderFontMetrics = QFontMetrics(m_trackHeaderFont);
175         m_trackHeaderBoldFont = npf.m_trackHeaderBoldFont;
176         m_trackHeaderBoldFontMetrics = QFontMetrics(m_trackHeaderBoldFont);
177         init(npf.m_font->getName(), npf.m_font->getSize());
178         m_textFontCache.clear();
179     }
180     return *this;
181 }
182 
183 void
init(QString fontName,int size)184 NotePixmapFactory::init(QString fontName, int size)
185 {
186     try {
187         m_style = NoteStyleFactory::getStyle(NoteStyleFactory::DefaultStyle);
188     } catch (const NoteStyleFactory::StyleUnavailable &u) {
189         StartupLogo::hideIfStillThere();
190         QMessageBox::critical(nullptr, tr("Rosegarden"), tr( u.getMessage().c_str() )  );
191         throw;
192     }
193 
194     int origSize = size;
195 
196     if (fontName != "") {
197         try {
198             if (size < 0) size = NoteFontFactory::getDefaultSize(fontName);
199             m_font = NoteFontFactory::getFont(fontName, size);
200             if (m_graceSize > 0) m_graceFont = NoteFontFactory::getFont(fontName, m_graceSize);
201             else m_graceFont = m_font;
202         } catch (const Exception &f) {
203             fontName = "";
204             // fall through
205         }
206     }
207 
208     if (fontName == "") { // either because it was passed in or because read failed
209         try {
210             fontName = NoteFontFactory::getDefaultFontName();
211             size = origSize;
212             if (size < 0) size = NoteFontFactory::getDefaultSize(fontName);
213             m_font = NoteFontFactory::getFont(fontName, size);
214             m_graceFont = NoteFontFactory::getFont(fontName, m_graceSize);
215         } catch (const Exception &f) { // already reported
216             throw;
217         }
218     }
219 
220     // Resize the fonts, because the original constructor used point
221     // sizes only and we want pixels
222     QFont timeSigFont(defaultTimeSigFontFamily),
223         textFont(defaultSerifFontFamily);
224 
225     QSettings settings;
226     settings.beginGroup( NotationViewConfigGroup );
227 
228     m_timeSigFont = settings.value("timesigfont", timeSigFont).toString();
229     m_timeSigFont.setBold(true);
230     m_timeSigFont.setPixelSize(size * 5 / 2);
231     m_timeSigFontMetrics = QFontMetrics(m_timeSigFont);
232 
233     m_bigTimeSigFont = settings.value("timesigfont", timeSigFont).toString();
234     m_bigTimeSigFont.setPixelSize(size * 4 + 2);
235     m_bigTimeSigFontMetrics = QFontMetrics(m_bigTimeSigFont);
236 
237     m_tupletCountFont = settings.value("textfont", textFont).toString();
238     m_tupletCountFont.setBold(true);
239     m_tupletCountFont.setPixelSize(size * 2);
240     m_tupletCountFontMetrics = QFontMetrics(m_tupletCountFont);
241 
242     m_textMarkFont = settings.value("textfont", textFont).toString();
243     m_textMarkFont.setBold(true);
244     m_textMarkFont.setItalic(true);
245     m_textMarkFont.setPixelSize(size * 2);
246     m_textMarkFontMetrics = QFontMetrics(m_textMarkFont);
247 
248     m_fingeringFont = settings.value("textfont", textFont).toString();
249     m_fingeringFont.setBold(true);
250     m_fingeringFont.setPixelSize(size * 5 / 3);
251     m_fingeringFontMetrics = QFontMetrics(m_fingeringFont);
252 
253     m_ottavaFont = settings.value("textfont", textFont).toString();
254     m_ottavaFont.setPixelSize(size * 2);
255     m_ottavaFontMetrics = QFontMetrics(m_ottavaFont);
256 
257     m_clefOttavaFont = settings.value("textfont", textFont).toString();
258     m_clefOttavaFont.setPixelSize(getLineSpacing() * 3 / 2);
259     m_clefOttavaFontMetrics = QFontMetrics(m_clefOttavaFont);
260 
261     m_trackHeaderFont = settings.value("sansfont", m_trackHeaderFont).toString();
262     m_trackHeaderFont.setPixelSize(9);
263     m_trackHeaderFontMetrics = QFontMetrics(m_trackHeaderFont);
264 
265     m_trackHeaderBoldFont = m_trackHeaderFont;
266     m_trackHeaderBoldFont.setBold(true);
267     m_trackHeaderBoldFontMetrics = QFontMetrics(m_trackHeaderBoldFont);
268 
269     settings.endGroup();
270 }
271 
~NotePixmapFactory()272 NotePixmapFactory::~NotePixmapFactory()
273 {
274     NOTATION_DEBUG << "NotePixmapFactory::~NotePixmapFactory:"
275               << " makeNotesCount = " << makeNotesCount
276               << ", makeRestsCount = " << makeRestsCount;
277 
278     delete m_p;
279 }
280 
281 QString
getFontName() const282 NotePixmapFactory::getFontName() const
283 {
284     return m_font->getName();
285 }
286 
287 int
getSize() const288 NotePixmapFactory::getSize() const
289 {
290     return m_font->getSize();
291 }
292 
293 void
dumpStats(std::ostream & s)294 NotePixmapFactory::dumpStats(std::ostream &s)
295 {
296 #ifdef DUMP_STATS
297 /*
298   s << "NotePixmapFactory: total times since last stats dump:\n"
299   << "makeNotePixmap: "
300   << (makeNotesTime * 1000 / CLOCKS_PER_SEC) << "ms\n"
301   << "drawBeams: "
302   << (drawBeamsTime * 1000 / CLOCKS_PER_SEC) << "ms"
303   << " (drew " << drawBeamsCount << " individual points in " << drawBeamsBeamCount << " beams)"
304  ;
305   makeNotesTime = 0;
306   drawBeamsTime = 0;
307   drawBeamsCount = 0;
308   drawBeamsBeamCount = 0;
309 */
310 #endif
311 
312     (void)s; // avoid warnings
313 }
314 
315 QGraphicsItem *
makeNote(const NotePixmapParameters & params)316 NotePixmapFactory::makeNote(const NotePixmapParameters &params)
317 {
318     Profiler profiler("NotePixmapFactory::makeNote");
319 
320     ++makeNotesCount;
321 
322     if (m_inPrinterMethod) {
323         return makeNotePixmapItem(params);
324     }
325 
326     NoteItem *item = new NoteItem(params, m_style, m_selected, m_shaded, this);
327     return item;
328 }
329 
330 void
getNoteDimensions(const NotePixmapParameters & params,NoteItemDimensions & dimensions)331 NotePixmapFactory::getNoteDimensions(const NotePixmapParameters &params,
332                                      NoteItemDimensions &dimensions)
333 {
334     calculateNoteDimensions(params);
335     dimensions = m_nd;
336 }
337 
338 void
drawNoteForItem(const NotePixmapParameters & params,const NoteItemDimensions & dimensions,NoteItem::DrawMode mode,QPainter * painter)339 NotePixmapFactory::drawNoteForItem(const NotePixmapParameters &params,
340                                    const NoteItemDimensions &dimensions,
341                                    NoteItem::DrawMode mode,
342                                    QPainter *painter)
343 {
344     if (mode == NoteItem::DrawTiny) {
345         sketchNoteTiny(params, dimensions, painter);
346         return;
347     }
348 
349     m_nd = dimensions;
350     drawNoteAux(params, painter, 0, 0);
351 }
352 
353 QGraphicsPixmapItem *
makeNotePixmapItem(const NotePixmapParameters & params)354 NotePixmapFactory::makeNotePixmapItem(const NotePixmapParameters &params)
355 {
356     Profiler profiler("NotePixmapFactory::makeNotePixmapItem");
357 
358     calculateNoteDimensions(params);
359     drawNoteAux(params, nullptr, 0, 0);
360 
361     QPoint hotspot(m_nd.left, m_nd.above + m_nd.noteBodyHeight / 2);
362 
363     //#define ROSE_DEBUG_NOTE_PIXMAP_FACTORY
364 #ifdef ROSE_DEBUG_NOTE_PIXMAP_FACTORY
365 
366     m_p->painter().setPen(QColor(Qt::red));
367     m_p->painter().setBrush(QColor(Qt::red));
368 
369     m_p->drawLine(0, 0, 0, m_generatedHeight - 1);
370     m_p->drawLine(m_generatedWidth - 1, 0,
371                   m_generatedWidth - 1,
372                   m_generatedHeight - 1);
373 
374     {
375         int hsx = hotspot.x();
376         int hsy = hotspot.y();
377         m_p->drawLine(hsx - 2, hsy - 2, hsx + 2, hsy + 2);
378         m_p->drawLine(hsx - 2, hsy + 2, hsx + 2, hsy - 2);
379     }
380 #endif
381 
382     return makeItem(hotspot);
383 }
384 
385 void
drawNote(const NotePixmapParameters & params,QPainter & painter,int x,int y)386 NotePixmapFactory::drawNote(const NotePixmapParameters &params,
387                             QPainter &painter, int x, int y)
388 {
389     Profiler profiler("NotePixmapFactory::drawNote");
390     m_inPrinterMethod = true;
391     calculateNoteDimensions(params);
392     drawNoteAux(params, &painter, x, y);
393     m_inPrinterMethod = false;
394 }
395 
396 void
calculateNoteDimensions(const NotePixmapParameters & params)397 NotePixmapFactory::calculateNoteDimensions(const NotePixmapParameters &params)
398 {
399     // use the full font for this unless a grace size was supplied in ctor
400     NoteFont *font = (m_haveGrace ? m_graceFont : m_font);
401 
402     NoteFont::CharacterType charType =
403         m_inPrinterMethod ? NoteFont::Printer : NoteFont::Screen;
404 
405     if (m_selected) {
406         NOTATION_DEBUG << "NotePixmapFactory::calculateNoteDimensions: selected";
407     }
408 
409     bool drawFlag = params.m_drawFlag;
410     if (params.m_beamed) drawFlag = false;
411 
412     // A note pixmap is formed of note head, stem, flags,
413     // accidentals, dots and beams.  Assume the note head first, then
414     // do the rest of the calculations left to right, ie accidentals,
415     // stem, flags, dots, beams
416 
417     m_nd.noteBodyWidth = getNoteBodyWidth(params.m_noteType);
418     m_nd.noteBodyHeight = getNoteBodyHeight(params.m_noteType);
419 
420     // Spacing surrounding the note head.  For top and bottom, we
421     // adjust this according to the discrepancy between the nominal
422     // and actual heights of the note head pixmap.  For left and
423     // right, we use the hotspot x coordinate of the head.
424     int temp;
425     if (!font->getHotspot(m_style->getNoteHeadCharName(params.m_noteType).first,
426                           m_nd.borderX, temp))
427         m_nd.borderX = 0;
428 
429     if (params.m_noteType == Note::Minim && params.m_stemGoesUp)
430         m_nd.borderX++;
431     int actualNoteBodyHeight =
432         font->getHeight(m_style->getNoteHeadCharName(params.m_noteType).first);
433 
434     m_nd.left = m_nd.right = m_nd.borderX;
435     m_nd.above = m_nd.borderY = (actualNoteBodyHeight - m_nd.noteBodyHeight) / 2;
436     m_nd.below = (actualNoteBodyHeight - m_nd.noteBodyHeight) - m_nd.above;
437 
438     //    NOTATION_DEBUG << "actualNoteBodyHeight: " << actualNoteBodyHeight
439     //		   << ", noteBodyHeight: " << m_nd.noteBodyHeight << ", borderX: "
440     //		   << m_nd.borderX << ", borderY: "
441     //		   << m_nd.borderY;
442 
443     bool isStemmed = m_style->hasStem(params.m_noteType);
444     int flagCount = m_style->getFlagCount(params.m_noteType);
445     int slashCount = params.m_slashes;
446     if (!slashCount) slashCount = m_style->getSlashCount(params.m_noteType);
447 
448     if (params.m_accidental != NoAccidental) {
449         makeRoomForAccidental(params.m_accidental,
450                               params.m_cautionary,
451                               params.m_accidentalShift,
452                               params.m_accidentalExtra);
453     }
454 
455     NoteCharacter dot(getCharacter(NoteCharacterNames::DOT, PlainColour, charType));
456     int dotWidth = dot.getWidth();
457     if (dotWidth < getNoteBodyWidth() / 2) dotWidth = getNoteBodyWidth() / 2;
458 
459     int stemLength = getStemLength(params);
460 
461     if (params.m_marks.size() > 0) {
462         makeRoomForMarks(isStemmed, params, stemLength);
463     }
464 
465     if (params.m_legerLines != 0) {
466         makeRoomForLegerLines(params);
467     }
468 
469     if (slashCount > 0) {
470         m_nd.left = std::max(m_nd.left, m_nd.noteBodyWidth / 2);
471         m_nd.right = std::max(m_nd.right, m_nd.noteBodyWidth / 2);
472     }
473 
474     if (params.m_tupletCount > 0) {
475         makeRoomForTuplingLine(params);
476     }
477 
478     m_nd.right = std::max(m_nd.right, params.m_dots * dotWidth + dotWidth / 2);
479     if (params.m_dotShifted) {
480         m_nd.right += m_nd.noteBodyWidth;
481     }
482     if (params.m_onLine) {
483         m_nd.above = std::max(m_nd.above, dot.getHeight() / 2);
484     }
485 
486     if (params.m_shifted) {
487         if (params.m_stemGoesUp) {
488             m_nd.right += m_nd.noteBodyWidth;
489         } else {
490             m_nd.left = std::max(m_nd.left, m_nd.noteBodyWidth);
491         }
492     }
493 
494     bool tieAbove = params.m_tieAbove;
495     if (!params.m_tiePositionExplicit) {
496         tieAbove = !params.m_stemGoesUp;
497     }
498 
499     if (params.m_tied) {
500         m_nd.right = std::max(m_nd.right, params.m_tieLength);
501         if (!tieAbove) {
502             m_nd.below = std::max(m_nd.below, m_nd.noteBodyHeight * 2);
503         } else {
504             m_nd.above = std::max(m_nd.above, m_nd.noteBodyHeight * 2);
505         }
506     }
507 
508     if (isStemmed && params.m_drawStem) {
509         makeRoomForStemAndFlags(drawFlag ? flagCount : 0, stemLength, params,
510                                 m_nd.stemStart, m_nd.stemEnd);
511     }
512 
513     if (isStemmed && params.m_drawStem && params.m_beamed) {
514         makeRoomForBeams(params);
515     }
516 }
517 
518 void
sketchNoteTiny(const NotePixmapParameters & params,const NoteItemDimensions & dimensions,QPainter * painter)519 NotePixmapFactory::sketchNoteTiny(const NotePixmapParameters &params,
520                                   const NoteItemDimensions &dimensions,
521                                   QPainter *painter)
522 {
523     if (params.m_drawStem && m_style->hasStem(params.m_noteType)) {
524         painter->drawLine(dimensions.left + dimensions.stemStart.x(),
525                           dimensions.above + dimensions.stemStart.y(),
526                           dimensions.left + dimensions.stemEnd.x(),
527                           dimensions.above + dimensions.stemEnd.y());
528     }
529     painter->drawRect(dimensions.left,
530                       dimensions.above,
531                       dimensions.noteBodyWidth,
532                       dimensions.noteBodyHeight);
533 }
534 
535 void
drawNoteAux(const NotePixmapParameters & params,QPainter * painter,int x,int y)536 NotePixmapFactory::drawNoteAux(const NotePixmapParameters &params,
537                                QPainter *painter, int x, int y)
538 {
539     NoteFont::CharacterType charType =
540         m_inPrinterMethod ? NoteFont::Printer : NoteFont::Screen;
541 
542     bool drawFlag = params.m_drawFlag;
543     if (params.m_beamed) drawFlag = false;
544 
545     bool isStemmed = m_style->hasStem(params.m_noteType);
546     int flagCount = m_style->getFlagCount(params.m_noteType);
547     int slashCount = params.m_slashes;
548     if (!slashCount) slashCount = m_style->getSlashCount(params.m_noteType);
549 
550     NoteCharacter dot;
551     if (params.m_forceColor) {
552         dot = getCharacter(NoteCharacterNames::DOT,
553                            params.m_forcedColor, charType);
554     } else {
555         dot = getCharacter(NoteCharacterNames::DOT, PlainColour, charType);
556     }
557 
558     int dotWidth = dot.getWidth();
559     if (dotWidth < getNoteBodyWidth() / 2) dotWidth = getNoteBodyWidth() / 2;
560 
561     int stemLength = getStemLength(params);
562 
563     bool tieAbove = params.m_tieAbove;
564     if (!params.m_tiePositionExplicit) {
565         tieAbove = !params.m_stemGoesUp;
566     }
567 
568     // for all other calculations we use the nominal note-body height
569     // (same as the gap between staff lines), but here we want to know
570     // if the pixmap itself is taller than that
571     /*!!!
572       int actualNoteBodyHeight = font->getHeight
573       (m_style->getNoteHeadCharName(params.m_noteType).first);
574       //	- 2*m_origin.y();
575       if (actualNoteBodyHeight > m_noteBodyHeight) {
576       m_below = std::max(m_below, actualNoteBodyHeight - m_noteBodyHeight);
577       }
578     */
579     if (painter) {
580         painter->save();
581         m_p->beginExternal(painter);
582 //        NOTATION_DEBUG << "Translate: (" << x << "," << y << ")";
583         painter->translate(x - m_nd.left, y - m_nd.above - m_nd.noteBodyHeight / 2);
584     } else {
585         createPixmap(m_nd.noteBodyWidth + m_nd.left + m_nd.right,
586                      m_nd.noteBodyHeight + m_nd.above + m_nd.below);
587     }
588 
589     if (params.m_tupletCount > 0) {
590         drawTuplingLine(params);
591     }
592 
593     if (isStemmed && params.m_drawStem && drawFlag) {
594         drawFlags(flagCount, params, m_nd.stemStart, m_nd.stemEnd);
595     }
596 
597     NoteStyle::CharNameRec charNameRec
598         (m_style->getNoteHeadCharName(params.m_noteType));
599     CharName charName = charNameRec.first;
600     bool inverted = charNameRec.second;
601 
602     NoteCharacter body;
603     if (params.m_forceColor) {
604         body = getCharacter(charName, params.m_forcedColor, inverted);
605     } else {
606         body = getCharacter
607             (charName,
608             params.m_memberOfParallel ? MemberOfParallelColour :
609             params.m_highlighted ? HighlightedColour :
610             params.m_quantized ? QuantizedColour :
611             (params.m_trigger == NotePixmapParameters::triggerYes) ? TriggerColour :
612             (params.m_trigger == NotePixmapParameters::triggerSkip) ? TriggerSkipColour :
613             params.m_inRange ? PlainColour : OutRangeColour,
614             inverted);
615     }
616 
617     QPoint bodyLocation(m_nd.left - m_nd.borderX,
618                         m_nd.above - m_nd.borderY + getStaffLineThickness() / 2);
619     if (params.m_shifted) {
620         if (params.m_stemGoesUp) {
621             bodyLocation.rx() += m_nd.noteBodyWidth;
622         } else {
623             bodyLocation.rx() -= m_nd.noteBodyWidth - 1;
624         }
625     }
626 
627     m_p->drawNoteCharacter(bodyLocation.x(), bodyLocation.y(), body);
628 
629     if (params.m_dots > 0) {
630 
631         int x = m_nd.left + m_nd.noteBodyWidth + dotWidth / 2;
632         int y = m_nd.above + m_nd.noteBodyHeight / 2 - dot.getHeight() / 2;
633 
634         if (params.m_onLine) y -= m_nd.noteBodyHeight / 2;
635 
636         if (params.m_shifted) x += m_nd.noteBodyWidth;
637         else if (params.m_dotShifted) x += m_nd.noteBodyWidth;
638 
639         for (int i = 0; i < params.m_dots; ++i) {
640             m_p->drawNoteCharacter(x, y, dot);
641             x += dotWidth;
642         }
643     }
644 
645     // Prepare color to draw accidental, stem, flags, etc...
646     if ((isStemmed && params.m_drawStem) || params.m_cautionary) {
647         if (m_selected) m_p->painter().setPen(GUIPalette::getColour(
648                                                 GUIPalette::SelectedElement));
649         else if (m_shaded) m_p->painter().setPen(QColor(Qt::gray));
650         else m_p->painter().setPen(QColor(Qt::black));
651 
652     }
653 
654     drawAccidental(params);
655 
656     if (isStemmed && params.m_drawStem) {
657 
658         if (flagCount > 0 && !drawFlag && params.m_beamed) {
659             drawBeams(m_nd.stemEnd, params, flagCount);
660         }
661 
662         if (slashCount > 0) {
663             drawSlashes(m_nd.stemStart, params, slashCount);
664         }
665 
666         // If we draw stems after beams, instead of beams after stems,
667         // beam anti-aliasing won't damage stems but we have to shorten the
668         // stems slightly first so that the stems don't extend all the way
669         // through the beam into the anti-aliased region on the
670         // other side of the beam that faces away from the note-heads.
671         int shortening;
672         if (flagCount > 0 && !drawFlag && params.m_beamed) shortening = 2;
673         else shortening = 0;
674         drawStem(params, m_nd.stemStart, m_nd.stemEnd, shortening);
675     }
676 
677     if (params.m_marks.size() > 0) {
678         drawMarks(isStemmed, params, stemLength);
679     }
680 
681     if (params.m_legerLines != 0) {
682         drawLegerLines(params);
683     }
684 
685     if (params.m_tied) {
686         drawTie(tieAbove, params.m_tieLength, dotWidth * params.m_dots);
687     }
688 
689     if (painter) {
690         painter->restore();
691     }
692 }
693 
694 
695 QGraphicsPixmapItem *
makeNoteHalo(const NotePixmapParameters & params)696 NotePixmapFactory::makeNoteHalo(const NotePixmapParameters &params)
697 {
698     int nbh0 = getNoteBodyHeight();
699     int nbh = getNoteBodyHeight(params.m_noteType);
700     int nbw0 = getNoteBodyHeight();
701     int nbw = getNoteBodyWidth(params.m_noteType);
702 
703     createPixmap(nbw + nbw0, nbh + nbh0);
704     drawNoteHalo(0, 0, nbw + nbw0, nbh + nbh0);
705 
706     return makeItem(QPoint(nbw0 / 2, nbh0));
707 }
708 
709 
710 void
drawNoteHalo(int x,int y,int w,int h)711 NotePixmapFactory::drawNoteHalo(int x, int y, int w, int h) {
712 
713     m_p->painter().setPen(QPen(QColor::fromHsv(GUIPalette::CollisionHaloHue,
714                                       GUIPalette::CollisionHaloSaturation,
715                                       255), 1));
716     m_p->painter().setBrush(QColor::fromHsv(GUIPalette::CollisionHaloHue,
717                                    GUIPalette::CollisionHaloSaturation,
718                                    255));
719     m_p->drawEllipse(x, y, w, h);
720 }
721 
722 
723 
724 int
getStemLength(const NotePixmapParameters & params) const725 NotePixmapFactory::getStemLength(const NotePixmapParameters &params) const
726 {
727     if (params.m_beamed && params.m_stemLength >= 0) {
728         return params.m_stemLength;
729     }
730 
731     int stemLength = getStemLength();
732 
733     int flagCount = m_style->getFlagCount(params.m_noteType);
734     int slashCount = params.m_slashes;
735     bool stemUp = params.m_stemGoesUp;
736     int nbh = m_nd.noteBodyHeight;
737 
738     if (flagCount > 2) {
739         stemLength += getLineSpacing() * (flagCount - 2);
740     }
741 
742     int width = 0, height = 0;
743 
744     if (flagCount > 0) {
745 
746         if (!stemUp)
747             stemLength += nbh / 2;
748 
749         if (m_font->getDimensions(m_style->getFlagCharName(flagCount),
750                                   width, height)) {
751 
752             stemLength = std::max(stemLength, height);
753 
754         } else if (m_font->getDimensions(m_style->getPartialFlagCharName(true),
755                                          width, height) ||
756                    m_font->getDimensions(m_style->getPartialFlagCharName(false),
757                                          width, height)) {
758 
759             unsigned int flagSpace = m_nd.noteBodyHeight;
760             (void)m_font->getFlagSpacing(flagSpace);
761 
762             stemLength = std::max(stemLength,
763                                   height + (flagCount - 1) * (int)flagSpace);
764         }
765     }
766 
767     if (slashCount > 3 && flagCount < 3) {
768         stemLength += (slashCount - 3) * (nbh / 2);
769     }
770 
771     if (params.m_stemLength >= 0) {
772         if (flagCount == 0)
773             return params.m_stemLength;
774         stemLength = std::max(stemLength, params.m_stemLength);
775     }
776 
777     return stemLength;
778 }
779 
780 void
makeRoomForAccidental(Accidental a,bool cautionary,int shift,bool extra)781 NotePixmapFactory::makeRoomForAccidental(Accidental a,
782                                          bool cautionary, int shift, bool extra)
783 {
784     // use the full font for this unless a grace size was supplied in ctor
785     NoteFont *font = (m_haveGrace ? m_graceFont : m_font);
786 
787     // General observation: where we're only using a character to
788     // determine its dimensions, we should (for the moment) just
789     // request it in screen mode, because it may be quicker and we
790     // don't need to render it, and the dimensions are the same.
791     NoteCharacter ac
792         (font->getCharacter(m_style->getAccidentalCharName(a)));
793 
794     QPoint ah(font->getHotspot(m_style->getAccidentalCharName(a)));
795 
796     m_nd.left += ac.getWidth() + (m_nd.noteBodyWidth / 4 - m_nd.borderX);
797 
798     if (shift > 0) {
799         if (extra) {
800             // The extra flag indicates that the first shift is to get
801             // out of the way of a note head, thus has to move
802             // possibly further, or at least a different amount.  So
803             // replace the first shift with a different one.
804             --shift;
805             m_nd.left += m_nd.noteBodyWidth - m_nd.noteBodyWidth / 5;
806         }
807         if (shift > 0) {
808             // The amount we shift for each accidental is the greater
809             // of the probable shift for that accidental and the
810             // probable shift for a sharp, on the assumption (usually
811             // true in classical notation) that the sharp is the
812             // widest accidental and that we may have other
813             // accidentals possibly including sharps on other notes in
814             // this chord that we can't know about here.
815             int step = ac.getWidth() - ah.x();
816             if (a != Accidentals::Sharp) {
817                 NoteCharacter acSharp
818                     (font->getCharacter(m_style->getAccidentalCharName
819                                         (Accidentals::Sharp)));
820                 QPoint ahSharp
821                     (font->getHotspot(m_style->getAccidentalCharName
822                                       (Accidentals::Sharp)));
823                 step = std::max(step, acSharp.getWidth() - ahSharp.x());
824             }
825             m_nd.left += shift * step;
826         }
827     }
828 
829     if (cautionary) m_nd.left += m_nd.noteBodyWidth;
830 
831     int above = ah.y() - m_nd.noteBodyHeight / 2;
832     int below = (ac.getHeight() - ah.y()) -
833         (m_nd.noteBodyHeight - m_nd.noteBodyHeight / 2); // subtract in case it's odd
834 
835     if (above > 0) m_nd.above = std::max(m_nd.above, above);
836     if (below > 0) m_nd.below = std::max(m_nd.below, below);
837 }
838 
839 void
drawAccidental(const NotePixmapParameters & params)840 NotePixmapFactory::drawAccidental(const NotePixmapParameters &params)
841 {
842     if (params.m_accidental == NoAccidental) return;   // Nothing to draw
843 
844     Accidental a = params.m_accidental;
845     bool cautionary = params.m_cautionary;
846 
847     // use the full font for this unless a grace size was supplied in ctor
848     NoteFont *font = (m_haveGrace ? m_graceFont : m_font);
849 
850     NoteCharacter ac;
851     if (params.m_forceColor) {
852         ac = getCharacter
853             (m_style->getAccidentalCharName(a), params.m_forcedColor, false);
854         // Prepare color in case drawing brackets is needed
855         m_p->painter().setPen(params.m_forcedColor);
856     } else {
857         ac = getCharacter
858             (m_style->getAccidentalCharName(a), PlainColour, false);
859     }
860 
861     QPoint ah(font->getHotspot(m_style->getAccidentalCharName(a)));
862 
863     int ax = 0;
864 
865     if (cautionary) {
866         ax += m_nd.noteBodyWidth / 2;
867         int bl = ac.getHeight() * 2 / 3;
868         int by = m_nd.above + m_nd.noteBodyHeight / 2 - bl / 2;
869         drawBracket(bl, true, false, m_nd.noteBodyWidth*3 / 8, by);
870         drawBracket(bl, false, false, ac.getWidth() + m_nd.noteBodyWidth*5 / 8, by);
871     }
872 
873     m_p->drawNoteCharacter(ax, m_nd.above + m_nd.noteBodyHeight / 2 - ah.y(), ac);
874 }
875 
876 void
makeRoomForMarks(bool isStemmed,const NotePixmapParameters & params,int stemLength)877 NotePixmapFactory::makeRoomForMarks(bool isStemmed,
878                                     const NotePixmapParameters &params,
879                                     int stemLength)
880 {
881     // if anyone puts marks over grace notes, they're stupid, so we're ignoring
882     // this case
883 
884     int height = 0, width = 0;
885     int gap = m_nd.noteBodyHeight / 5 + 1;
886 
887     std::vector<Mark> normalMarks = params.getNormalMarks();
888     std::vector<Mark> aboveMarks = params.getAboveMarks();
889 
890     for (std::vector<Mark>::iterator i = normalMarks.begin();
891          i != normalMarks.end(); ++i) {
892 
893         if (!Marks::isTextMark(*i)) {
894 
895             NoteCharacter character(m_font->getCharacter(m_style->getMarkCharName(*i)));
896             height += character.getHeight() + gap;
897             if (character.getWidth() > width)
898                 width = character.getWidth();
899 
900         } else {
901             // Inefficient to do this here _and_ in drawMarks, but
902             // text marks are not all that common
903             QString text = strtoqstr(Marks::getTextFromMark(*i));
904             QRect bounds = m_textMarkFontMetrics.boundingRect(text);
905             height += bounds.height() + gap;
906             if (bounds.width() > width)
907                 width = bounds.width();
908         }
909     }
910 
911     if (height > 0) {
912         if (isStemmed && params.m_stemGoesUp) {
913             m_nd.below += height + 1;
914         } else {
915             m_nd.above += height + 1;
916         }
917     }
918 
919     height = 0;
920 
921     if (params.m_safeVertDistance > 0 && !aboveMarks.empty()) {
922         m_nd.above = std::max(m_nd.above, params.m_safeVertDistance);
923     }
924 
925     for (std::vector<Mark>::iterator i = aboveMarks.begin();
926          i != aboveMarks.end(); ++i) {
927 
928         if (!Marks::isFingeringMark(*i)) {
929 
930             Mark m(*i);
931 
932             if (m == Marks::TrillLine)
933                 m = Marks::LongTrill;
934 
935             if (m == Marks::LongTrill) {
936                 m_nd.right = std::max(m_nd.right, params.m_width);
937             }
938 
939             NoteCharacter character(m_font->getCharacter(m_style->getMarkCharName(m)));
940             height += character.getHeight() + gap;
941             if (character.getWidth() > width)
942                 width = character.getWidth();
943 
944         } else {
945 
946             // Inefficient to do this here _and_ in drawMarks
947             QString text = strtoqstr(Marks::getFingeringFromMark(*i));
948             QRect bounds = m_fingeringFontMetrics.boundingRect(text);
949             height += bounds.height() + gap + 3;
950             if (bounds.width() > width)
951                 width = bounds.width();
952         }
953     }
954 
955     if (height > 0) {
956         if (isStemmed && params.m_stemGoesUp && params.m_safeVertDistance == 0) {
957             m_nd.above += stemLength + height + 1;
958         } else {
959             m_nd.above += height + 1;
960         }
961     }
962 
963     m_nd.left = std::max(m_nd.left, width / 2 - m_nd.noteBodyWidth / 2);
964     m_nd.right = std::max(m_nd.right, width / 2 - m_nd.noteBodyWidth / 2);
965 }
966 
967 void
drawMarks(bool isStemmed,const NotePixmapParameters & params,int stemLength,bool overRestHack)968 NotePixmapFactory::drawMarks(bool isStemmed,
969                              const NotePixmapParameters &params,
970                              int stemLength,
971                              bool overRestHack)
972 {
973     int gap = m_nd.noteBodyHeight / 5 + 1;
974     int dy = gap;
975 
976     std::vector<Mark> normalMarks = params.getNormalMarks();
977     std::vector<Mark> aboveMarks = params.getAboveMarks();
978 
979     bool normalMarksAreAbove = !(isStemmed && params.m_stemGoesUp);
980 
981     for (std::vector<Mark>::iterator i = normalMarks.begin();
982          i != normalMarks.end(); ++i) {
983 
984         if (!Marks::isTextMark(*i)) {
985 
986             NoteCharacter character = getCharacter
987                 (m_style->getMarkCharName(*i), PlainColour,
988                  !normalMarksAreAbove);
989 
990             int x = m_nd.left + m_nd.noteBodyWidth / 2 - character.getWidth() / 2;
991             int y = (normalMarksAreAbove ?
992                      (m_nd.above - dy - character.getHeight() - 1) :
993                      (m_nd.above + m_nd.noteBodyHeight + m_nd.borderY * 2 + dy));
994 
995             m_p->drawNoteCharacter(x, y, character);
996             dy += character.getHeight() + gap;
997 
998         } else {
999 
1000             QString text = strtoqstr(Marks::getTextFromMark(*i));
1001             QRect bounds = m_textMarkFontMetrics.boundingRect(text);
1002 
1003             m_p->painter().setFont(m_textMarkFont);
1004 
1005             int x = m_nd.left + m_nd.noteBodyWidth / 2 - bounds.width() / 2;
1006             int y = (normalMarksAreAbove ?
1007                      (m_nd.above - dy - 3) :
1008                      (m_nd.above + m_nd.noteBodyHeight + m_nd.borderY * 2 + dy + bounds.height() + 1));
1009 
1010             m_p->drawText(x, y, text);
1011             dy += bounds.height() + gap;
1012         }
1013     }
1014 
1015     if (!normalMarksAreAbove)
1016         dy = gap;
1017     if (params.m_safeVertDistance > 0) {
1018         if (normalMarksAreAbove) {
1019             dy = std::max(dy, params.m_safeVertDistance);
1020         } else {
1021             dy = params.m_safeVertDistance;
1022         }
1023     } else if (isStemmed && params.m_stemGoesUp) {
1024         dy += stemLength;
1025     }
1026 
1027     for (std::vector<Mark>::iterator i = aboveMarks.begin();
1028          i != aboveMarks.end(); ++i) {
1029 
1030         if (m_selected) m_p->painter().setPen(GUIPalette::getColour(
1031                                                 GUIPalette::SelectedElement));
1032         else if (m_shaded) m_p->painter().setPen(QColor(Qt::gray));
1033         else m_p->painter().setPen(QColor(Qt::black));
1034 
1035         if (!Marks::isFingeringMark(*i)) {
1036 
1037             int x = m_nd.left + m_nd.noteBodyWidth / 2;
1038             int y = m_nd.above - dy - 1;
1039 
1040             if (*i != Marks::TrillLine) {
1041 
1042                 NoteCharacter character(getCharacter(m_style->getMarkCharName(*i), PlainColour, false));
1043 
1044                 if (overRestHack) {
1045 
1046                     // Hmmm...  The "canvas" we can draw on is contrained by
1047                     // something somewhere else, and a fermata symbol won't fit
1048                     // on it.  After a few hours trying to pick this apart, I'm
1049                     // completely stuck.  Oh well.  I'll leave the code leading
1050                     // up to here in place in case I ever have any new
1051                     // inspiration.
1052 
1053                 } else {
1054 
1055                     x -= character.getWidth() / 2;
1056                     y -= character.getHeight();
1057 
1058                     m_p->drawNoteCharacter(x, y, character);
1059 
1060                     y += character.getHeight() / 2;
1061                     x += character.getWidth();
1062 
1063                     dy += character.getHeight() + gap;
1064                 }
1065 
1066             } else {
1067 
1068                 NoteCharacter character(getCharacter(m_style->getMarkCharName(
1069                                                          Marks::Trill), PlainColour, false));
1070                 y -= character.getHeight() / 2;
1071                 dy += character.getHeight() + gap;
1072             }
1073 
1074             if (*i == Marks::LongTrill ||
1075                 *i == Marks::TrillLine) {
1076                 NoteCharacter extension;
1077                 if (getCharacter(NoteCharacterNames::TRILL_LINE, extension,
1078                                  PlainColour, false)) {
1079                     x += extension.getHotspot().x();
1080                     while (x < m_nd.left + params.m_width - extension.getWidth()) {
1081                         x -= extension.getHotspot().x();
1082                         m_p->drawNoteCharacter(x, y, extension);
1083                         x += extension.getWidth();
1084                     }
1085                 }
1086                 if (*i == Marks::TrillLine)
1087                     dy += extension.getHeight() + gap;
1088             }
1089 
1090         } else {
1091             QString text = strtoqstr(Marks::getFingeringFromMark(*i));
1092             QRect bounds = m_fingeringFontMetrics.boundingRect(text);
1093 
1094             m_p->painter().setFont(m_fingeringFont);
1095 
1096             int x = m_nd.left + m_nd.noteBodyWidth / 2 - bounds.width() / 2;
1097             int y = m_nd.above - dy - 3;
1098 
1099             m_p->drawText(x, y, text);
1100             dy += bounds.height() + gap;
1101         }
1102     }
1103 }
1104 
1105 void
makeRoomForLegerLines(const NotePixmapParameters & params)1106 NotePixmapFactory::makeRoomForLegerLines(const NotePixmapParameters &params)
1107 {
1108     if (params.m_legerLines < 0 || params.m_restOutsideStave) {
1109         m_nd.above = std::max(m_nd.above,
1110                               (m_nd.noteBodyHeight + 1) *
1111                               ( -params.m_legerLines / 2));
1112     }
1113     if (params.m_legerLines > 0 || params.m_restOutsideStave) {
1114         m_nd.below = std::max(m_nd.below,
1115                               (m_nd.noteBodyHeight + 1) *
1116                               (params.m_legerLines / 2));
1117     }
1118     if (params.m_legerLines != 0) {
1119         m_nd.left = std::max(m_nd.left, m_nd.noteBodyWidth / 5 + 1);
1120         m_nd.right = std::max(m_nd.right, m_nd.noteBodyWidth / 5 + 1);
1121     }
1122     if (params.m_restOutsideStave) {
1123         m_nd.above += 1;
1124         m_nd.left = std::max(m_nd.left, m_nd.noteBodyWidth * 3 + 1);
1125         m_nd.right = std::max(m_nd.right, m_nd.noteBodyWidth * 3 + 1);
1126     }
1127 }
1128 
1129 void
drawLegerLines(const NotePixmapParameters & params)1130 NotePixmapFactory::drawLegerLines(const NotePixmapParameters &params)
1131 {
1132     int x0, x1, y;
1133 
1134     if (params.m_legerLines == 0)
1135         return ;
1136 
1137     if (params.m_restOutsideStave) {
1138         if (m_selected) m_p->painter().setPen(GUIPalette::getColour(
1139                                                 GUIPalette::SelectedElement));
1140         else if (m_shaded) m_p->painter().setPen(QColor(Qt::gray));
1141         else m_p->painter().setPen(QColor(Qt::black));
1142     }
1143     x0 = m_nd.left - m_nd.noteBodyWidth / 5 - 1;
1144     x1 = m_nd.left + m_nd.noteBodyWidth + m_nd.noteBodyWidth / 5 /* + 1 */;
1145 
1146     if (params.m_shifted) {
1147         if (params.m_stemGoesUp) {
1148             x0 += m_nd.noteBodyWidth;
1149             x1 += m_nd.noteBodyWidth;
1150         } else {
1151             x0 -= m_nd.noteBodyWidth;
1152             x1 -= m_nd.noteBodyWidth;
1153         }
1154     }
1155 
1156     int offset = m_nd.noteBodyHeight + getStaffLineThickness();
1157     int legerLines = params.m_legerLines;
1158     bool below = (legerLines < 0);
1159 
1160     if (below) {
1161         legerLines = -legerLines;
1162         offset = -offset;
1163     }
1164 
1165     if (params.m_restOutsideStave)
1166         y = m_nd.above;
1167     else {
1168         if (!below) { // note above staff
1169             if (legerLines % 2) { // note is between lines
1170                 y = m_nd.above + m_nd.noteBodyHeight;
1171             } else { // note is on a line
1172                 y = m_nd.above + m_nd.noteBodyHeight / 2 - getStaffLineThickness() / 2;
1173             }
1174         } else { // note below staff
1175             if (legerLines % 2) { // note is between lines
1176                 y = m_nd.above - getStaffLineThickness();
1177             } else { // note is on a line
1178                 y = m_nd.above + m_nd.noteBodyHeight / 2;
1179             }
1180         }
1181     }
1182     if (params.m_restOutsideStave) {
1183         NOTATION_DEBUG << "draw leger lines: " << legerLines << " lines, below "
1184                        << below
1185                        << ", note body height " << m_nd.noteBodyHeight
1186                        << ", thickness " << getLegerLineThickness()
1187                        << " (staff line " << getStaffLineThickness() << ")"
1188                        << ", offset " << offset;
1189     }
1190 
1191     //    NOTATION_DEBUG << "draw leger lines: " << legerLines << " lines, below "
1192     //		   << below
1193     //		   << ", note body height " << m_noteBodyHeight
1194     //		   << ", thickness " << getLegerLineThickness()
1195     //		   << " (staff line " << getStaffLineThickness() << ")"
1196     //		   << ", offset " << offset;
1197 
1198     //    bool first = true;
1199 
1200     if (getLegerLineThickness() > getStaffLineThickness()) {
1201         y -= (getLegerLineThickness() - getStaffLineThickness() + 1) / 2;
1202     }
1203 
1204     if (params.m_forceColor) {
1205         m_p->painter().save();
1206         m_p->painter().setPen(params.m_forcedColor);
1207     } else {
1208         // Sometimes needed to render segment link when an accent is under the note.
1209         //!!! Why ???
1210         if (m_shaded && !m_selected) m_p->painter().setPen(QColor(Qt::gray));
1211     }
1212 
1213     for (int i = legerLines - 1; i >= 0; --i) {
1214         if (i % 2) {
1215             //	    NOTATION_DEBUG << "drawing leger line at y = " << y;
1216             for (int j = 0; j < getLegerLineThickness(); ++j) {
1217                 m_p->drawLine(x0, y + j, x1, y + j);
1218             }
1219             y += offset;
1220             //	    if (first) {
1221             //		x0 += getStemThickness();
1222             //		x1 -= getStemThickness();
1223             //		first = false;
1224             //	    }
1225         }
1226     }
1227 
1228     if (params.m_forceColor) {
1229         m_p->painter().restore();
1230     }
1231 }
1232 
1233 void
makeRoomForStemAndFlags(int flagCount,int stemLength,const NotePixmapParameters & params,QPoint & s0,QPoint & s1)1234 NotePixmapFactory::makeRoomForStemAndFlags(int flagCount, int stemLength,
1235                                            const NotePixmapParameters &params,
1236                                            QPoint &s0, QPoint &s1)
1237 {
1238     // use the full font for this unless a grace size was supplied in ctor
1239     NoteFont *font = (m_haveGrace ? m_graceFont : m_font);
1240 
1241     // The coordinates we set in s0 and s1 are relative to (m_above, m_left)
1242 
1243     if (params.m_stemGoesUp) {
1244         m_nd.above = std::max
1245             (m_nd.above, stemLength - m_nd.noteBodyHeight / 2);
1246     } else {
1247         m_nd.below = std::max
1248             (m_nd.below, stemLength - m_nd.noteBodyHeight / 2 + 1);
1249     }
1250 
1251     if (flagCount > 0) {
1252         if (params.m_stemGoesUp) {
1253             int width = 0, height = 0;
1254             if (!font->getDimensions
1255                 (m_style->getFlagCharName(flagCount), width, height)) {
1256                 width = font->getWidth(m_style->getPartialFlagCharName(false));
1257             }
1258             m_nd.right += width;
1259         }
1260     }
1261 
1262     unsigned int stemThickness = getStemThickness();
1263 
1264     NoteStyle::HFixPoint hfix;
1265     NoteStyle::VFixPoint vfix;
1266     m_style->getStemFixPoints(params.m_noteType, hfix, vfix);
1267 
1268     switch (hfix) {
1269 
1270     case NoteStyle::Normal:
1271     case NoteStyle::Reversed:
1272         if (params.m_stemGoesUp ^ (hfix == NoteStyle::Reversed)) {
1273             s0.setX(m_nd.noteBodyWidth - stemThickness);
1274         } else {
1275             s0.setX(0);
1276         }
1277         break;
1278 
1279     case NoteStyle::Central:
1280         if (params.m_stemGoesUp ^ (hfix == NoteStyle::Reversed)) {
1281             s0.setX(m_nd.noteBodyWidth / 2 + 1);
1282         } else {
1283             s0.setX(m_nd.noteBodyWidth / 2);
1284         }
1285         break;
1286     }
1287 
1288     switch (vfix) {
1289 
1290     case NoteStyle::Near:
1291     case NoteStyle::Far:
1292         if (params.m_stemGoesUp ^ (vfix == NoteStyle::Far)) {
1293             s0.setY(0);
1294         } else {
1295             s0.setY(m_nd.noteBodyHeight);
1296         }
1297         if (vfix == NoteStyle::Near) {
1298             stemLength -= m_nd.noteBodyHeight / 2;
1299         } else {
1300             stemLength += m_nd.noteBodyHeight / 2;
1301         }
1302         break;
1303 
1304     case NoteStyle::Middle:
1305         if (params.m_stemGoesUp) {
1306             s0.setY(m_nd.noteBodyHeight * 3 / 8);
1307         } else {
1308             s0.setY(m_nd.noteBodyHeight * 5 / 8);
1309         }
1310         stemLength -= m_nd.noteBodyHeight / 8;
1311         break;
1312     }
1313 
1314     if (params.m_stemGoesUp) {
1315         s1.setY(s0.y() - stemLength + getStaffLineThickness());
1316     } else {
1317         s1.setY(s0.y() + stemLength);
1318     }
1319 
1320     s1.setX(s0.x());
1321 }
1322 
1323 void
drawFlags(int flagCount,const NotePixmapParameters & params,const QPoint &,const QPoint & s1)1324 NotePixmapFactory::drawFlags(int flagCount,
1325                              const NotePixmapParameters &params,
1326                              const QPoint &, const QPoint &s1)
1327 {
1328     if (flagCount < 1)
1329         return ;
1330 
1331     NoteCharacter flagChar;
1332     bool found;
1333     if (params.m_forceColor) {
1334         found = getCharacter(m_style->getFlagCharName(flagCount),
1335                              flagChar,
1336                              params.m_forcedColor,
1337                              !params.m_stemGoesUp);
1338     } else {
1339         found = getCharacter(m_style->getFlagCharName(flagCount),
1340                              flagChar,
1341                              PlainColour,
1342                              !params.m_stemGoesUp);
1343     }
1344 
1345     if (!found) {
1346 
1347         // Handle fonts that don't have all the flags in separate characters
1348 
1349         if (params.m_forceColor) {
1350             found = getCharacter(m_style->getPartialFlagCharName(false),
1351                                 flagChar,
1352                                 params.m_forcedColor,
1353                                 !params.m_stemGoesUp);
1354         } else {
1355             found = getCharacter(m_style->getPartialFlagCharName(false),
1356                                 flagChar,
1357                                 PlainColour,
1358                                 !params.m_stemGoesUp);
1359         }
1360 
1361         if (!found) {
1362             RG_WARNING << "Warning: NotePixmapFactory::drawFlags: No way to draw note with " << flagCount << " flags in this font!?";
1363             return ;
1364         }
1365 
1366         QPoint hotspot = flagChar.getHotspot();
1367 
1368         NoteCharacter oneFlagChar;
1369         bool foundOne;
1370         if (params.m_forceColor) {
1371             foundOne = getCharacter(m_style->getPartialFlagCharName(true),
1372                                     oneFlagChar,
1373                                     params.m_forcedColor,
1374                                     !params.m_stemGoesUp);
1375         } else {
1376             foundOne = getCharacter(m_style->getPartialFlagCharName(true),
1377                                     oneFlagChar,
1378                                     PlainColour,
1379                                     !params.m_stemGoesUp);
1380         }
1381 
1382         foundOne = flagCount > 1 ? foundOne : false;
1383 
1384 
1385         unsigned int flagSpace = m_nd.noteBodyHeight;
1386         (void)m_font->getFlagSpacing(flagSpace);
1387 
1388         for (int flag = 0; flag < flagCount; ++flag) {
1389 
1390             // use flag_1 in preference to flag_0 for the final flag, so
1391             // as to end with a flourish
1392             if (flag == flagCount - 1 && foundOne)
1393                 flagChar = oneFlagChar;
1394 
1395             int y = m_nd.above + s1.y();
1396             if (params.m_stemGoesUp)
1397                 y += flag * flagSpace;
1398             else
1399                 y -= (flag * flagSpace) + flagChar.getHeight();
1400 
1401 /*
1402   if (!m_inPrinterMethod) {
1403 
1404   m_p->end();
1405 
1406   // Super-slow
1407 
1408   PixmapFunctions::drawPixmapMasked(*m_generatedPixmap,
1409   *m_generatedMask,
1410   m_left + s1.x() - hotspot.x(),
1411   y,
1412   flagChar.getPixmap());
1413 
1414   m_p->begin(m_generatedPixmap, m_generatedMask);
1415 
1416   } else {
1417 */
1418             // No problem with mask here
1419             m_p->drawNoteCharacter(m_nd.left + s1.x() - hotspot.x(),
1420                                    y,
1421                                    flagChar);
1422 //            }
1423         }
1424 
1425     } else { // the normal case
1426 
1427         QPoint hotspot = flagChar.getHotspot();
1428 
1429         int y = m_nd.above + s1.y();
1430         if (!params.m_stemGoesUp)
1431             y -= flagChar.getHeight();
1432 
1433         m_p->drawNoteCharacter(m_nd.left + s1.x() - hotspot.x(), y, flagChar);
1434     }
1435 }
1436 
1437 void
drawStem(const NotePixmapParameters & params,const QPoint & s0,const QPoint & s1,int shortening)1438 NotePixmapFactory::drawStem(const NotePixmapParameters &params,
1439                             const QPoint &s0, const QPoint &s1,
1440                             int shortening)
1441 {
1442     if (params.m_stemGoesUp) shortening = -shortening;
1443 
1444     if (params.m_forceColor) {
1445         m_p->painter().save();
1446         m_p->painter().setPen(params.m_forcedColor);
1447     }
1448 
1449     for (int i = 0; i < getStemThickness(); ++i) {
1450         m_p->drawLine(m_nd.left + s0.x() + i, m_nd.above + s0.y(),
1451                       m_nd.left + s1.x() + i, m_nd.above + s1.y() - shortening);
1452     }
1453 
1454     if (params.m_forceColor) {
1455         m_p->painter().restore();
1456     }
1457 }
1458 
1459 void
makeRoomForBeams(const NotePixmapParameters & params)1460 NotePixmapFactory::makeRoomForBeams(const NotePixmapParameters &params)
1461 {
1462     int beamSpacing = (int)(params.m_width * params.m_gradient);
1463 
1464     if (params.m_stemGoesUp) {
1465 
1466         beamSpacing = -beamSpacing;
1467         if (beamSpacing < 0)
1468             beamSpacing = 0;
1469         m_nd.above += beamSpacing + 2;
1470 
1471         // allow a bit extra in case the h fixpoint is non-normal
1472         m_nd.right = std::max(m_nd.right, params.m_width + m_nd.noteBodyWidth);
1473 
1474     } else {
1475 
1476         if (beamSpacing < 0)
1477             beamSpacing = 0;
1478         m_nd.below += beamSpacing + 2;
1479 
1480         m_nd.right = std::max(m_nd.right, params.m_width);
1481     }
1482 }
1483 
1484 void
drawShallowLine(float x0,float y0,float x1,float y1,float thickness)1485 NotePixmapFactory::drawShallowLine(float x0, float y0, float x1, float y1,
1486                                    float thickness)
1487 {
1488     m_p->painter().save();
1489     m_p->painter().setRenderHints(QPainter::Antialiasing);
1490 
1491     m_p->painter().setPen(Qt::NoPen);
1492     if (m_selected) {
1493         m_p->painter().setBrush(GUIPalette::getColour(GUIPalette::SelectedElement));
1494     } else if (m_shaded) {
1495         m_p->painter().setBrush(Qt::gray);
1496     } else {
1497         m_p->painter().setBrush(Qt::black);
1498     }
1499 
1500     QPoint p[4];
1501     p[0] = QPoint(x0 + 0.5, y0 + 0.5);
1502     p[1] = QPoint(x1 + 1.5, y1 + 0.5);
1503     p[2] = QPoint(x1 + 1.5, y1 + thickness + 1.5);
1504     p[3] = QPoint(x0 + 0.5, y0 + thickness + 1.5);
1505     m_p->painter().drawPolygon(p, 4);
1506 
1507     m_p->painter().restore();
1508     return;
1509 }
1510 
1511 void
drawBeams(const QPoint & s1,const NotePixmapParameters & params,int beamCount)1512 NotePixmapFactory::drawBeams(const QPoint &s1,
1513                              const NotePixmapParameters &params,
1514                              int beamCount)
1515 {
1516 //    clock_t startTime = clock();
1517 
1518     // draw beams: first we draw all the beams common to both ends of
1519     // the section, then we draw beams for those that appear at the
1520     // end only
1521 
1522     int startY = m_nd.above + s1.y(), startX = m_nd.left + s1.x();
1523     int commonBeamCount = std::min(beamCount, params.m_nextBeamCount);
1524 
1525     unsigned int thickness;
1526     (void)m_font->getBeamThickness(thickness);
1527 
1528     int width = params.m_width;
1529     double grad = params.m_gradient;
1530     int spacing = getLineSpacing();
1531 
1532     int sign = (params.m_stemGoesUp ? 1 : -1);
1533 
1534     if (!params.m_stemGoesUp)
1535         startY -= thickness;
1536 
1537     if (grad > -0.01 && grad < 0.01)
1538         startY -= sign;
1539 
1540     if (m_inPrinterMethod) {
1541         startX += getStemThickness() / 2;
1542     }
1543 
1544     for (int j = 0; j < commonBeamCount; ++j) {
1545         int y = sign * j * spacing;
1546         drawShallowLine(startX, startY + y, startX + width,
1547                         startY + width*grad + y,
1548                         thickness);
1549         drawBeamsBeamCount ++;
1550     }
1551 
1552     int partWidth = width / 3;
1553     if (partWidth < 2)
1554         partWidth = 2;
1555     else if (partWidth > m_nd.noteBodyWidth)
1556         partWidth = m_nd.noteBodyWidth;
1557 
1558     if (params.m_thisPartialBeams) {
1559         for (int j = commonBeamCount; j < beamCount; ++j) {
1560             int y = sign * j * spacing;
1561             drawShallowLine(startX, startY + y, startX + partWidth,
1562                             startY + (int)(partWidth*grad) + y,
1563                             thickness);
1564             drawBeamsBeamCount ++;
1565         }
1566     }
1567 
1568     if (params.m_nextPartialBeams) {
1569         startX += width - partWidth;
1570         startY += (int)((width - partWidth) * grad);
1571 
1572         for (int j = commonBeamCount; j < params.m_nextBeamCount; ++j) {
1573             int y = sign * j * spacing;
1574             drawShallowLine(startX, startY + y, startX + partWidth,
1575                             startY + (int)(partWidth*grad) + y,
1576                             thickness);
1577             drawBeamsBeamCount ++;
1578         }
1579     }
1580 
1581 //    clock_t endTime = clock();
1582 //    drawBeamsTime += (endTime - startTime);
1583 }
1584 
1585 void
drawSlashes(const QPoint & s0,const NotePixmapParameters & params,int slashCount)1586 NotePixmapFactory::drawSlashes(const QPoint &s0,
1587                                const NotePixmapParameters &params,
1588                                int slashCount)
1589 {
1590     unsigned int thickness;
1591     (void)m_font->getBeamThickness(thickness);
1592     thickness = (slashCount == 1 && m_graceSize > 0 ? thickness / 3 : thickness * 3 / 4);
1593     if (thickness < 1)
1594         thickness = 1;
1595 
1596     int gap = thickness - 1;
1597     if (gap < 1)
1598         gap = 1;
1599 
1600     //!!! I think the thickness is coming out too heavy in this rewrite, which
1601     // is what was really swallowing the gap, but given the number of pixel
1602     // level drawing problems we already have (eg. stem location is randomly off
1603     // by one pixel on a given user's system, but not on another's) let's just
1604     // leave the thickness, and expand the gap by one.  Better too much space in
1605     // some cases than globbing it all together illegibly.
1606     gap++;
1607 
1608     int width = m_nd.noteBodyWidth * 4 / 5;
1609     int sign = (params.m_stemGoesUp ? -1 : 1);
1610 
1611     int offset =
1612         ((slashCount == 1 && m_graceSize > 0) ? m_nd.noteBodyHeight * 3 / 2 :
1613          slashCount == 1 ? m_nd.noteBodyHeight * 2 :
1614          slashCount == 2 ? m_nd.noteBodyHeight * 3 / 2 :
1615          m_nd.noteBodyHeight);
1616     int y = m_nd.above + s0.y() + sign * (offset + thickness / 2);
1617 
1618     for (int i = 0; i < slashCount; ++i) {
1619         int yoff = width / 2;
1620         drawShallowLine(m_nd.left + s0.x() - width / 2, y + yoff / 2,
1621                         m_nd.left + s0.x() + width / 2 + getStemThickness(), y - yoff / 2,
1622                         thickness);
1623         y += sign * (thickness + gap);
1624     }
1625 }
1626 
1627 void
makeRoomForTuplingLine(const NotePixmapParameters & params)1628 NotePixmapFactory::makeRoomForTuplingLine(const NotePixmapParameters &params)
1629 {
1630     int lineSpacing =
1631         (int)(params.m_tuplingLineWidth * params.m_tuplingLineGradient);
1632     int th = m_tupletCountFontMetrics.height();
1633 
1634     if (params.m_tuplingLineY < 0) {
1635 
1636         lineSpacing = -lineSpacing;
1637         if (lineSpacing < 0)
1638             lineSpacing = 0;
1639         m_nd.above = std::max(m_nd.above, -params.m_tuplingLineY + th / 2);
1640         m_nd.above += lineSpacing + 1;
1641 
1642     } else {
1643 
1644         if (lineSpacing < 0)
1645             lineSpacing = 0;
1646         m_nd.below = std::max(m_nd.below, params.m_tuplingLineY + th / 2);
1647         m_nd.below += lineSpacing + 1;
1648     }
1649 
1650     m_nd.right = std::max(m_nd.right, params.m_tuplingLineWidth);
1651 }
1652 
1653 void
drawTuplingLine(const NotePixmapParameters & params)1654 NotePixmapFactory::drawTuplingLine(const NotePixmapParameters &params)
1655 {
1656     int thickness = getStaffLineThickness() * 3 / 2;
1657     int countSpace = thickness * 2;
1658 
1659     QString count;
1660     count.setNum(params.m_tupletCount);
1661     QRect cr = m_tupletCountFontMetrics.boundingRect(count);
1662 
1663     int tlw = params.m_tuplingLineWidth;
1664     int indent = m_nd.noteBodyWidth / 2;
1665 
1666     if (tlw < (cr.width() + countSpace * 2 + m_nd.noteBodyWidth * 2)) {
1667         tlw += m_nd.noteBodyWidth - 1;
1668         indent = 0;
1669     }
1670 
1671     int w = (tlw - cr.width()) / 2 - countSpace;
1672 
1673     int startX = m_nd.left + indent;
1674     int endX = startX + w;
1675 
1676     int startY = params.m_tuplingLineY + m_nd.above + getLineSpacing() / 2;
1677     int endY = startY + (int)(params.m_tuplingLineGradient * w);
1678 
1679     if (startY == endY)
1680         ++thickness;
1681 
1682     int tickOffset = getLineSpacing() / 2;
1683     if (params.m_tuplingLineY >= 0)
1684         tickOffset = -tickOffset;
1685 
1686     //    NOTATION_DEBUG << "adjusted params.m_tuplingLineWidth = "
1687     //			 << tlw
1688     //			 << ", cr.width = " << cr.width()
1689     //			 << ", tickOffset = " << tickOffset;
1690     //    NOTATION_DEBUG << "line: (" << startX << "," << startY << ") -> ("
1691     //			 << endX << "," << endY << ")";
1692 
1693     if (!params.m_tuplingLineFollowsBeam) {
1694         m_p->drawLine(startX, startY, startX, startY + tickOffset);
1695         drawShallowLine(startX, startY, endX, endY, thickness);
1696     }
1697 
1698     // fix bug #1405: the tuplet numbers never displayed correctly when selected
1699     // or grayed, because this code snippet was always missing, I suspect
1700     if (m_selected) m_p->painter().setPen(GUIPalette::getColour(
1701                                             GUIPalette::SelectedElement));
1702     else if (m_shaded) m_p->painter().setPen(QColor(Qt::gray));
1703     else m_p->painter().setPen(QColor(Qt::black));
1704 
1705     m_p->painter().setFont(m_tupletCountFont);
1706 //    if (!m_inPrinterMethod)
1707 //        m_p->maskPainter().setFont(m_tupletCountFont);
1708 
1709     int textX = endX + countSpace;
1710     int textY = endY + cr.height() / 2;
1711     //    NOTATION_DEBUG << "text: (" << textX << "," << textY << ")";
1712 
1713     m_p->drawText(textX, textY, count);
1714 
1715     startX += tlw - w;
1716     endX = startX + w;
1717 
1718     startY += (int)(params.m_tuplingLineGradient * (tlw - w));
1719     endY = startY + (int)(params.m_tuplingLineGradient * w);
1720 
1721     //    NOTATION_DEBUG << "line: (" << startX << "," << startY << ") -> ("
1722     //			 << endX << "," << endY << ")";
1723 
1724     if (!params.m_tuplingLineFollowsBeam) {
1725         drawShallowLine(startX, startY, endX, endY, thickness);
1726         m_p->drawLine(endX, endY, endX, endY + tickOffset);
1727     }
1728 }
1729 
1730 void
drawTie(bool above,int length,int shift)1731 NotePixmapFactory::drawTie(bool above, int length, int shift)
1732 {
1733     int origLength = length;
1734 
1735     int x = m_nd.left + m_nd.noteBodyWidth + m_nd.noteBodyWidth / 4 + shift;
1736     length = origLength - m_nd.noteBodyWidth - m_nd.noteBodyWidth / 3 - shift;
1737 
1738     // if the length is short, move the tie a bit closer to both notes
1739     if (length < m_nd.noteBodyWidth*2) {
1740         x = m_nd.left + m_nd.noteBodyWidth + shift;
1741         length = origLength - m_nd.noteBodyWidth - shift;
1742     }
1743 
1744     if (length < m_nd.noteBodyWidth) {
1745         length = m_nd.noteBodyWidth;
1746     }
1747 
1748     // We can't request a smooth slur here, because that always involves
1749     // creating a new pixmap
1750 
1751     QPoint hotspot;
1752     drawSlurAux(length, 0, above, false, true, false, hotspot,
1753                 &m_p->painter(),
1754                 x,
1755                 above ? m_nd.above : m_nd.above + m_nd.noteBodyHeight);
1756 }
1757 
1758 QGraphicsItem *
makeRest(const NotePixmapParameters & params)1759 NotePixmapFactory::makeRest(const NotePixmapParameters &params)
1760 {
1761     ++makeRestsCount;
1762     Profiler profiler("NotePixmapFactory::makeRest");
1763 
1764     CharName charName(m_style->getRestCharName(params.m_noteType,
1765                                                params.m_restOutsideStave));
1766     // Check whether the font has the glyph for this charName;
1767     // if not, substitute a rest-on-stave glyph for a rest-outside-stave glyph,
1768     // and vice-versa.
1769     NoteCharacter character;
1770     if (!getCharacter(charName, character, PlainColour, false))
1771         charName = m_style->getRestCharName(params.m_noteType,
1772                                             !params.m_restOutsideStave);
1773 
1774     // bool encache = false;
1775 
1776     if (params.m_tupletCount == 0 && !m_selected && !m_shaded &&
1777         !params.m_restOutsideStave) {
1778 
1779         if (params.m_dots == 0) {
1780             if (params.m_forceColor) {
1781                 return getCharacter(charName,
1782                                     params.m_forcedColor, false).makeItem();
1783             } else {
1784                 return getCharacter(charName, PlainColour, false).makeItem();
1785             }
1786         }
1787     }
1788 
1789     QPoint hotspot(m_font->getHotspot(charName));
1790     drawRestAux(params, hotspot, nullptr, 0, 0);
1791 
1792     QGraphicsPixmapItem *canvasMap = makeItem(hotspot);
1793     return canvasMap;
1794 }
1795 
1796 void
drawRest(const NotePixmapParameters & params,QPainter & painter,int x,int y)1797 NotePixmapFactory::drawRest(const NotePixmapParameters &params,
1798                             QPainter &painter, int x, int y)
1799 {
1800     Profiler profiler("NotePixmapFactory::drawRest");
1801     m_inPrinterMethod = true;
1802     QPoint hotspot; // unused
1803     drawRestAux(params, hotspot, &painter, x, y);
1804     m_inPrinterMethod = false;
1805 }
1806 
1807 void
drawRestAux(const NotePixmapParameters & params,QPoint & hotspot,QPainter * painter,int x,int y)1808 NotePixmapFactory::drawRestAux(const NotePixmapParameters &params,
1809                                QPoint &hotspot, QPainter *painter, int x, int y)
1810 {
1811     CharName charName(m_style->getRestCharName(params.m_noteType,
1812                                                params.m_restOutsideStave));
1813 
1814     NoteCharacter character;
1815     NoteCharacter dot;
1816 
1817     if (params.m_forceColor) {
1818         NOTATION_DEBUG << "NotePixmapFactory::drawRestAux FORCE COLOR\n";
1819         character = getCharacter(charName, params.m_forcedColor, false);
1820         dot = getCharacter(NoteCharacterNames::DOT, params.m_forcedColor, false);
1821     } else {
1822         NOTATION_DEBUG << "NotePixmapFactory::drawRestAux std mode\n";
1823         character = getCharacter(charName,
1824                             params.m_quantized ? QuantizedColour : PlainColour,
1825                             false);
1826         dot = getCharacter(NoteCharacterNames::DOT, PlainColour, false);
1827     }
1828 
1829     int dotWidth = dot.getWidth();
1830     if (dotWidth < getNoteBodyWidth() / 2)
1831         dotWidth = getNoteBodyWidth() / 2;
1832 
1833     m_nd.above = m_nd.left = 0;
1834     m_nd.below = dot.getHeight() / 2; // for dotted shallow rests like semibreve
1835     m_nd.right = dotWidth / 2 + dotWidth * params.m_dots;
1836     m_nd.noteBodyWidth = character.getWidth();
1837     m_nd.noteBodyHeight = character.getHeight();
1838 
1839     if (params.m_tupletCount)
1840         makeRoomForTuplingLine(params);
1841 
1842     // we'll adjust this for tupling line after drawing rest character:
1843     hotspot = m_font->getHotspot(charName);
1844 
1845     if (params.m_restOutsideStave &&
1846         (charName == NoteCharacterNames::MULTI_REST ||
1847          charName == NoteCharacterNames::MULTI_REST_ON_STAFF)) {
1848         makeRoomForLegerLines(params);
1849     }
1850     if (painter) {
1851         painter->save();
1852         m_p->beginExternal(painter);
1853         painter->translate(x - m_nd.left, y - m_nd.above - hotspot.y());
1854     } else {
1855         createPixmap(m_nd.noteBodyWidth + m_nd.left + m_nd.right,
1856                      m_nd.noteBodyHeight + m_nd.above + m_nd.below);
1857     }
1858 
1859     m_p->drawNoteCharacter(m_nd.left, m_nd.above, character);
1860 
1861     if (params.m_tupletCount)
1862         drawTuplingLine(params);
1863 
1864     hotspot.setX(m_nd.left);
1865     hotspot.setY(m_nd.above + hotspot.y());
1866 
1867     int restY = hotspot.y() - dot.getHeight() - getStaffLineThickness();
1868     if (params.m_noteType == Note::Semibreve ||
1869         params.m_noteType == Note::Breve) {
1870         restY += getLineSpacing();
1871     }
1872 
1873     for (int i = 0; i < params.m_dots; ++i) {
1874         int x = m_nd.left + m_nd.noteBodyWidth + i * dotWidth + dotWidth / 2;
1875         m_p->drawNoteCharacter(x, restY, dot);
1876     }
1877 
1878     if (params.m_restOutsideStave &&
1879         (charName == NoteCharacterNames::MULTI_REST ||
1880          charName == NoteCharacterNames::MULTI_REST_ON_STAFF)) {
1881         drawLegerLines(params);
1882     }
1883 
1884     // a rest can have a text mark or a fermata; we permit this, so we should
1885     // draw this (fixes ancient bug report)
1886     if (params.m_marks.size() > 0) {
1887         NOTATION_DEBUG << "Groundbreaking.  We're drawing a fermata on a rest!  Or maybe a text mark!";
1888         // this next bit is the bit that isn't working yet...  we have a mark,
1889         // we find it, add it to restParams in NotationStaff, its m_marks.size()
1890         // is 1, it's passed here there and everywhere to wind up at this next
1891         // bit, which calls NoteFont looking for a FERMATA symbol, but never
1892         // draws the thing so far.
1893         drawMarks(false, params, 0, true);
1894     }
1895 
1896     if (painter) {
1897         painter->restore();
1898     }
1899 }
1900 
1901 QGraphicsPixmapItem *
makeClef(const Clef & clef,const ColourType colourType)1902 NotePixmapFactory::makeClef(const Clef &clef, const ColourType colourType)
1903 {
1904     Profiler profiler("NotePixmapFactory::makeClef");
1905 
1906     NoteCharacter plain = getCharacter(m_style->getClefCharName(clef),
1907                                        colourType, false);
1908 
1909     int oct = clef.getOctaveOffset();
1910     if (oct == 0) return plain.makeItem();
1911 
1912     // Since there was an offset, we have additional bits to draw, and must
1913     // match up the colour.  This bit is rather hacky, but I decided not to
1914     // embark on a little refactoring project to unify all of this colour
1915     // management under one consistent umbrella.
1916 
1917     // fix #1522784 and use 15 rather than 16 for double octave offset
1918     int adjustedOctave = (8 * (oct < 0 ? -oct : oct));
1919     if (adjustedOctave > 8)
1920         adjustedOctave--;
1921     else if (adjustedOctave < 8)
1922         adjustedOctave++;
1923 
1924     QString text = QString("%1").arg(adjustedOctave);
1925     int th = m_clefOttavaFontMetrics.height();
1926     int tw = m_clefOttavaFontMetrics.boundingRect(text).width();
1927     int ascent = m_clefOttavaFontMetrics.ascent();
1928 
1929     createPixmap(plain.getWidth(), plain.getHeight() + th);
1930 
1931     // The selected state is part of the ColourType enum, but it requires fluid
1932     // external control, and can't be managed in the same way as more static
1933     // colour states.  Thus we have to use m_selected to manage this case.
1934     if (m_selected) {
1935         m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
1936     } else {
1937 
1938         // the hackiest bit: we have to sync up the colour to the ColourType by
1939         // hand, because we know what these enum values really mean, but in a
1940         // cleaner world, this code here probably shouldn't have to know those
1941         // details.  Oh well.
1942         switch (colourType) {
1943         case PlainColourLight:
1944             m_p->painter().setPen(Qt::white);
1945             break;
1946 
1947         case ConflictColour:
1948             m_p->painter().setPen(Qt::red);
1949             break;
1950 
1951         case PlainColour:
1952         case QuantizedColour:
1953         case HighlightedColour:
1954         case TriggerColour:
1955         case TriggerSkipColour:
1956         case OutRangeColour:
1957         default:
1958             // fix bug with ottava marks not reflecting invisibility properly
1959             QColor plain = (m_shaded ? Qt::gray : Qt::black);
1960             m_p->painter().setPen(plain);
1961         }
1962     }
1963 
1964     m_p->drawNoteCharacter(0, oct < 0 ? 0 : th, plain);
1965 
1966     m_p->painter().setFont(m_clefOttavaFont);
1967 
1968     //!!! This fix may be turn out to be highly dependent on an individual
1969     // user's random system preferences.  Here on my system today and now, the
1970     // 8/15 above and below clef pixmaps was getting cut off.  Supplying an
1971     // adjustment of -2 and 4 yields just perfect results, but who knows how
1972     // this will turn out on somebody else's setup.
1973     m_p->drawText(plain.getWidth() / 2 - tw / 2,
1974                   ascent + (oct < 0 ? plain.getHeight() - 2 : 4), text);
1975 
1976     QPoint hotspot(plain.getHotspot());
1977     if (oct > 0) hotspot.setY(hotspot.y() + th);
1978     return makeItem(hotspot);
1979 }
1980 
1981 
1982 QGraphicsPixmapItem *
makeSymbol(const Symbol & symbol,const ColourType colourType)1983 NotePixmapFactory::makeSymbol(const Symbol &symbol, const ColourType colourType)
1984 {
1985     Profiler profiler("NotePixmapFactory::makeSymbol");
1986 
1987     NoteCharacter plain = getCharacter(m_style->getSymbolCharName(symbol),
1988                                        colourType, false);
1989     return plain.makeItem();
1990 }
1991 
1992 QGraphicsPixmapItem *
makePedalDown()1993 NotePixmapFactory::makePedalDown()
1994 {
1995     return getCharacter(NoteCharacterNames::PEDAL_MARK, PlainColour, false)
1996         .makeItem();
1997 }
1998 
1999 QGraphicsPixmapItem *
makePedalUp()2000 NotePixmapFactory::makePedalUp()
2001 {
2002     return getCharacter(NoteCharacterNames::PEDAL_UP_MARK, PlainColour, false)
2003         .makeItem();
2004 }
2005 
2006 QGraphicsPixmapItem *
makeUnknown()2007 NotePixmapFactory::makeUnknown()
2008 {
2009     Profiler profiler("NotePixmapFactory::makeUnknown");
2010     return getCharacter(NoteCharacterNames::UNKNOWN, PlainColour, false)
2011         .makeItem();
2012 }
2013 
2014 QPixmap
makeToolbarPixmap(QString name,bool menuSize)2015 NotePixmapFactory::makeToolbarPixmap(QString name, bool menuSize)
2016 {
2017     if (menuSize && !name.startsWith("menu-")) {
2018         QPixmap menuMap = makeToolbarPixmap("menu-" + name, false);
2019         if (!menuMap.isNull()) return menuMap;
2020     }
2021     return IconLoader::loadPixmap(name);
2022 }
2023 
2024 QPixmap
makeNoteMenuPixmap(timeT duration,timeT & errorReturn)2025 NotePixmapFactory::makeNoteMenuPixmap(timeT duration,
2026                                       timeT &errorReturn)
2027 {
2028     Note nearestNote = Note::getNearestNote(duration);
2029     bool triplet = false;
2030     errorReturn = 0;
2031 
2032     if (nearestNote.getDuration() != duration) {
2033         Note tripletNote = Note::getNearestNote(duration * 3 / 2);
2034         if (tripletNote.getDuration() == duration * 3 / 2) {
2035             nearestNote = tripletNote;
2036             triplet = true;
2037         } else {
2038             errorReturn = duration - nearestNote.getDuration();
2039         }
2040     }
2041 
2042     QString noteName = NotationStrings::getReferenceName(nearestNote);
2043     if (triplet) noteName = "3-" + noteName;
2044     noteName = "menu-" + noteName;
2045     return makeToolbarPixmap(noteName.toLocal8Bit().data(), true);
2046 }
2047 
2048 QPixmap
makeMarkMenuPixmap(Mark mark)2049 NotePixmapFactory::makeMarkMenuPixmap(Mark mark)
2050 {
2051     if (mark == Marks::Sforzando || mark == Marks::Rinforzando) {
2052         return makeToolbarPixmap( mark.c_str(), true );
2053     } else {
2054         NoteFont *font = nullptr;
2055         try {
2056             font = NoteFontFactory::getFont
2057                 (NoteFontFactory::getDefaultFontName(), 6);
2058         } catch (const Exception &) {
2059             font = NoteFontFactory::getFont
2060                 (NoteFontFactory::getDefaultFontName(),
2061                  NoteFontFactory::getDefaultSize(NoteFontFactory::getDefaultFontName()));
2062         }
2063         NoteCharacter character = font->getCharacter
2064             (NoteStyleFactory::getStyle(NoteStyleFactory::DefaultStyle)->
2065              getMarkCharName(mark));
2066         return character.getPixmap();
2067     }
2068 }
2069 
2070 QGraphicsPixmapItem *
makeKey(const Key & key,const Clef & clef,Key previousKey,const ColourType colourType)2071 NotePixmapFactory::makeKey(const Key &key,
2072                            const Clef &clef,
2073                            Key previousKey,
2074                            const ColourType colourType)
2075 {
2076 //    NOTATION_DEBUG << "NPF: makeKey(key: " << key.getName() << ", clef: "
2077 //              <<  clef.getClefType() << " , prevKey: "
2078 //              << previousKey.getName() <<  ", colType: " << (int)colourType
2079 //              << ")";
2080 
2081     Profiler profiler("NotePixmapFactory::makeKey");
2082 
2083     std::vector<int> ah0 = previousKey.getAccidentalHeights(clef);
2084     std::vector<int> ah1 = key.getAccidentalHeights(clef);
2085 
2086     int cancelCount = 0;
2087     if (key.isSharp() != previousKey.isSharp())
2088         cancelCount = ah0.size();
2089     else if (ah1.size() < ah0.size())
2090         cancelCount = ah0.size() - ah1.size();
2091 
2092     CharName keyCharName;
2093     if (key.isSharp()) keyCharName = NoteCharacterNames::SHARP;
2094     else keyCharName = NoteCharacterNames::FLAT;
2095 
2096     NoteCharacter keyCharacter;
2097     NoteCharacter cancelCharacter;
2098 
2099     keyCharacter = getCharacter(keyCharName, colourType, false);
2100     if (cancelCount > 0) {
2101         cancelCharacter = getCharacter(NoteCharacterNames::NATURAL, colourType, false);
2102     }
2103 
2104     int x = 0;
2105     int lw = getLineSpacing();
2106     int keyDelta = keyCharacter.getWidth() - keyCharacter.getHotspot().x();
2107 
2108     int cancelDelta = 0;
2109     int between = 0;
2110     if (cancelCount > 0) {
2111         cancelDelta = cancelCharacter.getWidth() + cancelCharacter.getWidth() / 3;
2112         between = cancelCharacter.getWidth();
2113     }
2114 
2115     createPixmap(keyDelta * ah1.size() + cancelDelta * cancelCount + between +
2116                  keyCharacter.getWidth() / 4,
2117                  lw * 8 + 1);
2118 
2119     if (key.isSharp() != previousKey.isSharp()) {
2120 
2121         // cancellation first
2122 
2123         for (int i = 0; i < cancelCount; ++i) {
2124 
2125             int h = ah0[ah0.size() - cancelCount + i];
2126             int y = (lw * 2) + ((8 - h) * lw) / 2 - cancelCharacter.getHotspot().y();
2127 
2128             m_p->drawNoteCharacter(x, y, cancelCharacter);
2129 
2130             x += cancelDelta;
2131         }
2132 
2133         if (cancelCount > 0) {
2134             x += between;
2135         }
2136     }
2137 
2138     for (unsigned int i = 0; i < ah1.size(); ++i) {
2139 
2140         int h = ah1[i];
2141         int y = (lw * 2) + ((8 - h) * lw) / 2 - keyCharacter.getHotspot().y();
2142 
2143         m_p->drawNoteCharacter(x, y, keyCharacter);
2144 
2145         x += keyDelta;
2146     }
2147 
2148     if (key.isSharp() == previousKey.isSharp()) {
2149 
2150         // cancellation afterwards
2151 
2152         if (cancelCount > 0) {
2153             x += between;
2154         }
2155 
2156         for (int i = 0; i < cancelCount; ++i) {
2157 
2158             int h = ah0[ah0.size() - cancelCount + i];
2159             int y = (lw * 2) + ((8 - h) * lw) / 2 - cancelCharacter.getHotspot().y();
2160 
2161             m_p->drawNoteCharacter(x, y, cancelCharacter);
2162 
2163             x += cancelDelta;
2164         }
2165     }
2166 
2167     return makeItem(QPoint());
2168 }
2169 
2170 QPixmap
makeClefDisplayPixmap(const Clef & clef,const ColourType colourType)2171 NotePixmapFactory::makeClefDisplayPixmap(const Clef &clef,
2172                                          const ColourType colourType)
2173 {
2174     QGraphicsPixmapItem *clefItem = makeClef(clef, colourType);
2175 
2176     int lw = getLineSpacing();
2177     int width = clefItem->pixmap().width() + 6 * getNoteBodyWidth();
2178 
2179     createPixmap(width, lw * 10 + 1);
2180 
2181     int h = clef.getAxisHeight();
2182     int y = (lw * 3) + ((8 - h) * lw) / 2;
2183     int x = 3 * getNoteBodyWidth();
2184     m_p->drawPixmap(x, y + clefItem->offset().y(), clefItem->pixmap());
2185 
2186     // error: ‘QColour’ was not declared in this scope
2187     //
2188     // Rrrrrrrrrrrrr!!!
2189 
2190     // we'll just use the colourType to figure out what color--yes, I said COLOR,
2191     // get over it you stiff-lipped Brits--to draw the lines
2192     QColor lines;
2193     switch (colourType) {
2194     case PlainColourLight:
2195         lines = Qt::white;
2196         break;
2197     case ConflictColour:
2198     case HighlightedColour:
2199     case OutRangeColour:
2200     case PlainColour:
2201     case QuantizedColour:
2202     case TriggerColour:
2203     case TriggerSkipColour:
2204     default:
2205         lines = Qt::black;
2206     }
2207 
2208     m_p->painter().setPen(lines);
2209 
2210     for (h = 0; h <= 8; h += 2) {
2211         y = (lw * 3) + ((8 - h) * lw) / 2;
2212         m_p->drawLine(x / 2, y, m_generatedWidth - x / 2 - 1, y);
2213     }
2214 
2215     delete clefItem;
2216     return makePixmap();
2217 }
2218 
2219 QPixmap
makeKeyDisplayPixmap(const Key & key,const Clef & clef,const ColourType colourType)2220 NotePixmapFactory::makeKeyDisplayPixmap(const Key &key, const Clef &clef,
2221                                         const ColourType colourType)
2222 {
2223     std::vector<int> ah = key.getAccidentalHeights(clef);
2224 
2225     CharName charName = (key.isSharp() ?
2226                          NoteCharacterNames::SHARP :
2227                          NoteCharacterNames::FLAT);
2228 
2229     QGraphicsPixmapItem *clefItem = makeClef(clef, colourType);
2230 
2231     NoteCharacter ch(getCharacter(charName, colourType, false));
2232     QPixmap accidentalPixmap = ch.getPixmap();
2233 
2234     QPoint hotspot(m_font->getHotspot(charName));
2235 
2236     int lw = getLineSpacing();
2237     int delta = accidentalPixmap.width() - hotspot.x();
2238     int maxDelta = getAccidentalWidth(Sharp);
2239     int width = clefItem->pixmap().width() + 5 * maxDelta + 7 * maxDelta;
2240     int x = clefItem->pixmap().width() + 5 * maxDelta / 2;
2241 
2242     createPixmap(width, lw * 10 + 1);
2243 
2244     int h = clef.getAxisHeight();
2245     int y = (lw * 3) + ((8 - h) * lw) / 2;
2246     m_p->drawPixmap(2 * maxDelta, y + clefItem->offset().y(), clefItem->pixmap());
2247 
2248     for (unsigned int i = 0; i < ah.size(); ++i) {
2249 
2250         h = ah[i];
2251         y = (lw * 3) + ((8 - h) * lw) / 2 - hotspot.y();
2252 
2253         m_p->drawPixmap(x, y, accidentalPixmap);
2254 
2255         x += delta;
2256     }
2257 
2258     // use the colourType to figure out what kuller to draw the lines with
2259     QColor kuller;
2260 
2261     switch (colourType) {
2262     case PlainColourLight:
2263         kuller = Qt::white;
2264         break;
2265     case ConflictColour:
2266     case HighlightedColour:
2267     case OutRangeColour:
2268     case PlainColour:
2269     case QuantizedColour:
2270     case TriggerColour:
2271     case TriggerSkipColour:
2272     default:
2273         kuller = Qt::black;
2274     }
2275 
2276     m_p->painter().setPen(kuller);
2277     m_p->painter().setBrush(kuller);
2278 
2279     for (h = 0; h <= 8; h += 2) {
2280         y = (lw * 3) + ((8 - h) * lw) / 2;
2281         m_p->drawLine(maxDelta, y, m_generatedWidth - 2*maxDelta - 1, y);
2282     }
2283 
2284     delete clefItem;
2285     return makePixmap();
2286 }
2287 
2288 int
getClefAndKeyWidth(const Key & key,const Clef & clef)2289 NotePixmapFactory::getClefAndKeyWidth(const Key &key, const Clef &clef)
2290 {
2291     std::vector<int> ah = key.getAccidentalHeights(clef);
2292     Accidental accidental = key.isSharp() ? Sharp : Flat;
2293     NoteCharacter plain = getCharacter(m_style->getClefCharName(clef),
2294                                        PlainColour, false);
2295 
2296     int clefWidth = plain.getWidth();
2297     int accWidth = getAccidentalWidth(accidental);
2298     int maxDelta = getAccidentalWidth(Sharp);
2299 
2300     int width = clefWidth + 2 * maxDelta + ah.size() * accWidth;
2301 
2302     return width;
2303 }
2304 
2305 int
getTrackHeaderNTL(int height)2306 NotePixmapFactory::getTrackHeaderNTL(int height)
2307 {
2308     int clefMaxHeight = 12 * getLineSpacing();
2309     int textLineHeight = getTrackHeaderTextLineSpacing();
2310     int numberOfLines = ((height - clefMaxHeight) / 2) / textLineHeight;
2311     return (numberOfLines > 0) ? numberOfLines : 1;
2312 }
2313 
2314 int
getTrackHeaderTextWidth(QString str)2315 NotePixmapFactory::getTrackHeaderTextWidth(QString str)
2316 {
2317     QRect bounds = m_trackHeaderFontMetrics.boundingRect(str);
2318     return bounds.width();
2319 }
2320 
2321 int
getTrackHeaderTextLineSpacing()2322 NotePixmapFactory::getTrackHeaderTextLineSpacing()
2323 {
2324     // 3/2 is some arbitrary line spacing
2325     return m_trackHeaderFont.pixelSize() * 3 / 2;
2326 }
2327 
2328 QString
getOneLine(QString & text,int width)2329 NotePixmapFactory::getOneLine(QString &text, int width)
2330 {
2331     QString str;
2332     int n;
2333 
2334     // Immediately stop if string is empty or only contains white spaces ...
2335     if (text.trimmed().isEmpty()) return QString("");
2336 
2337     // ... or if width is too small.
2338     if (width < m_trackHeaderFontMetrics.boundingRect(text.left(1)).width())
2339         return QString("");
2340 
2341     // Get a first approx. string length
2342     int totalLength = text.length();
2343     n = totalLength * width / getTrackHeaderTextWidth(text) + 1;
2344     if (n > totalLength) n = totalLength;
2345 
2346     // Verify string size is less than width then correct it if necessary
2347     while (((getTrackHeaderTextWidth(text.left(n))) > width) && n) n--;
2348 
2349     if (n == 0) {
2350         str = text;
2351         text = QString("");
2352     } else {
2353         str = text.left(n);
2354         text.remove(0, n);
2355     }
2356 
2357     return str;
2358 }
2359 
2360 QPixmap
makePitchDisplayPixmap(int p,const Clef & clef,bool useSharps,const ColourType colourType)2361 NotePixmapFactory::makePitchDisplayPixmap(int p, const Clef &clef,
2362                                           bool useSharps,
2363                                           const ColourType colourType)
2364 {
2365     NotationRules rules;
2366 
2367     Pitch pitch(p);
2368     Accidental accidental(pitch.getAccidental(useSharps));
2369     NotePixmapParameters params(Note::Crotchet, 0, accidental);
2370 
2371     QGraphicsPixmapItem *clefItem = makeClef(clef, colourType);
2372 
2373     int lw = getLineSpacing();
2374     int width = getClefWidth(Clef::Bass) + 10 * getNoteBodyWidth();
2375 
2376     int h = pitch.getHeightOnStaff(clef, useSharps);
2377     params.setStemGoesUp(rules.isStemUp(h));
2378 
2379     if (h < -1)
2380         params.setStemLength(lw * (4 - h) / 2);
2381     else if (h > 9)
2382         params.setStemLength(lw * (h - 4) / 2);
2383     if (h > 8)
2384         params.setLegerLines(h - 8);
2385     else if (h < 0)
2386         params.setLegerLines(h);
2387 
2388     params.setIsOnLine(h % 2 == 0);
2389     params.setSelected(m_selected);
2390 
2391     // use the colourType to figure out what kuller to draw the lines and note
2392     // with
2393     QColor kuller;
2394 
2395     switch (colourType) {
2396     case PlainColourLight:
2397         kuller = Qt::white;
2398         break;
2399     case ConflictColour:
2400     case HighlightedColour:
2401     case OutRangeColour:
2402     case PlainColour:
2403     case QuantizedColour:
2404     case TriggerColour:
2405     case TriggerSkipColour:
2406     default:
2407         kuller = Qt::black;
2408     }
2409     int hue, saturation, value;
2410     kuller.getHsv(&hue, &saturation, &value);
2411 
2412     // I can't think of any real use for the ability to draw all notation in
2413     // white, and given the complexity of adding that ability to all the various
2414     // bits and bobs called upon to draw a note, what we're going to do instead
2415     // is draw a conventional black note with unaltered bits and bobs, and then
2416     // recolour the resulting pixmap for this dialog-oriented display method
2417 
2418     QGraphicsPixmapItem *noteItem = makeNotePixmapItem(params);
2419     QPixmap colouredNote(PixmapFunctions::colourPixmap(noteItem->pixmap(), hue, value, saturation));
2420     noteItem->setPixmap(colouredNote);
2421 
2422     int pixmapHeight = lw * 12 + 1;
2423     int yoffset = lw * 3;
2424     if (h > 12) {
2425         pixmapHeight += 6 * lw;
2426         yoffset += 6 * lw;
2427     } else if (h < -4) {
2428         pixmapHeight += 6 * lw;
2429     }
2430 
2431     createPixmap(width, pixmapHeight);
2432 
2433     m_p->painter().setPen(kuller);
2434     m_p->painter().setBrush(kuller);
2435 
2436     int x =
2437         getClefWidth(Clef::Bass) + 5 * getNoteBodyWidth() -
2438         getAccidentalWidth(accidental);
2439     int y = yoffset + ((8 - h) * lw) / 2 + noteItem->offset().y();
2440     m_p->drawPixmap(x, y, noteItem->pixmap());
2441 
2442     h = clef.getAxisHeight();
2443     x = 3 * getNoteBodyWidth();
2444     y = yoffset + ((8 - h) * lw) / 2;
2445     m_p->drawPixmap(x, y + clefItem->offset().y(), clefItem->pixmap());
2446 
2447     m_p->painter().setPen(kuller);
2448     m_p->painter().setBrush(kuller);
2449 
2450     for (h = 0; h <= 8; h += 2) {
2451         y = yoffset + ((8 - h) * lw) / 2;
2452         m_p->drawLine(x / 2, y, m_generatedWidth - x / 2, y);
2453     }
2454 
2455     delete noteItem;
2456     delete clefItem;
2457 
2458     return makePixmap();
2459 }
2460 
2461 QPixmap
makePitchDisplayPixmap(int p,const Clef & clef,int octave,int step,const ColourType colourType)2462 NotePixmapFactory::makePitchDisplayPixmap(int p, const Clef &clef,
2463                                           int octave, int step,
2464                                           const ColourType colourType)
2465 {
2466     NotationRules rules;
2467 
2468     Pitch pitch(step, octave, p, 0);
2469     Accidental accidental = pitch.getDisplayAccidental(Key("C major"));
2470     NotePixmapParameters params(Note::Crotchet, 0, accidental);
2471 
2472     QGraphicsPixmapItem *clefItem = makeClef(clef, colourType);
2473 
2474     int lw = getLineSpacing();
2475     int width = getClefWidth(Clef::Bass) + 10 * getNoteBodyWidth();
2476 
2477     int h = pitch.getHeightOnStaff
2478         (clef,
2479          Key("C major"));
2480     params.setStemGoesUp(rules.isStemUp(h));
2481 
2482     if (h < -1)
2483         params.setStemLength(lw * (4 - h) / 2);
2484     else if (h > 9)
2485         params.setStemLength(lw * (h - 4) / 2);
2486     if (h > 8)
2487         params.setLegerLines(h - 8);
2488     else if (h < 0)
2489         params.setLegerLines(h);
2490 
2491     params.setIsOnLine(h % 2 == 0);
2492     params.setSelected(m_selected);
2493 
2494     // use the colourType to figure out what kuller to draw the lines and note
2495     // with
2496     QColor kuller;
2497 
2498     switch (colourType) {
2499     case PlainColourLight:
2500         kuller = Qt::white;
2501         break;
2502     case ConflictColour:
2503     case HighlightedColour:
2504     case OutRangeColour:
2505     case PlainColour:
2506     case QuantizedColour:
2507     case TriggerColour:
2508     case TriggerSkipColour:
2509     default:
2510         kuller = Qt::black;
2511     }
2512     int hue, saturation, value;
2513     kuller.getHsv(&hue, &saturation, &value);
2514 
2515     //!!! NOTE: I started this white on gray notation for dialogs thing on a
2516     // whim, and I tore it down bit by bit doing everything else before arriving
2517     // here in the "draw the notes" code.  The "draw the notes" code was so
2518     // involved to doctor that I sensibly just used the existing code unaltered,
2519     // and colored the resulting pixmap.  This suggests that it might be more
2520     // consistent to use this approach everywhere else, instead of passing
2521     // around an optional ColourType here, but not there, and then again over
2522     // here.  It's really a mixed bag which methods got the optional parameter,
2523     // and which ones didn't, whittled down bit by bit solving the individual
2524     // problems of making notation displayed in dialog contexts draw itself in
2525     // white.  Now that I can see the forest for the trees, this annoys me, and
2526     // I vow to fix all of this one day soon.  But not today.
2527     //
2528     // Years later, and still not today!  dmm
2529     //!!!
2530 
2531     // I can't think of any real use for the ability to draw all notation in
2532     // white, and given the complexity of adding that ability to all the various
2533     // bits and bobs called upon to draw a note, what we're going to do instead
2534     // is draw a conventional black note with unaltered bits and bobs, and then
2535     // recolour the resulting pixmap for this dialog-oriented display method
2536     QGraphicsPixmapItem *noteItem = makeNotePixmapItem(params);
2537     QPixmap colouredNote(PixmapFunctions::colourPixmap(noteItem->pixmap(), hue, value, saturation));
2538     noteItem->setPixmap(colouredNote);
2539 
2540     int pixmapHeight = lw * 12 + 1;
2541     int yoffset = lw * 3;
2542     if (h > 12) {
2543         pixmapHeight += 6 * lw;
2544         yoffset += 6 * lw;
2545     } else if (h < -4) {
2546         pixmapHeight += 6 * lw;
2547     }
2548 
2549     createPixmap(width, pixmapHeight);
2550     m_p->painter().setPen(kuller);
2551     m_p->painter().setBrush(kuller);
2552 
2553 
2554     int x =
2555         getClefWidth(Clef::Bass) + 5 * getNoteBodyWidth() -
2556         getAccidentalWidth(accidental);
2557     int y = yoffset + ((8 - h) * lw) / 2 + noteItem->offset().y();
2558     m_p->drawPixmap(x, y, noteItem->pixmap());
2559 
2560     h = clef.getAxisHeight();
2561     x = 3 * getNoteBodyWidth();
2562     y = yoffset + ((8 - h) * lw) / 2;
2563     m_p->drawPixmap(x, y + clefItem->offset().y(), clefItem->pixmap());
2564 
2565     m_p->painter().setPen(kuller);
2566     m_p->painter().setBrush(kuller);
2567 
2568     for (h = 0; h <= 8; h += 2) {
2569         y = yoffset + ((8 - h) * lw) / 2;
2570         m_p->drawLine(x / 2, y, m_generatedWidth - x / 2, y);
2571     }
2572 
2573     delete noteItem;
2574     delete clefItem;
2575 
2576     return makePixmap();
2577 }
2578 
2579 QGraphicsPixmapItem *
makeHairpin(int length,bool isCrescendo)2580 NotePixmapFactory::makeHairpin(int length, bool isCrescendo)
2581 {
2582     Profiler profiler("NotePixmapFactory::makeHairpin");
2583     drawHairpinAux(length, isCrescendo, nullptr, 0, 0);
2584     return makeItem(QPoint(0, m_generatedHeight / 2));
2585 }
2586 
2587 void
drawHairpin(int length,bool isCrescendo,QPainter & painter,int x,int y)2588 NotePixmapFactory::drawHairpin(int length, bool isCrescendo,
2589                                QPainter &painter, int x, int y)
2590 {
2591     Profiler profiler("NotePixmapFactory::drawHairpin");
2592     m_inPrinterMethod = true;
2593     drawHairpinAux(length, isCrescendo, &painter, x, y);
2594     m_inPrinterMethod = false;
2595 }
2596 
2597 void
drawHairpinAux(int length,bool isCrescendo,QPainter * painter,int x,int y)2598 NotePixmapFactory::drawHairpinAux(int length, bool isCrescendo,
2599                                   QPainter *painter, int x, int y)
2600 {
2601     int nbh = getNoteBodyHeight();
2602     int nbw = getNoteBodyWidth();
2603 
2604     int height = (int)(((double)nbh / (double)(nbw * 40)) * length) + nbh;
2605     int thickness = getStaffLineThickness() * 3 / 2;
2606 
2607     //    NOTATION_DEBUG << "NotePixmapFactory::makeHairpinPixmap: mapped length " << length << " to height " << height << " (nbh = " << nbh << ", nbw = " << nbw << ")";
2608 
2609     if (height < nbh)
2610         height = nbh;
2611     if (height > nbh*2)
2612         height = nbh * 2;
2613 
2614     height += thickness - 1;
2615 
2616     if (painter) {
2617         painter->save();
2618         m_p->beginExternal(painter);
2619         painter->translate(x, y - height / 2);
2620     } else {
2621         createPixmap(length, height);
2622     }
2623 
2624     if (m_selected) {
2625         m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
2626     }
2627 
2628     int left = 1, right = length - 2 * nbw / 3 + 1;
2629 
2630     if (isCrescendo) {
2631         drawShallowLine(left, height / 2 - 1,
2632                         right, height - thickness - 1, thickness);
2633         drawShallowLine(left, height / 2 - 1, right, 0, thickness);
2634     } else {
2635         drawShallowLine(left, 0, right, height / 2 - 1, thickness);
2636         drawShallowLine(left, height - thickness - 1,
2637                         right, height / 2 - 1, thickness);
2638     }
2639 
2640     m_p->painter().setPen(QColor(Qt::black));
2641 
2642     if (painter) {
2643         painter->restore();
2644     }
2645 }
2646 
2647 QGraphicsPixmapItem *
makeSlur(int length,int dy,bool above,bool phrasing)2648 NotePixmapFactory::makeSlur(int length, int dy, bool above, bool phrasing)
2649 {
2650     Profiler profiler("NotePixmapFactory::makeSlur");
2651 
2652     //!!! could remove "height > 5" requirement if we did a better job of
2653     // sizing so that any horizontal part was rescaled down to exactly
2654     // 1 pixel wide instead of blurring
2655     bool smooth = m_font->isSmooth() && getNoteBodyHeight() > 5;
2656     QPoint hotspot;
2657     if (length < getNoteBodyWidth()*2)
2658         length = getNoteBodyWidth() * 2;
2659     drawSlurAux(length, dy, above, smooth, false, phrasing, hotspot, nullptr, 0, 0);
2660 
2661     m_p->end();
2662 
2663     if (smooth) {
2664 
2665         QImage i = m_generatedPixmap->toImage();
2666         if (i.depth() == 1) i = i.convertToFormat(QImage::Format_ARGB32);
2667         i = i.scaled(i.width() / 2, i.height() / 2, Qt::KeepAspectRatio, Qt::SmoothTransformation);
2668 
2669         delete m_generatedPixmap;
2670 //        delete m_generatedMask;
2671         QPixmap newPixmap = QPixmap::fromImage(i);
2672         QGraphicsPixmapItem *p = new QGraphicsPixmapItem(newPixmap);
2673         p->setOffset(QPointF(-hotspot.x(), -hotspot.y()));
2674 //!!!        p->setMask(PixmapFunctions::generateMask(newPixmap,
2675 //                   QColor(Qt::white).rgb()));
2676         return p;
2677 
2678     } else {
2679 
2680         QGraphicsPixmapItem *p = new QGraphicsPixmapItem(*m_generatedPixmap);
2681         p->setOffset(QPointF(-hotspot.x(), -hotspot.y()));
2682 //!!!        p->setMask(PixmapFunctions::generateMask(*m_generatedPixmap,
2683 //                   QColor(Qt::white).rgb()));
2684         delete m_generatedPixmap;
2685 //        delete m_generatedMask;
2686         return p;
2687     }
2688 }
2689 
2690 void
drawSlur(int length,int dy,bool above,bool phrasing,QPainter & painter,int x,int y)2691 NotePixmapFactory::drawSlur(int length, int dy, bool above, bool phrasing,
2692                             QPainter &painter, int x, int y)
2693 {
2694     Profiler profiler("NotePixmapFactory::drawSlur");
2695     QPoint hotspot;
2696     m_inPrinterMethod = true;
2697     if (length < getNoteBodyWidth()*2)
2698         length = getNoteBodyWidth() * 2;
2699     drawSlurAux(length, dy, above, false, false, phrasing, hotspot, &painter, x, y);
2700     m_inPrinterMethod = false;
2701 }
2702 
2703 void
drawSlurAux(int length,int dy,bool above,bool smooth,bool flat,bool phrasing,QPoint & hotspot,QPainter * painter,int x,int y)2704 NotePixmapFactory::drawSlurAux(int length, int dy, bool above,
2705                                bool smooth, bool flat, bool phrasing,
2706                                QPoint &hotspot, QPainter *painter, int x, int y)
2707 {
2708 //     QMatrix::TransformationMode mode = QMatrix::transformationMode();	//&&&
2709 //     QMatrix::setTransformationMode(QMatrix::Points);
2710 
2711     int thickness = getStaffLineThickness() * 2;
2712     if (phrasing)
2713         thickness = thickness * 3 / 4;
2714     int nbh = getNoteBodyHeight(), nbw = getNoteBodyWidth();
2715 
2716     // Experiment with rotating the painter rather than the control points.
2717     double theta = 0;
2718     bool rotate = false;
2719     if (dy != 0) {
2720         // We have opposite (dy) and adjacent (length).
2721         theta = atan(double(dy) / double(length)) * 180.0 / M_PI;
2722         //	NOTATION_DEBUG << "slur: dy is " << dy << ", length " << length << ", rotating through " << theta;
2723         rotate = true;
2724     }
2725 
2726     // draw normal slur for very slopey phrasing slur:
2727     if (theta < -5 || theta > 5)
2728         phrasing = false;
2729 
2730     int y0 = 0, my = 0;
2731 
2732     float noteLengths = float(length) / nbw;
2733     if (noteLengths < 1)
2734         noteLengths = 1;
2735 
2736     my = int(0 - nbh * sqrt(noteLengths) / 2);
2737     if (flat) my = my * 2 / 3;
2738     else if (phrasing) my = my * 3 / 4;
2739     if (!above) my = -my;
2740 
2741     bool havePixmap = false;
2742     QPoint topLeft, bottomRight;
2743 
2744     if (smooth) thickness += 2;
2745 
2746     for (int i = 0; i < thickness; ++i) {
2747 
2748         //!!! use QPainterPath::cubicTo
2749 
2750         Spline::PointList pl;
2751 
2752         if (!phrasing) {
2753             pl.push_back(QPoint(length / 6, my));
2754             pl.push_back(QPoint(length - length / 6, my));
2755         } else {
2756             pl.push_back(QPoint(abs(my) / 4, my / 3));
2757             pl.push_back(QPoint(length / 6, my));
2758 
2759             if (theta > 1) {
2760                 pl.push_back(QPoint(length * 3 / 8, my * 3 / 2));
2761             } else if (theta < -1) {
2762                 pl.push_back(QPoint(length * 5 / 8, my * 3 / 2));
2763             } else {
2764                 pl.push_back(QPoint(length / 2, my * 4 / 3));
2765             }
2766 
2767             pl.push_back(QPoint(length - length / 6, my));
2768             pl.push_back(QPoint(length - abs(my) / 4, my / 3));
2769         }
2770 
2771         Spline::PointList *polyPoints = Spline::calculate
2772             (QPoint(0, y0), QPoint(length - 1, y0), pl, topLeft, bottomRight);
2773 
2774         if (!havePixmap) {
2775             int width = bottomRight.x() - topLeft.x();
2776             int height = bottomRight.y() - topLeft.y() + thickness - 1 + abs(dy);
2777             hotspot = QPoint(0, -topLeft.y() + (dy < 0 ? -dy : 0));
2778 
2779             //	    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;
2780 
2781             if (painter) {
2782 
2783                 // This conditional is because we're also called with
2784                 // a painter arg from non-printer drawTie.  It's a big
2785                 // hack.
2786 
2787                 if (m_inPrinterMethod) {
2788                     painter->save();
2789                     m_p->beginExternal(painter);
2790                     painter->translate(x, y);
2791                     if (rotate) {
2792                         painter->rotate(theta);
2793                     }
2794                 } else {
2795                     m_p->painter().save();
2796                     m_p->painter().translate(x, y);
2797                     if (rotate) {
2798                         m_p->painter().rotate(theta);
2799                     }
2800                 }
2801 
2802             } else {
2803                 createPixmap(smooth ? width*2 + 1 : width,
2804                              smooth ? height*2 + thickness*2 : height + thickness);
2805 
2806                 QTransform m;
2807                 if (smooth) {
2808                     m.translate(2 * hotspot.x(), 2 * hotspot.y());
2809                 } else {
2810                     m.translate(hotspot.x(), hotspot.y());
2811                 }
2812                 m.rotate(theta);
2813                 m_p->painter().setWorldTransform(m);
2814             }
2815 
2816             if (m_selected)
2817                 m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
2818             else if (m_shaded) {
2819                 m_p->painter().setPen(QColor(Qt::gray));
2820             }
2821             havePixmap = true;
2822         }
2823         /*
2824           for (int j = 0; j < pl.size(); ++j) {
2825           if (smooth) {
2826           m_p->drawPoint(pl[j].x()*2, pl[j].y()*2);
2827           } else {
2828           m_p->drawPoint(pl[j].x(), pl[j].y());
2829           }
2830           }
2831         */
2832         int ppc = polyPoints->size();
2833         QPolygon qp(ppc);
2834 
2835         for (int j = 0; j < ppc; ++j) {
2836             qp.setPoint(j, (*polyPoints)[j].x(), (*polyPoints)[j].y());
2837         }
2838 
2839         delete polyPoints;
2840 
2841         if (!smooth || (i > 0 && i < thickness - 1)) {
2842             if (smooth) {
2843                 for (int j = 0; j < ppc; ++j) {
2844                     qp.setPoint(j, qp.point(j).x()*2, qp.point(j).y()*2);
2845                 }
2846                 m_p->drawPolyline(qp);
2847                 for (int j = 0; j < ppc; ++j) {
2848                     qp.setPoint(j, qp.point(j).x(), qp.point(j).y() + 1);
2849                 }
2850                 m_p->drawPolyline(qp);
2851             } else {
2852                 m_p->drawPolyline(qp);
2853             }
2854         }
2855 
2856         if (above) {
2857             ++my;
2858             if (i % 2)
2859                 ++y0;
2860         } else {
2861             --my;
2862             if (i % 2)
2863                 --y0;
2864         }
2865     }
2866 
2867     if (m_selected) {
2868         m_p->painter().setPen(QColor(Qt::black));
2869     }
2870 
2871 //     QMatrix::setTransformationMode(mode);	//&&&
2872 
2873     if (painter) {
2874         painter->restore();
2875 //        if (!m_inPrinterMethod)
2876 //            m_p->maskPainter().restore();
2877     }
2878 }
2879 
2880 QGraphicsPixmapItem *
makeOttava(int length,int octavesUp)2881 NotePixmapFactory::makeOttava(int length, int octavesUp)
2882 {
2883     Profiler profiler("NotePixmapFactory::makeOttava");
2884     m_inPrinterMethod = false;
2885     drawOttavaAux(length, octavesUp, nullptr, 0, 0);
2886     return makeItem(QPoint(0, m_generatedHeight - 1));
2887 }
2888 
2889 void
drawOttava(int length,int octavesUp,QPainter & painter,int x,int y)2890 NotePixmapFactory::drawOttava(int length, int octavesUp,
2891                               QPainter &painter, int x, int y)
2892 {
2893     Profiler profiler("NotePixmapFactory::drawOttava");
2894     m_inPrinterMethod = true;
2895     drawOttavaAux(length, octavesUp, &painter, x, y);
2896     m_inPrinterMethod = false;
2897 }
2898 
2899 void
drawOttavaAux(int length,int octavesUp,QPainter * painter,int x,int y)2900 NotePixmapFactory::drawOttavaAux(int length, int octavesUp,
2901                                  QPainter *painter, int x, int y)
2902 {
2903     int height = m_ottavaFontMetrics.height();
2904     int backpedal = 0;
2905     QString label;
2906     QRect r;
2907 
2908     if (octavesUp == 2 || octavesUp == -2) {
2909         if (octavesUp == 2) label = "15ma  ";
2910         else label = "15mb  ";
2911         backpedal = m_ottavaFontMetrics.boundingRect("15").width() / 2;
2912     } else {
2913         if (octavesUp == 1) label = "8va  ";
2914         else label = "8vb  ";
2915         backpedal = m_ottavaFontMetrics.boundingRect("8").width() / 2;
2916     }
2917 
2918     int width = length + backpedal;
2919 
2920     if (painter) {
2921         painter->save();
2922         m_p->beginExternal(painter);
2923         painter->translate(x - backpedal, y - height);
2924     } else {
2925         NOTATION_DEBUG << "NotePixmapFactory::drawOttavaAux: making pixmap and mask " << width << "x" << height;
2926         createPixmap(width, height);
2927     }
2928 
2929     int thickness = getStemThickness();
2930     QPen pen(QColor(Qt::black), thickness, Qt::DotLine);
2931 
2932     if (m_selected) {
2933         m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
2934         pen.setColor(GUIPalette::getColour(GUIPalette::SelectedElement));
2935     } else if (m_shaded) {
2936         m_p->painter().setPen(QColor(Qt::gray));
2937         pen.setColor(QColor(Qt::gray));
2938     }
2939 
2940     m_p->painter().setFont(m_ottavaFont);
2941 //    if (!m_inPrinterMethod)
2942 //        m_p->maskPainter().setFont(m_ottavaFont);
2943 
2944     m_p->drawText(0, m_ottavaFontMetrics.ascent(), label);
2945 
2946     m_p->painter().setPen(pen);
2947     //    if (!m_inPrinterMethod) m_p->maskPainter().setPen(pen);
2948 
2949     int x0 = m_ottavaFontMetrics.boundingRect(label).width() + thickness;
2950     int x1 = width - thickness;
2951     int y0 = m_ottavaFontMetrics.ascent() * 2 / 3 - thickness / 2;
2952     int y1 = (octavesUp < 0 ? 0 : m_ottavaFontMetrics.ascent());
2953 
2954     NOTATION_DEBUG << "NotePixmapFactory::drawOttavaAux: drawing " << x0 << "," << y0 << " to " << x1 << "," << y0 << ", thickness " << thickness;
2955 
2956     m_p->drawLine(x0, y0, x1, y0);
2957 
2958     pen.setStyle(Qt::SolidLine);
2959     m_p->painter().setPen(pen);
2960     //    if (!m_inPrinterMethod) m_p->maskPainter().setPen(pen);
2961 
2962     NOTATION_DEBUG << "NotePixmapFactory::drawOttavaAux: drawing " << x1 << "," << y0 << " to " << x1 << "," << y1 << ", thickness " << thickness;
2963 
2964     m_p->drawLine(x1, y0, x1, y1);
2965 
2966     m_p->painter().setPen(QPen());
2967 //    if (!m_inPrinterMethod)
2968 //        m_p->maskPainter().setPen(QPen());
2969 
2970     if (painter) {
2971         painter->restore();
2972     }
2973 }
2974 
2975 QGraphicsPixmapItem *
makeTrillLine(int length)2976 NotePixmapFactory::makeTrillLine(int length)
2977 {
2978     drawTrillLineAux(length, nullptr, 0, 0);
2979     return makeItem(QPoint(0, m_generatedHeight / 2));
2980 }
2981 
2982 void
drawTrillLineAux(int length,QPainter * painter,int x,int y)2983 NotePixmapFactory::drawTrillLineAux(int length, QPainter *painter, int x, int y)
2984 {
2985     // arbitrary gap calculation from elsewhere
2986     int gap = m_nd.noteBodyHeight / 5 + 1;
2987 
2988     // make a character for the tr bit first, so we can use its height
2989     NoteCharacter character(getCharacter(m_style->getMarkCharName(Marks::Trill),
2990                                          PlainColour, false));
2991     int height = character.getHeight();
2992 
2993     // create a pixmap of a suitable width and height to contain our tr~~~
2994     //
2995     // I think the "if (painter)" bit is a block-copied hold-over that shouldn't
2996     // be necessary in new code, but I'm not completely sure.  A lot of my
2997     // drawing-related code is still lucky guesswork more than method
2998     if (painter) {
2999         painter->save();
3000         m_p->beginExternal(painter);
3001         painter->translate(x, y - height / 2);
3002     } else {
3003         createPixmap(length, height * 2);
3004     }
3005 
3006     if (m_selected) {
3007         m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
3008     }
3009 
3010     // draw the tr
3011     m_p->drawNoteCharacter(x, y, character);
3012     // increment x so we start drawing after the tr; 3 is an arbitrary figure
3013     // that seems about right, but I'm only testing with one font at one size
3014     x += character.getWidth() + gap;
3015 
3016 
3017     // make a character for the /\/\/\/ bit, and keep drawing it at spaced
3018     // increments until one glyph width just before running out of space, to
3019     // avoid clipping the last glyph
3020     NoteCharacter extension;
3021     if (getCharacter(NoteCharacterNames::TRILL_LINE, extension,
3022                      PlainColour, false)) {
3023         x += extension.getHotspot().x();
3024 
3025         // align the /\/\ bit with the center of the tr bit; assumes the tr bit
3026         // is always taller
3027         y = ((character.getHeight() - extension.getHeight()) / 2 );
3028 
3029         while (x < (length - extension.getWidth())) {
3030             x -= extension.getHotspot().x();
3031             m_p->drawNoteCharacter(x, y, extension);
3032             x += extension.getWidth();
3033         }
3034     }
3035 
3036     m_p->painter().setPen(QColor(Qt::black));
3037 
3038     if (painter) {
3039         painter->restore();
3040     }
3041 }
3042 
3043 void
drawBracket(int length,bool left,bool,int x,int y)3044 NotePixmapFactory::drawBracket(int length, bool left, bool /*curly*/, int x, int y)
3045 {
3046     // curly mode not yet implemented
3047 
3048     int thickness = getStemThickness() * 2;
3049 
3050     int m1 = length / 6;
3051     int m2 = length - length / 6 - 1;
3052 
3053     int off0 = 0, moff = 0;
3054 
3055     int nbh = getNoteBodyHeight(), nbw = getNoteBodyWidth();
3056     float noteLengths = float(length) / nbw;
3057     if (noteLengths < 1)
3058         noteLengths = 1;
3059     moff = int(nbh * sqrt(noteLengths) / 2);
3060     moff = moff * 2 / 3;
3061 
3062     if (left)
3063         moff = -moff;
3064 
3065     QPoint topLeft, bottomRight;
3066 
3067     for (int i = 0; i < thickness; ++i) {
3068 
3069         Spline::PointList pl;
3070         pl.push_back(QPoint((int)moff, m1));
3071         pl.push_back(QPoint((int)moff, m2));
3072         /*
3073           NOTATION_DEBUG << "bracket spline controls: " << moff << "," << m1
3074           << ", " << moff << "," << m2 << "; end points "
3075           << off0 << ",0, " << off0 << "," << length-1
3076          ;
3077         */
3078         Spline::PointList *polyPoints = Spline::calculate
3079             (QPoint(off0, 0), QPoint(off0, length - 1), pl, topLeft, bottomRight);
3080 
3081         int ppc = polyPoints->size();
3082         QPolygon qp(ppc);
3083         /*
3084           NOTATION_DEBUG << "bracket spline polypoints: ";
3085           for (int j = 0; j < ppc; ++j) {
3086           NOTATION_DEBUG << (*polyPoints)[j].x() << "," << (*polyPoints)[j].y();
3087           }
3088         */
3089 
3090         for (int j = 0; j < ppc; ++j) {
3091             qp.setPoint(j, x + (*polyPoints)[j].x(), y + (*polyPoints)[j].y());
3092         }
3093 
3094         delete polyPoints;
3095 
3096         m_p->drawPolyline(qp);
3097 
3098         if (!left) {
3099             ++moff;
3100             if (i % 2)
3101                 ++off0;
3102         } else {
3103             --moff;
3104             if (i % 2)
3105                 --off0;
3106         }
3107     }
3108 }
3109 
3110 QGraphicsPixmapItem *
makeTimeSig(const TimeSignature & sig)3111 NotePixmapFactory::makeTimeSig(const TimeSignature& sig)
3112 {
3113     Profiler profiler("NotePixmapFactory::makeTimeSig");
3114 
3115     if (sig.isCommon()) {
3116 
3117         NoteCharacter character;
3118 
3119         CharName charName;
3120         if (sig.getNumerator() == 2) {
3121             charName = NoteCharacterNames::CUT_TIME;
3122         } else {
3123             charName = NoteCharacterNames::COMMON_TIME;
3124         }
3125 
3126         if (getCharacter(charName, character, PlainColour, false)) {
3127             createPixmap(character.getWidth(), character.getHeight());
3128             m_p->drawNoteCharacter(0, 0, character);
3129             return makeItem(QPoint(0, character.getHeight() / 2));
3130         }
3131 
3132         QString c("c");
3133         QRect r = m_bigTimeSigFontMetrics.boundingRect(c);
3134 
3135         int dy = getLineSpacing() / 4;
3136         createPixmap(r.width(), r.height() + dy*2);
3137 
3138         if (m_selected) {
3139             m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
3140         } else if (m_shaded) {
3141             m_p->painter().setPen(QColor(Qt::gray));
3142         }
3143 
3144         m_p->painter().setFont(m_bigTimeSigFont);
3145 //        if (!m_inPrinterMethod)
3146 //            m_p->maskPainter().setFont(m_bigTimeSigFont);
3147 
3148         m_p->drawText(0, r.height() + dy, c);
3149 
3150         if (sig.getNumerator() == 2) { // cut common
3151 
3152             int x = r.width() * 3 / 5 - getStemThickness();
3153 
3154             for (int i = 0; i < getStemThickness() * 2; ++i, ++x) {
3155                 m_p->drawLine(x, 0, x, r.height() + dy*2 - 1);
3156             }
3157         }
3158 
3159         m_p->painter().setPen(QColor(Qt::black));
3160         return makeItem(QPoint(0, r.height() / 2 + dy));
3161 
3162     } else {
3163 
3164         int numerator = sig.getNumerator(),
3165             denominator = sig.getDenominator();
3166 
3167         QString numS, denomS;
3168 
3169         numS.setNum(numerator);
3170         denomS.setNum(denominator);
3171 
3172         NoteCharacter character;
3173         if (getCharacter(m_style->getTimeSignatureDigitName(0), character,
3174                          PlainColour, false)) {
3175 
3176             // if the 0 digit exists, we assume 1-9 also all exist
3177             // and all have the same width
3178 
3179             int numW = character.getWidth() * numS.length();
3180             int denomW = character.getWidth() * denomS.length();
3181 
3182             int width = std::max(numW, denomW);
3183             int height = getLineSpacing() * 4 - getStaffLineThickness();
3184 
3185             createPixmap(width, height);
3186 
3187             for (int i = 0; i < numS.length(); ++i) {
3188                 int x = width - (width - numW) / 2 - (i + 1) * character.getWidth();
3189                 int y = height / 4 - (character.getHeight() / 2);
3190                 NoteCharacter charCharacter = getCharacter
3191                     (m_style->getTimeSignatureDigitName(numerator % 10),
3192                      PlainColour, false);
3193                 m_p->drawNoteCharacter(x, y, charCharacter);
3194                 numerator /= 10;
3195             }
3196 
3197             for (int i = 0; i < denomS.length(); ++i) {
3198                 int x = width - (width - denomW) / 2 - (i + 1) * character.getWidth();
3199                 int y = height - height / 4 - (character.getHeight() / 2);
3200                 NoteCharacter charCharacter = getCharacter
3201                     (m_style->getTimeSignatureDigitName(denominator % 10),
3202                      PlainColour, false);
3203                 m_p->drawNoteCharacter(x, y, charCharacter);
3204                 denominator /= 10;
3205             }
3206 
3207             return makeItem(QPoint(0, height / 2));
3208         }
3209 
3210         QRect numR = m_timeSigFontMetrics.boundingRect(numS);
3211         QRect denomR = m_timeSigFontMetrics.boundingRect(denomS);
3212         int width = std::max(numR.width(), denomR.width()) + 2;
3213         int x;
3214 
3215         createPixmap(width, denomR.height() * 2 + getNoteBodyHeight());
3216 
3217         if (m_selected) {
3218             m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
3219         } else if (m_shaded) {
3220             m_p->painter().setPen(QColor(Qt::gray));
3221         }
3222 
3223         m_p->painter().setFont(m_timeSigFont);
3224 //        if (!m_inPrinterMethod)
3225 //            m_p->maskPainter().setFont(m_timeSigFont);
3226 
3227         x = (width - numR.width()) / 2 - 1;
3228         m_p->drawText(x, denomR.height(), numS);
3229 
3230         x = (width - denomR.width()) / 2 - 1;
3231         m_p->drawText(x, denomR.height() * 2 + (getNoteBodyHeight() / 2) - 1, denomS);
3232 
3233         m_p->painter().setPen(QColor(Qt::black));
3234 
3235         return makeItem(QPoint(0, denomR.height() +
3236                                (getNoteBodyHeight() / 4) - 1));
3237     }
3238 }
3239 
getTimeSigWidth(const TimeSignature & sig) const3240 int NotePixmapFactory::getTimeSigWidth(const TimeSignature &sig) const
3241 {
3242     if (sig.isCommon()) {
3243 
3244         QRect r(m_bigTimeSigFontMetrics.boundingRect("c"));
3245         return r.width() + 2;
3246 
3247     } else {
3248 
3249         int numerator = sig.getNumerator(),
3250             denominator = sig.getDenominator();
3251 
3252         QString numS, denomS;
3253 
3254         numS.setNum(numerator);
3255         denomS.setNum(denominator);
3256 
3257         QRect numR = m_timeSigFontMetrics.boundingRect(numS);
3258         QRect denomR = m_timeSigFontMetrics.boundingRect(denomS);
3259         int width = std::max(numR.width(), denomR.width()) + 2;
3260 
3261         return width;
3262     }
3263 }
3264 
3265 QFont
getTextFont(const Text & text) const3266 NotePixmapFactory::getTextFont(const Text &text) const
3267 {
3268     std::string type(text.getTextType());
3269     TextFontCache::iterator i = m_textFontCache.find(type.c_str());
3270     if (i != m_textFontCache.end())
3271         return i->second;
3272 
3273     /*
3274      * Text types:
3275      *
3276      * UnspecifiedType:    Nothing known, use small roman
3277      * StaffName:          Large roman, to left of start of staff
3278      * ChordName:          Not normally shown in score, use small roman
3279      * KeyName:            Not normally shown in score, use small roman
3280      * Lyric:              Small roman, below staff and dynamic texts
3281      * Chord:              Small bold roman, above staff
3282      * Dynamic:	           Small italic, below staff
3283      * Direction:          Large roman, above staff (by barline?)
3284      * LocalDirection:     Small bold italic, below staff (by barline?)
3285      * Tempo:              Large bold roman, above staff
3286      * LocalTempo:         Small bold roman, above staff
3287      * Annotation:         Very small sans-serif, in a yellow box
3288      * LilyPondDirective:  Very small sans-serif, in a green box
3289      */
3290 
3291     QFont::Weight weight = QFont::Normal;
3292     bool italic = false;
3293     bool large = false;
3294     bool tiny = false;
3295     bool serif = true;
3296 
3297     if (type == Text::Tempo ||
3298         type == Text::LocalTempo ||
3299         type == Text::LocalDirection ||
3300         type == Text::Chord) {
3301         weight = QFont::Bold;
3302     }
3303 
3304     if (type == Text::Dynamic ||
3305         type == Text::LocalDirection) {
3306         italic = true;
3307     }
3308 
3309     if (type == Text::StaffName ||
3310         type == Text::Direction ||
3311         type == Text::Tempo) {
3312         large = true;
3313     }
3314 
3315     if (type == Text::Annotation ||
3316         type == Text::LilyPondDirective) {
3317         serif = false;
3318         tiny = true;
3319     }
3320 
3321     QSettings settings;
3322     //@@@ JAS Check here first for errors.  Added .beginGroup()
3323     settings.beginGroup( NotationViewConfigGroup );
3324 
3325     QFont textFont;
3326 
3327     if (serif) {
3328         textFont = QFont(defaultSerifFontFamily);
3329         textFont = settings.value("textfont", textFont).toString();
3330     } else {
3331         textFont = QFont(defaultSansSerifFontFamily);
3332         textFont = settings.value("sansfont", textFont).toString();
3333     }
3334     settings.endGroup();
3335 
3336     textFont.setStyleStrategy(QFont::StyleStrategy(QFont::PreferDefault |
3337                                                    QFont::PreferMatch));
3338 
3339     int size;
3340     if (large)
3341         size = (getLineSpacing() * 7) / 2;
3342     else if (tiny)
3343         size = (getLineSpacing() * 4) / 3;
3344     else if (serif)
3345         size = (getLineSpacing() * 2);
3346     else
3347         size = (getLineSpacing() * 3) / 2;
3348 
3349     textFont.setPixelSize(size);
3350     textFont.setStyleHint(serif ? QFont::Serif : QFont::SansSerif);
3351     textFont.setWeight(weight);
3352     textFont.setItalic(italic);
3353 
3354     NOTATION_DEBUG << "NotePixmapFactory::getTextFont: requested size " << size
3355      		   << " for type " << type;
3356 
3357     NOTATION_DEBUG << "NotePixmapFactory::getTextFont: returning font '"
3358                    << textFont.toString() << "' for type " << type.c_str()
3359                    << " text : " << text.getText().c_str();
3360 
3361     m_textFontCache[type.c_str()] = textFont;
3362     return textFont;
3363 }
3364 
3365 QPixmap
makeTextPixmap(const Text & text)3366 NotePixmapFactory::makeTextPixmap(const Text &text)
3367 {
3368     QGraphicsPixmapItem *item = makeText(text);
3369     QPixmap map = item->pixmap();
3370     delete item;
3371     return map;
3372 }
3373 
3374 QGraphicsPixmapItem *
makeText(const Text & text)3375 NotePixmapFactory::makeText(const Text &text)
3376 {
3377     Profiler profiler("NotePixmapFactory::makeText");
3378 
3379     std::string type(text.getTextType());
3380 
3381     if (type == Text::Annotation ||
3382         type == Text::LilyPondDirective) {
3383         return makeAnnotation(text, (type == Text::LilyPondDirective));
3384     }
3385 
3386     drawTextAux(text, nullptr, 0, 0);
3387     return makeItem(QPoint(2, 2));
3388 }
3389 
3390 QGraphicsPixmapItem *
makeGuitarChord(const Guitar::Fingering & fingering,int x,int y)3391 NotePixmapFactory::makeGuitarChord(const Guitar::Fingering &fingering,
3392                                    int x,
3393                                    int y)
3394 {
3395     using namespace Guitar;
3396     Profiler profiler("NotePixmapFactory::makeGuitarChord");
3397 
3398     int guitarChordWidth = getLineSpacing() * 6;
3399     int guitarChordHeight = getLineSpacing() * 6;
3400 
3401     createPixmap(guitarChordWidth, guitarChordHeight);
3402 
3403     if (m_selected) {
3404         m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
3405         m_p->painter().setBrush(GUIPalette::getColour(GUIPalette::SelectedElement));
3406     } else {
3407         m_p->painter().setPen(QColor(Qt::black));
3408         m_p->painter().setBrush(QColor(Qt::black));
3409     }
3410 
3411     Guitar::NoteSymbols ns(Guitar::Fingering::DEFAULT_NB_STRINGS, FingeringBox::DEFAULT_NB_DISPLAYED_FRETS);
3412     Guitar::NoteSymbols::drawFingeringPixmap(fingering, ns, &(m_p->painter()));
3413 
3414     return makeItem(QPoint (x, y));
3415 }
3416 
3417 void
drawText(const Text & text,QPainter & painter,int x,int y)3418 NotePixmapFactory::drawText(const Text &text,
3419                             QPainter &painter, int x, int y)
3420 {
3421     Profiler profiler("NotePixmapFactory::drawText");
3422 
3423     //     NOTATION_DEBUG << "NotePixmapFactory::drawText() " << text.getText().c_str()
3424     //                    << " - type : " << text.getTextType().c_str();
3425 
3426     std::string type(text.getTextType());
3427 
3428     if (type == Text::Annotation ||
3429         type == Text::LilyPondDirective) {
3430         QGraphicsPixmapItem *map = makeAnnotation(text, (type == Text::LilyPondDirective));
3431         painter.drawPixmap(x, y, map->pixmap());
3432         delete map;
3433         return ;
3434     }
3435 
3436     m_inPrinterMethod = true;
3437     drawTextAux(text, &painter, x, y);
3438     m_inPrinterMethod = false;
3439 }
3440 
3441 void
drawTextAux(const Text & text,QPainter * painter,int x,int y)3442 NotePixmapFactory::drawTextAux(const Text &text,
3443                                QPainter *painter, int x, int y)
3444 {
3445     QString s(strtoqstr(text.getText()));
3446     QFont textFont(getTextFont(text));
3447     QFontMetrics textMetrics(textFont);
3448 
3449     int offset = 2;
3450     int width = textMetrics.boundingRect(s).width() + 2 * offset;
3451     int height = textMetrics.height() + 2 * offset;
3452 
3453     if (painter) {
3454         painter->save();
3455         m_p->beginExternal(painter);
3456         painter->translate(x - offset, y - offset);
3457     } else {
3458         createPixmap(width, height);
3459     }
3460 
3461     if (m_selected)
3462         m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
3463     else if (m_shaded)
3464         m_p->painter().setPen(QColor(Qt::gray));
3465 
3466     m_p->painter().setFont(textFont);
3467 //    if (!m_inPrinterMethod)
3468 //        m_p->maskPainter().setFont(textFont);
3469 
3470     m_p->drawText(offset, textMetrics.ascent() + offset, s);
3471 
3472     m_p->painter().setPen(QColor(Qt::black));
3473 
3474     if (painter) {
3475         painter->restore();
3476     }
3477 }
3478 
3479 QGraphicsPixmapItem *
makeAnnotation(const Text & text)3480 NotePixmapFactory::makeAnnotation(const Text &text)
3481 {
3482     return makeAnnotation(text, false);
3483 }
3484 
3485 QGraphicsPixmapItem *
makeAnnotation(const Text & text,const bool isLilyPondDirective)3486 NotePixmapFactory::makeAnnotation(const Text &text, const bool isLilyPondDirective)
3487 {
3488     QString s(strtoqstr(text.getText()));
3489 
3490     QFont textFont(getTextFont(text));
3491     QFontMetrics textMetrics(textFont);
3492 
3493     int annotationWidth = getLineSpacing() * 16;
3494     int annotationHeight = getLineSpacing() * 6;
3495 
3496     int topGap = getLineSpacing() / 4 + 1;
3497     int bottomGap = getLineSpacing() / 3 + 1;
3498     int sideGap = getLineSpacing() / 4 + 1;
3499 
3500     QRect r = textMetrics.boundingRect
3501         (0, 0, annotationWidth, annotationHeight, Qt::TextWordWrap, s);
3502 
3503     int pixmapWidth = r.width() + sideGap * 2;
3504     int pixmapHeight = r.height() + topGap + bottomGap;
3505 
3506     createPixmap(pixmapWidth, pixmapHeight);
3507 
3508     if (m_selected)
3509         m_p->painter().setPen(GUIPalette::getColour(GUIPalette::SelectedElement));
3510     else if (m_shaded)
3511         m_p->painter().setPen(QColor(Qt::gray));
3512 
3513     m_p->painter().setFont(textFont);
3514 //    if (!m_inPrinterMethod)
3515 //        m_p->maskPainter().setFont(textFont);
3516 
3517     if (isLilyPondDirective) {
3518         m_p->painter().setBrush(GUIPalette::getColour(GUIPalette::TextLilyPondDirectiveBackground));
3519     } else {
3520         m_p->painter().setBrush(GUIPalette::getColour(GUIPalette::TextAnnotationBackground));
3521     }
3522 
3523     m_p->drawRect(0, 0, pixmapWidth, pixmapHeight);
3524 
3525     m_p->painter().setBrush(QColor(Qt::black));
3526     m_p->painter().drawText(QRect(sideGap, topGap,
3527                                   annotationWidth + sideGap,
3528                                   pixmapHeight - bottomGap),
3529                             Qt::TextWordWrap, s);
3530 
3531     /* unnecessary following the rectangle draw
3532        m_pm.drawText(QRect(sideGap, topGap,
3533        annotationWidth + sideGap, annotationHeight + topGap),
3534        Qt::TextWordWrap, s);
3535     */
3536 
3537     return makeItem(QPoint(0, 0));
3538 }
3539 
3540 void
createPixmap(int width,int height)3541 NotePixmapFactory::createPixmap(int width, int height)
3542 {
3543     if (width == 0 || height == 0) {
3544         RG_WARNING << "NotePixmapFactory::createPixmap: WARNING: invalid size " << width << "x" << height;
3545         m_generatedPixmap = new QPixmap();
3546         return;
3547     }
3548 
3549     m_generatedWidth = width;
3550     m_generatedHeight = height;
3551     m_generatedPixmap = new QPixmap(width, height);
3552     m_generatedPixmap->fill(Qt::transparent);
3553 
3554     // initiate painting
3555     NOTATION_DEBUG << "NotePixmapFactory::createPixmap(" << width << "," << height << "): about to begin painter";
3556     m_p->begin(m_generatedPixmap);
3557 
3558 }
3559 
3560 QGraphicsPixmapItem *
makeItem(QPoint hotspot)3561 NotePixmapFactory::makeItem(QPoint hotspot)
3562 {
3563 //    NOTATION_DEBUG << "NotePixmapFactory::makeItem(" << hotspot << ")";
3564 
3565     if (!m_generatedPixmap->isNull()) {
3566         m_p->end();
3567     }// else NOTATION_DEBUG << "m_generatedPixmap was nullptr!";
3568 
3569     QGraphicsPixmapItem *p = new QGraphicsPixmapItem;
3570 
3571     p->setPixmap(*m_generatedPixmap);
3572     p->setOffset(QPointF(-hotspot.x(), -hotspot.y()));
3573 
3574     // The hit test QGraphicsScene::items(), called by NotationScene::setupMouseEvent,
3575     // calls contains() which calls shape(), which by default generates a mask (slow!).
3576     // Change the shape mode to BoundingRectShape to be able to click on text items anywhere
3577     // in the bounding rect, rather than having to aim for a black pixel.
3578     p->setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
3579 
3580 //    NOTATION_DEBUG << "NotePixmapFactory::makeItem: item = " << p << " (scene = " << p->scene() << ")";
3581 
3582     delete m_generatedPixmap;
3583     return p;
3584 }
3585 
3586 QPixmap
makePixmap()3587 NotePixmapFactory::makePixmap()
3588 {
3589     if (!m_generatedPixmap->isNull()) {
3590         m_p->end();
3591     }
3592 
3593     QPixmap p = *m_generatedPixmap;
3594     delete m_generatedPixmap;
3595     return p;
3596 }
3597 
3598 NoteCharacter
getCharacter(CharName name,ColourType type,bool inverted)3599 NotePixmapFactory::getCharacter(CharName name, ColourType type, bool inverted)
3600 {
3601     NoteCharacter ch;
3602     getCharacter(name, ch, type, inverted);
3603     return ch;
3604 }
3605 
3606 bool
getCharacter(CharName name,NoteCharacter & ch,ColourType type,bool inverted)3607 NotePixmapFactory::getCharacter(CharName name, NoteCharacter &ch,
3608                                 ColourType type, bool inverted)
3609 {
3610     // use the full font for this unless a grace size was supplied in ctor
3611     NoteFont *font = (m_haveGrace ? m_graceFont : m_font);
3612 
3613     NoteFont::CharacterType charType =
3614         m_inPrinterMethod ? NoteFont::Printer : NoteFont::Screen;
3615 
3616     if (m_selected) {
3617         return font->getCharacterColoured
3618             (name,
3619              GUIPalette::SelectedElementHue,
3620              GUIPalette::SelectedElementMinValue,
3621              ch, charType, inverted);
3622     }
3623 
3624     QColor white = Qt::white;
3625     QColor red = Qt::red;
3626     QColor gray = Qt::gray;
3627     QColor magenta = Qt::magenta;
3628 
3629     int h, s, v;
3630 
3631     // getCharacterShaded() has been removed and replaced with a call to
3632     // getCharacterColoured() using Qt::gray, in order to preserve the
3633     // antialiasing of the character, and avoid blowing it out
3634     if (m_shaded) {
3635         gray.getHsv(&h, &s, &v);
3636         return font->getCharacterColoured
3637             (name,
3638              h,
3639              v,
3640              ch, charType, inverted, s);
3641     }
3642 
3643     switch (type) {
3644 
3645     case PlainColour:
3646         return font->getCharacter(name, ch, charType, inverted);
3647 
3648     case QuantizedColour:
3649         return font->getCharacterColoured
3650             (name,
3651              GUIPalette::QuantizedNoteHue,
3652              GUIPalette::QuantizedNoteMinValue,
3653              ch, charType, inverted);
3654 
3655     case HighlightedColour:
3656         return font->getCharacterColoured
3657             (name,
3658              GUIPalette::HighlightedElementHue,
3659              GUIPalette::HighlightedElementMinValue,
3660              ch, charType, inverted);
3661 
3662     case TriggerColour:
3663         return font->getCharacterColoured
3664             (name,
3665              GUIPalette::TriggerNoteHue,
3666              GUIPalette::TriggerNoteMinValue,
3667              ch, charType, inverted);
3668 
3669     case TriggerSkipColour:
3670         return font->getCharacterColoured
3671             (name,
3672              GUIPalette::TriggerSkipHue,
3673              GUIPalette::TriggerSkipMinValue,
3674              ch, charType, inverted);
3675 
3676     case OutRangeColour:
3677         return font->getCharacterColoured
3678             (name,
3679              GUIPalette::OutRangeNoteHue,
3680              GUIPalette::OutRangeNoteMinValue,
3681              ch, charType, inverted);
3682 
3683     case PlainColourLight:
3684         white.getHsv(&h, &s, &v);
3685         return font->getCharacterColoured
3686             (name,
3687              h,
3688              v,
3689              ch, charType, inverted, s);
3690 
3691     case ConflictColour:
3692         red.getHsv(&h, &s, &v);
3693         return font->getCharacterColoured
3694             (name,
3695              h,
3696              v,
3697              ch, charType, inverted, s);
3698 
3699     case MemberOfParallelColour:
3700         magenta.getHsv(&h, &s, &v);
3701         return font->getCharacterColoured
3702             (name,
3703              h,
3704              v,
3705              ch, charType, inverted, s);
3706     }
3707 
3708     return font->getCharacter(name, ch, charType, inverted);
3709 }
3710 
3711 NoteCharacter
getCharacter(CharName name,QColor color,bool inverted)3712 NotePixmapFactory::getCharacter(CharName name, QColor color, bool inverted)
3713 {
3714     NoteCharacter ch;
3715 
3716     // use the full font for this unless a grace size was supplied in ctor
3717     NoteFont *font = (m_haveGrace ? m_graceFont : m_font);
3718 
3719     NoteFont::CharacterType charType =
3720         m_inPrinterMethod ? NoteFont::Printer : NoteFont::Screen;
3721 
3722     int h, s, v;
3723     color.getHsv(&h, &s, &v);
3724     font->getCharacterColoured(name, h, v, ch, charType, inverted, s);
3725     return ch;
3726 }
3727 
3728 bool
getCharacter(CharName name,NoteCharacter & ch,QColor color,bool inverted)3729 NotePixmapFactory::getCharacter(CharName name,
3730                                 NoteCharacter &ch, QColor color, bool inverted)
3731 {
3732     // use the full font for this unless a grace size was supplied in ctor
3733     NoteFont *font = (m_haveGrace ? m_graceFont : m_font);
3734 
3735     NoteFont::CharacterType charType =
3736         m_inPrinterMethod ? NoteFont::Printer : NoteFont::Screen;
3737 
3738     int h, s, v;
3739     color.getHsv(&h, &s, &v);
3740     return font->getCharacterColoured(name, h, v, ch, charType, inverted, s);
3741 }
3742 
3743 
getNoteBodyWidth(Note::Type type) const3744 int NotePixmapFactory::getNoteBodyWidth(Note::Type type)
3745     const
3746 {
3747     // use the full font for this unless a grace size was supplied in ctor
3748     NoteFont *font = (m_haveGrace ? m_graceFont : m_font);
3749 
3750     CharName charName(m_style->getNoteHeadCharName(type).first);
3751     int hx, hy;
3752     if (!font->getHotspot(charName, hx, hy))
3753         hx = 0;
3754     return font->getWidth(charName) - hx * 2;
3755 }
3756 
getNoteBodyHeight(Note::Type) const3757 int NotePixmapFactory::getNoteBodyHeight(Note::Type )
3758     const
3759 {
3760     // use full size for this, never grace size, because we never want to change
3761     // the vertical scaling
3762 
3763     // this is by definition
3764     return m_font->getSize();
3765 }
3766 
getLineSpacing() const3767 int NotePixmapFactory::getLineSpacing() const
3768 {
3769     // use full size for this, never grace size
3770     return m_font->getSize() + getStaffLineThickness();
3771 }
3772 
getAccidentalWidth(const Accidental & a,int shift,bool extraShift) const3773 int NotePixmapFactory::getAccidentalWidth(const Accidental &a,
3774                                           int shift, bool extraShift) const
3775 {
3776     if (a == Accidentals::NoAccidental)
3777         return 0;
3778     int w = m_font->getWidth(m_style->getAccidentalCharName(a));
3779     if (!shift)
3780         return w;
3781     else {
3782         int sw = w;
3783         if (extraShift) {
3784             --shift;
3785             w += getNoteBodyWidth() + getStemThickness();
3786         }
3787         w += shift *
3788             (sw - m_font->getHotspot(m_style->getAccidentalCharName(a)).x());
3789     }
3790     return w;
3791 }
3792 
getAccidentalHeight(const Accidental & a) const3793 int NotePixmapFactory::getAccidentalHeight(const Accidental &a) const
3794 {
3795     return m_font->getHeight(m_style->getAccidentalCharName(a));
3796 }
3797 
getStemLength() const3798 int NotePixmapFactory::getStemLength() const
3799 {
3800     // use the full font for this unless a grace size was supplied in ctor
3801     NoteFont *font = (m_haveGrace ? m_graceFont : m_font);
3802 
3803     unsigned int l = 1;
3804     (void)font->getStemLength(l);
3805     return l;
3806 }
3807 
getStemThickness() const3808 int NotePixmapFactory::getStemThickness() const
3809 {
3810     // use the full font for this unless a grace size was supplied in ctor
3811     NoteFont *font = (m_haveGrace ? m_graceFont : m_font);
3812 
3813     unsigned int i = 1;
3814     (void)font->getStemThickness(i);
3815     return i;
3816 }
3817 
getStaffLineThickness() const3818 int NotePixmapFactory::getStaffLineThickness() const
3819 {
3820     unsigned int i;
3821     (void)m_font->getStaffLineThickness(i);
3822     return i;
3823 }
3824 
getLegerLineThickness() const3825 int NotePixmapFactory::getLegerLineThickness() const
3826 {
3827     unsigned int i;
3828     (void)m_font->getLegerLineThickness(i);
3829     return i;
3830 }
3831 
getDotWidth() const3832 int NotePixmapFactory::getDotWidth() const
3833 {
3834     return m_font->getWidth(NoteCharacterNames::DOT);
3835 }
3836 
getClefWidth(const Clef & clef) const3837 int NotePixmapFactory::getClefWidth(const Clef &clef) const
3838 {
3839     return m_font->getWidth(m_style->getClefCharName(clef.getClefType()));
3840 }
3841 
getBarMargin() const3842 int NotePixmapFactory::getBarMargin() const
3843 {
3844     return getNoteBodyWidth() * 2;
3845 }
3846 
getRestWidth(const Note & restType) const3847 int NotePixmapFactory::getRestWidth(const Note &restType) const
3848 {
3849     return m_font->getWidth(m_style->getRestCharName(restType.getNoteType(),
3850                                                      false)) // small inaccuracy!
3851         + (restType.getDots() * getDotWidth());
3852 }
3853 
getKeyWidth(const Key & key,Key previousKey) const3854 int NotePixmapFactory::getKeyWidth(const Key &key,
3855                                    Key previousKey) const
3856 {
3857     std::vector<int> ah0 = previousKey.getAccidentalHeights(Clef());
3858     std::vector<int> ah1 = key.getAccidentalHeights(Clef());
3859 
3860     int cancelCount = 0;
3861     if (key.isSharp() != previousKey.isSharp())
3862         cancelCount = ah0.size();
3863     else if (ah1.size() < ah0.size())
3864         cancelCount = ah0.size() - ah1.size();
3865 
3866     CharName keyCharName;
3867     if (key.isSharp())
3868         keyCharName = NoteCharacterNames::SHARP;
3869     else
3870         keyCharName = NoteCharacterNames::FLAT;
3871 
3872     NoteCharacter keyCharacter;
3873     NoteCharacter cancelCharacter;
3874 
3875     keyCharacter = m_font->getCharacter(keyCharName);
3876     if (cancelCount > 0) {
3877         cancelCharacter = m_font->getCharacter(NoteCharacterNames::NATURAL);
3878     }
3879 
3880     //int x = 0;
3881     //int lw = getLineSpacing();
3882     int keyDelta = keyCharacter.getWidth() - keyCharacter.getHotspot().x();
3883 
3884     int cancelDelta = 0;
3885     int between = 0;
3886     if (cancelCount > 0) {
3887         cancelDelta = cancelCharacter.getWidth() + cancelCharacter.getWidth() / 3;
3888         between = cancelCharacter.getWidth();
3889     }
3890 
3891     return (keyDelta * ah1.size() + cancelDelta * cancelCount + between +
3892             keyCharacter.getWidth() / 4);
3893 }
3894 
getTextWidth(const Text & text) const3895 int NotePixmapFactory::getTextWidth(const Text &text) const
3896 {
3897     QFontMetrics metrics(getTextFont(text));
3898     return metrics.boundingRect(strtoqstr(text.getText())).width() + 4;
3899 }
3900 
3901 }
3902