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