Audacity 3.2.0
NoteTrackView.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3Audacity: A Digital Audio Editor
4
5NoteTrackView.cpp
6
7Paul Licameli split from TrackPanel.cpp
8
9**********************************************************************/
10#include "NoteTrackView.h"
12
13#ifdef USE_MIDI
14#include "WrapAllegro.h"
15
17#include "NoteTrack.h"
18
19#include "AColor.h"
20#include "AllThemeResources.h"
21#include "../../../../HitTestResult.h"
22#include "PendingTracks.h"
23#include "Theme.h"
24#include "../../../../TrackArt.h"
25#include "../../../../TrackArtist.h"
26#include "../../../../TrackPanelDrawingContext.h"
27#include "../../../../TrackPanelMouseEvent.h"
28#include "ViewInfo.h"
29#include "../../../ui/SelectHandle.h"
30#include "StretchHandle.h"
32
33#include <wx/dc.h>
34
35NoteTrackView::NoteTrackView(const std::shared_ptr<Channel> &pChannel)
36 : CommonChannelView{ pChannel }
37{
38}
39
41{
42}
43
44std::vector<UIHandlePtr> NoteTrackView::DetailedHitTest(
45 const TrackPanelMouseState &state, const AudacityProject *pProject,
46 int, bool )
47{
48 // Eligible for stretch?
49 UIHandlePtr result;
50 std::vector<UIHandlePtr> results;
51#ifdef USE_MIDI
52#ifdef EXPERIMENTAL_MIDI_STRETCHING
54 mStretchHandle, state, pProject, FindChannel<NoteTrack>());
55 if (result)
56 results.push_back(result);
57#endif
58#endif
59
60 return results;
61}
62
65 return [](NoteTrack &track, size_t) {
66 return std::make_shared<NoteTrackView>(
67 track.SharedPointer<NoteTrack>());
68 };
69}
70
71std::shared_ptr<ChannelVRulerControls> NoteTrackView::DoGetVRulerControls()
72{
73 return
74 std::make_shared<NoteTrackVRulerControls>( shared_from_this() );
75}
76
77#define TIME_TO_X(t) (zoomInfo.TimeToPosition((t), rect.x))
78#define X_TO_TIME(xx) (zoomInfo.PositionToTime((xx), rect.x))
79
80std::shared_ptr<CommonTrackCell> NoteTrackView::GetAffordanceControls()
81{
82 if (mpAffordanceCellControl == nullptr)
83 {
84 mpAffordanceCellControl = std::make_shared<NoteTrackAffordanceControls>(DoFindTrack());
85 }
87}
88
89namespace {
90
91/*
92Note: recall that Allegro attributes end in a type identifying letter.
93
94In addition to standard notes, an Allegro_Note can denote a graphic.
95A graphic is a note with a loud of zero (for quick testing) and an
96attribute named "shapea" set to one of the following atoms:
97 line
98 from (time, pitch) to (time+dur, y1r), where y1r is an
99 attribute
100 rectangle
101 from (time, pitch) to (time+dur, y1r), where y1r is an
102 attribute
103 triangle
104 coordinates are (time, pitch), (x1r, y1r), (x2r, y2r)
105 dur must be the max of x1r-time, x2r-time
106 polygon
107 coordinates are (time, pitch), (x1r, y1r), (x2r, y2r),
108 (x3r, y3r), ... are coordinates (since we cannot represent
109 arrays as attribute values, we just generate as many
110 attribute names as we need)
111 dur must be the max of xNr-time for all N
112 oval
113 similar to rectangle
114 Note: this oval has horizontal and vertical axes only
115 text
116 drawn at (time, pitch)
117 duration should be zero (text is clipped based on time and duration,
118 NOT based on actual coordinates)
119
120and optional attributes as follows:
121 linecolori is 0x00rrggbb format color for line or text foreground
122 fillcolori is 0x00rrggbb format color for fill or text background
123 linethicki is line thickness in pixels, 0 for no line
124 filll is true to fill rectangle or draw text background (default is false)
125 fonta is one of ['roman', 'swiss', 'modern'] (font, otherwise use default)
126 weighta may be 'bold' (font) (default is normal)
127 sizei is font size (default is 8)
128 justifys is a string containing two letters, a horizontal code and a
129 vertical code. The horizontal code is as follows:
130 l: the coordinate is to the left of the string (default)
131 c: the coordinate is at the center of the string
132 r: the coordinate is at the right of the string
133 The vertical code is as follows:
134 t: the coordinate is at the top of the string
135 c: the coordinate is at the center of the string
136 b: the coordinate is at the bottom of the string
137 d: the coordinate is at the baseline of the string (default)
138 Thus, -justifys:"lt" places the left top of the string at the point
139 given by (pitch, time). The default value is "ld".
140*/
141
142// returns NULL if note is not a shape,
143// returns atom (string) value of note if note is a shape
144const char *IsShape(Alg_note_ptr note)
145{
146 Alg_parameters_ptr parameters = note->parameters;
147 while (parameters) {
148 if (strcmp(parameters->parm.attr_name(), "shapea") == 0) {
149 return parameters->parm.a;
150 }
151 parameters = parameters->next;
152 }
153 return NULL;
154}
155
156// returns value of attr, or default if not found
157double LookupRealAttribute(Alg_note_ptr note, Alg_attribute attr, double def)
158{
159 Alg_parameters_ptr parameters = note->parameters;
160 while (parameters) {
161 if (parameters->parm.attr_name() == attr + 1 &&
162 parameters->parm.attr_type() == 'r') {
163 return parameters->parm.r;
164 }
165 parameters = parameters->next;
166 }
167 return def;
168}
169
170// returns value of attr, or default if not found
171long LookupIntAttribute(Alg_note_ptr note, Alg_attribute attr, long def)
172{
173 Alg_parameters_ptr parameters = note->parameters;
174 while (parameters) {
175 if (parameters->parm.attr_name() == attr + 1 &&
176 parameters->parm.attr_type() == 'i') {
177 return parameters->parm.i;
178 }
179 parameters = parameters->next;
180 }
181 return def;
182}
183
184// returns value of attr, or default if not found
185bool LookupLogicalAttribute(Alg_note_ptr note, Alg_attribute attr, bool def)
186{
187 Alg_parameters_ptr parameters = note->parameters;
188 while (parameters) {
189 if (parameters->parm.attr_name() == attr + 1 &&
190 parameters->parm.attr_type() == 'l') {
191 return parameters->parm.l;
192 }
193 parameters = parameters->next;
194 }
195 return def;
196}
197
198// returns value of attr, or default if not found
199const char *LookupStringAttribute(Alg_note_ptr note, Alg_attribute attr, const char *def)
200{
201 Alg_parameters_ptr parameters = note->parameters;
202 while (parameters) {
203 if (parameters->parm.attr_name() == attr + 1 &&
204 parameters->parm.attr_type() == 's') {
205 return parameters->parm.s;
206 }
207 parameters = parameters->next;
208 }
209 return def;
210}
211
212// returns value of attr, or default if not found
213const char *LookupAtomAttribute(Alg_note_ptr note, Alg_attribute attr, char *def)
214{
215 Alg_parameters_ptr parameters = note->parameters;
216 while (parameters) {
217 if (parameters->parm.attr_name() == attr + 1 &&
218 parameters->parm.attr_type() == 'a') {
219 return parameters->parm.s;
220 }
221 parameters = parameters->next;
222 }
223 return def;
224}
225
226// CLIP(x) changes x to lie between +/- CLIP_MAX due to graphics display problems
227// with very large coordinate values (this happens when you zoom in very far)
228// This will cause incorrect things to be displayed, but at these levels of zoom
229// you will only see a small fraction of the overall shape. Note that rectangles
230// and lines are clipped in a way that preserves correct graphics, so in
231// particular, line plots will be correct at any zoom (limited by floating point
232// precision).
233#define CLIP_MAX 16000
234#define CLIP(xx) { long c = (xx); if (c < -CLIP_MAX) c = -CLIP_MAX; \
235 if (c > CLIP_MAX) c = CLIP_MAX; (xx) = c; }
236
237#define RED(i) ( unsigned char )( (((i) >> 16) & 0xff) )
238#define GREEN(i) ( unsigned char )( (((i) >> 8) & 0xff) )
239#define BLUE(i) ( unsigned char )( ((i) & 0xff) )
240
241//#define PITCH_TO_Y(p) (rect.y + rect.height - (int)(pitchht * ((p) + 0.5 - pitch0) + 0.5))
242
243/*
244int PitchToY(double p, int bottom)
245{
246 int octave = (((int) (p + 0.5)) / 12);
247 int n = ((int) (p + 0.5)) % 12;
248
249 return IPITCH_TO_Y((int) (p + 0.5));
250 // was: bottom - octave * octaveHeight - notePos[n] - 4;
251}
252*/
253
254/* DrawNoteBackground is called by DrawNoteTrack twice: once to draw
255 the unselected background, and once to draw the selected background.
256 The selected background is the same except for the horizontal range
257 and the colors. The background rectangle region is given by rect; the
258 selected region is given by sel. The first time this is called,
259 sel is equal to rect, and the entire region is drawn with unselected
260 background colors.
261 */
263 const NoteTrack &track, const wxRect &rect, const wxRect &sel,
264 const wxBrush &wb, const wxPen &wp, const wxBrush &bb, const wxPen &bp,
265 const wxPen &mp)
266{
267 auto &dc = context.dc;
268 const auto artist = TrackArtist::Get( context );
269 const auto &zoomInfo = *artist->pZoomInfo;
270
271 dc.SetBrush(wb);
272 dc.SetPen(wp);
273
274 int left = TIME_TO_X(track.GetStartTime());
275 if (left < sel.x) left = sel.x; // clip on left
276
277 int right = TIME_TO_X(track.GetStartTime() + track.GetSeq().get_real_dur());
278 if (right > sel.x + sel.width) right = sel.x + sel.width; // clip on right
279
280 // need overlap between MIDI data and the background region
281 if (left >= right) return;
282
283 NoteTrackDisplayData data{ track, rect };
284 dc.SetBrush(bb);
285 int octave = 0;
286 // obottom is the window coordinate of octave divider line
287 int obottom = data.GetOctaveBottom(octave);
288 // eOffset is for the line between E and F; there's another line
289 // between B and C, hence the offset of 2 for two line thicknesses
290 int eOffset = data.GetPitchHeight(5) + 2;
291 while (obottom > rect.y + data.GetNoteMargin() + 3) {
292 // draw a black line separating octaves if this octave bottom is visible
293 if (obottom < rect.y + rect.height - data.GetNoteMargin()) {
294 dc.SetPen(*wxBLACK_PEN);
295 // obottom - 1 because obottom is at the bottom of the line
296 AColor::Line(dc, left, obottom - 1, right, obottom - 1);
297 }
298 dc.SetPen(bp);
299 // draw a black-key stripe colored line separating E and F if visible
300 if (obottom - eOffset > rect.y && obottom - eOffset < rect.y + rect.height) {
301 AColor::Line(dc, left, obottom - eOffset,
302 right, obottom - eOffset);
303 }
304
305 // draw visible black key lines
306 wxRect br;
307 br.x = left;
308 br.width = right - left;
309 br.height = data.GetPitchHeight(1);
310 for (int black = 0; black < 5; black++) {
311 br.y = obottom - data.GetBlackPos(black);
312 if (br.y > rect.y && br.y + br.height < rect.y + rect.height) {
313 dc.DrawRectangle(br); // draw each black key background stripe
314 }
315 }
316 obottom = data.GetOctaveBottom(++octave);
317 }
318
319 // draw bar lines
320 Alg_seq_ptr seq = &track.GetSeq();
321 // We assume that sliding a NoteTrack around slides the barlines
322 // along with the notes. This means that when we write out a track
323 // as Allegro or MIDI without the offset, we'll need to insert an
324 // integer number of measures of silence, using tempo change to
325 // match the duration to the offset.
326 // Iterate over all time signatures to generate beat positions of
327 // bar lines, map the beats to times, map the times to position,
328 // and draw the bar lines that fall within the region of interest (sel)
329 // seq->convert_to_beats();
330 dc.SetPen(mp);
331 Alg_time_sigs &sigs = seq->time_sig;
332 int i = 0; // index into ts[]
333 double next_bar_beat = 0.0;
334 double beats_per_measure = 4.0;
335 while (true) {
336 if (i < sigs.length() && sigs[i].beat < next_bar_beat + ALG_EPS) {
337 // NEW time signature takes effect
338 Alg_time_sig &sig = sigs[i++];
339 next_bar_beat = sig.beat;
340 beats_per_measure = (sig.num * 4.0) / sig.den;
341 }
342 // map beat to time
343 double t = seq->get_time_map()->beat_to_time(next_bar_beat);
344 // map time to position
345 int xx = TIME_TO_X(t + track.GetStartTime());
346 if (xx > right) break;
347 AColor::Line(dc, xx, sel.y, xx, sel.y + sel.height);
348 next_bar_beat += beats_per_measure;
349 }
350}
351
352/* DrawNoteTrack:
353Draws a piano-roll style display of sequence data with added
354graphics. Since there may be notes outside of the display region,
355reserve a half-note-height margin at the top and bottom of the
356window and draw out-of-bounds notes here instead.
357*/
359 const NoteTrack &track, const wxRect & rect, bool muted, bool selected)
360{
361 auto &dc = context.dc;
362 const auto artist = TrackArtist::Get( context );
363 const auto &selectedRegion = *artist->pSelectedRegion;
364 const auto &zoomInfo = *artist->pZoomInfo;
365
367 double sel0 = selectedRegion.t0();
368 double sel1 = selectedRegion.t1();
369
370 const double h = X_TO_TIME(rect.x);
371 const double h1 = X_TO_TIME(rect.x + rect.width);
372
373 Alg_seq_ptr seq = &track.GetSeq();
374
375 if (!track.GetSelected())
376 sel0 = sel1 = 0.0;
377
378 NoteTrackDisplayData data{ track, rect };
379
380 // reserve 1/2 note height at top and bottom of track for
381 // out-of-bounds notes
382 int numPitches = (rect.height) / data.GetPitchHeight(1);
383 if (numPitches < 0) numPitches = 0; // cannot be negative
384
385 // Background comes in 4 colors, that are now themed.
386 // 214, 214,214 -- unselected white keys
387 // 192,192,192 -- black keys
388 // 170,170,170 -- bar lines
389 // 165,165,190 -- selected white keys
390
391 wxPen blackStripePen;
392 blackStripePen.SetColour(theTheme.Colour( clrMidiZebra));
393 wxBrush blackStripeBrush;
394 blackStripeBrush.SetColour(theTheme.Colour( clrMidiZebra));
395 wxPen barLinePen;
396 barLinePen.SetColour(theTheme.Colour( clrMidiLines));
397
398 const auto &blankBrush = artist->blankBrush;
399 const auto &blankPen = artist->blankPen;
400 DrawNoteBackground(context, track, rect, rect, blankBrush, blankPen,
401 blackStripeBrush, blackStripePen, barLinePen);
402
403 dc.SetClippingRegion(rect);
404
405 // Draw the selection background
406 // First, the white keys, as a single rectangle
407 // In other words fill the selection area with selectedWhiteKeyPen
408 wxRect selBG;
409 selBG.y = rect.y;
410 selBG.height = rect.height;
411 selBG.x = TIME_TO_X(sel0);
412 selBG.width = TIME_TO_X(sel1) - TIME_TO_X(sel0);
413
414 wxPen selectedWhiteKeyPen;
415 selectedWhiteKeyPen.SetColour(165, 165, 190);
416 dc.SetPen(selectedWhiteKeyPen);
417
418 wxBrush selectedWhiteKeyBrush;
419 selectedWhiteKeyBrush.SetColour(theTheme.Colour( clrSelected ));
420 // Then, the black keys and octave stripes, as smaller rectangles
421 wxPen selectedBlackKeyPen;
422 selectedBlackKeyPen.SetColour(theTheme.Colour( clrMidiZebra));
423 wxBrush selectedBlackKeyBrush;
424 selectedBlackKeyBrush.SetColour(theTheme.Colour( clrMidiZebra));
425 wxPen selectedBarLinePen;
426 selectedBarLinePen.SetColour(theTheme.Colour( clrMidiLines));
427
428 DrawNoteBackground(context, track, rect, selBG,
429 selectedWhiteKeyBrush, selectedWhiteKeyPen,
430 selectedBlackKeyBrush, selectedBlackKeyPen,
431 selectedBarLinePen);
434 int marg = data.GetNoteMargin();
435
436 // NOTE: it would be better to put this in some global initialization
437 // function rather than do lookups every time.
438 Alg_attribute line = symbol_table.insert_string("line");
439 Alg_attribute rectangle = symbol_table.insert_string("rectangle");
440 Alg_attribute triangle = symbol_table.insert_string("triangle");
441 Alg_attribute polygon = symbol_table.insert_string("polygon");
442 Alg_attribute oval = symbol_table.insert_string("oval");
443 Alg_attribute text = symbol_table.insert_string("text");
444 Alg_attribute texts = symbol_table.insert_string("texts");
445 Alg_attribute x1r = symbol_table.insert_string("x1r");
446 Alg_attribute x2r = symbol_table.insert_string("x2r");
447 Alg_attribute y1r = symbol_table.insert_string("y1r");
448 Alg_attribute y2r = symbol_table.insert_string("y2r");
449 Alg_attribute linecolori = symbol_table.insert_string("linecolori");
450 Alg_attribute fillcolori = symbol_table.insert_string("fillcolori");
451 Alg_attribute linethicki = symbol_table.insert_string("linethicki");
452 Alg_attribute filll = symbol_table.insert_string("filll");
453 Alg_attribute fonta = symbol_table.insert_string("fonta");
454 Alg_attribute roman = symbol_table.insert_string("roman");
455 Alg_attribute swiss = symbol_table.insert_string("swiss");
456 Alg_attribute modern = symbol_table.insert_string("modern");
457 Alg_attribute weighta = symbol_table.insert_string("weighta");
458 Alg_attribute bold = symbol_table.insert_string("bold");
459 Alg_attribute sizei = symbol_table.insert_string("sizei");
460 Alg_attribute justifys = symbol_table.insert_string("justifys");
461
462 // We want to draw in seconds, so we need to convert to seconds
463 seq->convert_to_seconds();
464
465 Alg_iterator iterator(seq, false);
466 iterator.begin();
467 //for every event
468 Alg_event_ptr evt;
469 while (0 != (evt = iterator.next())) {
470 if (evt->get_type() == 'n') { // 'n' means a note
471 Alg_note_ptr note = (Alg_note_ptr) evt;
472 // if the note's channel is visible
473 if (track.IsVisibleChan(evt->chan)) {
474 double xx = note->time + track.GetStartTime();
475 double x1 = xx + note->dur;
476 if (xx < h1 && x1 > h) { // omit if outside box
477 const char *shape = NULL;
478 if (note->loud > 0.0 || 0 == (shape = IsShape(note))) {
479 wxRect nr; // "note rectangle"
480 nr.y = data.PitchToY(note->pitch);
481 nr.height = data.GetPitchHeight(1);
482
483 nr.x = TIME_TO_X(xx);
484 nr.width = TIME_TO_X(x1) - nr.x;
485
486 if (nr.x + nr.width >= rect.x && nr.x < rect.x + rect.width) {
487 if (nr.x < rect.x) {
488 nr.width -= (rect.x - nr.x);
489 nr.x = rect.x;
490 }
491 if (nr.x + nr.width > rect.x + rect.width) // clip on right
492 nr.width = rect.x + rect.width - nr.x;
493
494 if (nr.y + nr.height < rect.y + marg + 3) {
495 // too high for window
496 nr.y = rect.y;
497 nr.height = marg;
498 dc.SetBrush(*wxBLACK_BRUSH);
499 dc.SetPen(*wxBLACK_PEN);
500 dc.DrawRectangle(nr);
501 } else if (nr.y >= rect.y + rect.height - marg - 1) {
502 // too low for window
503 nr.y = rect.y + rect.height - marg;
504 nr.height = marg;
505 dc.SetBrush(*wxBLACK_BRUSH);
506 dc.SetPen(*wxBLACK_PEN);
507 dc.DrawRectangle(nr);
508 } else {
509 if (nr.y + nr.height > rect.y + rect.height - marg)
510 nr.height = rect.y + rect.height - nr.y;
511 if (nr.y < rect.y + marg) {
512 int offset = rect.y + marg - nr.y;
513 nr.height -= offset;
514 nr.y += offset;
515 }
516 // nr.y += rect.y;
517 if (muted)
518 AColor::LightMIDIChannel(&dc, note->chan + 1);
519 else
520 AColor::MIDIChannel(&dc, note->chan + 1);
521 dc.DrawRectangle(nr);
522 if (data.GetPitchHeight(1) > 2) {
523 AColor::LightMIDIChannel(&dc, note->chan + 1);
524 AColor::Line(dc, nr.x, nr.y, nr.x + nr.width-2, nr.y);
525 AColor::Line(dc, nr.x, nr.y, nr.x, nr.y + nr.height-2);
526 AColor::DarkMIDIChannel(&dc, note->chan + 1);
527 AColor::Line(dc, nr.x+nr.width-1, nr.y,
528 nr.x+nr.width-1, nr.y+nr.height-1);
529 AColor::Line(dc, nr.x, nr.y+nr.height-1,
530 nr.x+nr.width-1, nr.y+nr.height-1);
531 }
532// }
533 }
534 }
535 } else if (shape) {
536 // draw a shape according to attributes
537 // add 0.5 to pitch because pitches are plotted with
538 // height = PITCH_HEIGHT; thus, the center is raised
539 // by PITCH_HEIGHT * 0.5
540 int yy = data.PitchToY(note->pitch);
541 long linecolor = LookupIntAttribute(note, linecolori, -1);
542 long linethick = LookupIntAttribute(note, linethicki, 1);
543 long fillcolor = -1;
544 long fillflag = 0;
545
546 // set default color to be that of channel
547 AColor::MIDIChannel(&dc, note->chan+1);
548 if (shape != text) {
549 if (linecolor != -1)
550 dc.SetPen(wxPen(wxColour(RED(linecolor),
551 GREEN(linecolor),
552 BLUE(linecolor)),
553 linethick, wxPENSTYLE_SOLID));
554 }
555 if (shape != line) {
556 fillcolor = LookupIntAttribute(note, fillcolori, -1);
557 fillflag = LookupLogicalAttribute(note, filll, false);
558
559 if (fillcolor != -1)
560 dc.SetBrush(wxBrush(wxColour(RED(fillcolor),
561 GREEN(fillcolor),
562 BLUE(fillcolor)),
563 wxBRUSHSTYLE_SOLID));
564 if (!fillflag) dc.SetBrush(*wxTRANSPARENT_BRUSH);
565 }
566 int y1 = data.PitchToY(LookupRealAttribute(note, y1r, note->pitch));
567 if (shape == line) {
568 // extreme zooms caues problems under windows, so we have to do some
569 // clipping before calling display routine
570 if (xx < h) { // clip line on left
571 yy = (int)((yy + (y1 - yy) * (h - xx) / (x1 - xx)) + 0.5);
572 xx = h;
573 }
574 if (x1 > h1) { // clip line on right
575 y1 = (int)((yy + (y1 - yy) * (h1 - xx) / (x1 - xx)) + 0.5);
576 x1 = h1;
577 }
578 AColor::Line(dc, TIME_TO_X(xx), yy, TIME_TO_X(x1), y1);
579 } else if (shape == rectangle) {
580 if (xx < h) { // clip on left, leave 10 pixels to spare
581 xx = X_TO_TIME(rect.x - (linethick + 10));
582 }
583 if (x1 > h1) { // clip on right, leave 10 pixels to spare
584 xx = X_TO_TIME(rect.x + rect.width + linethick + 10);
585 }
586 dc.DrawRectangle(TIME_TO_X(xx), yy, TIME_TO_X(x1) - TIME_TO_X(xx), y1 - yy + 1);
587 } else if (shape == triangle) {
588 wxPoint points[3];
589 points[0].x = TIME_TO_X(xx);
590 CLIP(points[0].x);
591 points[0].y = yy;
592 points[1].x = TIME_TO_X(LookupRealAttribute(note, x1r, note->pitch));
593 CLIP(points[1].x);
594 points[1].y = y1;
595 points[2].x = TIME_TO_X(LookupRealAttribute(note, x2r, xx));
596 CLIP(points[2].x);
597 points[2].y = data.PitchToY(LookupRealAttribute(note, y2r, note->pitch));
598 dc.DrawPolygon(3, points);
599 } else if (shape == polygon) {
600 wxPoint points[20]; // upper bound of 20 sides
601 points[0].x = TIME_TO_X(xx);
602 CLIP(points[0].x);
603 points[0].y = yy;
604 points[1].x = TIME_TO_X(LookupRealAttribute(note, x1r, xx));
605 CLIP(points[1].x);
606 points[1].y = y1;
607 points[2].x = TIME_TO_X(LookupRealAttribute(note, x2r, xx));
608 CLIP(points[2].x);
609 points[2].y = data.PitchToY(LookupRealAttribute(note, y2r, note->pitch));
610 int n = 3;
611 while (n < 20) {
612 char name[8];
613 sprintf(name, "x%dr", n);
614 Alg_attribute attr = symbol_table.insert_string(name);
615 double xn = LookupRealAttribute(note, attr, -1000000.0);
616 if (xn == -1000000.0) break;
617 points[n].x = TIME_TO_X(xn);
618 CLIP(points[n].x);
619 sprintf(name, "y%dr", n - 1);
620 attr = symbol_table.insert_string(name);
621 double yn = LookupRealAttribute(note, attr, -1000000.0);
622 if (yn == -1000000.0) break;
623 points[n].y = data.PitchToY(yn);
624 n++;
625 }
626 dc.DrawPolygon(n, points);
627 } else if (shape == oval) {
628 int ix = TIME_TO_X(xx);
629 CLIP(ix);
630 int ix1 = TIME_TO_X(x1) - TIME_TO_X(xx);
631 if (ix1 > CLIP_MAX * 2) ix1 = CLIP_MAX * 2; // CLIP a width
632 dc.DrawEllipse(ix, yy, ix1, y1 - yy + 1);
633 } else if (shape == text) {
634 if (linecolor != -1)
635 dc.SetTextForeground(wxColour(RED(linecolor),
636 GREEN(linecolor),
637 BLUE(linecolor)));
638 // if no color specified, copy color from brush
639 else dc.SetTextForeground(dc.GetBrush().GetColour());
640
641 // This seems to have no effect, so I commented it out. -RBD
642 //if (fillcolor != -1)
643 // dc.SetTextBackground(wxColour(RED(fillcolor),
644 // GREEN(fillcolor),
645 // BLUE(fillcolor)));
647 //else dc.SetTextBackground(dc.GetPen().GetColour());
648
649 const char *font = LookupAtomAttribute(note, fonta, NULL);
650 const char *weight = LookupAtomAttribute(note, weighta, NULL);
651 int size = LookupIntAttribute(note, sizei, 8);
652 const char *justify = LookupStringAttribute(note, justifys, "ld");
653 wxFont wxfont;
654 wxfont.SetFamily(font == roman ? wxFONTFAMILY_ROMAN :
655 (font == swiss ? wxFONTFAMILY_SWISS :
656 (font == modern ? wxFONTFAMILY_MODERN : wxFONTFAMILY_DEFAULT)));
657 wxfont.SetStyle(wxFONTSTYLE_NORMAL);
658 wxfont.SetWeight(weight == bold ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL);
659 wxfont.SetPointSize(size);
660 dc.SetFont(wxfont);
661
662 // now do justification
663 const char *s = LookupStringAttribute(note, texts, "");
664 wxCoord textWidth, textHeight;
665 dc.GetTextExtent(wxString::FromUTF8(s), &textWidth, &textHeight);
666 long hoffset = 0;
667 long voffset = -textHeight; // default should be baseline of text
668
669 if (strlen(justify) != 2) justify = "ld";
670
671 if (justify[0] == 'c') hoffset = -(textWidth/2);
672 else if (justify[0] == 'r') hoffset = -textWidth;
673
674 if (justify[1] == 't') voffset = 0;
675 else if (justify[1] == 'c') voffset = -(textHeight/2);
676 else if (justify[1] == 'b') voffset = -textHeight;
677 if (fillflag) {
678 // It should be possible to do this with background color,
679 // but maybe because of the transfer mode, no background is
680 // drawn. To fix this, just draw a rectangle:
681 dc.SetPen(wxPen(wxColour(RED(fillcolor),
682 GREEN(fillcolor),
683 BLUE(fillcolor)),
684 1, wxPENSTYLE_SOLID));
685 dc.DrawRectangle(TIME_TO_X(xx) + hoffset, yy + voffset,
686 textWidth, textHeight);
687 }
688 dc.DrawText(LAT1CTOWX(s), TIME_TO_X(xx) + hoffset, yy + voffset);
689 }
690 }
691 }
692 }
693 }
694 }
695 iterator.end();
696 // draw black line between top/bottom margins and the track
697 dc.SetPen(*wxBLACK_PEN);
698 AColor::Line(dc, rect.x, rect.y + marg, rect.x + rect.width, rect.y + marg);
699 AColor::Line(dc, rect.x, rect.y + rect.height - marg - 1, // subtract 1 to get
700 rect.x + rect.width, rect.y + rect.height - marg - 1); // top of line
701
702 if (h == 0.0 && track.GetStartTime() < 0.0) {
704 }
705
706 //draw clip edges
707 {
708 int left = TIME_TO_X(track.GetStartTime());
709 int right = TIME_TO_X(track.GetStartTime() + track.GetSeq().get_real_dur());
710
711 TrackArt::DrawClipEdges(dc, wxRect(left, rect.GetTop(), right - left + 1, rect.GetHeight()), selected);
712 }
713
714 dc.DestroyClippingRegion();
716}
717
718}
719
722 const wxRect &rect, unsigned iPass )
723{
724 const auto artist = TrackArtist::Get(context);
725 const auto &pendingTracks = *artist->pPendingTracks;
726
727 if ( iPass == TrackArtist::PassTracks ) {
728 const auto pChannel = FindChannel();
729 if (!pChannel)
730 return;
731 const auto &nt = static_cast<const NoteTrack&>(
732 pendingTracks.SubstitutePendingChangedChannel(*pChannel));
733 bool muted = false;
734 const auto artist = TrackArtist::Get( context );
735 const auto hasSolo = artist->hasSolo;
736 muted = (hasSolo || nt.GetMute()) && !nt.GetSolo();
737
740 bool selected{ false };
741 if (auto affordance = std::dynamic_pointer_cast<NoteTrackAffordanceControls>(GetAffordanceControls()))
742 {
743 selected = affordance->IsSelected();
744 }
745
746 DrawNoteTrack(context, nt, rect, muted, selected);
747 }
748 CommonChannelView::Draw(context, rect, iPass);
749}
750
751#include "SyncLock.h"
752
756 return [](auto &) { return SyncLockPolicy::Grouped; };
757}
758#endif
std::shared_ptr< UIHandle > UIHandlePtr
Definition: CellularPanel.h:28
const TranslatableString name
Definition: Distortion.cpp:76
@ rectangle
Definition: Dither.h:20
@ triangle
Definition: Dither.h:20
#define LAT1CTOWX(X)
Definition: Internat.h:158
#define SonifyBeginNoteBackground()
Definition: NoteTrack.h:231
#define SonifyEndNoteBackground()
Definition: NoteTrack.h:232
#define SonifyBeginNoteForeground()
Definition: NoteTrack.h:233
#define SonifyEndNoteForeground()
Definition: NoteTrack.h:234
DEFINE_ATTACHED_VIRTUAL_OVERRIDE(DoGetNoteTrackView)
#define RED(i)
#define X_TO_TIME(xx)
#define CLIP_MAX
#define GREEN(i)
#define CLIP(xx)
#define BLUE(i)
#define TIME_TO_X(t)
@ Grouped
Can be part of a group.
THEME_API Theme theTheme
Definition: Theme.cpp:82
static wxBrush labelSelectedBrush
Definition: AColor.h:122
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:187
static void LightMIDIChannel(wxDC *dc, int channel)
Definition: AColor.cpp:669
static void MIDIChannel(wxDC *dc, int channel)
Definition: AColor.cpp:653
static wxBrush labelUnselectedBrush
Definition: AColor.h:121
static void DarkMIDIChannel(wxDC *dc, int channel)
Definition: AColor.cpp:687
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
double GetStartTime() const
Get the minimum of Start() values of intervals, or 0 when none.
Definition: Channel.cpp:50
std::shared_ptr< Track > DoFindTrack() override
auto FindChannel() -> std::shared_ptr< Subtype >
May return null.
Implements some hit-testing shared by many ChannelView subtypes.
Temporary data used to display a note track.
A Track that is used for Midi notes. (Somewhat old code).
Definition: NoteTrack.h:78
bool IsVisibleChan(int c) const
Definition: NoteTrack.h:155
Alg_seq & GetSeq() const
Definition: NoteTrack.cpp:158
std::shared_ptr< CommonTrackCell > mpAffordanceCellControl
Definition: NoteTrackView.h:40
std::vector< UIHandlePtr > DetailedHitTest(const TrackPanelMouseState &state, const AudacityProject *pProject, int currentTool, bool bMultiTool) override
std::shared_ptr< CommonTrackCell > GetAffordanceControls() override
NoteTrackView(const NoteTrackView &)=delete
~NoteTrackView() override
std::shared_ptr< ChannelVRulerControls > DoGetVRulerControls() override
void Draw(TrackPanelDrawingContext &context, const wxRect &rect, unsigned iPass) override
static UIHandlePtr HitTest(std::weak_ptr< StretchHandle > &holder, const TrackPanelMouseState &state, const AudacityProject *pProject, const std::shared_ptr< NoteTrack > &pTrack)
wxColour & Colour(int iIndex)
static TrackArtist * Get(TrackPanelDrawingContext &)
Definition: TrackArtist.cpp:69
bool GetSelected() const
Selectedness is always the same for all channels of a group.
Definition: Track.cpp:78
std::shared_ptr< Subclass > SharedPointer()
Definition: Track.h:146
virtual void Draw(TrackPanelDrawingContext &context, const wxRect &rect, unsigned iPass)
AUDACITY_DLL_API void DrawClipEdges(wxDC &dc, const wxRect &clipRect, bool selected=false)
Definition: TrackArt.cpp:309
AUDACITY_DLL_API void DrawNegativeOffsetTrackArrows(TrackPanelDrawingContext &context, const wxRect &rect)
Definition: TrackArt.cpp:143
AUDACITY_DLL_API void DrawBackgroundWithSelection(TrackPanelDrawingContext &context, const wxRect &rect, const Channel &channel, const wxBrush &selBrush, const wxBrush &unselBrush, bool useSelection=true)
Definition: TrackArt.cpp:651
bool LookupLogicalAttribute(Alg_note_ptr note, Alg_attribute attr, bool def)
const char * IsShape(Alg_note_ptr note)
const char * LookupStringAttribute(Alg_note_ptr note, Alg_attribute attr, const char *def)
const char * LookupAtomAttribute(Alg_note_ptr note, Alg_attribute attr, char *def)
double LookupRealAttribute(Alg_note_ptr note, Alg_attribute attr, double def)
void DrawNoteTrack(TrackPanelDrawingContext &context, const NoteTrack &track, const wxRect &rect, bool muted, bool selected)
void DrawNoteBackground(TrackPanelDrawingContext &context, const NoteTrack &track, const wxRect &rect, const wxRect &sel, const wxBrush &wb, const wxPen &wp, const wxBrush &bb, const wxPen &bp, const wxPen &mp)
long LookupIntAttribute(Alg_note_ptr note, Alg_attribute attr, long def)
For defining overrides of the method.