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 ¶ms)
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 ¶ms,
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 ¶ms,
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 ¶ms)
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 ¶ms,
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 ¶ms)
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 ¶ms,
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 ¶ms,
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 ¶ms)
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 ¶ms) 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 ¶ms)
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 ¶ms,
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 ¶ms,
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 ¶ms)
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 ¶ms)
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 ¶ms,
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 ¶ms,
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 ¶ms,
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 ¶ms)
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 ¶ms,
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 ¶ms,
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 ¶ms)
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 ¶ms)
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 ¶ms)
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 ¶ms,
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 ¶ms,
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