Audacity  3.0.3
LabelTrackView.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3 Audacity: A Digital Audio Editor
4 
5 LabelTrackView.cpp
6 
7 Paul Licameli split from TrackPanel.cpp
8 
9 **********************************************************************/
10 
11 
12 #include "LabelTrackView.h"
13 
15 #include "LabelGlyphHandle.h"
16 #include "LabelTextHandle.h"
17 
18 #include "../../../LabelTrack.h"
19 
20 #include "../../../AColor.h"
21 #include "../../../AllThemeResources.h"
22 #include "../../../HitTestResult.h"
23 #include "../../../Project.h"
24 #include "../../../ProjectHistory.h"
25 #include "../../../ProjectSettings.h"
26 #include "../../../ProjectWindow.h"
27 #include "../../../RefreshCode.h"
28 #include "../../../Theme.h"
29 #include "../../../TrackArtist.h"
30 #include "../../../TrackPanelAx.h"
31 #include "../../../TrackPanel.h"
32 #include "../../../TrackPanelMouseEvent.h"
33 #include "../../../UndoManager.h"
34 #include "../../../ViewInfo.h"
35 #include "../../../widgets/ErrorDialog.h"
36 
37 #include <wx/clipbrd.h>
38 #include <wx/dcclient.h>
39 #include <wx/font.h>
40 #include <wx/frame.h>
41 #include <wx/menu.h>
42 
44 : mIndex(-1),
45  mModified(false)
46 {
47 }
48 
50 : mIndex(index),
51  mModified(false)
52 {
53 }
54 
56 {
57  if (index != mIndex) {
58  mModified = false;
59  }
60  mIndex = index;
61  return *this;
62 }
63 
65 {
66  mModified = false;
67  mIndex += 1;
68  return *this;
69 }
70 
72 {
73  mModified = false;
74  mIndex -= 1;
75  return *this;
76 }
77 
78 LabelTrackView::Index::operator int() const
79 {
80  return mIndex;
81 }
82 
84 {
85  return mModified;
86 }
87 
89 {
90  mModified = modified;
91 }
92 
93 LabelTrackView::LabelTrackView( const std::shared_ptr<Track> &pTrack )
94  : CommonTrackView{ pTrack }
95 {
96  ResetFont();
97  CreateCustomGlyphs();
98  ResetFlags();
99 
100  // Events will be emitted by the track
101  const auto pLabelTrack = FindLabelTrack();
102  BindTo( pLabelTrack.get() );
103 }
104 
106 {
107 }
108 
109 void LabelTrackView::Reparent( const std::shared_ptr<Track> &parent )
110 {
111  auto oldParent = FindLabelTrack();
112  auto newParent = track_cast<LabelTrack*>(parent.get());
113  if (oldParent.get() != newParent) {
114  UnbindFrom( oldParent.get() );
115  BindTo( newParent );
116  }
117  CommonTrackView::Reparent( parent );
118 }
119 
121 {
122  pParent->Bind(
123  EVT_LABELTRACK_ADDITION, &LabelTrackView::OnLabelAdded, this );
124  pParent->Bind(
125  EVT_LABELTRACK_DELETION, &LabelTrackView::OnLabelDeleted, this );
126  pParent->Bind(
127  EVT_LABELTRACK_PERMUTED, &LabelTrackView::OnLabelPermuted, this );
128  pParent->Bind(
129  EVT_LABELTRACK_SELECTION, &LabelTrackView::OnSelectionChange, this );
130 }
131 
133 {
134  pParent->Unbind(
135  EVT_LABELTRACK_ADDITION, &LabelTrackView::OnLabelAdded, this );
136  pParent->Unbind(
137  EVT_LABELTRACK_DELETION, &LabelTrackView::OnLabelDeleted, this );
138  pParent->Unbind(
139  EVT_LABELTRACK_PERMUTED, &LabelTrackView::OnLabelPermuted, this );
140  pParent->Unbind(
141  EVT_LABELTRACK_SELECTION, &LabelTrackView::OnSelectionChange, this );
142 }
143 
144 void LabelTrackView::CopyTo( Track &track ) const
145 {
146  TrackView::CopyTo( track );
147  auto &other = TrackView::Get( track );
148 
149  if ( const auto pOther = dynamic_cast< const LabelTrackView* >( &other ) ) {
150  pOther->mSelIndex = mSelIndex;
151  pOther->mInitialCursorPos = mInitialCursorPos;
152  pOther->mCurrentCursorPos = mCurrentCursorPos;
153  pOther->mDrawCursor = mDrawCursor;
154  pOther->mUndoLabel = mUndoLabel;
155  }
156 }
157 
159 {
160  return static_cast< LabelTrackView& >( TrackView::Get( track ) );
161 }
162 
164 {
165  return static_cast< const LabelTrackView& >( TrackView::Get( track ) );
166 }
167 
168 std::shared_ptr<LabelTrack> LabelTrackView::FindLabelTrack()
169 {
170  return std::static_pointer_cast<LabelTrack>( FindTrack() );
171 }
172 
173 std::shared_ptr<const LabelTrack> LabelTrackView::FindLabelTrack() const
174 {
175  return const_cast<LabelTrackView*>(this)->FindLabelTrack();
176 }
177 
178 std::vector<UIHandlePtr> LabelTrackView::DetailedHitTest
179 (const TrackPanelMouseState &st,
180  const AudacityProject *WXUNUSED(pProject), int, bool)
181 {
182  UIHandlePtr result;
183  std::vector<UIHandlePtr> results;
184  const wxMouseState &state = st.state;
185 
186  const auto pTrack = FindLabelTrack();
187  result = LabelGlyphHandle::HitTest(
188  mGlyphHandle, state, pTrack, st.rect);
189  if (result)
190  results.push_back(result);
191 
192  result = LabelTextHandle::HitTest(
193  mTextHandle, state, pTrack);
194  if (result)
195  results.push_back(result);
196 
197  return results;
198 }
199 
200 // static member variables.
202 
204 
214 
216 
218 {
219  mInitialCursorPos = 1;
220  mCurrentCursorPos = 1;
221  mDrawCursor = false;
222 }
223 
225 {
228  mSelIndex = flags.mSelIndex;
229  mDrawCursor = flags.mDrawCursor;
230 }
231 
232 wxFont LabelTrackView::GetFont(const wxString &faceName, int size)
233 {
234  wxFontEncoding encoding;
235  if (faceName.empty())
236  encoding = wxFONTENCODING_DEFAULT;
237  else
238  encoding = wxFONTENCODING_SYSTEM;
239  return wxFont(size, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL,
240  wxFONTWEIGHT_NORMAL, false, faceName, encoding);
241 }
242 
244 {
245  mFontHeight = -1;
246  wxString facename = gPrefs->Read(wxT("/GUI/LabelFontFacename"), wxT(""));
247  int size = gPrefs->Read(wxT("/GUI/LabelFontSize"), DefaultFontSize);
248  msFont = GetFont(facename, size);
249 }
250 
264 void LabelTrackView::ComputeTextPosition(const wxRect & r, int index) const
265 {
266  const auto pTrack = FindLabelTrack();
267  const auto &mLabels = pTrack->GetLabels();
268 
269  const auto &labelStruct = mLabels[index];
270 
271  // xExtra is extra space
272  // between the text and the endpoints.
273  const int xExtra=mIconWidth;
274  int x = labelStruct.x; // left endpoint
275  int x1 = labelStruct.x1; // right endpoint.
276  int width = labelStruct.width;
277 
278  int xText; // This is where the text will end up.
279 
280  // Will the text all fit at this zoom?
281  bool bTooWideForScreen = width > (r.width-2*xExtra);
282 // bool bSimpleCentering = !bTooWideForScreen;
283  bool bSimpleCentering = false;
284 
285  //TODO (possibly):
286  // Add configurable options as to when to use simple
287  // and when complex centering.
288  //
289  // Simple centering does its best to keep the text
290  // centered between the label limits.
291  //
292  // Complex centering positions the text proportionally
293  // to how far we are through the label.
294  //
295  // If we add preferences for this, we want to be able to
296  // choose separately whether:
297  // a) Wide text labels centered simple/complex.
298  // b) Other text labels centered simple/complex.
299  //
300 
301  if( bSimpleCentering )
302  {
303  // Center text between the two end points.
304  xText = (x+x1-width)/2;
305  }
306  else
307  {
308  // Calculate xText position to make text line
309  // scroll sideways evenly as r moves right.
310 
311  // xText is a linear function of r.x.
312  // These variables are used to compute that function.
313  int rx0,rx1,xText0,xText1;
314 
315  // Since we will be using a linear function,
316  // we should blend smoothly between left and right
317  // aligned text as r, the 'viewport' moves.
318  if( bTooWideForScreen )
319  {
320  rx0=x; // when viewport at label start.
321  xText0=x+xExtra; // text aligned left.
322  rx1=x1-r.width; // when viewport end at label end
323  xText1=x1-(width+xExtra); // text aligned right.
324  }
325  else
326  {
327  // when label start + width + extra spacing at viewport end..
328  rx0=x-r.width+width+2*xExtra;
329  // ..text aligned left.
330  xText0=x+xExtra;
331  // when viewport start + width + extra spacing at label end..
332  rx1=x1-(width+2*xExtra);
333  // ..text aligned right.
334  xText1=x1-(width+xExtra);
335  }
336 
337  if( rx1 > rx0 ) // Avoid divide by zero case.
338  {
339  // Compute the blend between left and right aligned.
340 
341  // Don't use:
342  //
343  // xText = xText0 + ((xText1-xText0)*(r.x-rx0))/(rx1-rx0);
344  //
345  // The problem with the above is that it integer-oveflows at
346  // high zoom.
347 
348  // Instead use:
349  xText = xText0 + (int)((xText1-xText0)*(((float)(r.x-rx0))/(rx1-rx0)));
350  }
351  else
352  {
353  // Avoid divide by zero by reverting to
354  // simple centering.
355  //
356  // We could also fall into this case if x and x1
357  // are swapped, in which case we'll end up
358  // left aligned anyway because code later on
359  // will catch that.
360  xText = (x+x1-width)/2;
361  }
362  }
363 
364  // Is the text now appearing partly outside r?
365  bool bOffLeft = xText < r.x+xExtra;
366  bool bOffRight = xText > r.x+r.width-width-xExtra;
367 
368  // IF any part of the text is offscreen
369  // THEN we may bring it back.
370  if( bOffLeft == bOffRight )
371  {
372  //IF both sides on screen, THEN nothing to do.
373  //IF both sides off screen THEN don't do
374  //anything about it.
375  //(because if we did, you'd never get to read
376  //all the text by scrolling).
377  }
378  else if( bOffLeft != bTooWideForScreen)
379  {
380  // IF we're off on the left, OR we're
381  // too wide for the screen and off on the right
382  // (only) THEN align left.
383  xText = r.x+xExtra;
384  }
385  else
386  {
387  // We're off on the right, OR we're
388  // too wide and off on the left (only)
389  // SO align right.
390  xText =r.x+r.width-width-xExtra;
391  }
392 
393  // But if we've taken the text out from its endpoints
394  // we must move it back so that it's between the endpoints.
395 
396  // We test the left end point last because the
397  // text might not even fit between the endpoints (at this
398  // zoom factor), and in that case we'd like to position
399  // the text at the left end point.
400  if( xText > (x1-width-xExtra))
401  xText=(x1-width-xExtra);
402  if( xText < x+xExtra )
403  xText=x+xExtra;
404 
405  labelStruct.xText = xText;
406 }
407 
411 void LabelTrackView::ComputeLayout(const wxRect & r, const ZoomInfo &zoomInfo) const
412 {
413  int xUsed[MAX_NUM_ROWS];
414 
415  int iRow;
416  // Rows are the 'same' height as icons or as the text,
417  // whichever is taller.
418  const int yRowHeight = wxMax(mTextHeight,mIconHeight)+3;// pixels.
419  // Extra space at end of rows.
420  // We allow space for one half icon at the start and two
421  // half icon widths for extra x for the text frame.
422  // [we don't allow half a width space for the end icon since it is
423  // allowed to be obscured by the text].
424  const int xExtra= (3 * mIconWidth)/2;
425 
426  bool bAvoidName = false;
427  const int nRows = wxMin((r.height / yRowHeight) + 1, MAX_NUM_ROWS);
428  if( nRows > 2 )
429  bAvoidName = gPrefs->ReadBool(wxT("/GUI/ShowTrackNameInWaveform"), false);
430  // Initially none of the rows have been used.
431  // So set a value that is less than any valid value.
432  {
433  // Bug 502: With dragging left of zeros, labels can be in
434  // negative space. So set least possible value as starting point.
435  const int xStart = INT_MIN;
436  for (auto &x : xUsed)
437  x = xStart;
438  }
439  int nRowsUsed=0;
440 
441  const auto pTrack = FindLabelTrack();
442  const auto &mLabels = pTrack->GetLabels();
443 
444  { int i = -1; for (const auto &labelStruct : mLabels) { ++i;
445  const int x = zoomInfo.TimeToPosition(labelStruct.getT0(), r.x);
446  const int x1 = zoomInfo.TimeToPosition(labelStruct.getT1(), r.x);
447  int y = r.y;
448 
449  labelStruct.x=x;
450  labelStruct.x1=x1;
451  labelStruct.y=-1;// -ve indicates nothing doing.
452  iRow=0;
453  // Our first preference is a row that ends where we start.
454  // (This is to encourage merging of adjacent label boundaries).
455  while( (iRow<nRowsUsed) && (xUsed[iRow] != x ))
456  iRow++;
457 
458  // IF we didn't find one THEN
459  // find any row that can take a span starting at x.
460  if( iRow >= nRowsUsed )
461  {
462  iRow=0;
463  while( (iRow<nRows) && (xUsed[iRow] > x ))
464  iRow++;
465  }
466  // IF we found such a row THEN record a valid position.
467  if( iRow<nRows )
468  {
469  // Logic to ameliorate case where first label is under the
470  // (on track) track name. For later labels it does not matter
471  // as we can scroll left or right and/or zoom.
472  // A possible alternative idea would be to (instead) increase the
473  // translucency of the track name, when the mouse is inside it.
474  if( (i==0 ) && (iRow==0) && bAvoidName ){
475  // reserve some space in first row.
476  // reserve max of 200px or t1, or text box right edge.
477  const int x2 = zoomInfo.TimeToPosition(0.0, r.x) + 200;
478  xUsed[iRow]=x+labelStruct.width+xExtra;
479  if( xUsed[iRow] < x1 ) xUsed[iRow]=x1;
480  if( xUsed[iRow] < x2 ) xUsed[iRow]=x2;
481  iRow=1;
482  }
483 
484  // Possibly update the number of rows actually used.
485  if( iRow >= nRowsUsed )
486  nRowsUsed=iRow+1;
487  // Record the position for this label
488  y= r.y + iRow * yRowHeight +(yRowHeight/2)+1;
489  labelStruct.y=y;
490  // On this row we have used up to max of end marker and width.
491  // Plus also allow space to show the start icon and
492  // some space for the text frame.
493  xUsed[iRow]=x+labelStruct.width+xExtra;
494  if( xUsed[iRow] < x1 ) xUsed[iRow]=x1;
495  ComputeTextPosition( r, i );
496  }
497  }}
498 }
499 
505  wxDC & dc, const LabelStruct &ls, const wxRect & r)
506 {
507  auto &x = ls.x;
508  auto &x1 = ls.x1;
509  auto &y = ls.y;
510 
511  // Bug 2388 - Point label and range label can appear identical
512  // If the start and end times are not actually the same, but they
513  // would appear so when drawn as lines at current zoom, be sure to draw
514  // two lines - i.e. displace the second line slightly.
515  if (ls.getT0() != ls.getT1()) {
516  if (x == x1)
517  x1++;
518  }
519 
520  // How far out from the centre line should the vertical lines
521  // start, i.e. what is the y position of the icon?
522  // We adjust this so that the line encroaches on the icon
523  // slightly (there is white space in the design).
524  const int yIconStart = y - (mIconHeight /2)+1+(mTextHeight+3)/2;
525  const int yIconEnd = yIconStart + mIconHeight-2;
526 
527  // If y is positive then it is the center line for the
528  // Label.
529  if( y >= 0 )
530  {
531  if((x >= r.x) && (x <= (r.x+r.width)))
532  {
533  // Draw line above and below left dragging widget.
534  AColor::Line(dc, x, r.y, x, yIconStart - 1);
535  AColor::Line(dc, x, yIconEnd, x, r.y + r.height);
536  }
537  if((x1 >= r.x) && (x1 <= (r.x+r.width)))
538  {
539  // Draw line above and below right dragging widget.
540  AColor::Line(dc, x1, r.y, x1, yIconStart - 1);
541  AColor::Line(dc, x1, yIconEnd, x1, r.y + r.height);
542  }
543  }
544  else
545  {
546  // Draw the line, even though the widget is off screen
547  AColor::Line(dc, x, r.y, x, r.y + r.height);
548  AColor::Line(dc, x1, r.y, x1, r.y + r.height);
549  }
550 }
551 
556  wxDC & dc, const LabelStruct &ls, const wxRect & r,
557  int GlyphLeft, int GlyphRight)
558 {
559  auto &y = ls.y;
560 
561  const int xHalfWidth=mIconWidth/2;
562  const int yStart=y-mIconHeight/2+(mTextHeight+3)/2;
563 
564  // If y == -1, nothing to draw
565  if( y == -1 )
566  return;
567 
568  auto &x = ls.x;
569  auto &x1 = ls.x1;
570 
571  if((x >= r.x) && (x <= (r.x+r.width)))
572  dc.DrawBitmap(GetGlyph(GlyphLeft), x-xHalfWidth,yStart, true);
573  // The extra test commented out here would suppress right hand markers
574  // when they overlap the left hand marker (e.g. zoomed out) or to the left.
575  if((x1 >= r.x) && (x1 <= (r.x+r.width)) /*&& (x1>x+mIconWidth)*/)
576  dc.DrawBitmap(GetGlyph(GlyphRight), x1-xHalfWidth,yStart, true);
577 }
578 
585 void LabelTrackView::DrawText(wxDC & dc, const LabelStruct &ls, const wxRect & r)
586 {
587  //If y is positive then it is the center line for the
588  //text we are about to draw.
589  //if it isn't, nothing to draw.
590 
591  auto &y = ls.y;
592  if( y == -1 )
593  return;
594 
595  // Draw frame for the text...
596  // We draw it half an icon width left of the text itself.
597  {
598  auto &xText = ls.xText;
599  const int xStart=wxMax(r.x,xText-mIconWidth/2);
600  const int xEnd=wxMin(r.x+r.width,xText+ls.width+mIconWidth/2);
601  const int xWidth = xEnd-xStart;
602 
603  if( (xStart < (r.x+r.width)) && (xEnd > r.x) && (xWidth>0))
604  {
605  // Now draw the text itself.
606  dc.DrawText(ls.title, xText, y-mTextHeight/2);
607  }
608  }
609 
610 }
611 
613  wxDC & dc, const LabelStruct &ls, const wxRect & r)
614 {
615  //If y is positive then it is the center line for the
616  //text we are about to draw.
617  const int yBarHeight=3;
618  const int yFrameHeight = mTextHeight+3;
619  const int xBarShorten = mIconWidth+4;
620  auto &y = ls.y;
621  if( y == -1 )
622  return;
623 
624  {
625  auto &x = ls.x;
626  auto &x1 = ls.x1;
627  const int xStart=wxMax(r.x,x+xBarShorten/2);
628  const int xEnd=wxMin(r.x+r.width,x1-xBarShorten/2);
629  const int xWidth = xEnd-xStart;
630 
631  if( (xStart < (r.x+r.width)) && (xEnd > r.x) && (xWidth>0))
632  {
633 
634  wxRect bar( xStart,y-yBarHeight/2+yFrameHeight/2,
635  xWidth,yBarHeight);
636  if( x1 > x+xBarShorten )
637  dc.DrawRectangle(bar);
638  }
639  }
640 
641  // In drawing the bar and the frame, we compute the clipping
642  // to the viewport ourselves. Under Win98 the GDI does its
643  // calculations in 16 bit arithmetic, and so gets it completely
644  // wrong at higher zooms where the bar can easily be
645  // more than 65536 pixels wide.
646 
647  // Draw bar for label extent...
648  // We don't quite draw from x to x1 because we allow
649  // half an icon width at each end.
650  {
651  auto &xText = ls.xText;
652  const int xStart=wxMax(r.x,xText-mIconWidth/2);
653  const int xEnd=wxMin(r.x+r.width,xText+ls.width+mIconWidth/2);
654  const int xWidth = xEnd-xStart;
655 
656  if( (xStart < (r.x+r.width)) && (xEnd > r.x) && (xWidth>0))
657  {
658  wxRect frame(
659  xStart,y-yFrameHeight/2,
660  xWidth,yFrameHeight );
661  dc.DrawRectangle(frame);
662  }
663  }
664 }
665 
667 void LabelTrackView::DrawHighlight( wxDC & dc, const LabelStruct &ls,
668  int xPos1, int xPos2, int charHeight)
669 {
670  wxPen curPen = dc.GetPen();
671  curPen.SetColour(wxString(wxT("BLUE")));
672  wxBrush curBrush = dc.GetBrush();
673  curBrush.SetColour(wxString(wxT("BLUE")));
674  auto &y = ls.y;
675  if (xPos1 < xPos2)
676  dc.DrawRectangle(xPos1-1, y-charHeight/2, xPos2-xPos1+1, charHeight);
677  else
678  dc.DrawRectangle(xPos2-1, y-charHeight/2, xPos1-xPos2+1, charHeight);
679 }
680 
681 namespace {
682 void getXPos( const LabelStruct &ls, wxDC & dc, int * xPos1, int cursorPos)
683 {
684  *xPos1 = ls.xText;
685  if( cursorPos > 0)
686  {
687  int partWidth;
688  // Calculate the width of the substring and add it to Xpos
689  dc.GetTextExtent(ls.title.Left(cursorPos), &partWidth, NULL);
690  *xPos1 += partWidth;
691  }
692 }
693 }
694 
695 bool LabelTrackView::CalcCursorX( AudacityProject &project, int * x) const
696 {
697  if ( HasSelection( project ) ) {
698  wxMemoryDC dc;
699 
700  if (msFont.Ok()) {
701  dc.SetFont(msFont);
702  }
703 
704  const auto pTrack = FindLabelTrack();
705  const auto &mLabels = pTrack->GetLabels();
706 
707  getXPos(mLabels[mSelIndex], dc, x, mCurrentCursorPos);
708  *x += mIconWidth / 2;
709  return true;
710  }
711 
712  return false;
713 }
714 
715 void LabelTrackView::CalcHighlightXs(int *x1, int *x2) const
716 {
717  wxMemoryDC dc;
718 
719  if (msFont.Ok()) {
720  dc.SetFont(msFont);
721  }
722 
723  int pos1 = mInitialCursorPos, pos2 = mCurrentCursorPos;
724  if (pos1 > pos2)
725  std::swap(pos1, pos2);
726 
727  const auto pTrack = FindLabelTrack();
728  const auto &mLabels = pTrack->GetLabels();
729  const auto &labelStruct = mLabels[mSelIndex];
730 
731  // find the left X pos of highlighted area
732  getXPos(labelStruct, dc, x1, pos1);
733  // find the right X pos of highlighted area
734  getXPos(labelStruct, dc, x2, pos2);
735 }
736 
737 #include "LabelGlyphHandle.h"
738 namespace {
740  {
741  if (! pPanel )
742  return nullptr;
743 
744  // Fetch the highlighting state
745  auto target = pPanel->Target();
746  if (target) {
747  auto handle = dynamic_cast<LabelGlyphHandle*>( target.get() );
748  if (handle)
749  return &*handle->mpHit;
750  }
751  return nullptr;
752  }
753 }
754 
755 #include "../../../TrackPanelDrawingContext.h"
756 #include "LabelTextHandle.h"
757 
762 ( TrackPanelDrawingContext &context, const wxRect & r ) const
763 {
764  auto &dc = context.dc;
765  const auto artist = TrackArtist::Get( context );
766  const auto &zoomInfo = *artist->pZoomInfo;
767 
768  auto pHit = findHit( artist->parent );
769 
770  if(msFont.Ok())
771  dc.SetFont(msFont);
772 
773  if (mFontHeight == -1)
775 
776  const auto pTrack = std::static_pointer_cast< const LabelTrack >(
777  FindTrack()->SubstitutePendingChangedTrack());
778  const auto &mLabels = pTrack->GetLabels();
779 
780  TrackArt::DrawBackgroundWithSelection( context, r, pTrack.get(),
782  ( pTrack->GetSelected() || pTrack->IsSyncLockSelected() ) );
783 
784  wxCoord textWidth, textHeight;
785 
786  // Get the text widths.
787  // TODO: Make more efficient by only re-computing when a
788  // text label title changes.
789  for (const auto &labelStruct : mLabels) {
790  dc.GetTextExtent(labelStruct.title, &textWidth, &textHeight);
791  labelStruct.width = textWidth;
792  }
793 
794  // TODO: And this only needs to be done once, but we
795  // do need the dc to do it.
796  // We need to set mTextHeight to something sensible,
797  // guarding against the case where there are no
798  // labels or all are empty strings, which for example
799  // happens with a NEW label track.
800  dc.GetTextExtent(wxT("Demo Text x^y"), &textWidth, &textHeight);
801  mTextHeight = (int)textHeight;
802  ComputeLayout( r, zoomInfo );
803  dc.SetTextForeground(theTheme.Colour( clrLabelTrackText));
804  dc.SetBackgroundMode(wxTRANSPARENT);
805  dc.SetBrush(AColor::labelTextNormalBrush);
806  dc.SetPen(AColor::labelSurroundPen);
807  int GlyphLeft;
808  int GlyphRight;
809  // Now we draw the various items in this order,
810  // so that the correct things overpaint each other.
811 
812  // Draw vertical lines that show where the end positions are.
813  for (const auto &labelStruct : mLabels)
814  DrawLines( dc, labelStruct, r );
815 
816  // Draw the end glyphs.
817  { int i = -1; for (const auto &labelStruct : mLabels) { ++i;
818  GlyphLeft=0;
819  GlyphRight=1;
820  if( pHit && i == pHit->mMouseOverLabelLeft )
821  GlyphLeft = (pHit->mEdge & 4) ? 6:9;
822  if( pHit && i == pHit->mMouseOverLabelRight )
823  GlyphRight = (pHit->mEdge & 4) ? 7:4;
824  DrawGlyphs( dc, labelStruct, r, GlyphLeft, GlyphRight );
825  }}
826 
827  auto &project = *artist->parent->GetProject();
828 
829  // Draw the label boxes.
830  {
831 #ifdef EXPERIMENTAL_TRACK_PANEL_HIGHLIGHTING
832  bool highlightTrack = false;
833  auto target = dynamic_cast<LabelTextHandle*>(context.target.get());
834  highlightTrack = target && target->GetTrack().get() == this;
835 #endif
836  int i = -1; for (const auto &labelStruct : mLabels) { ++i;
837  bool highlight = false;
838 #ifdef EXPERIMENTAL_TRACK_PANEL_HIGHLIGHTING
839  highlight = highlightTrack && target->GetLabelNum() == i;
840 #endif
841  bool selected = GetSelectedIndex( project ) == i;
842 
843  if( selected )
844  dc.SetBrush( AColor::labelTextEditBrush );
845  else if ( highlight )
846  dc.SetBrush( AColor::uglyBrush );
847  DrawTextBox( dc, labelStruct, r );
848 
849  if (highlight || selected)
850  dc.SetBrush(AColor::labelTextNormalBrush);
851  }
852  }
853 
854  // Draw highlights
855  if ( (mInitialCursorPos != mCurrentCursorPos) && HasSelection( project ) )
856  {
857  int xpos1, xpos2;
858  CalcHighlightXs(&xpos1, &xpos2);
859  DrawHighlight(dc, mLabels[mSelIndex],
860  xpos1, xpos2, mFontHeight);
861  }
862 
863  // Draw the text and the label boxes.
864  { int i = -1; for (const auto &labelStruct : mLabels) { ++i;
865  if( GetSelectedIndex( project ) == i )
866  dc.SetBrush(AColor::labelTextEditBrush);
867  DrawText( dc, labelStruct, r );
868  if( GetSelectedIndex( project ) == i )
869  dc.SetBrush(AColor::labelTextNormalBrush);
870  }}
871 
872  // Draw the cursor, if there is one.
873  if( mDrawCursor && HasSelection( project ) )
874  {
875  const auto &labelStruct = mLabels[mSelIndex];
876  int xPos = labelStruct.xText;
877 
878  if( mCurrentCursorPos > 0)
879  {
880  // Calculate the width of the substring and add it to Xpos
881  int partWidth;
882  dc.GetTextExtent(labelStruct.title.Left(mCurrentCursorPos), &partWidth, NULL);
883  xPos += partWidth;
884  }
885 
886  wxPen currentPen = dc.GetPen();
887  const int CursorWidth=2;
888  currentPen.SetWidth(CursorWidth);
889  AColor::Line(dc,
890  xPos-1, labelStruct.y - mFontHeight/2 + 1,
891  xPos-1, labelStruct.y + mFontHeight/2 - 1);
892  currentPen.SetWidth(1);
893  }
894 }
895 
897  TrackPanelDrawingContext &context,
898  const wxRect &rect, unsigned iPass )
899 {
900  if ( iPass == TrackArtist::PassTracks )
901  Draw( context, rect );
902  CommonTrackView::Draw( context, rect, iPass );
903 }
904 
906 {
907  if ( index >= 0 && index < (int)FindLabelTrack()->GetLabels().size() )
908  mSelIndex = index;
909  else
910  mSelIndex = -1;
911 }
912 
916 {
917  int result = -1;
918  wxMemoryDC dc;
919  if(msFont.Ok())
920  dc.SetFont(msFont);
921 
922  // A bool indicator to see if set the cursor position or not
923  bool finished = false;
924  int charIndex = 1;
925  int partWidth;
926  int oneWidth;
927  double bound;
928  wxString subString;
929 
930  const auto pTrack = FindLabelTrack();
931  const auto &mLabels = pTrack->GetLabels();
932  const auto &labelStruct = mLabels[mSelIndex];
933  const auto &title = labelStruct.title;
934  const int length = title.length();
935  while (!finished && (charIndex < length + 1))
936  {
937  int unichar = (int)title.at( charIndex-1 );
938  if( (0xDC00 <= unichar) && (unichar <= 0xDFFF)){
939  charIndex++;
940  continue;
941  }
942  subString = title.Left(charIndex);
943  // Get the width of substring
944  dc.GetTextExtent(subString, &partWidth, NULL);
945 
946  // Get the width of the last character
947  dc.GetTextExtent(subString.Right(1), &oneWidth, NULL);
948  bound = labelStruct.xText + partWidth - oneWidth * 0.5;
949 
950  if (xPos <= bound)
951  {
952  // Found
953  result = charIndex - 1;
954  finished = true;
955  }
956  else
957  {
958  // Advance
959  charIndex++;
960  }
961  }
962  if (!finished)
963  // Cursor should be in the last position
964  result = length;
965 
966  return result;
967 }
968 
970 {
971  mCurrentCursorPos = pos;
972 }
973 
975  int initialPosition, int currentPosition )
976 {
977  mInitialCursorPos = initialPosition;
979  mDrawCursor = true;
980 }
981 
983 {
984  int charDescent;
985  int charLeading;
986 
987  // Calculate the width of the substring and add it to Xpos
988  dc.GetTextExtent(wxT("(Test String)|[yp]"), NULL, &mFontHeight, &charDescent, &charLeading);
989 
990  // The cursor will have height charHeight. We don't include the descender as
991  // part of the height because for phonetic fonts this leads to cursors which are
992  // too tall. We don't include leading either - it is usually 0.
993  // To make up for ignoring the descender height, we add one pixel above and below
994  // using CursorExtraHeight so that the cursor is just a little taller than the
995  // body of the characters.
996  const int CursorExtraHeight=2;
997  mFontHeight += CursorExtraHeight - (charLeading+charDescent);
998 }
999 
1001 {
1002  if ( !HasSelection( project ) )
1003  return false;
1005  return false;
1006  return true;
1007 }
1008 
1012 {
1013  if (!IsTextSelected( project ))
1014  return false;
1015 
1016  const auto pTrack = FindLabelTrack();
1017  const auto &mLabels = pTrack->GetLabels();
1018 
1019  wxString left, right;
1020  auto labelStruct = mLabels[mSelIndex];
1021  auto &text = labelStruct.title;
1022 
1023  if (!mSelIndex.IsModified()) {
1024  mUndoLabel = text;
1025  }
1026 
1027  int init = mInitialCursorPos;
1028  int cur = mCurrentCursorPos;
1029  if (init > cur)
1030  std::swap(init, cur);
1031 
1032  // data for cutting
1033  wxString data = text.Mid(init, cur - init);
1034 
1035  // get left-remaining text
1036  if (init > 0)
1037  left = text.Left(init);
1038 
1039  // get right-remaining text
1040  if (cur < (int)text.length())
1041  right = text.Mid(cur);
1042 
1043  // set title to the combination of the two remainders
1044  text = left + right;
1045 
1046  pTrack->SetLabel( mSelIndex, labelStruct );
1047 
1048  // copy data onto clipboard
1049  if (wxTheClipboard->Open()) {
1050  // Clipboard owns the data you give it
1051  wxTheClipboard->SetData(safenew wxTextDataObject(data));
1052  wxTheClipboard->Close();
1053  }
1054 
1055  // set cursor positions
1056  mInitialCursorPos = mCurrentCursorPos = left.length();
1057 
1058  mSelIndex.SetModified(true);
1059  return true;
1060 }
1061 
1065 {
1066  if ( !HasSelection( project ) )
1067  return false;
1068 
1069  const auto pTrack = FindLabelTrack();
1070  const auto &mLabels = pTrack->GetLabels();
1071 
1072  const auto &labelStruct = mLabels[mSelIndex];
1073 
1074  int init = mInitialCursorPos;
1075  int cur = mCurrentCursorPos;
1076  if (init > cur)
1077  std::swap(init, cur);
1078 
1079  if (init == cur)
1080  return false;
1081 
1082  // data for copying
1083  wxString data = labelStruct.title.Mid(init, cur-init);
1084 
1085  // copy the data on clipboard
1086  if (wxTheClipboard->Open()) {
1087  // Clipboard owns the data you give it
1088  wxTheClipboard->SetData(safenew wxTextDataObject(data));
1089  wxTheClipboard->Close();
1090  }
1091 
1092  return true;
1093 }
1094 
1095 // PRL: should this set other fields of the label selection?
1099  AudacityProject &project, double sel0, double sel1 )
1100 {
1101  const auto pTrack = FindLabelTrack();
1102 
1103  if ( !HasSelection( project ) )
1104  AddLabel(SelectedRegion(sel0, sel1));
1105 
1106  wxString text, left, right;
1107 
1108  // if text data is available
1109  if (IsTextClipSupported()) {
1110  if (wxTheClipboard->Open()) {
1111  wxTextDataObject data;
1112  wxTheClipboard->GetData(data);
1113  wxTheClipboard->Close();
1114  text = data.GetText();
1115  }
1116 
1117  if (!mSelIndex.IsModified()) {
1118  mUndoLabel = text;
1119  }
1120 
1121  // Convert control characters to blanks
1122  for (int i = 0; i < (int)text.length(); i++) {
1123  if (wxIscntrl(text[i])) {
1124  text[i] = wxT(' ');
1125  }
1126  }
1127  }
1128 
1129  const auto &mLabels = pTrack->GetLabels();
1130  auto labelStruct = mLabels[mSelIndex];
1131  auto &title = labelStruct.title;
1132  int cur = mCurrentCursorPos, init = mInitialCursorPos;
1133  if (init > cur)
1134  std::swap(init, cur);
1135  left = title.Left(init);
1136  if (cur < (int)title.length())
1137  right = title.Mid(cur);
1138 
1139  title = left + text + right;
1140 
1141  pTrack->SetLabel( mSelIndex, labelStruct );
1142 
1143  mInitialCursorPos = mCurrentCursorPos = left.length() + text.length();
1144 
1145  mSelIndex.SetModified(true);
1146  return true;
1147 }
1148 
1151 {
1152  return wxTheClipboard->IsSupported(wxDF_UNICODETEXT);
1153 }
1154 
1155 
1157 {
1158  // may make delayed update of mutable mSelIndex after track selection change
1159  auto track = FindLabelTrack();
1160  if ( track->GetSelected() ||
1161  TrackFocus::Get( project ).Get() == track.get()
1162  )
1163  return mSelIndex = std::max( -1,
1164  std::min<int>( track->GetLabels().size() - 1, mSelIndex ) );
1165  else
1166  return mSelIndex = -1;
1167 }
1168 
1173  const LabelTrack &track, LabelTrackHit &hit, int x, int y)
1174 {
1175  //Determine the NEW selection.
1176  int result=0;
1177  const int d1=10; //distance in pixels, used for have we hit drag handle.
1178  const int d2=5; //distance in pixels, used for have we hit drag handle center.
1179 
1180  //If not over a label, reset it
1181  hit.mMouseOverLabelLeft = -1;
1182  hit.mMouseOverLabelRight = -1;
1183  hit.mEdge = 0;
1184 
1185  const auto pTrack = &track;
1186  const auto &mLabels = pTrack->GetLabels();
1187  { int i = -1; for (const auto &labelStruct : mLabels) { ++i;
1188  //over left or right selection bound
1189  //Check right bound first, since it is drawn after left bound,
1190  //so give it precedence for matching/highlighting.
1191  if( abs(labelStruct.y - (y - (mTextHeight+3)/2)) < d1 &&
1192  abs(labelStruct.x1 - d2 -x) < d1)
1193  {
1194  hit.mMouseOverLabelRight = i;
1195  if(abs(labelStruct.x1 - x) < d2 )
1196  {
1197  result |= 4;
1198  // If left and right co-incident at this resolution, then we drag both.
1199  // We were more stringent about co-incidence here in the past.
1200  if( abs(labelStruct.x1-labelStruct.x) < 5.0 )
1201  {
1202  result |=1;
1203  hit.mMouseOverLabelLeft = i;
1204  }
1205  }
1206  result |= 2;
1207  }
1208  // Use else-if here rather than else to avoid detecting left and right
1209  // of the same label.
1210  else if( abs(labelStruct.y - (y - (mTextHeight+3)/2)) < d1 &&
1211  abs(labelStruct.x + d2 - x) < d1 )
1212  {
1213  hit.mMouseOverLabelLeft = i;
1214  if(abs(labelStruct.x - x) < d2 )
1215  result |= 4;
1216  result |= 1;
1217  }
1218 
1219  // give text box better priority for selecting
1220  if(OverTextBox(&labelStruct, x, y))
1221  {
1222  result = 0;
1223  }
1224 
1225  }}
1226  hit.mEdge = result;
1227 }
1228 
1229 int LabelTrackView::OverATextBox( const LabelTrack &track, int xx, int yy )
1230 {
1231  const auto pTrack = &track;
1232  const auto &mLabels = pTrack->GetLabels();
1233  for (int nn = (int)mLabels.size(); nn--;) {
1234  const auto &labelStruct = mLabels[nn];
1235  if ( OverTextBox( &labelStruct, xx, yy ) )
1236  return nn;
1237  }
1238 
1239  return -1;
1240 }
1241 
1242 // return true if the mouse is over text box, false otherwise
1243 bool LabelTrackView::OverTextBox(const LabelStruct *pLabel, int x, int y)
1244 {
1245  if( (pLabel->xText-(mIconWidth/2) < x) &&
1246  (x<pLabel->xText+pLabel->width+(mIconWidth/2)) &&
1247  (abs(pLabel->y-y)<mIconHeight/2))
1248  {
1249  return true;
1250  }
1251  return false;
1252 }
1253 
1255 static bool IsGoodLabelFirstKey(const wxKeyEvent & evt)
1256 {
1257  int keyCode = evt.GetKeyCode();
1258  return (keyCode < WXK_START
1259  && keyCode != WXK_SPACE && keyCode != WXK_DELETE && keyCode != WXK_RETURN) ||
1260  (keyCode >= WXK_NUMPAD0 && keyCode <= WXK_DIVIDE) ||
1261  (keyCode >= WXK_NUMPAD_EQUAL && keyCode <= WXK_NUMPAD_DIVIDE) ||
1262 #if defined(__WXMAC__)
1263  (keyCode > WXK_RAW_CONTROL) ||
1264 #endif
1265  (keyCode > WXK_WINDOWS_MENU);
1266 }
1267 
1269 static bool IsGoodLabelEditKey(const wxKeyEvent & evt)
1270 {
1271  int keyCode = evt.GetKeyCode();
1272 
1273  // Accept everything outside of WXK_START through WXK_COMMAND, plus the keys
1274  // within that range that are usually printable, plus the ones we use for
1275  // keyboard navigation.
1276  return keyCode < WXK_START ||
1277  (keyCode >= WXK_END && keyCode < WXK_UP) ||
1278  (keyCode == WXK_RIGHT) ||
1279  (keyCode >= WXK_NUMPAD0 && keyCode <= WXK_DIVIDE) ||
1280  (keyCode >= WXK_NUMPAD_SPACE && keyCode <= WXK_NUMPAD_ENTER) ||
1281  (keyCode >= WXK_NUMPAD_HOME && keyCode <= WXK_NUMPAD_END) ||
1282  (keyCode >= WXK_NUMPAD_DELETE && keyCode <= WXK_NUMPAD_DIVIDE) ||
1283 #if defined(__WXMAC__)
1284  (keyCode > WXK_RAW_CONTROL) ||
1285 #endif
1286  (keyCode > WXK_WINDOWS_MENU);
1287 }
1288 
1289 // Check for keys that we will process
1291  AudacityProject &project, wxKeyEvent & event )
1292 {
1293  // Check for modifiers and only allow shift
1294  int mods = event.GetModifiers();
1295  if (mods != wxMOD_NONE && mods != wxMOD_SHIFT) {
1296  return false;
1297  }
1298 
1299  // Always capture the navigation keys, if we have any labels
1300  auto code = event.GetKeyCode();
1301  const auto pTrack = FindLabelTrack();
1302  const auto &mLabels = pTrack->GetLabels();
1303  if ((code == WXK_TAB || code == WXK_NUMPAD_TAB) &&
1304  !mLabels.empty())
1305  return true;
1306 
1307  if ( HasSelection( project ) ) {
1308  if (IsGoodLabelEditKey(event)) {
1309  return true;
1310  }
1311  }
1312  else {
1313  bool typeToCreateLabel;
1314  gPrefs->Read(wxT("/GUI/TypeToCreateLabel"), &typeToCreateLabel, false);
1315  if (IsGoodLabelFirstKey(event) && typeToCreateLabel) {
1316 
1317 
1318 // The commented out code can prevent label creation, causing bug 1551
1319 // We should only be in DoCaptureKey IF this label track has focus,
1320 // and in that case creating a Label is the expected/intended thing.
1321 #if 0
1322  // If we're playing, don't capture if the selection is the same as the
1323  // playback region (this helps prevent label track creation from
1324  // stealing unmodified kbd. shortcuts)
1325  auto gAudioIO = AudioIOBase::Get();
1326  if (pProj->GetAudioIOToken() > 0 &&
1327  gAudioIO->IsStreamActive(pProj->GetAudioIOToken()))
1328  {
1329  double t0, t1;
1330  pProj->GetPlayRegion(&t0, &t1);
1331  if (pProj->mViewInfo.selectedRegion.t0() == t0 &&
1332  pProj->mViewInfo.selectedRegion.t1() == t1) {
1333  return false;
1334  }
1335  }
1336 #endif
1337 
1338  // If there's a label there already don't capture
1339  auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
1340  if( GetLabelIndex(selectedRegion.t0(),
1341  selectedRegion.t1()) != wxNOT_FOUND ) {
1342  return false;
1343  }
1344 
1345  return true;
1346  }
1347  }
1348 
1349  return false;
1350 }
1351 
1353  wxKeyEvent & event, ViewInfo &, wxWindow *, AudacityProject *project )
1354 {
1355  event.Skip(!DoCaptureKey( *project, event ));
1356  return RefreshCode::RefreshNone;
1357 }
1358 
1360  wxKeyEvent & event, ViewInfo &viewInfo, wxWindow *WXUNUSED(pParent),
1361  AudacityProject *project)
1362 {
1363  double bkpSel0 = viewInfo.selectedRegion.t0(),
1364  bkpSel1 = viewInfo.selectedRegion.t1();
1365 
1366  if (!mSelIndex.IsModified() && HasSelection( *project )) {
1367  const auto pTrack = FindLabelTrack();
1368  const auto &mLabels = pTrack->GetLabels();
1369  auto labelStruct = mLabels[mSelIndex];
1370  auto &title = labelStruct.title;
1371  mUndoLabel = title;
1372  }
1373 
1374  // Pass keystroke to labeltrack's handler and add to history if any
1375  // updates were done
1376  if (DoKeyDown( *project, viewInfo.selectedRegion, event )) {
1377  ProjectHistory::Get( *project ).PushState(XO("Modified Label"),
1378  XO("Label Edit"),
1380 
1381  mSelIndex.SetModified(true);
1382  }
1383 
1384  if (!mSelIndex.IsModified()) {
1385  mUndoLabel.clear();
1386  }
1387 
1388  // Make sure caret is in view
1389  int x;
1390  if (CalcCursorX( *project, &x ))
1391  ProjectWindow::Get( *project ).ScrollIntoView(x);
1392 
1393  // If selection modified, refresh
1394  // Otherwise, refresh track display if the keystroke was handled
1395  if (bkpSel0 != viewInfo.selectedRegion.t0() ||
1396  bkpSel1 != viewInfo.selectedRegion.t1())
1397  return RefreshCode::RefreshAll;
1398  else if (!event.GetSkipped())
1399  return RefreshCode::RefreshCell;
1400 
1401  return RefreshCode::RefreshNone;
1402 }
1403 
1405  wxKeyEvent & event, ViewInfo &viewInfo, wxWindow *, AudacityProject *project)
1406 {
1407  double bkpSel0 = viewInfo.selectedRegion.t0(),
1408  bkpSel1 = viewInfo.selectedRegion.t1();
1409  // Pass keystroke to labeltrack's handler and add to history if any
1410  // updates were done
1411 
1412  if (!mSelIndex.IsModified() && HasSelection( *project )) {
1413  const auto pTrack = FindLabelTrack();
1414  const auto &mLabels = pTrack->GetLabels();
1415  auto labelStruct = mLabels[mSelIndex];
1416  auto &title = labelStruct.title;
1417  mUndoLabel = title;
1418  }
1419 
1420  if (DoChar( *project, viewInfo.selectedRegion, event )) {
1421  ProjectHistory::Get( *project ).PushState(XO("Modified Label"),
1422  XO("Label Edit"),
1424 
1425  mSelIndex.SetModified(true);
1426  }
1427 
1428  if (!mSelIndex.IsModified()) {
1429  mUndoLabel.clear();
1430  }
1431 
1432  // If selection modified, refresh
1433  // Otherwise, refresh track display if the keystroke was handled
1434  if (bkpSel0 != viewInfo.selectedRegion.t0() ||
1435  bkpSel1 != viewInfo.selectedRegion.t1())
1436  return RefreshCode::RefreshAll;
1437  else if (!event.GetSkipped())
1438  return RefreshCode::RefreshCell;
1439 
1440  return RefreshCode::RefreshNone;
1441 }
1442 
1445  AudacityProject &project, NotifyingSelectedRegion &newSel, wxKeyEvent & event)
1446 {
1447  // Only track true changes to the label
1448  bool updated = false;
1449 
1450  // Cache the keycode
1451  int keyCode = event.GetKeyCode();
1452  const int mods = event.GetModifiers();
1453 
1454  // Check for modifiers and only allow shift
1455  if (mods != wxMOD_NONE && mods != wxMOD_SHIFT) {
1456  event.Skip();
1457  return updated;
1458  }
1459 
1460  // All editing keys are only active if we're currently editing a label
1461  const auto pTrack = FindLabelTrack();
1462  const auto &mLabels = pTrack->GetLabels();
1463  if ( HasSelection( project ) ) {
1464  auto labelStruct = mLabels[mSelIndex];
1465  auto &title = labelStruct.title;
1466  wxUniChar wchar;
1467  bool more=true;
1468 
1469  switch (keyCode) {
1470 
1471  case WXK_BACK:
1472  {
1473  int len = title.length();
1474 
1475  //IF the label is not blank THEN get rid of a letter or letters according to cursor position
1476  if (len > 0)
1477  {
1478  // IF there are some highlighted letters, THEN DELETE them
1481  else
1482  {
1483  // DELETE one codepoint leftwards
1484  while ((mCurrentCursorPos > 0) && more) {
1485  wchar = title.at( mCurrentCursorPos-1 );
1486  title.erase(mCurrentCursorPos-1, 1);
1488  if( ((int)wchar > 0xDFFF) || ((int)wchar <0xDC00)){
1489  pTrack->SetLabel(mSelIndex, labelStruct);
1490  more = false;
1491  }
1492  }
1493  }
1494  }
1495  else
1496  {
1497  // ELSE no text in text box, so DELETE whole label.
1498  pTrack->DeleteLabel( mSelIndex );
1499  }
1501  updated = true;
1502  }
1503  break;
1504 
1505  case WXK_DELETE:
1506  case WXK_NUMPAD_DELETE:
1507  {
1508  int len = title.length();
1509 
1510  //If the label is not blank get rid of a letter according to cursor position
1511  if (len > 0)
1512  {
1513  // if there are some highlighted letters, DELETE them
1516  else
1517  {
1518  // DELETE one codepoint rightwards
1519  while ((mCurrentCursorPos < len) && more) {
1520  wchar = title.at( mCurrentCursorPos );
1521  title.erase(mCurrentCursorPos, 1);
1522  if( ((int)wchar > 0xDBFF) || ((int)wchar <0xD800)){
1523  pTrack->SetLabel(mSelIndex, labelStruct);
1524  more = false;
1525  }
1526  }
1527  }
1528  }
1529  else
1530  {
1531  // DELETE whole label if no text in text box
1532  pTrack->DeleteLabel( mSelIndex );
1533  }
1535  updated = true;
1536  }
1537  break;
1538 
1539  case WXK_HOME:
1540  case WXK_NUMPAD_HOME:
1541  // Move cursor to beginning of label
1542  mCurrentCursorPos = 0;
1543  if (mods == wxMOD_SHIFT)
1544  ;
1545  else
1547  break;
1548 
1549  case WXK_END:
1550  case WXK_NUMPAD_END:
1551  // Move cursor to end of label
1552  mCurrentCursorPos = (int)title.length();
1553  if (mods == wxMOD_SHIFT)
1554  ;
1555  else
1557  break;
1558 
1559  case WXK_LEFT:
1560  case WXK_NUMPAD_LEFT:
1561  // Moving cursor left
1562  while ((mCurrentCursorPos > 0) && more) {
1563  wchar = title.at( mCurrentCursorPos-1 );
1564  more = !( ((int)wchar > 0xDFFF) || ((int)wchar <0xDC00));
1565 
1567  if (mods == wxMOD_SHIFT)
1568  ;
1569  else
1572  }
1573  break;
1574 
1575  case WXK_RIGHT:
1576  case WXK_NUMPAD_RIGHT:
1577  // Moving cursor right
1578  while ((mCurrentCursorPos < (int)title.length())&& more) {
1579  wchar = title.at( mCurrentCursorPos );
1580  more = !( ((int)wchar > 0xDBFF) || ((int)wchar <0xD800));
1581 
1583  if (mods == wxMOD_SHIFT)
1584  ;
1585  else
1588  }
1589  break;
1590 
1591  case WXK_ESCAPE:
1592  if (mSelIndex.IsModified()) {
1593  title = mUndoLabel;
1594  pTrack->SetLabel(mSelIndex, labelStruct);
1595 
1596  ProjectHistory::Get( project ).PushState(XO("Modified Label"),
1597  XO("Label Edit"),
1599  }
1600 
1601  case WXK_RETURN:
1602  case WXK_NUMPAD_ENTER:
1603  if (mRestoreFocus >= 0) {
1604  auto track = *TrackList::Get( project ).Any()
1605  .begin().advance(mRestoreFocus);
1606  if (track)
1607  TrackFocus::Get( project ).Set(track);
1608  mRestoreFocus = -2;
1609  }
1610  mSelIndex = -1;
1611  break;
1612 
1613  case WXK_TAB:
1614  case WXK_NUMPAD_TAB:
1615  if (event.ShiftDown()) {
1616  --mSelIndex;
1617  } else {
1618  ++mSelIndex;
1619  }
1620 
1621  mSelIndex = (mSelIndex + (int)mLabels.size()) % (int)mLabels.size(); // wrap round if necessary
1622  {
1623  const auto &newLabel = mLabels[mSelIndex];
1624  mCurrentCursorPos = newLabel.title.length();
1626  //Set the selection region to be equal to the selection bounds of the tabbed-to label.
1627  newSel = newLabel.selectedRegion;
1628  }
1629  break;
1630 
1631  case '\x10': // OSX
1632  case WXK_MENU:
1633  case WXK_WINDOWS_MENU:
1634  ShowContextMenu( project );
1635  break;
1636 
1637  default:
1638  if (!IsGoodLabelEditKey(event)) {
1639  event.Skip();
1640  }
1641  break;
1642  }
1643  }
1644  else
1645  {
1646  switch (keyCode) {
1647 
1648  case WXK_TAB:
1649  case WXK_NUMPAD_TAB:
1650  if (!mLabels.empty()) {
1651  int len = (int) mLabels.size();
1652  if (event.ShiftDown()) {
1653  mSelIndex = len - 1;
1654  if (newSel.t0() > mLabels[0].getT0()) {
1655  while (mSelIndex >= 0 &&
1656  mLabels[mSelIndex].getT0() > newSel.t0()) {
1657  --mSelIndex;
1658  }
1659  }
1660  } else {
1661  mSelIndex = 0;
1662  if (newSel.t0() < mLabels[len - 1].getT0()) {
1663  while (mSelIndex < len &&
1664  mLabels[mSelIndex].getT0() < newSel.t0()) {
1665  ++mSelIndex;
1666  }
1667  }
1668  }
1669 
1670  if (mSelIndex >= 0 && mSelIndex < len) {
1671  const auto &labelStruct = mLabels[mSelIndex];
1672  mCurrentCursorPos = labelStruct.title.length();
1674  //Set the selection region to be equal to the selection bounds of the tabbed-to label.
1675  newSel = labelStruct.selectedRegion;
1676  }
1677  else {
1678  mSelIndex = -1;
1679  }
1680  }
1681  break;
1682 
1683  default:
1684  if (!IsGoodLabelFirstKey(event)) {
1685  event.Skip();
1686  }
1687  break;
1688  }
1689  }
1690 
1691  // Make sure the caret is visible
1692  mDrawCursor = true;
1693 
1694  return updated;
1695 }
1696 
1700  AudacityProject &project, NotifyingSelectedRegion &WXUNUSED(newSel),
1701  wxKeyEvent & event)
1702 {
1703  // Check for modifiers and only allow shift.
1704  //
1705  // We still need to check this or we will eat the top level menu accelerators
1706  // on Windows if our capture or key down handlers skipped the event.
1707  const int mods = event.GetModifiers();
1708  if (mods != wxMOD_NONE && mods != wxMOD_SHIFT) {
1709  event.Skip();
1710  return false;
1711  }
1712 
1713  // Only track true changes to the label
1714  bool updated = false;
1715 
1716  // Cache the character
1717  wxChar charCode = event.GetUnicodeKey();
1718 
1719  // Skip if it's not a valid unicode character or a control character
1720  if (charCode == 0 || wxIscntrl(charCode)) {
1721  event.Skip();
1722  return false;
1723  }
1724 
1725  // If we've reached this point and aren't currently editing, add NEW label
1726  const auto pTrack = FindLabelTrack();
1727  if ( !HasSelection( project ) ) {
1728  // Don't create a NEW label for a space
1729  if (wxIsspace(charCode)) {
1730  event.Skip();
1731  return false;
1732  }
1733  bool useDialog;
1734  gPrefs->Read(wxT("/GUI/DialogForNameNewLabel"), &useDialog, false);
1735  auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
1736  if (useDialog) {
1737  wxString title;
1738  if (DialogForLabelName(
1739  project, selectedRegion, charCode, title) ==
1740  wxID_CANCEL) {
1741  return false;
1742  }
1743  pTrack->SetSelected(true);
1744  pTrack->AddLabel(selectedRegion, title);
1745  ProjectHistory::Get( project )
1746  .PushState(XO("Added label"), XO("Label"));
1747  return false;
1748  }
1749  else {
1750  pTrack->SetSelected(true);
1751  AddLabel( selectedRegion );
1752  ProjectHistory::Get( project )
1753  .PushState(XO("Added label"), XO("Label"));
1754  }
1755  }
1756 
1757  //
1758  // Now we are definitely in a label; append the incoming character
1759  //
1760 
1761  // Test if cursor is in the end of string or not
1764 
1765  const auto& mLabels = pTrack->GetLabels();
1766  auto labelStruct = mLabels[mSelIndex];
1767  auto& title = labelStruct.title;
1768 
1769  if (mCurrentCursorPos < (int)title.length()) {
1770  // Get substring on the righthand side of cursor
1771  wxString rightPart = title.Mid(mCurrentCursorPos);
1772  // Set title to substring on the lefthand side of cursor
1773  title = title.Left(mCurrentCursorPos);
1774  //append charcode
1775  title += charCode;
1776  //append the right part substring
1777  title += rightPart;
1778  }
1779  else
1780  //append charCode
1781  title += charCode;
1782 
1783  pTrack->SetLabel( mSelIndex, labelStruct );
1784 
1785  //moving cursor position forward
1787  updated = true;
1788 
1789  // Make sure the caret is visible
1790  mDrawCursor = true;
1791 
1792  return updated;
1793 }
1794 
1795 enum
1796 {
1797  OnCutSelectedTextID = 1, // OSX doesn't like a 0 menu id
1802 };
1803 
1805 {
1806  wxWindow *parent = wxWindow::FindFocus();
1807 
1808  // Bug 2044. parent can be nullptr after a context switch.
1809  if( !parent )
1810  parent = &GetProjectFrame( project );
1811 
1812  if( parent )
1813  {
1814  wxMenu menu;
1815  menu.Bind(wxEVT_MENU,
1816  [this, &project]( wxCommandEvent &event ){
1817  OnContextMenu( project, event ); }
1818  );
1819 
1820  menu.Append(OnCutSelectedTextID, _("Cu&t Label text"));
1821  menu.Append(OnCopySelectedTextID, _("&Copy Label text"));
1822  menu.Append(OnPasteSelectedTextID, _("&Paste"));
1823  menu.Append(OnDeleteSelectedLabelID, _("&Delete Label"));
1824  menu.Append(OnEditSelectedLabelID, _("&Edit Label..."));
1825 
1826  menu.Enable(OnCutSelectedTextID, IsTextSelected( project ));
1827  menu.Enable(OnCopySelectedTextID, IsTextSelected( project ));
1829  menu.Enable(OnDeleteSelectedLabelID, true);
1830  menu.Enable(OnEditSelectedLabelID, true);
1831 
1832  if( !HasSelection( project ) ) {
1833  wxASSERT( false );
1834  return;
1835  }
1836 
1837  const auto pTrack = FindLabelTrack();
1838  const LabelStruct *ls = pTrack->GetLabel(mSelIndex);
1839 
1840  wxClientDC dc(parent);
1841 
1842  if (msFont.Ok())
1843  {
1844  dc.SetFont(msFont);
1845  }
1846 
1847  int x = 0;
1848  bool success = CalcCursorX( project, &x );
1849  wxASSERT(success);
1850  static_cast<void>(success); // Suppress unused variable warning if debug mode is disabled
1851 
1852  // Bug #2571: Hackage alert! For some reason wxGTK does not like
1853  // displaying the LabelDialog from within the PopupMenu "context".
1854  // So, workaround it by editing the label AFTER the popup menu is
1855  // closed. It's really ugly, but it works. :-(
1856  mEditIndex = -1;
1857  parent->PopupMenu(&menu, x, ls->y + (mIconHeight / 2) - 1);
1858  if (mEditIndex >= 0)
1859  {
1860  DoEditLabels( project, FindLabelTrack().get(), mEditIndex );
1861  }
1862  }
1863 }
1864 
1866  AudacityProject &project, wxCommandEvent & evt )
1867 {
1868  auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
1869 
1870  switch (evt.GetId())
1871  {
1873  case OnCutSelectedTextID:
1874  if (CutSelectedText( project ))
1875  {
1876  ProjectHistory::Get( project ).PushState(XO("Modified Label"),
1877  XO("Label Edit"),
1879  }
1880  break;
1881 
1883  case OnCopySelectedTextID:
1884  CopySelectedText( project );
1885  break;
1886 
1888  case OnPasteSelectedTextID:
1889  if (PasteSelectedText(
1890  project, selectedRegion.t0(), selectedRegion.t1() ))
1891  {
1892  ProjectHistory::Get( project ).PushState(XO("Modified Label"),
1893  XO("Label Edit"),
1895  }
1896  break;
1897 
1899  case OnDeleteSelectedLabelID: {
1900  int ndx = GetLabelIndex(selectedRegion.t0(), selectedRegion.t1());
1901  if (ndx != -1)
1902  {
1903  const auto pTrack = FindLabelTrack();
1904  pTrack->DeleteLabel(ndx);
1905  ProjectHistory::Get( project ).PushState(XO("Deleted Label"),
1906  XO("Label Edit"),
1908  }
1909  }
1910  break;
1911 
1912  case OnEditSelectedLabelID: {
1913  // Bug #2571: See above
1914  mEditIndex = GetLabelIndex(selectedRegion.t0(), selectedRegion.t1());
1915  }
1916  break;
1917  }
1918 }
1919 
1921 {
1922  wxString left, right;
1923 
1924  int init = mInitialCursorPos;
1925  int cur = mCurrentCursorPos;
1926  if (init > cur)
1927  std::swap(init, cur);
1928 
1929  const auto pTrack = FindLabelTrack();
1930  const auto &mLabels = pTrack->GetLabels();
1931  auto labelStruct = mLabels[mSelIndex];
1932  auto &title = labelStruct.title;
1933 
1934  if (init > 0)
1935  left = title.Left(init);
1936 
1937  if (cur < (int)title.length())
1938  right = title.Mid(cur);
1939 
1940  title = left + right;
1941  pTrack->SetLabel( mSelIndex, labelStruct );
1942  mInitialCursorPos = mCurrentCursorPos = left.length();
1943 }
1944 
1946 {
1947  const auto selIndex = GetSelectedIndex( project );
1948  return (selIndex >= 0 &&
1949  selIndex < (int)FindLabelTrack()->GetLabels().size());
1950 }
1951 
1952 int LabelTrackView::GetLabelIndex(double t, double t1)
1953 {
1954  //We'd have liked to have times in terms of samples,
1955  //because then we're doing an intrger comparison.
1956  //Never mind. Instead we look for near enough.
1957  //This level of (in)accuracy is only a problem if we
1958  //deal with sounds in the MHz range.
1959  const double delta = 1.0e-7;
1960  const auto pTrack = FindLabelTrack();
1961  const auto &mLabels = pTrack->GetLabels();
1962  { int i = -1; for (const auto &labelStruct : mLabels) { ++i;
1963  if( fabs( labelStruct.getT0() - t ) > delta )
1964  continue;
1965  if( fabs( labelStruct.getT1() - t1 ) > delta )
1966  continue;
1967  return i;
1968  }}
1969 
1970  return wxNOT_FOUND;
1971 }
1972 
1973 
1974 // restoreFocus of -1 is the default, and sets the focus to this label.
1975 // restoreFocus of -2 or other value leaves the focus unchanged.
1976 // restoreFocus >= 0 will later cause focus to move to that track.
1977 int LabelTrackView::AddLabel(const SelectedRegion &selectedRegion,
1978  const wxString &title, int restoreFocus)
1979 {
1980  const auto pTrack = FindLabelTrack();
1981  mRestoreFocus = restoreFocus;
1982  auto pos = pTrack->AddLabel( selectedRegion, title );
1983  return pos;
1984 }
1985 
1987 {
1988  e.Skip();
1989  if ( e.mpTrack.lock() != FindTrack() )
1990  return;
1991 
1992  const auto &title = e.mTitle;
1993  const auto pos = e.mPresentPosition;
1994 
1995  mInitialCursorPos = mCurrentCursorPos = title.length();
1996 
1997  // restoreFocus is -2 e.g. from Nyquist label creation, when we should not
1998  // even lose the focus and open the label to edit in the first place.
1999  // -1 means we don't need to restore it to anywhere.
2000  // 0 or above is the track to restore to after editing the label is complete.
2001  if( mRestoreFocus >= -1 )
2002  mSelIndex = pos;
2003 
2004  if( mRestoreFocus < 0 )
2005  mRestoreFocus = -2;
2006 
2007  // Make sure the caret is visible
2008  //
2009  // LLL: The cursor will not be drawn when the first label
2010  // is added since mDrawCursor will be false. Presumably,
2011  // if the user adds a label, then a cursor should be drawn
2012  // to indicate that typing is expected.
2013  //
2014  // If the label is added during actions like import, then the
2015  // mDrawCursor flag will be reset once the action is complete.
2016  mDrawCursor = true;
2017 }
2018 
2020 {
2021  e.Skip();
2022  if ( e.mpTrack.lock() != FindTrack() )
2023  return;
2024 
2025  auto index = e.mFormerPosition;
2026 
2027  // IF we've deleted the selected label
2028  // THEN set no label selected.
2029  if( mSelIndex== index )
2030  {
2031  mSelIndex = -1;
2032  mCurrentCursorPos = 1;
2033  }
2034  // IF we removed a label before the selected label
2035  // THEN the NEW selected label number is one less.
2036  else if( index < mSelIndex )
2037  {
2038  --mSelIndex;
2039  }
2040 }
2041 
2043 {
2044  e.Skip();
2045  if ( e.mpTrack.lock() != FindTrack() )
2046  return;
2047 
2048  auto former = e.mFormerPosition;
2049  auto present = e.mPresentPosition;
2050 
2051  if ( mSelIndex == former )
2052  mSelIndex = present;
2053  else if ( former < mSelIndex && mSelIndex <= present )
2054  -- mSelIndex;
2055  else if ( former > mSelIndex && mSelIndex >= present )
2056  ++ mSelIndex;
2057 }
2058 
2060 {
2061  e.Skip();
2062  if ( e.mpTrack.lock() != FindTrack() )
2063  return;
2064 
2065  if ( !FindTrack()->GetSelected() )
2066  mSelIndex = -1;
2067 }
2068 
2069 wxBitmap & LabelTrackView::GetGlyph( int i)
2070 {
2071  return theTheme.Bitmap( i + bmpLabelGlyph0);
2072 }
2073 
2074 // This one XPM spec is used to generate a number of
2075 // different wxIcons.
2076 /* XPM */
2077 static const char *const GlyphXpmRegionSpec[] = {
2078 /* columns rows colors chars-per-pixel */
2079 "15 23 7 1",
2080 /* Default colors, with first color transparent */
2081 ". c none",
2082 "2 c black",
2083 "3 c black",
2084 "4 c black",
2085 "5 c #BEBEF0",
2086 "6 c #BEBEF0",
2087 "7 c #BEBEF0",
2088 /* pixels */
2089 "...............",
2090 "...............",
2091 "...............",
2092 "....333.444....",
2093 "...3553.4774...",
2094 "...3553.4774...",
2095 "..35553.47774..",
2096 "..35522222774..",
2097 ".3552666662774.",
2098 ".3526666666274.",
2099 "355266666662774",
2100 "355266666662774",
2101 "355266666662774",
2102 ".3526666666274.",
2103 ".3552666662774.",
2104 "..35522222774..",
2105 "..35553.47774..",
2106 "...3553.4774...",
2107 "...3553.4774...",
2108 "....333.444....",
2109 "...............",
2110 "...............",
2111 "..............."
2112 };
2113 
2130 {
2131  int iConfig;
2132  int iHighlight;
2133  int index;
2134  const int nSpecRows =
2135  sizeof( GlyphXpmRegionSpec )/sizeof( GlyphXpmRegionSpec[0]);
2136  const char *XmpBmp[nSpecRows];
2137 
2138  // The glyphs are declared static wxIcon; so we only need
2139  // to create them once, no matter how many LabelTracks.
2140  if( mbGlyphsReady )
2141  return;
2142 
2143  // We're about to tweak the basic color spec to get 12 variations.
2144  for( iConfig=0;iConfig<NUM_GLYPH_CONFIGS;iConfig++)
2145  {
2146  for( iHighlight=0;iHighlight<NUM_GLYPH_HIGHLIGHTS;iHighlight++)
2147  {
2148  index = iConfig + NUM_GLYPH_CONFIGS * iHighlight;
2149  // Copy the basic spec...
2150  memcpy( XmpBmp, GlyphXpmRegionSpec, sizeof( GlyphXpmRegionSpec ));
2151  // The highlighted region (if any) is white...
2152  if( iHighlight==1 ) XmpBmp[5]="5 c #FFFFFF";
2153  if( iHighlight==2 ) XmpBmp[6]="6 c #FFFFFF";
2154  if( iHighlight==3 ) XmpBmp[7]="7 c #FFFFFF";
2155  // For left or right arrow the other side of the glyph
2156  // is the transparent color.
2157  if( iConfig==0) { XmpBmp[3]="3 c none"; XmpBmp[5]="5 c none"; }
2158  if( iConfig==1) { XmpBmp[4]="4 c none"; XmpBmp[7]="7 c none"; }
2159  // Create the icon from the tweaked spec.
2160  mBoundaryGlyphs[index] = wxBitmap(XmpBmp);
2161  // Create the mask
2162  // SetMask takes ownership
2163  mBoundaryGlyphs[index].SetMask(safenew wxMask(mBoundaryGlyphs[index], wxColour(192, 192, 192)));
2164  }
2165  }
2166 
2167  mIconWidth = mBoundaryGlyphs[0].GetWidth();
2168  mIconHeight = mBoundaryGlyphs[0].GetHeight();
2169  mTextHeight = mIconHeight; // until proved otherwise...
2170  // The icon should have an odd width so that the
2171  // line goes exactly down the middle.
2172  wxASSERT( (mIconWidth %2)==1);
2173 
2174  mbGlyphsReady=true;
2175 }
2176 
2177 #include "../../../LabelDialog.h"
2178 
2180 (AudacityProject &project, LabelTrack *lt, int index)
2181 {
2182  const auto &settings = ProjectSettings::Get( project );
2183  auto format = settings.GetSelectionFormat(),
2184  freqFormat = settings.GetFrequencySelectionFormatName();
2185  auto &tracks = TrackList::Get( project );
2186  auto rate = ProjectSettings::Get( project ).GetRate();
2187  auto &viewInfo = ViewInfo::Get( project );
2188  auto &window = ProjectWindow::Get( project );
2189 
2190  LabelDialog dlg(&window, project, &tracks,
2191  lt, index,
2192  viewInfo, rate,
2193  format, freqFormat);
2194 #ifdef __WXGTK__
2195  dlg.Raise();
2196 #endif
2197 
2198  if (dlg.ShowModal() == wxID_OK) {
2199  ProjectHistory::Get( project )
2200  .PushState(XO("Edited labels"), XO("Label"));
2201  }
2202 }
2203 
2205  AudacityProject &project,
2206  const SelectedRegion& region, const wxString& initialValue, wxString& value)
2207 {
2208  auto &trackFocus = TrackFocus::Get( project );
2209  auto &trackPanel = TrackPanel::Get( project );
2210  auto &viewInfo = ViewInfo::Get( project );
2211 
2212  wxPoint position =
2213  trackPanel.FindTrackRect( trackFocus.Get() ).GetBottomLeft();
2214  // The start of the text in the text box will be roughly in line with the label's position
2215  // if it's a point label, or the start of its region if it's a region label.
2216  position.x += viewInfo.GetLabelWidth()
2217  + std::max(0, static_cast<int>(viewInfo.TimeToPosition(region.t0())))
2218  -40;
2219  position.y += 2; // just below the bottom of the track
2220  position = trackPanel.ClientToScreen(position);
2221  auto &window = GetProjectFrame( project );
2222  AudacityTextEntryDialog dialog{ &window,
2223  XO("Name:"),
2224  XO("New label"),
2225  initialValue,
2226  wxOK | wxCANCEL,
2227  position };
2228 
2229  // keep the dialog within Audacity's window, so that the dialog is always fully visible
2230  wxRect dialogScreenRect = dialog.GetScreenRect();
2231  wxRect projScreenRect = window.GetScreenRect();
2232  wxPoint max = projScreenRect.GetBottomRight() + wxPoint{ -dialogScreenRect.width, -dialogScreenRect.height };
2233  if (dialogScreenRect.x > max.x) {
2234  position.x = max.x;
2235  dialog.Move(position);
2236  }
2237  if (dialogScreenRect.y > max.y) {
2238  position.y = max.y;
2239  dialog.Move(position);
2240  }
2241 
2242  dialog.SetInsertionPointEnd(); // because, by default, initial text is selected
2243  int status = dialog.ShowModal();
2244  if (status != wxID_CANCEL) {
2245  value = dialog.GetValue();
2246  value.Trim(true).Trim(false);
2247  }
2248 
2249  return status;
2250 }
2251 
2253 template<> template<> auto DoGetLabelTrackView::Implementation() -> Function {
2254  return [](LabelTrack &track) {
2255  return std::make_shared<LabelTrackView>( track.SharedPointer() );
2256  };
2257 }
2259 
2260 std::shared_ptr<TrackVRulerControls> LabelTrackView::DoGetVRulerControls()
2261 {
2262  return
2263  std::make_shared<LabelTrackVRulerControls>( shared_from_this() );
2264 }
LabelTrackView::OnLabelPermuted
void OnLabelPermuted(LabelTrackEvent &)
Definition: LabelTrackView.cpp:2042
LabelStruct::x1
int x1
Pixel position of left hand glyph.
Definition: LabelTrack.h:76
ViewInfo::Get
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:156
IsGoodLabelEditKey
static bool IsGoodLabelEditKey(const wxKeyEvent &evt)
This returns true for keys we capture for label editing.
Definition: LabelTrackView.cpp:1269
LabelTrackView::DrawGlyphs
static void DrawGlyphs(wxDC &dc, const LabelStruct &ls, const wxRect &r, int GlyphLeft, int GlyphRight)
Definition: LabelTrackView.cpp:555
LabelTextHandle
Definition: LabelTextHandle.h:24
LabelTrackView::DoGetVRulerControls
std::shared_ptr< TrackVRulerControls > DoGetVRulerControls() override
Definition: LabelTrackView.cpp:2260
LabelTrackView::ResetFlags
void ResetFlags()
Definition: LabelTrackView.cpp:217
LabelTrackView::mGlyphHandle
std::weak_ptr< LabelGlyphHandle > mGlyphHandle
Definition: LabelTrackView.h:251
CommonTrackView
Definition: CommonTrackView.h:20
AColor::labelUnselectedBrush
static wxBrush labelUnselectedBrush
Definition: AColor.h:111
LabelTrackView::IsTextClipSupported
static bool IsTextClipSupported()
Definition: LabelTrackView.cpp:1150
LabelTrackView::ComputeTextPosition
void ComputeTextPosition(const wxRect &r, int index) const
Definition: LabelTrackView.cpp:264
LabelTrackView::CopySelectedText
bool CopySelectedText(AudacityProject &project)
Definition: LabelTrackView.cpp:1064
LabelDialog
Dialog for editing labels.
Definition: LabelDialog.h:36
LabelTrackView::CalcCursorX
bool CalcCursorX(AudacityProject &project, int *x) const
Definition: LabelTrackView.cpp:695
LabelTrackView::mCurrentCursorPos
int mCurrentCursorPos
Definition: LabelTrackView.h:202
RefreshCode::RefreshAll
@ RefreshAll
Definition: RefreshCode.h:26
LabelStruct::width
int width
Text of the label.
Definition: LabelTrack.h:72
currentPosition
size_t currentPosition
Definition: ScripterCallback.cpp:62
TrackView::Get
static TrackView & Get(Track &)
Definition: TrackView.cpp:63
RefreshCode::RefreshNone
@ RefreshNone
Definition: RefreshCode.h:21
LabelStruct::title
wxString title
Definition: LabelTrack.h:71
LabelTrackView::Char
unsigned Char(wxKeyEvent &event, ViewInfo &viewInfo, wxWindow *pParent, AudacityProject *project) override
Definition: LabelTrackView.cpp:1404
gPrefs
FileConfig * gPrefs
Definition: Prefs.cpp:68
LabelStruct::x
int x
width of the text in pixels.
Definition: LabelTrack.h:75
LabelTrackView::CreateCustomGlyphs
void CreateCustomGlyphs()
Definition: LabelTrackView.cpp:2129
LabelTrackView::mTextHeight
static int mTextHeight
Definition: LabelTrackView.h:196
LabelTrackView::DetailedHitTest
std::vector< UIHandlePtr > DetailedHitTest(const TrackPanelMouseState &state, const AudacityProject *pProject, int currentTool, bool bMultiTool) override
Definition: LabelTrackView.cpp:179
ZoomInfo
Definition: ZoomInfo.h:47
ClientData::Site::Get
Subclass & Get(const RegisteredFactory &key)
Get reference to an attachment, creating on demand if not present, down-cast it to Subclass.
Definition: ClientData.h:309
NUM_GLYPH_HIGHLIGHTS
constexpr int NUM_GLYPH_HIGHLIGHTS
Definition: LabelTrackView.h:34
LabelTrackView::FindLabelTrack
std::shared_ptr< LabelTrack > FindLabelTrack()
Definition: LabelTrackView.cpp:168
AudacityTextEntryDialog
Wrap wxTextEntryDialog so that caption IS translatable.
Definition: ErrorDialog.h:80
CommonTrackPanelCell::FindTrack
std::shared_ptr< Track > FindTrack()
Definition: CommonTrackPanelCell.h:44
LabelTrackView::mIconHeight
static int mIconHeight
Definition: LabelTrackView.h:194
TrackPanelDrawingContext
Definition: TrackPanelDrawingContext.h:22
LabelTrackView::Index::operator--
Index & operator--()
Definition: LabelTrackView.cpp:71
AColor::Line
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:112
ViewInfo
Definition: ViewInfo.h:172
LabelTrackView::DrawTextBox
static void DrawTextBox(wxDC &dc, const LabelStruct &ls, const wxRect &r)
Definition: LabelTrackView.cpp:612
LabelTrackView::HasSelection
bool HasSelection(AudacityProject &project) const
Definition: LabelTrackView.cpp:1945
LabelTrackView::DrawLines
static void DrawLines(wxDC &dc, const LabelStruct &ls, const wxRect &r)
Definition: LabelTrackView.cpp:504
LabelTrackEvent::mFormerPosition
int mFormerPosition
Definition: LabelTrack.h:207
LabelTrackView::Index::SetModified
void SetModified(bool modified)
Definition: LabelTrackView.cpp:88
TrackPanel::Get
static TrackPanel & Get(AudacityProject &project)
Definition: TrackPanel.cpp:222
UndoPush::NONE
@ NONE
XO
#define XO(s)
Definition: Internat.h:31
LabelTrackView::GetFont
static wxFont GetFont(const wxString &faceName, int size=DefaultFontSize)
Definition: LabelTrackView.cpp:232
ProjectSettings::Get
static ProjectSettings & Get(AudacityProject &project)
Definition: ProjectSettings.cpp:40
AColor::uglyBrush
static wxBrush uglyBrush
Definition: AColor.h:132
LabelTextHandle.h
LabelTrack
A LabelTrack is a Track that holds labels (LabelStruct).
Definition: LabelTrack.h:88
LabelTrackView::msFont
static wxFont msFont
Definition: LabelTrackView.h:254
LabelTrackView::mSelIndex
Index mSelIndex
Definition: LabelTrackView.h:190
LabelTrackView::mRestoreFocus
int mRestoreFocus
Definition: LabelTrackView.h:207
TrackPanelDrawingContext::dc
wxDC & dc
Definition: TrackPanelDrawingContext.h:23
ProjectWindow::Get
static ProjectWindow & Get(AudacityProject &project)
Definition: ProjectWindow.cpp:532
LabelGlyphHandle::HitTest
static UIHandlePtr HitTest(std::weak_ptr< LabelGlyphHandle > &holder, const wxMouseState &state, const std::shared_ptr< LabelTrack > &pLT, const wxRect &rect)
Definition: LabelGlyphHandle.cpp:97
OnPasteSelectedTextID
@ OnPasteSelectedTextID
Definition: LabelTrackView.cpp:1799
GlyphXpmRegionSpec
static const char *const GlyphXpmRegionSpec[]
Definition: LabelTrackView.cpp:2077
anonymous_namespace{LabelTrackView.cpp}::getXPos
void getXPos(const LabelStruct &ls, wxDC &dc, int *xPos1, int cursorPos)
Definition: LabelTrackView.cpp:682
LabelTrackView::ComputeLayout
void ComputeLayout(const wxRect &r, const ZoomInfo &zoomInfo) const
Definition: LabelTrackView.cpp:411
NUM_GLYPH_CONFIGS
constexpr int NUM_GLYPH_CONFIGS
Definition: LabelTrackView.h:33
LabelTrackView::OverTextBox
static bool OverTextBox(const LabelStruct *pLabel, int x, int y)
Definition: LabelTrackView.cpp:1243
LabelTrackView::Flags::mSelIndex
Index mSelIndex
Definition: LabelTrackView.h:146
LabelTrackEvent::mPresentPosition
int mPresentPosition
Definition: LabelTrack.h:210
TrackPanel
The TrackPanel class coordinates updates and operations on the main part of the screen which contains...
Definition: TrackPanel.h:64
LabelTrackView::PasteSelectedText
bool PasteSelectedText(AudacityProject &project, double sel0, double sel1)
Definition: LabelTrackView.cpp:1098
LabelStruct
A LabelStruct holds information for ONE label in a LabelTrack.
Definition: LabelTrack.h:30
TrackPanelMouseState::rect
const wxRect & rect
Definition: TrackPanelMouseEvent.h:39
ZoomInfo::TimeToPosition
wxInt64 TimeToPosition(double time, wxInt64 origin=0, bool ignoreFisheye=false) const
STM: Converts a project time to screen x position.
Definition: ZoomInfo.cpp:53
LabelTrackView::mbGlyphsReady
static bool mbGlyphsReady
Definition: LabelTrackView.h:198
LabelTrackView::RestoreFlags
void RestoreFlags(const Flags &flags)
Definition: LabelTrackView.cpp:224
LabelTrackView::mIconWidth
static int mIconWidth
Definition: LabelTrackView.h:195
LabelTrackHit::mEdge
int mEdge
Definition: LabelGlyphHandle.h:37
NotifyingSelectedRegion::t1
double t1() const
Definition: ViewInfo.h:46
ThemeBase::Bitmap
wxBitmap & Bitmap(int iIndex)
Definition: Theme.cpp:1212
LabelTrackView::calculateFontHeight
static void calculateFontHeight(wxDC &dc)
Definition: LabelTrackView.cpp:982
AColor::labelTextEditBrush
static wxBrush labelTextEditBrush
Definition: AColor.h:110
LabelTrackView::BindTo
void BindTo(LabelTrack *pParent)
Definition: LabelTrackView.cpp:120
LabelTextHandle::GetTrack
std::shared_ptr< LabelTrack > GetTrack() const
Definition: LabelTextHandle.h:38
LabelStruct::y
int y
Pixel position of left hand side of text box.
Definition: LabelTrack.h:78
AttachedVirtualFunction::Override::Implementation
static Function Implementation()
A function returning a std::function that must be defined so that the program links.
Definition: LabelTrackControls.cpp:182
ProjectWindow::ScrollIntoView
void ScrollIntoView(double pos)
Definition: ProjectWindow.cpp:783
LabelTrackView
Definition: LabelTrackView.h:40
LabelTrackView::FindCursorPosition
int FindCursorPosition(wxCoord xPos)
convert pixel coordinate to character position in text box
Definition: LabelTrackView.cpp:915
LabelTrackView::KeyDown
unsigned KeyDown(wxKeyEvent &event, ViewInfo &viewInfo, wxWindow *pParent, AudacityProject *project) override
Definition: LabelTrackView.cpp:1359
LabelTrackView::Reparent
void Reparent(const std::shared_ptr< Track > &parent) override
Definition: LabelTrackView.cpp:109
ViewInfo::selectedRegion
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:199
TrackArtist::Get
static TrackArtist * Get(TrackPanelDrawingContext &)
Definition: TrackArtist.cpp:75
LabelTextHandle::HitTest
static UIHandlePtr HitTest(std::weak_ptr< LabelTextHandle > &holder, const wxMouseState &state, const std::shared_ptr< LabelTrack > &pLT)
Definition: LabelTextHandle.cpp:53
LabelTrackView::OverATextBox
static int OverATextBox(const LabelTrack &track, int xx, int yy)
Definition: LabelTrackView.cpp:1229
LabelTrackView::mFontHeight
static int mFontHeight
Definition: LabelTrackView.h:201
LabelTrackView::OverGlyph
static void OverGlyph(const LabelTrack &track, LabelTrackHit &hit, int x, int y)
Definition: LabelTrackView.cpp:1172
LabelTrackView::SetSelectedIndex
void SetSelectedIndex(int index)
Definition: LabelTrackView.cpp:905
LabelTrackView::CutSelectedText
bool CutSelectedText(AudacityProject &project)
Definition: LabelTrackView.cpp:1011
LabelStruct::getT0
double getT0() const
Definition: LabelTrack.h:40
LabelTrackView::SetCurrentCursorPosition
void SetCurrentCursorPosition(int pos)
Definition: LabelTrackView.cpp:969
GetProjectFrame
AUDACITY_DLL_API wxFrame & GetProjectFrame(AudacityProject &project)
Get the top-level window associated with the project (as a wxFrame only, when you do not need to use ...
Definition: Project.cpp:186
LabelTrackView::mBoundaryGlyphs
static wxBitmap mBoundaryGlyphs[NUM_GLYPH_CONFIGS *NUM_GLYPH_HIGHLIGHTS]
Definition: LabelTrackView.h:199
LabelTrackView::DefaultFontSize
@ DefaultFontSize
Definition: LabelTrackView.h:47
LabelTrackView::OnLabelDeleted
void OnLabelDeleted(LabelTrackEvent &)
Definition: LabelTrackView.cpp:2019
LabelTrackView::mEditIndex
int mEditIndex
Definition: LabelTrackView.h:257
LabelTrackView::OnSelectionChange
void OnSelectionChange(LabelTrackEvent &)
Definition: LabelTrackView.cpp:2059
AColor::labelSelectedBrush
static wxBrush labelSelectedBrush
Definition: AColor.h:112
LabelTrackView.h
UIHandlePtr
std::shared_ptr< UIHandle > UIHandlePtr
Definition: CellularPanel.h:28
LabelTrack::GetLabels
const LabelArray & GetLabels() const
Definition: LabelTrack.h:130
LabelTrackHit::mMouseOverLabelRight
int mMouseOverLabelRight
Keeps track of which left label the mouse is currently over.
Definition: LabelGlyphHandle.h:39
format
int format
Definition: ExportPCM.cpp:54
LabelTrackEvent::mTitle
wxString mTitle
Definition: LabelTrack.h:204
LabelTrackView::Flags::mDrawCursor
bool mDrawCursor
Definition: LabelTrackView.h:147
AudioIOBase::Get
static AudioIOBase * Get()
Definition: AudioIOBase.cpp:94
LabelTrackView::ShowContextMenu
void ShowContextMenu(AudacityProject &project)
Definition: LabelTrackView.cpp:1804
LabelTrackView::UnbindFrom
void UnbindFrom(LabelTrack *pParent)
Definition: LabelTrackView.cpp:132
CellularPanel::Target
UIHandlePtr Target()
Definition: CellularPanel.cpp:1097
LabelTrackView::Get
static LabelTrackView & Get(LabelTrack &)
Definition: LabelTrackView.cpp:158
TrackFocus::Get
Track * Get()
Definition: TrackPanelAx.cpp:755
LabelTrackView::Index::IsModified
bool IsModified() const
Definition: LabelTrackView.cpp:83
anonymous_namespace{NoteTrack.cpp}::swap
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
Definition: NoteTrack.cpp:735
LabelTrackEvent
Definition: LabelTrack.h:184
LabelTrackView::CopyTo
void CopyTo(Track &track) const override
Definition: LabelTrackView.cpp:144
RefreshCode::RefreshCell
@ RefreshCell
Definition: RefreshCode.h:24
LabelTrackView::Draw
void Draw(TrackPanelDrawingContext &context, const wxRect &r) const
Definition: LabelTrackView.cpp:762
LabelTrackView::Flags::mInitialCursorPos
int mInitialCursorPos
Definition: LabelTrackView.h:145
LabelTrackView::mUndoLabel
wxString mUndoLabel
Keeps track of the currently selected label.
Definition: LabelTrackView.h:192
LabelGlyphHandle.h
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
LabelGlyphHandle
Definition: LabelGlyphHandle.h:49
ProjectHistory::PushState
void PushState(const TranslatableString &desc, const TranslatableString &shortDesc)
Definition: ProjectHistory.cpp:90
LabelTrackView::GetGlyph
static wxBitmap & GetGlyph(int i)
Definition: LabelTrackView.cpp:2069
SelectedRegion::t0
double t0() const
Definition: SelectedRegion.h:94
LabelTrackView::mInitialCursorPos
int mInitialCursorPos
current cursor position
Definition: LabelTrackView.h:203
LabelTrackView::SetTextHighlight
void SetTextHighlight(int initialPosition, int currentPosition)
Definition: LabelTrackView.cpp:974
LabelTrackView::LabelTrackView
LabelTrackView(const LabelTrackView &)=delete
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:386
LabelTrackView::ResetFont
static void ResetFont()
Definition: LabelTrackView.cpp:243
TrackList::Get
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:495
anonymous_namespace{LabelTrackView.cpp}::findHit
LabelTrackHit * findHit(TrackPanel *pPanel)
Definition: LabelTrackView.cpp:739
LabelGlyphHandle::mpHit
std::shared_ptr< LabelTrackHit > mpHit
Definition: LabelGlyphHandle.h:86
Track
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:238
LabelTrackView::DoEditLabels
static void DoEditLabels(AudacityProject &project, LabelTrack *lt=nullptr, int index=-1)
Definition: LabelTrackView.cpp:2180
_
#define _(s)
Definition: Internat.h:75
LabelTrackView::DoChar
bool DoChar(AudacityProject &project, NotifyingSelectedRegion &sel, wxKeyEvent &event)
Definition: LabelTrackView.cpp:1699
LabelTrackView::OnContextMenu
void OnContextMenu(AudacityProject &project, wxCommandEvent &evt)
Definition: LabelTrackView.cpp:1865
AColor::labelTextNormalBrush
static wxBrush labelTextNormalBrush
Definition: AColor.h:109
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:112
OnDeleteSelectedLabelID
@ OnDeleteSelectedLabelID
Definition: LabelTrackView.cpp:1800
LabelTrackView::DrawHighlight
static void DrawHighlight(wxDC &dc, const LabelStruct &ls, int xPos1, int xPos2, int charHeight)
Draws text-selected region within the label.
Definition: LabelTrackView.cpp:667
ProjectSettings::GetRate
double GetRate() const
Definition: ProjectSettings.cpp:168
LabelTrackView::~LabelTrackView
~LabelTrackView() override
Definition: LabelTrackView.cpp:105
LabelTrackView::RemoveSelectedText
void RemoveSelectedText()
Definition: LabelTrackView.cpp:1920
TrackPanelMouseState
Definition: TrackPanelMouseEvent.h:28
TrackView::CopyTo
void CopyTo(Track &track) const override
Definition: TrackView.cpp:52
TrackPanelDrawingContext::target
UIHandlePtr target
Definition: TrackPanelDrawingContext.h:24
UndoPush::CONSOLIDATE
@ CONSOLIDATE
ThemeBase::Colour
wxColour & Colour(int iIndex)
Definition: Theme.cpp:1193
CommonTrackCell::Reparent
virtual void Reparent(const std::shared_ptr< Track > &parent)
Definition: CommonTrackPanelCell.cpp:64
AttachedVirtualFunction::Override
For defining overrides of the method.
Definition: AttachedVirtualFunction.h:190
OnCutSelectedTextID
@ OnCutSelectedTextID
Definition: LabelTrackView.cpp:1797
LabelTrackView::Index::operator=
Index & operator=(int index)
Definition: LabelTrackView.cpp:55
LabelTrackView::mTextHandle
std::weak_ptr< LabelTextHandle > mTextHandle
Definition: LabelTrackView.h:252
TrackArtist::PassTracks
@ PassTracks
Definition: TrackArtist.h:63
LabelTrackView::DoKeyDown
bool DoKeyDown(AudacityProject &project, NotifyingSelectedRegion &sel, wxKeyEvent &event)
KeyEvent is called for every keypress when over the label track.
Definition: LabelTrackView.cpp:1444
LabelTrackView::CalcHighlightXs
void CalcHighlightXs(int *x1, int *x2) const
Definition: LabelTrackView.cpp:715
ZoomInfo::GetLabelWidth
int GetLabelWidth() const
Definition: ZoomInfo.h:101
theTheme
AUDACITY_DLL_API Theme theTheme
Definition: Theme.cpp:202
LabelTrackView::DialogForLabelName
static int DialogForLabelName(AudacityProject &project, const SelectedRegion &region, const wxString &initialValue, wxString &value)
Definition: LabelTrackView.cpp:2204
NotifyingSelectedRegion::t0
double t0() const
Definition: ViewInfo.h:45
LabelTrackView::DoCaptureKey
bool DoCaptureKey(AudacityProject &project, wxKeyEvent &event)
Definition: LabelTrackView.cpp:1290
OnEditSelectedLabelID
@ OnEditSelectedLabelID
Definition: LabelTrackView.cpp:1801
TrackListEvent::mpTrack
std::weak_ptr< Track > mpTrack
Definition: Track.h:1221
TrackList::Any
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:1354
safenew
#define safenew
Definition: MemoryX.h:10
LabelTrackView::CaptureKey
unsigned CaptureKey(wxKeyEvent &event, ViewInfo &viewInfo, wxWindow *pParent, AudacityProject *project) override
Definition: LabelTrackView.cpp:1352
LabelTrackHit
Definition: LabelGlyphHandle.h:33
settings
static Settings & settings()
Definition: TrackInfo.cpp:87
LabelTrackHit::mMouseOverLabelLeft
int mMouseOverLabelLeft
Definition: LabelGlyphHandle.h:38
LabelTrackView::Index
Definition: LabelTrackView.h:127
LabelTrackView::Flags::mCurrentCursorPos
int mCurrentCursorPos
Definition: LabelTrackView.h:145
LabelTrackView::Index::operator++
Index & operator++()
Definition: LabelTrackView.cpp:64
LabelStruct::xText
int xText
Pixel position of right hand glyph.
Definition: LabelTrack.h:77
IsGoodLabelFirstKey
static bool IsGoodLabelFirstKey(const wxKeyEvent &evt)
Returns true for keys we capture to start a label.
Definition: LabelTrackView.cpp:1255
LabelTrackView::OnLabelAdded
void OnLabelAdded(LabelTrackEvent &)
Definition: LabelTrackView.cpp:1986
LabelTrackView::DrawText
static void DrawText(wxDC &dc, const LabelStruct &ls, const wxRect &r)
Definition: LabelTrackView.cpp:585
LabelTrackVRulerControls.h
OnCopySelectedTextID
@ OnCopySelectedTextID
Definition: LabelTrackView.cpp:1798
LabelTrackView::Index::Index
Index()
Definition: LabelTrackView.cpp:43
MAX_NUM_ROWS
constexpr int MAX_NUM_ROWS
Definition: LabelTrackView.h:35
registerDoGetLabelTrackView
static DoGetLabelTrackView registerDoGetLabelTrackView
Definition: LabelTrackView.cpp:2258
LabelTrackView::AddLabel
int AddLabel(const SelectedRegion &region, const wxString &title={}, int restoreFocus=-1)
Definition: LabelTrackView.cpp:1977
LabelStruct::getT1
double getT1() const
Definition: LabelTrack.h:41
LabelTrackView::GetLabelIndex
int GetLabelIndex(double t, double t1)
Definition: LabelTrackView.cpp:1952
LabelTrackView::IsTextSelected
bool IsTextSelected(AudacityProject &project) const
Definition: LabelTrackView.cpp:1000
TrackPanelMouseState::state
wxMouseState & state
Definition: TrackPanelMouseEvent.h:38
LabelTrackView::GetSelectedIndex
int GetSelectedIndex(AudacityProject &project) const
Definition: LabelTrackView.cpp:1156
ProjectHistory::Get
static ProjectHistory & Get(AudacityProject &project)
Definition: ProjectHistory.cpp:26
LabelTrackView::mDrawCursor
bool mDrawCursor
initial cursor position
Definition: LabelTrackView.h:205
AColor::labelSurroundPen
static wxPen labelSurroundPen
Definition: AColor.h:117
LabelTrackView::Flags
Definition: LabelTrackView.h:144
NotifyingSelectedRegion
Definition: ViewInfo.h:42
SelectedRegion
Defines a selected portion of a project.
Definition: SelectedRegion.h:38
TrackPanelDrawable::Draw
virtual void Draw(TrackPanelDrawingContext &context, const wxRect &rect, unsigned iPass)
Definition: TrackPanelDrawable.cpp:17