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