Audacity  2.3.1
TrackPanel.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  TrackPanel.cpp
6 
7  Dominic Mazzoni
8  and lots of other contributors
9 
10  Implements TrackPanel and TrackInfo.
11 
12 ********************************************************************//***************************************************************//*****************************************************************//**************************************************************//*****************************************************************/
62 
63 #include "Audacity.h"
64 #include "AdornedRulerPanel.h"
65 #include "Experimental.h"
66 #include "TrackPanel.h"
67 #include "Project.h"
68 #include "TrackPanelMouseEvent.h"
69 #include "TrackPanelResizeHandle.h"
70 //#define DEBUG_DRAW_TIMING 1
71 
72 #include "AColor.h"
73 #include "AllThemeResources.h"
74 #include "AudioIO.h"
75 #include "float_cast.h"
76 
77 #include "Prefs.h"
78 #include "RefreshCode.h"
79 #include "TrackArtist.h"
80 #include "TrackPanelAx.h"
81 #include "WaveTrack.h"
82 #ifdef EXPERIMENTAL_MIDI_OUT
83 #include "NoteTrack.h"
84 #endif
85 
87 #include "toolbars/ToolsToolBar.h"
88 
89 //This loads the appropriate set of cursors, depending on platform.
90 #include "../images/Cursors.h"
91 
92 #include "widgets/ASlider.h"
93 #include <algorithm>
94 
95 wxDEFINE_EVENT(EVT_TRACK_PANEL_TIMER, wxCommandEvent);
96 
160 // Is the distance between A and B less than D?
161 template < class A, class B, class DIST > bool within(A a, B b, DIST d)
162 {
163  return (a > b - d) && (a < b + d);
164 }
165 
166 BEGIN_EVENT_TABLE(TrackPanel, CellularPanel)
167  EVT_MOUSE_EVENTS(TrackPanel::OnMouseEvent)
168  EVT_KEY_DOWN(TrackPanel::OnKeyDown)
169 
170  EVT_PAINT(TrackPanel::OnPaint)
171 
172  EVT_TIMER(wxID_ANY, TrackPanel::OnTimer)
174 
177 std::unique_ptr<wxCursor> MakeCursor( int WXUNUSED(CursorId), const char * const pXpm[36], int HotX, int HotY )
178 {
179 #ifdef CURSORS_SIZE32
180  const int HotAdjust =0;
181 #else
182  const int HotAdjust =8;
183 #endif
184 
185  wxImage Image = wxImage(wxBitmap(pXpm).ConvertToImage());
186  Image.SetMaskColour(255,0,0);
187  Image.SetMask();// Enable mask.
188 
189  Image.SetOption( wxIMAGE_OPTION_CUR_HOTSPOT_X, HotX-HotAdjust );
190  Image.SetOption( wxIMAGE_OPTION_CUR_HOTSPOT_Y, HotY-HotAdjust );
191  return std::make_unique<wxCursor>( Image );
192 }
193 
194 
195 
196 // Don't warn us about using 'this' in the base member initializer list.
197 #ifndef __WXGTK__ //Get rid if this pragma for gtk
198 #pragma warning( disable: 4355 )
199 #endif
200 TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id,
201  const wxPoint & pos,
202  const wxSize & size,
203  const std::shared_ptr<TrackList> &tracks,
204  ViewInfo * viewInfo,
205  TrackPanelListener * listener,
206  AdornedRulerPanel * ruler)
207  : CellularPanel(parent, id, pos, size, viewInfo,
208  wxWANTS_CHARS | wxNO_BORDER),
209  mListener(listener),
210  mTracks(tracks),
211  mRuler(ruler),
212  mTrackArtist(nullptr),
213  mRefreshBacking(false),
214  vrulerSize(36,0)
215 #ifndef __WXGTK__ //Get rid if this pragma for gtk
216 #pragma warning( default: 4355 )
217 #endif
218 {
220  TrackInfo::UpdatePrefs( this );
221 
222  SetLayoutDirection(wxLayout_LeftToRight);
223  SetLabel(_("Track Panel"));
224  SetName(_("Track Panel"));
225  SetBackgroundStyle(wxBG_STYLE_PAINT);
226 
227  {
228  auto pAx = std::make_unique <TrackPanelAx>( this );
229 #if wxUSE_ACCESSIBILITY
230  // wxWidgets owns the accessible object
231  SetAccessible(mAx = pAx.release());
232 #else
233  // wxWidgets does not own the object, but we need to retain it
234  mAx = std::move(pAx);
235 #endif
236  }
237 
238  mRedrawAfterStop = false;
239 
240  mTrackArtist = std::make_unique<TrackArtist>( this );
241 
242  mTimeCount = 0;
243  mTimer.parent = this;
244  // Timer is started after the window is visible
245  GetProject()->Bind(wxEVT_IDLE, &TrackPanel::OnIdle, this);
246 
247  // Register for tracklist updates
248  mTracks->Bind(EVT_TRACKLIST_RESIZING,
250  this);
251  mTracks->Bind(EVT_TRACKLIST_ADDITION,
253  this);
254  mTracks->Bind(EVT_TRACKLIST_DELETION,
256  this);
257  wxTheApp->Bind(EVT_AUDIOIO_PLAYBACK,
259  this);
260 }
261 
262 
264 {
265  mTimer.Stop();
266 
267  // This can happen if a label is being edited and the user presses
268  // ALT+F4 or Command+Q
269  if (HasCapture())
270  ReleaseMouse();
271 }
272 
274 {
275  auto rect = FindTrackRect( wt, true );
276  wxRect sliderRect;
277  TrackInfo::GetGainRect( rect.GetTopLeft(), sliderRect );
278  return TrackInfo::GainSlider(sliderRect, wt, false, this);
279 }
280 
282 {
283  auto rect = FindTrackRect( wt, true );
284  wxRect sliderRect;
285  TrackInfo::GetPanRect( rect.GetTopLeft(), sliderRect );
286  return TrackInfo::PanSlider(sliderRect, wt, false, this);
287 }
288 
289 #ifdef EXPERIMENTAL_MIDI_OUT
291 {
292  auto rect = FindTrackRect( nt, true );
293  wxRect sliderRect;
294  TrackInfo::GetVelocityRect( rect.GetTopLeft(), sliderRect );
295  return TrackInfo::VelocitySlider(sliderRect, nt, false, this);
296 }
297 #endif
298 
299 wxString TrackPanel::gSoloPref;
300 
302 {
303  gPrefs->Read(wxT("/GUI/AutoScroll"), &mViewInfo->bUpdateTrackIndicator,
304  true);
305  gPrefs->Read(wxT("/GUI/Solo"), &gSoloPref, wxT("Simple"));
306 
308 
309  if (mTrackArtist) {
310  mTrackArtist->UpdatePrefs();
311  }
312 
313  // All vertical rulers must be recalculated since the minimum and maximum
314  // frequences may have been changed.
315  UpdateVRulers();
316 
317  TrackInfo::UpdatePrefs( this );
318 
319  Refresh();
320 }
321 
323 {
325 }
326 
327 
328 void TrackPanel::GetTracksUsableArea(int *width, int *height) const
329 {
330  GetSize(width, height);
331  if (width) {
332  *width -= GetLeftOffset();
333  *width -= kRightMargin;
334  *width = std::max(0, *width);
335  }
336 }
337 
341 {
342  //JKC casting away constness here.
343  //Do it in two stages in case 'this' is not a wxWindow.
344  //when the compiler will flag the error.
345  wxWindow const * const pConstWind = this;
346  wxWindow * pWind=(wxWindow*)pConstWind;
347 #ifdef EXPERIMENTAL_NOTEBOOK
348  pWind = pWind->GetParent(); //Page
349  wxASSERT( pWind );
350  pWind = pWind->GetParent(); //Notebook
351  wxASSERT( pWind );
352 #endif
353  pWind = pWind->GetParent(); //MainPanel
354  wxASSERT( pWind );
355  pWind = pWind->GetParent(); //Project
356  wxASSERT( pWind );
357  return (AudacityProject*)pWind;
358 }
359 
360 void TrackPanel::OnIdle(wxIdleEvent& event)
361 {
362  // The window must be ready when the timer fires (#1401)
363  if (IsShownOnScreen())
364  {
365  mTimer.Start(kTimerInterval, FALSE);
366 
367  // Timer is started, we don't need the event anymore
368  GetProject()->Unbind(wxEVT_IDLE, &TrackPanel::OnIdle, this);
369  }
370  else
371  {
372  // Get another idle event, wx only guarantees we get one
373  // event after "some other normal events occur"
374  event.RequestMore();
375  }
376 }
377 
379 void TrackPanel::OnTimer(wxTimerEvent& )
380 {
381 #ifdef __WXMAC__
382  // Unfortunate part of fix for bug 1431
383  // Without this, the toolbars hide only every other time that you press
384  // the yellow title bar button. For some reason, not every press sends
385  // us a deactivate event for the application.
386  {
387  auto project = GetProject();
388  if (project->IsIconized())
389  project->MacShowUndockedToolbars(false);
390  }
391 #endif
392 
393  mTimeCount++;
394 
395  AudacityProject *const p = GetProject();
396 
397  // Check whether we were playing or recording, but the stream has stopped.
398  if (p->GetAudioIOToken()>0 && !IsAudioActive())
399  {
400  //the stream may have been started up after this one finished (by some other project)
401  //in that case reset the buttons don't stop the stream
403  }
404 
405  // Next, check to see if we were playing or recording
406  // audio, but now Audio I/O is completely finished.
407  if (p->GetAudioIOToken()>0 &&
409  {
410  p->FixScrollbars();
411  p->SetAudioIOToken(0);
412  p->RedrawProject();
413 
414  mRedrawAfterStop = false;
415 
416  //ANSWER-ME: Was DisplaySelection added to solve a repaint problem?
418  }
421  }
422 
423  // Notify listeners for timer ticks
424  {
425  wxCommandEvent e(EVT_TRACK_PANEL_TIMER);
426  p->GetEventHandler()->ProcessEvent(e);
427  }
428 
429  DrawOverlays(false);
430  mRuler->DrawOverlays(false);
431 
433 
434  // Periodically update the display while recording
435 
436  if (!mRedrawAfterStop) {
437  mRedrawAfterStop = true;
439  mListener->TP_ScrollUpDown( 99999999 );
440  Refresh( false );
441  }
442  else {
443  if ((mTimeCount % 5) == 0) {
444  // Must tell OnPaint() to recreate the backing bitmap
445  // since we've not done a full refresh.
446  mRefreshBacking = true;
447  Refresh( false );
448  }
449  }
450  }
451  if(mTimeCount > 1000)
452  mTimeCount = 0;
453 }
454 
456 {
457  int width;
458  GetTracksUsableArea(&width, NULL);
459  return mViewInfo->PositionToTime(width, 0, true);
460 }
461 
464 void TrackPanel::OnPaint(wxPaintEvent & /* event */)
465 {
467 
468 #if DEBUG_DRAW_TIMING
469  wxStopWatch sw;
470 #endif
471 
472  {
473  wxPaintDC dc(this);
474 
475  // Retrieve the damage rectangle
476  wxRect box = GetUpdateRegion().GetBox();
477 
478  // Recreate the backing bitmap if we have a full refresh
479  // (See TrackPanel::Refresh())
480  if (mRefreshBacking || (box == GetRect()))
481  {
482  // Reset (should a mutex be used???)
483  mRefreshBacking = false;
484 
485  // Redraw the backing bitmap
487 
488  // Copy it to the display
489  DisplayBitmap(dc);
490  }
491  else
492  {
493  // Copy full, possibly clipped, damage rectangle
494  RepairBitmap(dc, box.x, box.y, box.width, box.height);
495  }
496 
497  // Done with the clipped DC
498 
499  // Drawing now goes directly to the client area.
500  // DrawOverlays() may need to draw outside the clipped region.
501  // (Used to make a NEW, separate wxClientDC, but that risks flashing
502  // problems on Mac.)
503  dc.DestroyClippingRegion();
504  DrawOverlays(true, &dc);
505  }
506 
507 #if DEBUG_DRAW_TIMING
508  sw.Pause();
509  wxLogDebug(wxT("Total: %ld milliseconds"), sw.Time());
510  wxPrintf(wxT("Total: %ld milliseconds\n"), sw.Time());
511 #endif
512 }
513 
514 void TrackPanel::MakeParentModifyState(bool bWantsAutoSave)
515 {
516  mListener->TP_ModifyState(bWantsAutoSave);
517 }
518 
520 {
522 }
523 
524 namespace {
525  std::shared_ptr<Track> FindTrack(TrackPanelCell *pCell )
526  {
527  if (pCell)
528  return static_cast<CommonTrackPanelCell*>( pCell )->FindTrack();
529  return {};
530  }
531 }
532 
534  (TrackPanelCell *pClickedCell, TrackPanelCell *pLatestCell,
535  UIHandle::Result refreshResult)
536 {
537  const auto panel = this;
538  auto pLatestTrack = FindTrack( pLatestCell ).get();
539 
540  // This precaution checks that the track is not only nonnull, but also
541  // really owned by the track list
542  auto pClickedTrack = GetTracks()->Lock(
543  std::weak_ptr<Track>{ FindTrack( pClickedCell ) }
544  ).get();
545 
546  // TODO: make a finer distinction between refreshing the track control area,
547  // and the waveform area. As it is, redraw both whenever you must redraw either.
548 
549  // Copy data from the underlying tracks to the pending tracks that are
550  // really displayed
551  panel->GetProject()->GetTracks()->UpdatePendingTracks();
552 
553  using namespace RefreshCode;
554 
555  if (refreshResult & DestroyedCell) {
556  panel->UpdateViewIfNoTracks();
557  // Beware stale pointer!
558  if (pLatestTrack == pClickedTrack)
559  pLatestTrack = NULL;
560  pClickedTrack = NULL;
561  }
562 
563  if (pClickedTrack && (refreshResult & RefreshCode::UpdateVRuler))
564  panel->UpdateVRuler(pClickedTrack);
565 
566  if (refreshResult & RefreshCode::DrawOverlays) {
567  panel->DrawOverlays(false);
568  mRuler->DrawOverlays(false);
569  }
570 
571  // Refresh all if told to do so, or if told to refresh a track that
572  // is not known.
573  const bool refreshAll =
574  ( (refreshResult & RefreshAll)
575  || ((refreshResult & RefreshCell) && !pClickedTrack)
576  || ((refreshResult & RefreshLatestCell) && !pLatestTrack));
577 
578  if (refreshAll)
579  panel->Refresh(false);
580  else {
581  if (refreshResult & RefreshCell)
582  panel->RefreshTrack(pClickedTrack);
583  if (refreshResult & RefreshLatestCell)
584  panel->RefreshTrack(pLatestTrack);
585  }
586 
587  if (refreshResult & FixScrollbars)
588  panel->MakeParentRedrawScrollbars();
589 
590  if (refreshResult & Resize)
591  panel->GetListener()->TP_HandleResize();
592 
593  // This flag is superfluous if you do full refresh,
594  // because TrackPanel::Refresh() does this too
595  if (refreshResult & UpdateSelection) {
596  panel->DisplaySelection();
597 
598  {
599  // Formerly in TrackPanel::UpdateSelectionDisplay():
600 
601  // Make sure the ruler follows suit.
602  // mRuler->DrawSelection();
603 
604  // ... but that too is superfluous it does nothing but refresh
605  // the ruler, while DisplaySelection calls TP_DisplaySelection which
606  // also always refreshes the ruler.
607  }
608  }
609 
610  if ((refreshResult & RefreshCode::EnsureVisible) && pClickedTrack)
611  panel->EnsureVisible(pClickedTrack);
612 }
613 
615 {
617 }
618 
620 {
622 }
623 
625 {
627  return p->IsAudioActive();
628 }
629 
630 void TrackPanel::UpdateStatusMessage( const wxString &st )
631 {
632  auto status = st;
633  if (HasEscape())
634  /* i18n-hint Esc is a key on the keyboard */
635  status += wxT(" "), status += _("(Esc to cancel)");
637 }
638 
640 {
641  return true;
642 }
643 
645 {
646  // Full refresh since the label area may need to indicate
647  // newly selected tracks.
648  Refresh(false);
649 
650  // Make sure the ruler follows suit.
652 
653  // As well as the SelectionBar.
655 }
656 
658 {
659  if (mAx)
660  mAx->Updated();
661 }
662 
663 // Counts tracks, counting stereo tracks as one track.
665 {
666  return GetTracks()->Leaders().size();
667 }
668 
669 // Counts selected tracks, counting stereo tracks as one track.
671 {
672  return GetTracks()->SelectedLeaders().size();
673 }
674 
675 void TrackPanel::MessageForScreenReader(const wxString& message)
676 {
677  if (mAx)
678  mAx->MessageForScreenReader(message);
679 }
680 
682 {
683  if (mTracks->empty())
684  {
685  // BG: There are no more tracks on screen
686  //BG: Set zoom to normal
688 
689  //STM: Set selection to 0,0
690  //PRL: and default the rest of the selection information
692 
693  // PRL: Following causes the time ruler to align 0 with left edge.
694  // Bug 972
695  mViewInfo->h = 0;
696 
699  mListener->TP_DisplayStatusMessage(wxT("")); //STM: Clear message if all tracks are removed
700  }
701 }
702 
703 void TrackPanel::OnPlayback(wxEvent &e)
704 {
705  e.Skip();
706  // Starting or stopping of play or record affects some cursors.
707  // Start or stop is in progress now, not completed; so delay the cursor
708  // change until next idle time.
709  CallAfter( [this] { HandleCursorForPresentMouseState(); } );
710 }
711 
712 // The tracks positions within the list have changed, so update the vertical
713 // ruler size for the track that triggered the event.
715 {
716  auto t = e.mpTrack.lock();
717  // A deleted track can trigger the event. In which case do nothing here.
718  if( t )
719  UpdateVRuler(t.get());
720  e.Skip();
721 }
722 
723 // Tracks have been removed from the list.
725 {
726  // copy shared_ptr for safety, as in HandleClick
727  auto handle = Target();
728  if (handle) {
729  handle->OnProjectChange(GetProject());
730  }
731 
732  // If the focused track disappeared but there are still other tracks,
733  // this reassigns focus.
734  GetFocusedTrack();
735 
737 
738  e.Skip();
739 }
740 
742  using DrawFunction = void (*)(
743  TrackPanelDrawingContext &context,
744  const wxRect &rect,
745  const Track *maybeNULL
746  );
747 
748  unsigned items; // a bitwise OR of values of the enum above
749  int height;
752 };
753 
754 namespace {
755 
756 #define RANGE(array) (array), (array) + sizeof(array)/sizeof(*(array))
757 using TCPLines = std::vector< TrackInfo::TCPLine >;
758 
759 enum : unsigned {
760  // The sequence is not significant, just keep bits distinct
761  kItemBarButtons = 1 << 0,
762  kItemStatusInfo1 = 1 << 1,
763  kItemMute = 1 << 2,
764  kItemSolo = 1 << 3,
765  kItemGain = 1 << 4,
766  kItemPan = 1 << 5,
767  kItemVelocity = 1 << 6,
768  kItemMidiControlsRect = 1 << 7,
769  kItemMinimize = 1 << 8,
770  kItemSyncLock = 1 << 9,
771  kItemStatusInfo2 = 1 << 10,
772 
773  kHighestBottomItem = kItemMinimize,
774 };
775 
776 
777 #ifdef EXPERIMENTAL_DA
778 
779  #define TITLE_ITEMS \
780  { kItemBarButtons, kTrackInfoBtnSize, 4, \
781  &TrackInfo::CloseTitleDrawFunction },
782  // DA: Has Mute and Solo on separate lines.
783  #define MUTE_SOLO_ITEMS(extra) \
784  { kItemMute, kTrackInfoBtnSize + 1, 1, \
785  &TrackInfo::WideMuteDrawFunction }, \
786  { kItemSolo, kTrackInfoBtnSize + 1, extra, \
787  &TrackInfo::WideSoloDrawFunction },
788  // DA: Does not have status information for a track.
789  #define STATUS_ITEMS
790 
791 #else
792 
793  #define TITLE_ITEMS \
794  { kItemBarButtons, kTrackInfoBtnSize, 0, \
795  &TrackInfo::CloseTitleDrawFunction },
796  #define MUTE_SOLO_ITEMS(extra) \
797  { kItemMute | kItemSolo, kTrackInfoBtnSize + 1, extra, \
798  &TrackInfo::MuteAndSoloDrawFunction },
799  #define STATUS_ITEMS \
800  { kItemStatusInfo1, 12, 0, \
801  &TrackInfo::Status1DrawFunction }, \
802  { kItemStatusInfo2, 12, 0, \
803  &TrackInfo::Status2DrawFunction },
804 
805 #endif
806 
807 #define COMMON_ITEMS \
808  TITLE_ITEMS
809 
810 const TrackInfo::TCPLine defaultCommonTrackTCPLines[] = {
812 };
813 TCPLines commonTrackTCPLines{ RANGE(defaultCommonTrackTCPLines) };
814 
815 const TrackInfo::TCPLine defaultWaveTrackTCPLines[] = {
817  MUTE_SOLO_ITEMS(2)
823 };
824 TCPLines waveTrackTCPLines{ RANGE(defaultWaveTrackTCPLines) };
825 
826 const TrackInfo::TCPLine defaultNoteTrackTCPLines[] = {
828 #ifdef EXPERIMENTAL_MIDI_OUT
829  MUTE_SOLO_ITEMS(0)
830  { kItemMidiControlsRect, kMidiCellHeight * 4, 0,
834 #endif
835 };
836 TCPLines noteTrackTCPLines{ RANGE(defaultNoteTrackTCPLines) };
837 
838 int totalTCPLines( const TCPLines &lines, bool omitLastExtra )
839 {
840  int total = 0;
841  int lastExtra = 0;
842  for ( const auto line : lines ) {
843  lastExtra = line.extraSpace;
844  total += line.height + lastExtra;
845  }
846  if (omitLastExtra)
847  total -= lastExtra;
848  return total;
849 }
850 
851 const TCPLines &getTCPLines( const Track &track )
852 {
853  auto lines = track.TypeSwitch< TCPLines * >(
854 #ifdef USE_MIDI
855  [](const NoteTrack*){
856  return &noteTrackTCPLines;
857  },
858 #endif
859  [](const WaveTrack*){
860  return &waveTrackTCPLines;
861  },
862  [](const Track*){
863  return &commonTrackTCPLines;
864  }
865  );
866 
867  if (lines)
868  return *lines;
869 
870  return commonTrackTCPLines;
871 }
872 
873 // return y value and height
874 std::pair< int, int > CalcItemY( const TCPLines &lines, unsigned iItem )
875 {
876  int y = 0;
877  auto pLines = lines.begin();
878  while ( pLines != lines.end() &&
879  0 == (pLines->items & iItem) ) {
880  y += pLines->height + pLines->extraSpace;
881  ++pLines;
882  }
883  int height = 0;
884  if ( pLines != lines.end() )
885  height = pLines->height;
886  return { y, height };
887 }
888 
889 // Items for the bottom of the panel, listed bottom-upwards
890 // As also with the top items, the extra space is below the item
891 const TrackInfo::TCPLine defaultCommonTrackTCPBottomLines[] = {
892  // The '0' avoids impinging on bottom line of TCP
893  // Use -1 if you do want to do so.
894  { kItemSyncLock | kItemMinimize, kTrackInfoBtnSize, 0,
896 };
897 TCPLines commonTrackTCPBottomLines{ RANGE(defaultCommonTrackTCPBottomLines) };
898 
899 // return y value and height
900 std::pair< int, int > CalcBottomItemY
901  ( const TCPLines &lines, unsigned iItem, int height )
902 {
903  int y = height;
904  auto pLines = lines.begin();
905  while ( pLines != lines.end() &&
906  0 == (pLines->items & iItem) ) {
907  y -= pLines->height + pLines->extraSpace;
908  ++pLines;
909  }
910  if (pLines != lines.end())
911  y -= (pLines->height + pLines->extraSpace );
912  return { y, pLines->height };
913 }
914 
915 }
916 
918 {
919  unsigned height = 0;
920  if (!commonTrackTCPLines.empty())
921  height += commonTrackTCPLines.front().height;
922  if (!commonTrackTCPBottomLines.empty())
923  height += commonTrackTCPBottomLines.front().height;
924  // + 1 prevents the top item from disappearing for want of enough space,
925  // according to the rules in HideTopItem.
926  return height + kTopMargin + kBottomMargin + 1;
927 }
928 
929 bool TrackInfo::HideTopItem( const wxRect &rect, const wxRect &subRect,
930  int allowance ) {
931  auto limit = CalcBottomItemY
932  ( commonTrackTCPBottomLines, kHighestBottomItem, rect.height).first;
933  // Return true if the rectangle is even touching the limit
934  // without an overlap. That was the behavior as of 2.1.3.
935  return subRect.y + subRect.height - allowance >= rect.y + limit;
936 }
937 
938 void TrackPanel::OnKeyDown(wxKeyEvent & event)
939 {
940  switch (event.GetKeyCode())
941  {
942  // Allow PageUp and PageDown keys to
943  //scroll the Track Panel left and right
944  case WXK_PAGEUP:
945  HandlePageUpKey();
946  return;
947 
948  case WXK_PAGEDOWN:
950  return;
951 
952  default:
953  // fall through to base class handler
954  event.Skip();
955  }
956 }
957 
958 void TrackPanel::OnMouseEvent(wxMouseEvent & event)
959 {
960  if (event.LeftDown()) {
961  // wxTimers seem to be a little unreliable, so this
962  // "primes" it to make sure it keeps going for a while...
963 
964  // When this timer fires, we call TrackPanel::OnTimer and
965  // possibly update the screen for offscreen scrolling.
966  mTimer.Stop();
967  mTimer.Start(kTimerInterval, FALSE);
968  }
969 
970 
971  if (event.ButtonUp()) {
972  //EnsureVisible should be called after processing the up-click.
973  this->CallAfter( [this, event]{
974  const auto foundCell = FindCell(event.m_x, event.m_y);
975  const auto t = FindTrack( foundCell.pCell.get() );
976  if ( t )
977  EnsureVisible(t.get());
978  } );
979  }
980 
981  // Must also fall through to base class handler
982  event.Skip();
983 }
984 
986 {
988 }
989 
990 void TrackPanel::RefreshTrack(Track *trk, bool refreshbacking)
991 {
992  if (!trk)
993  return;
994 
995  trk = *GetTracks()->FindLeader(trk);
996  auto height =
999 
1000  // subtract insets and shadows from the rectangle, but not border
1001  // This matters because some separators do paint over the border
1002  wxRect rect(kLeftInset,
1003  -mViewInfo->vpos + trk->GetY() + kTopInset,
1004  GetRect().GetWidth() - kLeftInset - kRightInset - kShadowThickness,
1005  height);
1006 
1007  if( refreshbacking )
1008  {
1009  mRefreshBacking = true;
1010  }
1011 
1012  Refresh( false, &rect );
1013 }
1014 
1015 
1020 void TrackPanel::Refresh(bool eraseBackground /* = TRUE */,
1021  const wxRect *rect /* = NULL */)
1022 {
1023  // Tell OnPaint() to refresh the backing bitmap.
1024  //
1025  // Originally I had the check within the OnPaint() routine and it
1026  // was working fine. That was until I found that, even though a full
1027  // refresh was requested, Windows only set the onscreen portion of a
1028  // window as damaged.
1029  //
1030  // So, if any part of the trackpanel was off the screen, full refreshes
1031  // didn't work and the display got corrupted.
1032  if( !rect || ( *rect == GetRect() ) )
1033  {
1034  mRefreshBacking = true;
1035  }
1036  wxWindow::Refresh(eraseBackground, rect);
1037  DisplaySelection();
1038 }
1039 
1040 #include "TrackPanelDrawingContext.h"
1041 
1045 void TrackPanel::DrawTracks(wxDC * dc)
1046 {
1047  wxRegion region = GetUpdateRegion();
1048 
1049  const wxRect clip = GetRect();
1050 
1051  mTrackArtist->pSelectedRegion = &mViewInfo->selectedRegion;
1052  mTrackArtist->pZoomInfo = mViewInfo;
1053  TrackPanelDrawingContext context {
1054  *dc, Target(), mLastMouseState, mTrackArtist.get()
1055  };
1056 
1057  // Draw margins on two or three sides.
1058  ClearLeftAndRightMargins(context, clip);
1059  if ( GetTracks()->Any() )
1060  // This margin may may scrolled up out of view
1061  ClearTopMargin( context, clip );
1062 
1063  // Don't draw a bottom margin here.
1064 
1066  bool bMultiToolDown = pTtb->IsDown(multiTool);
1067  bool envelopeFlag = pTtb->IsDown(envelopeTool) || bMultiToolDown;
1068  bool bigPointsFlag = pTtb->IsDown(drawTool) || bMultiToolDown;
1069  bool sliderFlag = bMultiToolDown;
1070 
1071  const bool hasSolo = GetTracks()->Any< PlayableTrack >()
1072  .any_of( []( const PlayableTrack *pt ) {
1073  pt = static_cast< const PlayableTrack * >(
1074  pt->SubstitutePendingChangedTrack().get() );
1075  return (pt && pt->GetSolo());
1076  } );
1077 
1078  mTrackArtist->leftOffset = GetLeftOffset();
1079  mTrackArtist->drawEnvelope = envelopeFlag;
1080  mTrackArtist->bigPoints = bigPointsFlag;
1081  mTrackArtist->drawSliders = sliderFlag;
1082  mTrackArtist->hasSolo = hasSolo;
1083  TrackArt::DrawTracks( context, GetTracks(), region, clip );
1084 
1085  // Draw the rest, including the click-to-deselect blank area below all
1086  // tracks
1087  DrawEverythingElse(context, region, clip);
1088 }
1089 
1095  const wxRegion &region,
1096  const wxRect & clip)
1097 {
1098  // We draw everything else
1099  auto dc = &context.dc;
1100 
1101  // Fix the horizontal extent, will vary the vertical:
1102  wxRect trackRect{
1103  kLeftMargin, 0, clip.width - (kLeftMargin + kRightMargin), 0
1104  };
1105  wxRect focusRect{};
1106 
1107  // The loop below now groups each track with the margin BELOW it, to
1108  // correspond better with the subdivision of panel area used in hit testing.
1109 
1110  for ( auto leaderTrack : GetTracks()->Leaders< const Track >()
1111  // Predicate is true iff any channel in the group is wholly or partly
1112  // visible:
1113  + IsVisibleTrack{ GetProject() } ) {
1114  const auto channels = TrackList::Channels(leaderTrack);
1115  bool focused = false;
1116  wxRect teamRect = trackRect;
1117  teamRect.height = 0;
1118  bool first = true;
1119  for (auto channel : channels) {
1120  focused = focused || mAx->IsFocused(channel);
1121  channel = channel->SubstitutePendingChangedTrack().get();
1122  if (first)
1123  first = false,
1124  teamRect.y = channel->GetY() - mViewInfo->vpos + kTopMargin;
1125  teamRect.height += channel->GetHeight();
1126  }
1127 
1128  if (focused) {
1129  focusRect = teamRect;
1130  focusRect.height -= kSeparatorThickness;
1131  }
1132  DrawOutside(context, leaderTrack, teamRect);
1133 
1134  // Believe it or not, we can speed up redrawing if we don't
1135  // redraw the vertical ruler when only the waveform data has
1136  // changed. An example is during recording.
1137 
1138 #if DEBUG_DRAW_TIMING
1139 // wxRect rbox = region.GetBox();
1140 // wxPrintf(wxT("Update Region: %d %d %d %d\n"),
1141 // rbox.x, rbox.y, rbox.width, rbox.height);
1142 #endif
1143 
1144  for (auto channel : channels) {
1145  bool bSelected = channel->GetSelected();
1146  channel = channel->SubstitutePendingChangedTrack().get();
1147  trackRect.y = channel->GetY() - mViewInfo->vpos + kTopMargin;
1148  trackRect.height = channel->GetHeight();
1149  if (region.Contains(
1150  0, trackRect.y, GetLeftOffset(), trackRect.height)) {
1151  wxRect rect{
1152  GetVRulerOffset(),
1153  trackRect.y,
1154  GetVRulerWidth() + 1,
1155  trackRect.height - kSeparatorThickness
1156  };
1157  TrackArt::DrawVRuler(context, channel, rect, bSelected);
1158  }
1159  }
1160  }
1161 
1162  auto target = Target();
1163  if (target)
1164  target->DrawExtras(UIHandle::Cells, dc, region, clip);
1165 
1166  // Paint over the part below the tracks
1167  trackRect.y += trackRect.height;
1168  if (trackRect.y < clip.GetBottom()) {
1169  AColor::TrackPanelBackground(dc, false);
1170  dc->DrawRectangle(trackRect.x,
1171  trackRect.y,
1172  trackRect.width,
1173  clip.height - trackRect.y);
1174  }
1175 
1176  // Sometimes highlight is not drawn on backing bitmap. I thought
1177  // it was because FindFocus did not return "this" on Mac, but
1178  // when I removed that test, yielding this condition:
1179  // if (GetFocusedTrack() != NULL) {
1180  // the highlight was reportedly drawn even when something else
1181  // was the focus and no highlight should be drawn. -RBD
1182  if (GetFocusedTrack() != NULL && GetProject()->IsFocused( this )) {
1183  HighlightFocusedTrack(dc, focusRect);
1184  }
1185 
1186  if (target)
1187  target->DrawExtras(UIHandle::Panel, dc, region, clip);
1188 }
1189 
1190 // Make this #include go away!
1191 #include "tracks/ui/TrackControls.h"
1192 
1195  const wxRect &rect, const Track &track )
1196 {
1197  const auto topLines = getTCPLines( track );
1198  const auto bottomLines = commonTrackTCPBottomLines;
1199  DrawItems
1200  ( context, rect, &track, topLines, bottomLines );
1201 }
1202 
1205  const wxRect &rect, const Track *pTrack,
1206  const std::vector<TCPLine> &topLines, const std::vector<TCPLine> &bottomLines )
1207 {
1208  auto dc = &context.dc;
1210  dc->SetTextForeground(theTheme.Colour(clrTrackPanelText));
1211 
1212  {
1213  int yy = 0;
1214  for ( const auto &line : topLines ) {
1215  wxRect itemRect{
1216  rect.x, rect.y + yy,
1217  rect.width, line.height
1218  };
1219  if ( !TrackInfo::HideTopItem( rect, itemRect ) &&
1220  line.drawFunction )
1221  line.drawFunction( context, itemRect, pTrack );
1222  yy += line.height + line.extraSpace;
1223  }
1224  }
1225  {
1226  int yy = rect.height;
1227  for ( const auto &line : bottomLines ) {
1228  yy -= line.height + line.extraSpace;
1229  if ( line.drawFunction ) {
1230  wxRect itemRect{
1231  rect.x, rect.y + yy,
1232  rect.width, line.height
1233  };
1234  line.drawFunction( context, itemRect, pTrack );
1235  }
1236  }
1237  }
1238 }
1239 
1243  const wxRect &rect, const Track *pTrack )
1244 {
1245  auto dc = &context.dc;
1246  bool selected = pTrack ? pTrack->GetSelected() : true;
1247  {
1248  wxRect bev = rect;
1249  GetCloseBoxHorizontalBounds( rect, bev );
1250  auto target = dynamic_cast<CloseButtonHandle*>( context.target.get() );
1251  bool hit = target && target->GetTrack().get() == pTrack;
1252  bool captured = hit && target->IsClicked();
1253  bool down = captured && bev.Contains( context.lastState.GetPosition());
1254  AColor::Bevel2(*dc, !down, bev, selected, hit );
1255 
1256 #ifdef EXPERIMENTAL_THEMING
1257  wxPen pen( theTheme.Colour( clrTrackPanelText ));
1258  dc->SetPen( pen );
1259 #else
1260  dc->SetPen(*wxBLACK_PEN);
1261 #endif
1262  bev.Inflate( -1, -1 );
1263  // Draw the "X"
1264  const int s = 6;
1265 
1266  int ls = bev.x + ((bev.width - s) / 2);
1267  int ts = bev.y + ((bev.height - s) / 2);
1268  int rs = ls + s;
1269  int bs = ts + s;
1270 
1271  AColor::Line(*dc, ls, ts, rs, bs);
1272  AColor::Line(*dc, ls + 1, ts, rs + 1, bs);
1273  AColor::Line(*dc, rs, ts, ls, bs);
1274  AColor::Line(*dc, rs + 1, ts, ls + 1, bs);
1275 
1276  // bev.Inflate(-1, -1);
1277  }
1278 
1279  {
1280  wxRect bev = rect;
1281  GetTitleBarHorizontalBounds( rect, bev );
1282  auto target = dynamic_cast<MenuButtonHandle*>( context.target.get() );
1283  bool hit = target && target->GetTrack().get() == pTrack;
1284  bool captured = hit && target->IsClicked();
1285  bool down = captured && bev.Contains( context.lastState.GetPosition());
1286  wxString titleStr =
1287  pTrack ? pTrack->GetName() : _("Name");
1288 
1289  //bev.Inflate(-1, -1);
1290  AColor::Bevel2(*dc, !down, bev, selected, hit);
1291 
1292  // Draw title text
1293  SetTrackInfoFont(dc);
1294 
1295  // Bug 1660 The 'k' of 'Audio Track' was being truncated.
1296  // Constant of 32 found by counting pixels on a windows machine.
1297  // I believe it's the size of the X close button + the size of the
1298  // drop down arrow.
1299  int allowableWidth = rect.width - 32;
1300 
1301  wxCoord textWidth, textHeight;
1302  dc->GetTextExtent(titleStr, &textWidth, &textHeight);
1303  while (textWidth > allowableWidth) {
1304  titleStr = titleStr.Left(titleStr.Length() - 1);
1305  dc->GetTextExtent(titleStr, &textWidth, &textHeight);
1306  }
1307 
1308  // Pop-up triangle
1309  #ifdef EXPERIMENTAL_THEMING
1310  wxColour c = theTheme.Colour( clrTrackPanelText );
1311  #else
1312  wxColour c = *wxBLACK;
1313  #endif
1314 
1315  // wxGTK leaves little scraps (antialiasing?) of the
1316  // characters if they are repeatedly drawn. This
1317  // happens when holding down mouse button and moving
1318  // in and out of the title bar. So clear it first.
1319  // AColor::MediumTrackInfo(dc, t->GetSelected());
1320  // dc->DrawRectangle(bev);
1321 
1322  dc->SetTextForeground( c );
1323  dc->SetTextBackground( wxTRANSPARENT );
1324  dc->DrawText(titleStr, bev.x + 2, bev.y + (bev.height - textHeight) / 2);
1325 
1326 
1327 
1328  dc->SetPen(c);
1329  dc->SetBrush(c);
1330 
1331  int s = 10; // Width of dropdown arrow...height is half of width
1332  AColor::Arrow(*dc,
1333  bev.GetRight() - s - 3, // 3 to offset from right border
1334  bev.y + ((bev.height - (s / 2)) / 2),
1335  s);
1336 
1337  }
1338 }
1339 
1342  const wxRect &rect, const Track *pTrack )
1343 {
1344  auto dc = &context.dc;
1345  bool selected = pTrack ? pTrack->GetSelected() : true;
1346  bool syncLockSelected = pTrack ? pTrack->IsSyncLockSelected() : true;
1347  bool minimized = pTrack ? pTrack->GetMinimized() : false;
1348  {
1349  wxRect bev = rect;
1350  GetMinimizeHorizontalBounds(rect, bev);
1351  auto target = dynamic_cast<MinimizeButtonHandle*>( context.target.get() );
1352  bool hit = target && target->GetTrack().get() == pTrack;
1353  bool captured = hit && target->IsClicked();
1354  bool down = captured && bev.Contains( context.lastState.GetPosition());
1355 
1356  // Clear background to get rid of previous arrow
1357  //AColor::MediumTrackInfo(dc, t->GetSelected());
1358  //dc->DrawRectangle(bev);
1359 
1360  AColor::Bevel2(*dc, !down, bev, selected, hit);
1361 
1362 #ifdef EXPERIMENTAL_THEMING
1363  wxColour c = theTheme.Colour(clrTrackPanelText);
1364  dc->SetBrush(c);
1365  dc->SetPen(c);
1366 #else
1367  AColor::Dark(dc, selected);
1368 #endif
1369 
1370  AColor::Arrow(*dc,
1371  bev.x - 5 + bev.width / 2,
1372  bev.y - 2 + bev.height / 2,
1373  10,
1374  minimized);
1375  }
1376 
1377  // Draw the sync-lock indicator if this track is in a sync-lock selected group.
1378  if (syncLockSelected)
1379  {
1380  wxRect syncLockIconRect = rect;
1381 
1382  GetSyncLockHorizontalBounds( rect, syncLockIconRect );
1383  wxBitmap syncLockBitmap(theTheme.Image(bmpSyncLockIcon));
1384  // Icon is 12x12 and syncLockIconRect is 16x16.
1385  dc->DrawBitmap(syncLockBitmap,
1386  syncLockIconRect.x + 3,
1387  syncLockIconRect.y + 2,
1388  true);
1389  }
1390 }
1391 
1395  const wxRect &rect, const Track *pTrack )
1396 {
1397 #ifdef EXPERIMENTAL_MIDI_OUT
1398  auto target = dynamic_cast<NoteTrackButtonHandle*>( context.target.get() );
1399  bool hit = target && target->GetTrack().get() == pTrack;
1400  auto channel = hit ? target->GetChannel() : -1;
1401  auto &dc = context.dc;
1402  wxRect midiRect = rect;
1403  GetMidiControlsHorizontalBounds(rect, midiRect);
1405  ( static_cast<const NoteTrack *>(pTrack), dc, midiRect, channel );
1406 #endif // EXPERIMENTAL_MIDI_OUT
1407 }
1408 
1409 template<typename TrackClass>
1411 ( LWSlider *(*Selector)
1412  (const wxRect &sliderRect, const TrackClass *t, bool captured, wxWindow*),
1413  wxDC *dc, const wxRect &rect, const Track *pTrack,
1414  bool captured, bool highlight )
1415 {
1416  wxRect sliderRect = rect;
1417  TrackInfo::GetSliderHorizontalBounds( rect.GetTopLeft(), sliderRect );
1418  auto wt = static_cast<const TrackClass*>( pTrack );
1419  Selector( sliderRect, wt, captured, nullptr )->OnPaint(*dc, highlight);
1420 }
1421 
1425  const wxRect &rect, const Track *pTrack )
1426 {
1427  auto target = dynamic_cast<PanSliderHandle*>( context.target.get() );
1428  auto dc = &context.dc;
1429  bool hit = target && target->GetTrack().get() == pTrack;
1430  bool captured = hit && target->IsClicked();
1431  SliderDrawFunction<WaveTrack>
1432  ( &TrackInfo::PanSlider, dc, rect, pTrack, captured, hit);
1433 }
1434 
1437  const wxRect &rect, const Track *pTrack )
1438 {
1439  auto target = dynamic_cast<GainSliderHandle*>( context.target.get() );
1440  auto dc = &context.dc;
1441  bool hit = target && target->GetTrack().get() == pTrack;
1442  if( hit )
1443  hit=hit;
1444  bool captured = hit && target->IsClicked();
1445  SliderDrawFunction<WaveTrack>
1446  ( &TrackInfo::GainSlider, dc, rect, pTrack, captured, hit);
1447 }
1448 
1449 #ifdef EXPERIMENTAL_MIDI_OUT
1453  const wxRect &rect, const Track *pTrack )
1454 {
1455  auto dc = &context.dc;
1456  auto target = dynamic_cast<VelocitySliderHandle*>( context.target.get() );
1457  bool hit = target && target->GetTrack().get() == pTrack;
1458  bool captured = hit && target->IsClicked();
1459  SliderDrawFunction<NoteTrack>
1460  ( &TrackInfo::VelocitySlider, dc, rect, pTrack, captured, hit);
1461 }
1462 #endif
1463 
1465 ( wxDC *dc, const wxRect &bev, const Track *pTrack, bool down,
1466  bool WXUNUSED(captured),
1467  bool solo, bool hit )
1468 {
1469  //bev.Inflate(-1, -1);
1470  bool selected = pTrack ? pTrack->GetSelected() : true;
1471  auto pt = dynamic_cast<const PlayableTrack *>(pTrack);
1472  bool value = pt ? (solo ? pt->GetSolo() : pt->GetMute()) : false;
1473 
1474 #if 0
1475  AColor::MediumTrackInfo( dc, t->GetSelected());
1476  if( solo )
1477  {
1478  if( pt && pt->GetSolo() )
1479  {
1480  AColor::Solo(dc, pt->GetSolo(), t->GetSelected());
1481  }
1482  }
1483  else
1484  {
1485  if( pt && pt->GetMute() )
1486  {
1487  AColor::Mute(dc, pt->GetMute(), t->GetSelected(), pt->GetSolo());
1488  }
1489  }
1490  //(solo) ? AColor::Solo(dc, t->GetSolo(), t->GetSelected()) :
1491  // AColor::Mute(dc, t->GetMute(), t->GetSelected(), t->GetSolo());
1492  dc->SetPen( *wxTRANSPARENT_PEN );//No border!
1493  dc->DrawRectangle(bev);
1494 #endif
1495 
1496  wxCoord textWidth, textHeight;
1497  wxString str = (solo) ?
1498  /* i18n-hint: This is on a button that will silence all the other tracks.*/
1499  _("Solo") :
1500  /* i18n-hint: This is on a button that will silence this track.*/
1501  _("Mute");
1502 
1504  *dc,
1505  value == down,
1506  bev,
1507  selected, hit
1508  );
1509 
1510  SetTrackInfoFont(dc);
1511  dc->GetTextExtent(str, &textWidth, &textHeight);
1512  dc->DrawText(str, bev.x + (bev.width - textWidth) / 2, bev.y + (bev.height - textHeight) / 2);
1513 }
1514 
1518  const wxRect &rect, const Track *pTrack )
1519 {
1520  auto dc = &context.dc;
1521  wxRect bev = rect;
1522  GetWideMuteSoloHorizontalBounds( rect, bev );
1523  auto target = dynamic_cast<MuteButtonHandle*>( context.target.get() );
1524  bool hit = target && target->GetTrack().get() == pTrack;
1525  bool captured = hit && target->IsClicked();
1526  bool down = captured && bev.Contains( context.lastState.GetPosition());
1527  MuteOrSoloDrawFunction( dc, bev, pTrack, down, captured, false, hit );
1528 }
1529 
1532  const wxRect &rect, const Track *pTrack )
1533 {
1534  auto dc = &context.dc;
1535  wxRect bev = rect;
1536  GetWideMuteSoloHorizontalBounds( rect, bev );
1537  auto target = dynamic_cast<SoloButtonHandle*>( context.target.get() );
1538  bool hit = target && target->GetTrack().get() == pTrack;
1539  bool captured = hit && target->IsClicked();
1540  bool down = captured && bev.Contains( context.lastState.GetPosition());
1541  MuteOrSoloDrawFunction( dc, bev, pTrack, down, captured, true, hit );
1542 }
1543 
1546  const wxRect &rect, const Track *pTrack )
1547 {
1548  auto dc = &context.dc;
1549  bool bHasSoloButton = TrackPanel::HasSoloButton();
1550 
1551  wxRect bev = rect;
1552  if ( bHasSoloButton )
1553  GetNarrowMuteHorizontalBounds( rect, bev );
1554  else
1555  GetWideMuteSoloHorizontalBounds( rect, bev );
1556  {
1557  auto target = dynamic_cast<MuteButtonHandle*>( context.target.get() );
1558  bool hit = target && target->GetTrack().get() == pTrack;
1559  bool captured = hit && target->IsClicked();
1560  bool down = captured && bev.Contains( context.lastState.GetPosition());
1561  MuteOrSoloDrawFunction( dc, bev, pTrack, down, captured, false, hit );
1562  }
1563 
1564  if( !bHasSoloButton )
1565  return;
1566 
1567  GetNarrowSoloHorizontalBounds( rect, bev );
1568  {
1569  auto target = dynamic_cast<SoloButtonHandle*>( context.target.get() );
1570  bool hit = target && target->GetTrack().get() == pTrack;
1571  bool captured = hit && target->IsClicked();
1572  bool down = captured && bev.Contains( context.lastState.GetPosition());
1573  MuteOrSoloDrawFunction( dc, bev, pTrack, down, captured, true, hit );
1574  }
1575 }
1576 
1578  ( const wxString &string, wxDC *dc, const wxRect &rect )
1579 {
1580  static const int offset = 3;
1581  dc->DrawText(string, rect.x + offset, rect.y);
1582 }
1583 
1586  const wxRect &rect, const Track *pTrack )
1587 {
1588  auto dc = &context.dc;
1589  auto wt = static_cast<const WaveTrack*>(pTrack);
1590 
1594  auto rate = wt ? wt->GetRate() : 44100.0;
1595  wxString s;
1596  if (!pTrack || TrackList::Channels(pTrack).size() > 1)
1597  // TODO: more-than-two-channels-message
1598  // more appropriate strings
1599  s = _("Stereo, %dHz");
1600  else {
1601  if (wt->GetChannel() == Track::MonoChannel)
1602  s = _("Mono, %dHz");
1603  else if (wt->GetChannel() == Track::LeftChannel)
1604  s = _("Left, %dHz");
1605  else if (wt->GetChannel() == Track::RightChannel)
1606  s = _("Right, %dHz");
1607  }
1608  s = wxString::Format( s, (int) (rate + 0.5) );
1609 
1610  StatusDrawFunction( s, dc, rect );
1611 }
1612 
1615  const wxRect &rect, const Track *pTrack )
1616 {
1617  auto dc = &context.dc;
1618  auto wt = static_cast<const WaveTrack*>(pTrack);
1619  auto format = wt ? wt->GetSampleFormat() : floatSample;
1620  auto s = GetSampleFormatStr(format);
1621  StatusDrawFunction( s, dc, rect );
1622 }
1623 
1626  const Track * t, const wxRect & rec)
1627 {
1628  // Given rectangle excludes left and right margins, and encompasses a
1629  // channel group of tracks, plus the resizer area below
1630 
1631  auto dc = &context.dc;
1632 
1633  // Start with whole track rect
1634  wxRect rect = rec;
1635 
1636  {
1637  ClearSeparator(context, rect);
1638 
1639  // Now exclude the resizer below
1640  rect.height -= kSeparatorThickness;
1641 
1642  int labelw = GetLabelWidth();
1643  int vrul = GetVRulerOffset();
1644 
1645  TrackInfo::DrawBackground( dc, rect, t->GetSelected(), vrul );
1646 
1647  // Vaughan, 2010-08-24: No longer doing this.
1648  // Draw sync-lock tiles in ruler area.
1649  //if (t->IsSyncLockSelected()) {
1650  // wxRect tileFill = rect;
1651  // tileFill.x = GetVRulerOffset();
1652  // tileFill.width = GetVRulerWidth();
1653  // TrackArt::DrawSyncLockTiles(dc, tileFill);
1654  //}
1655 
1656  DrawBordersAroundTrack( dc, rect );
1657  {
1658  auto channels = TrackList::Channels(t);
1659  // omit last (perhaps, only) channel
1660  --channels.second;
1661  for (auto channel : channels) {
1662  // draw the sash below this channel
1663  channel = channel->SubstitutePendingChangedTrack().get();
1664  auto yy =
1665  channel->GetY() - mViewInfo->vpos + channel->GetHeight()
1666  - kBottomMargin;
1667  wxRect sashRect{
1668  vrul, yy, rect.GetRight() - vrul, kSeparatorThickness
1669  };
1670  DrawSash( dc, sashRect, labelw, t->GetSelected() );
1671  }
1672  }
1673 
1674  DrawShadow( dc, rect );
1675  }
1676 
1677  // Draw things within the track control panel
1678  rect.width = kTrackInfoWidth;
1679  TrackInfo::DrawItems( context, rect, *t );
1680 
1681  //mTrackInfo.DrawBordersWithin( dc, rect, *t );
1682 }
1683 
1685 (TrackPanelDrawingContext &context, const wxRect &clip)
1686 {
1687  auto dc = &context.dc;
1688 
1689  // Area above the first track if there is one
1690  AColor::TrackPanelBackground(dc, false);
1691  wxRect side{
1692  clip.x + kLeftMargin,
1693  -mViewInfo->vpos,
1694  clip.width - ( kLeftMargin + kRightMargin ),
1695  kTopMargin
1696  };
1697 
1698  if (side.Intersects(clip))
1699  dc->DrawRectangle(side);
1700 }
1701 
1702 // Paint the inset areas of the whole panel, left and right, in a background
1703 // color
1705 (TrackPanelDrawingContext &context, const wxRect & clip)
1706 {
1707  auto dc = &context.dc;
1708 
1709  // Fill in area outside of tracks
1710  AColor::TrackPanelBackground(dc, false);
1711  wxRect side;
1712 
1713  // Area between panel border and left track border
1714  side = clip;
1715  side.width = kLeftMargin;
1716  dc->DrawRectangle(side);
1717 
1718  // Area between panel border and right track border
1719  side = clip;
1720  side.x += side.width - kRightMargin;
1721  side.width = kRightMargin;
1722  dc->DrawRectangle(side);
1723 }
1724 
1725 // Given rectangle should be the whole track rectangle
1726 // Paint the separator area below in a background color
1728 (TrackPanelDrawingContext &context, const wxRect & rect)
1729 {
1730  auto dc = &context.dc;
1731 
1732  // Fill in area outside of the track
1733  AColor::TrackPanelBackground(dc, false);
1734 
1735  // Area below the track, where the resizer will be
1736  auto height = kSeparatorThickness;
1737  wxRect side{
1738  rect.x,
1739  rect.y + rect.height - height,
1740  rect.width,
1741  height
1742  };
1743  dc->DrawRectangle(side);
1744 }
1745 
1747  wxDC * dc, const wxRect & rect, int labelw, bool bSelected )
1748 {
1749  // Area between channels of a group
1750  // Paint the channel separator over (what would be) the lower border of this
1751  // channel, down to and including the upper border of the next channel
1752 
1753  ADCChanger cleanup{ dc };
1754 
1755  // Paint the left part of the background
1756  AColor::MediumTrackInfo(dc, bSelected);
1757  dc->DrawRectangle( rect.GetX(), rect.GetY(), labelw, rect.GetHeight() );
1758 
1759  // Stroke the left border
1760  dc->SetPen(*wxBLACK_PEN);
1761  {
1762  const auto left = rect.GetLeft();
1763  AColor::Line( *dc, left, rect.GetTop(), left, rect.GetBottom() );
1764  }
1765 
1766  AColor::TrackPanelBackground(dc, false);
1767 
1768  wxRect rec{ rect };
1769  rec.width -= labelw - rec.x;
1770  rec.x = labelw;
1771 
1772  dc->DrawRectangle( wxRect( rec ).Inflate( 0, -kBorderThickness ) );
1773 
1774  // These lines stroke over what is otherwise "border" of each channel
1775  dc->SetBrush(*wxTRANSPARENT_BRUSH);
1776  dc->SetPen(*wxBLACK_PEN);
1777  const auto left = rec.GetLeft();
1778  const auto right = rec.GetRight();
1779  const auto top = rec.GetTop();
1780  const auto bottom = rec.GetBottom();
1781  AColor::Line( *dc, left, top, right, top );
1782  AColor::Line( *dc, left, bottom, right, bottom );
1783 }
1784 
1785 
1787 (const std::shared_ptr< TrackPanelCell > &pCell)
1788 {
1789  mpBackground = pCell;
1790 }
1791 
1792 std::shared_ptr< TrackPanelCell > TrackPanel::GetBackgroundCell()
1793 {
1794  return mpBackground;
1795 }
1796 
1798 void TrackPanel::HighlightFocusedTrack(wxDC * dc, const wxRect & rect)
1799 {
1800  wxRect theRect = rect;
1801  theRect.Inflate( kBorderThickness );
1802  theRect.width += kShadowThickness;
1803  theRect.height += kShadowThickness;
1804  dc->SetBrush(*wxTRANSPARENT_BRUSH);
1805 
1806  AColor::TrackFocusPen(dc, 0);
1807  theRect.Inflate(1);
1808  dc->DrawRectangle(theRect);
1809 
1810  AColor::TrackFocusPen(dc, 1);
1811  theRect.Inflate(1);
1812  dc->DrawRectangle(theRect);
1813 
1814  AColor::TrackFocusPen(dc, 2);
1815  theRect.Inflate(1);
1816  dc->DrawRectangle(theRect);
1817 }
1818 
1820 {
1821  for (auto t : GetTracks()->Any< const WaveTrack >())
1822  UpdateTrackVRuler(t);
1823 
1824  UpdateVRulerSize();
1825 }
1826 
1828 {
1829  if (t)
1830  UpdateTrackVRuler(t);
1831 
1832  UpdateVRulerSize();
1833 }
1834 
1836 {
1837  wxASSERT(t);
1838  if (!t)
1839  return;
1840 
1841  wxRect rect(GetVRulerOffset(),
1842  kTopMargin,
1843  GetVRulerWidth(),
1844  0);
1845 
1846 
1847  for (auto channel : TrackList::Channels(t)) {
1848  rect.height = channel->GetHeight() - (kTopMargin + kBottomMargin);
1849  mTrackArtist->UpdateVRuler(channel, rect);
1850  }
1851 }
1852 
1854 {
1855  auto trackRange = GetTracks()->Any();
1856  if (trackRange) {
1857  wxSize s { 0, 0 };
1858  for (auto t : trackRange)
1859  s.IncTo(t->vrulerSize);
1860 
1861  if (vrulerSize != s) {
1862  vrulerSize = s;
1863  mRuler->SetLeftOffset(GetLeftOffset()); // bevel on AdornedRuler
1864  mRuler->Refresh();
1865  }
1866  }
1867  Refresh(false);
1868 }
1869 
1870 // Make sure selection edge is in view
1872 {
1873  int w;
1874  GetTracksUsableArea( &w, NULL );
1875 
1876  int pixel = mViewInfo->TimeToPosition(pos);
1877  if (pixel < 0 || pixel >= w)
1878  {
1880  (mViewInfo->OffsetTimeByPixels(pos, -(w / 2)));
1881  Refresh(false);
1882  }
1883 }
1884 
1886 {
1888 }
1889 
1891 {
1893 }
1894 
1896 {
1897  auto t = *GetTracks()->Selected().begin();
1898  if (t)
1899  return t;
1900  else
1901  //if nothing is selected, return the first track
1902  return *GetTracks()->Any().begin();
1903 }
1904 
1906 {
1907  SetFocusedTrack(t);
1908 
1909  int trackTop = 0;
1910  int trackHeight =0;
1911 
1912  for (auto it : GetTracks()->Leaders()) {
1913  trackTop += trackHeight;
1914 
1915  auto channels = TrackList::Channels(it);
1916  trackHeight = channels.sum( &Track::GetHeight );
1917 
1918  //We have found the track we want to ensure is visible.
1919  if (channels.contains(t)) {
1920 
1921  //Get the size of the trackpanel.
1922  int width, height;
1923  GetSize(&width, &height);
1924 
1925  if (trackTop < mViewInfo->vpos) {
1926  height = mViewInfo->vpos - trackTop + mViewInfo->scrollStep;
1927  height /= mViewInfo->scrollStep;
1928  mListener->TP_ScrollUpDown(-height);
1929  }
1930  else if (trackTop + trackHeight > mViewInfo->vpos + height) {
1931  height = (trackTop + trackHeight) - (mViewInfo->vpos + height);
1932  height = (height + mViewInfo->scrollStep + 1) / mViewInfo->scrollStep;
1933  mListener->TP_ScrollUpDown(height);
1934  }
1935 
1936  break;
1937  }
1938  }
1939  Refresh(false);
1940 }
1941 
1942 // 0.0 scrolls to top
1943 // 1.0 scrolls to bottom.
1944 void TrackPanel::VerticalScroll( float fracPosition){
1945 
1946  int trackTop = 0;
1947  int trackHeight = 0;
1948 
1949  auto tracks = GetTracks();
1950  auto GetHeight =
1951  [&]( const Track *t ){ return tracks->GetGroupHeight(t); };
1952 
1953  auto range = tracks->Leaders();
1954  if (!range.empty()) {
1955  trackHeight = GetHeight( *range.rbegin() );
1956  --range.second;
1957  }
1958  trackTop = range.sum( GetHeight );
1959 
1960  int delta;
1961 
1962  //Get the size of the trackpanel.
1963  int width, height;
1964  GetSize(&width, &height);
1965 
1966  delta = (fracPosition * (trackTop + trackHeight - height)) - mViewInfo->vpos + mViewInfo->scrollStep;
1967  //wxLogDebug( "Scroll down by %i pixels", delta );
1968  delta /= mViewInfo->scrollStep;
1969  mListener->TP_ScrollUpDown(delta);
1970  Refresh(false);
1971 }
1972 
1973 
1974 // Draw a rectangular border
1975 void TrackPanel::DrawBordersAroundTrack( wxDC * dc, const wxRect & rect )
1976 {
1977  // Border around track and label area
1978  // leaving room for the shadow
1979  dc->SetBrush(*wxTRANSPARENT_BRUSH);
1980  dc->SetPen(*wxBLACK_PEN);
1981  dc->DrawRectangle( rect.Inflate( kBorderThickness, kBorderThickness ) );
1982 }
1983 
1984 // Given rectangle is the track rectangle excluding the border
1985 // Stroke lines along bottom and right, which are slightly short at
1986 // bottom-left and top-right
1987 void TrackPanel::DrawShadow( wxDC * dc, const wxRect & rect )
1988 {
1989  int right = rect.GetRight() + kBorderThickness + kShadowThickness;
1990  int bottom = rect.GetBottom() + kBorderThickness + kShadowThickness;
1991 
1992  // shadow color for lines
1993  dc->SetPen(*wxBLACK_PEN);
1994 
1995  // bottom
1996  AColor::Line(*dc, rect.x, bottom, right, bottom);
1997  // right
1998  AColor::Line(*dc, right, rect.y, right, bottom);
1999 
2000  // background color erases small parts of those lines
2001  AColor::TrackPanelBackground(dc, false);
2002 
2003  // bottom-left
2004  AColor::Line(*dc, rect.x, bottom, rect.x + 1, bottom);
2005  // top-right
2006  AColor::Line(*dc, right, rect.y, right, rect.y + 1);
2007 }
2008 
2009 namespace {
2010 
2011 // Helper classes to implement the subdivision of TrackPanel area for
2012 // CellularPanel
2013 
2014 struct EmptyCell final : CommonTrackPanelCell {
2015  std::vector< UIHandlePtr > HitTest(
2016  const TrackPanelMouseState &, const AudacityProject *) override
2017  { return {}; }
2018  virtual std::shared_ptr< Track > FindTrack() override { return {}; }
2019  static std::shared_ptr<EmptyCell> Instance()
2020  {
2021  static auto instance = std::make_shared< EmptyCell >();
2022  return instance;
2023  }
2024 };
2025 
2026 // A vertical ruler left of a channel
2027 struct VRulerAndChannel final : TrackPanelGroup {
2028  VRulerAndChannel(
2029  const std::shared_ptr< Track > &pChannel, wxCoord leftOffset )
2030  : mpChannel{ pChannel }, mLeftOffset{ leftOffset } {}
2031  Subdivision Children( const wxRect &rect ) override
2032  {
2033  return { Axis::X, Refinement{
2034  { rect.GetLeft(), mpChannel->GetVRulerControl() },
2035  { mLeftOffset, mpChannel }
2036  } };
2037  }
2038  std::shared_ptr< Track > mpChannel;
2039  wxCoord mLeftOffset;
2040 };
2041 
2042 // n channels with vertical rulers, alternating with n - 1 resizers
2043 struct ChannelGroup final : TrackPanelGroup {
2044  ChannelGroup( const std::shared_ptr< Track > &pTrack, wxCoord leftOffset )
2045  : mpTrack{ pTrack }, mLeftOffset{ leftOffset } {}
2046  Subdivision Children( const wxRect &rect ) override
2047  {
2048  Refinement refinement;
2049 
2050  const auto channels = TrackList::Channels( mpTrack.get() );
2051  const auto pLast = *channels.rbegin();
2052  wxCoord yy = rect.GetTop();
2053  for ( auto channel : channels ) {
2054  refinement.emplace_back( yy,
2055  std::make_shared< VRulerAndChannel >(
2056  Track::Pointer( channel ), mLeftOffset ) );
2057  if ( channel != pLast ) {
2058  const auto substitute =
2059  Track::Pointer( channel )->SubstitutePendingChangedTrack();
2060  yy += substitute->GetHeight();
2061  refinement.emplace_back(
2062  yy - kSeparatorThickness, channel->GetResizer() );
2063  }
2064  }
2065 
2066  return { Axis::Y, std::move( refinement ) };
2067  }
2068  std::shared_ptr< Track > mpTrack;
2069  wxCoord mLeftOffset;
2070 };
2071 
2072 // A track control panel, left of n vertical rulers and n channels
2073 // alternating with n - 1 resizers
2074 struct LabeledChannelGroup final : TrackPanelGroup {
2075  LabeledChannelGroup(
2076  const std::shared_ptr< Track > &pTrack, wxCoord leftOffset )
2077  : mpTrack{ pTrack }, mLeftOffset{ leftOffset } {}
2078  Subdivision Children( const wxRect &rect ) override
2079  { return { Axis::X, Refinement{
2080  { rect.GetLeft(), mpTrack->GetTrackControl() },
2081  { rect.GetLeft() + kTrackInfoWidth,
2082  std::make_shared< ChannelGroup >( mpTrack, mLeftOffset ) }
2083  } }; }
2084  std::shared_ptr< Track > mpTrack;
2085  wxCoord mLeftOffset;
2086 };
2087 
2088 // Stacks a label and a single or multi-channel track on a resizer below,
2089 // which is associated with the last channel
2090 struct ResizingChannelGroup final : TrackPanelGroup {
2091  ResizingChannelGroup(
2092  const std::shared_ptr< Track > &pTrack, wxCoord leftOffset )
2093  : mpTrack{ pTrack }, mLeftOffset{ leftOffset } {}
2094  Subdivision Children( const wxRect &rect ) override
2095  { return { Axis::Y, Refinement{
2096  { rect.GetTop(),
2097  std::make_shared< LabeledChannelGroup >( mpTrack, mLeftOffset ) },
2098  { rect.GetTop() + rect.GetHeight() - kSeparatorThickness,
2099  ( *TrackList::Channels( mpTrack.get() ).rbegin() )->GetResizer() }
2100  } }; }
2101  std::shared_ptr< Track > mpTrack;
2102  wxCoord mLeftOffset;
2103 };
2104 
2105 // Stacks a dead area at top, the tracks, and the click-to-deselect area below
2106 struct Subgroup final : TrackPanelGroup {
2107  explicit Subgroup( TrackPanel &panel ) : mPanel{ panel } {}
2108  Subdivision Children( const wxRect &rect ) override
2109  {
2110  wxCoord yy = -mPanel.GetViewInfo()->vpos;
2111  Refinement refinement;
2112 
2113  auto &tracks = *mPanel.GetTracks();
2114  if ( tracks.Any() )
2115  refinement.emplace_back( yy, EmptyCell::Instance() ),
2116  yy += kTopMargin;
2117 
2118  for ( const auto leader : tracks.Leaders() ) {
2119  wxCoord height = 0;
2120  for ( auto channel : TrackList::Channels( leader ) ) {
2121  auto substitute =
2122  Track::Pointer( channel )->SubstitutePendingChangedTrack();
2123  height += substitute->GetHeight();
2124  }
2125  refinement.emplace_back( yy,
2126  std::make_shared< ResizingChannelGroup >(
2127  Track::Pointer( leader ), mPanel.GetLeftOffset() )
2128  );
2129  yy += height;
2130  }
2131 
2132  refinement.emplace_back( std::max( 0, yy ), mPanel.GetBackgroundCell() );
2133 
2134  return { Axis::Y, std::move( refinement ) };
2135  }
2136  TrackPanel &mPanel;
2137 };
2138 
2139 // Main group shaves off the left and right margins
2140 struct MainGroup final : TrackPanelGroup {
2141  explicit MainGroup( TrackPanel &panel ) : mPanel{ panel } {}
2142  Subdivision Children( const wxRect &rect ) override
2143  { return { Axis::X, Refinement{
2144  { 0, EmptyCell::Instance() },
2145  { kLeftMargin, std::make_shared< Subgroup >( mPanel ) },
2146  { rect.GetRight() + 1 - kRightMargin, EmptyCell::Instance() }
2147  } }; }
2148  TrackPanel &mPanel;
2149 };
2150 
2151 }
2152 
2153 std::shared_ptr<TrackPanelNode> TrackPanel::Root()
2154 {
2155  // Root and other subgroup objects are throwaways.
2156  // They might instead be cached to avoid repeated allocation.
2157  // That cache would need invalidation when there is addition, deletion, or
2158  // permutation of tracks, or change of width of the vertical rulers.
2159  return std::make_shared< MainGroup >( *this );
2160 }
2161 
2162 // This finds the rectangle of a given track (including all channels),
2163 // either that of the label 'adornment' or the track itself
2164 // The given track is assumed to be the first channel
2165 wxRect TrackPanel::FindTrackRect( const Track * target, bool label )
2166 {
2167  if (!target) {
2168  return { 0, 0, 0, 0 };
2169  }
2170 
2171  // PRL: I think the following very old comment misused the term "race
2172  // condition" for a bug that happened with only a single thread. I think the
2173  // real problem referred to, was that this function could be reached, via
2174  // TrackPanelAx callbacks, during low-level operations while the TrackList
2175  // was not in a consistent state.
2176  // Now the problem is fixed by delaying the handling of events generated
2177  // by TrackList. And besides that, we use Channels() instead of looking
2178  // directly at the links.
2179 
2180  // Old comment:
2181  // The check for a null linked track is necessary because there's
2182  // a possible race condition between the time the 2 linked tracks
2183  // are added and when wxAccessible methods are called. This is
2184  // most evident when using Jaws.
2185  auto height = TrackList::Channels( target ).sum( &Track::GetHeight );
2186 
2187  wxRect rect{
2188  0,
2189  target->GetY() - mViewInfo->vpos,
2190  GetSize().GetWidth(),
2191  height
2192  };
2193 
2194 
2195  rect.x += kLeftMargin;
2196  if (label)
2197  rect.width = GetVRulerOffset() - kLeftMargin;
2198  else
2199  rect.width -= (kLeftMargin + kRightMargin);
2200 
2201  rect.y += kTopMargin;
2202  rect.height -= (kTopMargin + kBottomMargin);
2203 
2204  return rect;
2205 }
2206 
2208 {
2209  return vrulerSize.x;
2210 }
2211 
2214 {
2215  if (!mListener)
2216  return;
2217 
2218  // DM: Note that the Selection Bar can actually MODIFY the selection
2219  // if snap-to mode is on!!!
2221 }
2222 
2224 {
2225  return mAx->GetFocus().get();
2226 }
2227 
2229 {
2230  return static_cast<Track*>( GetFocusedCell() );
2231 }
2232 
2234 {
2236 }
2237 
2239 {
2240  // Make sure we always have the first linked track of a stereo track
2241  t = *GetTracks()->FindLeader(t);
2242 
2243  auto cell = mAx->SetFocus( Track::Pointer( t ) ).get();
2244 
2245  if (cell) {
2247  Refresh( false );
2248  }
2249 }
2250 
2251 /**********************************************************************
2252 
2253  TrackInfo code is destined to move out of this file.
2254 
2255 **********************************************************************/
2256 
2257 namespace {
2258 
2259 wxFont gFont;
2260 
2261 std::unique_ptr<LWSlider>
2262  gGainCaptured
2263  , gPanCaptured
2264  , gGain
2265  , gPan
2266 #ifdef EXPERIMENTAL_MIDI_OUT
2267  , gVelocityCaptured
2268  , gVelocity
2269 #endif
2270 ;
2271 
2272 }
2273 
2274 void TrackInfo::ReCreateSliders( wxWindow *pParent ){
2275  const wxPoint point{ 0, 0 };
2276  wxRect sliderRect;
2277  GetGainRect(point, sliderRect);
2278 
2279  float defPos = 1.0;
2280  /* i18n-hint: Title of the Gain slider, used to adjust the volume */
2281  gGain = std::make_unique<LWSlider>(pParent, _("Gain"),
2282  wxPoint(sliderRect.x, sliderRect.y),
2283  wxSize(sliderRect.width, sliderRect.height),
2284  DB_SLIDER);
2285  gGain->SetDefaultValue(defPos);
2286 
2287  gGainCaptured = std::make_unique<LWSlider>(pParent, _("Gain"),
2288  wxPoint(sliderRect.x, sliderRect.y),
2289  wxSize(sliderRect.width, sliderRect.height),
2290  DB_SLIDER);
2291  gGainCaptured->SetDefaultValue(defPos);
2292 
2293  GetPanRect(point, sliderRect);
2294 
2295  defPos = 0.0;
2296  /* i18n-hint: Title of the Pan slider, used to move the sound left or right */
2297  gPan = std::make_unique<LWSlider>(pParent, _("Pan"),
2298  wxPoint(sliderRect.x, sliderRect.y),
2299  wxSize(sliderRect.width, sliderRect.height),
2300  PAN_SLIDER);
2301  gPan->SetDefaultValue(defPos);
2302 
2303  gPanCaptured = std::make_unique<LWSlider>(pParent, _("Pan"),
2304  wxPoint(sliderRect.x, sliderRect.y),
2305  wxSize(sliderRect.width, sliderRect.height),
2306  PAN_SLIDER);
2307  gPanCaptured->SetDefaultValue(defPos);
2308 
2309 #ifdef EXPERIMENTAL_MIDI_OUT
2310  GetVelocityRect(point, sliderRect);
2311 
2312  /* i18n-hint: Title of the Velocity slider, used to adjust the volume of note tracks */
2313  gVelocity = std::make_unique<LWSlider>(pParent, _("Velocity"),
2314  wxPoint(sliderRect.x, sliderRect.y),
2315  wxSize(sliderRect.width, sliderRect.height),
2316  VEL_SLIDER);
2317  gVelocity->SetDefaultValue(0.0);
2318  gVelocityCaptured = std::make_unique<LWSlider>(pParent, _("Velocity"),
2319  wxPoint(sliderRect.x, sliderRect.y),
2320  wxSize(sliderRect.width, sliderRect.height),
2321  VEL_SLIDER);
2322  gVelocityCaptured->SetDefaultValue(0.0);
2323 #endif
2324 
2325 }
2326 
2327 void TrackInfo::GetCloseBoxHorizontalBounds( const wxRect & rect, wxRect &dest )
2328 {
2329  dest.x = rect.x;
2330  dest.width = kTrackInfoBtnSize;
2331 }
2332 
2333 void TrackInfo::GetCloseBoxRect(const wxRect & rect, wxRect & dest)
2334 {
2335  GetCloseBoxHorizontalBounds( rect, dest );
2336  auto results = CalcItemY( commonTrackTCPLines, kItemBarButtons );
2337  dest.y = rect.y + results.first;
2338  dest.height = results.second;
2339 }
2340 
2341 static const int TitleSoloBorderOverlap = 1;
2342 
2343 void TrackInfo::GetTitleBarHorizontalBounds( const wxRect & rect, wxRect &dest )
2344 {
2345  // to right of CloseBoxRect, plus a little more
2346  wxRect closeRect;
2347  GetCloseBoxHorizontalBounds( rect, closeRect );
2348  dest.x = rect.x + closeRect.width + 1;
2349  dest.width = rect.x + rect.width - dest.x + TitleSoloBorderOverlap;
2350 }
2351 
2352 void TrackInfo::GetTitleBarRect(const wxRect & rect, wxRect & dest)
2353 {
2354  GetTitleBarHorizontalBounds( rect, dest );
2355  auto results = CalcItemY( commonTrackTCPLines, kItemBarButtons );
2356  dest.y = rect.y + results.first;
2357  dest.height = results.second;
2358 }
2359 
2360 void TrackInfo::GetNarrowMuteHorizontalBounds( const wxRect & rect, wxRect &dest )
2361 {
2362  dest.x = rect.x;
2363  dest.width = rect.width / 2 + 1;
2364 }
2365 
2366 void TrackInfo::GetNarrowSoloHorizontalBounds( const wxRect & rect, wxRect &dest )
2367 {
2368  wxRect muteRect;
2369  GetNarrowMuteHorizontalBounds( rect, muteRect );
2370  dest.x = rect.x + muteRect.width;
2371  dest.width = rect.width - muteRect.width + TitleSoloBorderOverlap;
2372 }
2373 
2374 void TrackInfo::GetWideMuteSoloHorizontalBounds( const wxRect & rect, wxRect &dest )
2375 {
2376  // Larger button, symmetrically placed intended.
2377  // On windows this gives 15 pixels each side.
2378  dest.width = rect.width - 2 * kTrackInfoBtnSize + 6;
2379  dest.x = rect.x + kTrackInfoBtnSize -3;
2380 }
2381 
2383 (const wxRect & rect, wxRect & dest, bool solo, bool bHasSoloButton,
2384  const Track *pTrack)
2385 {
2386 
2387  auto resultsM = CalcItemY( getTCPLines( *pTrack ), kItemMute );
2388  auto resultsS = CalcItemY( getTCPLines( *pTrack ), kItemSolo );
2389  dest.height = resultsS.second;
2390 
2391  int yMute = resultsM.first;
2392  int ySolo = resultsS.first;
2393 
2394  bool bSameRow = ( yMute == ySolo );
2395  bool bNarrow = bSameRow && bHasSoloButton;
2396 
2397  if( bNarrow )
2398  {
2399  if( solo )
2400  GetNarrowSoloHorizontalBounds( rect, dest );
2401  else
2402  GetNarrowMuteHorizontalBounds( rect, dest );
2403  }
2404  else
2405  GetWideMuteSoloHorizontalBounds( rect, dest );
2406 
2407  if( bSameRow || !solo )
2408  dest.y = rect.y + yMute;
2409  else
2410  dest.y = rect.y + ySolo;
2411 
2412 }
2413 
2414 void TrackInfo::GetSliderHorizontalBounds( const wxPoint &topleft, wxRect &dest )
2415 {
2416  dest.x = topleft.x + 6;
2417  dest.width = kTrackInfoSliderWidth;
2418 }
2419 
2420 void TrackInfo::GetGainRect(const wxPoint &topleft, wxRect & dest)
2421 {
2422  GetSliderHorizontalBounds( topleft, dest );
2423  auto results = CalcItemY( waveTrackTCPLines, kItemGain );
2424  dest.y = topleft.y + results.first;
2425  dest.height = results.second;
2426 }
2427 
2428 void TrackInfo::GetPanRect(const wxPoint &topleft, wxRect & dest)
2429 {
2430  GetGainRect( topleft, dest );
2431  auto results = CalcItemY( waveTrackTCPLines, kItemPan );
2432  dest.y = topleft.y + results.first;
2433 }
2434 
2435 #ifdef EXPERIMENTAL_MIDI_OUT
2436 void TrackInfo::GetVelocityRect(const wxPoint &topleft, wxRect & dest)
2437 {
2438  GetSliderHorizontalBounds( topleft, dest );
2439  auto results = CalcItemY( noteTrackTCPLines, kItemVelocity );
2440  dest.y = topleft.y + results.first;
2441  dest.height = results.second;
2442 }
2443 #endif
2444 
2445 void TrackInfo::GetMinimizeHorizontalBounds( const wxRect &rect, wxRect &dest )
2446 {
2447  const int space = 0;// was 3.
2448  dest.x = rect.x + space;
2449 
2450  wxRect syncLockRect;
2451  GetSyncLockHorizontalBounds( rect, syncLockRect );
2452 
2453  // Width is rect.width less space on left for track select
2454  // and on right for sync-lock icon.
2455  dest.width = rect.width - (space + syncLockRect.width);
2456 }
2457 
2458 void TrackInfo::GetMinimizeRect(const wxRect & rect, wxRect &dest)
2459 {
2460  GetMinimizeHorizontalBounds( rect, dest );
2461  auto results = CalcBottomItemY
2462  ( commonTrackTCPBottomLines, kItemMinimize, rect.height);
2463  dest.y = rect.y + results.first;
2464  dest.height = results.second;
2465 }
2466 
2467 void TrackInfo::GetSyncLockHorizontalBounds( const wxRect &rect, wxRect &dest )
2468 {
2469  dest.width = kTrackInfoBtnSize;
2470  dest.x = rect.x + rect.width - dest.width;
2471 }
2472 
2473 void TrackInfo::GetSyncLockIconRect(const wxRect & rect, wxRect &dest)
2474 {
2475  GetSyncLockHorizontalBounds( rect, dest );
2476  auto results = CalcBottomItemY
2477  ( commonTrackTCPBottomLines, kItemSyncLock, rect.height);
2478  dest.y = rect.y + results.first;
2479  dest.height = results.second;
2480 }
2481 
2482 #ifdef USE_MIDI
2484 ( const wxRect &rect, wxRect &dest )
2485 {
2486  dest.x = rect.x + 1; // To center slightly
2487  // PRL: TODO: kMidiCellWidth is defined in terms of the other constant
2488  // kTrackInfoWidth but I am trying to avoid use of that constant.
2489  // Can cell width be computed from dest.width instead?
2490  dest.width = kMidiCellWidth * 4;
2491 }
2492 
2493 void TrackInfo::GetMidiControlsRect(const wxRect & rect, wxRect & dest)
2494 {
2495  GetMidiControlsHorizontalBounds( rect, dest );
2496  auto results = CalcItemY( noteTrackTCPLines, kItemMidiControlsRect );
2497  dest.y = rect.y + results.first;
2498  dest.height = results.second;
2499 }
2500 #endif
2501 
2504 {
2505  dc->SetFont(gFont);
2506 }
2507 
2508 #if 0
2509 void TrackInfo::DrawBordersWithin
2510  ( wxDC* dc, const wxRect & rect, const Track &track ) const
2511 {
2512  AColor::Dark(dc, false); // same color as border of toolbars (ToolBar::OnPaint())
2513 
2514  // below close box and title bar
2515  wxRect buttonRect;
2516  GetTitleBarRect( rect, buttonRect );
2517  AColor::Line
2518  (*dc, rect.x, buttonRect.y + buttonRect.height,
2519  rect.width - 1, buttonRect.y + buttonRect.height);
2520 
2521  // between close box and title bar
2522  AColor::Line
2523  (*dc, buttonRect.x, buttonRect.y,
2524  buttonRect.x, buttonRect.y + buttonRect.height - 1);
2525 
2526  GetMuteSoloRect( rect, buttonRect, false, true, &track );
2527 
2528  bool bHasMuteSolo = dynamic_cast<const PlayableTrack*>( &track ) != NULL;
2529  if( bHasMuteSolo && !TrackInfo::HideTopItem( rect, buttonRect ) )
2530  {
2531  // above mute/solo
2532  AColor::Line
2533  (*dc, rect.x, buttonRect.y,
2534  rect.width - 1, buttonRect.y);
2535 
2536  // between mute/solo
2537  // Draw this little line; if there is no solo, wide mute button will
2538  // overpaint it later:
2539  AColor::Line
2540  (*dc, buttonRect.x + buttonRect.width, buttonRect.y,
2541  buttonRect.x + buttonRect.width, buttonRect.y + buttonRect.height - 1);
2542 
2543  // below mute/solo
2544  AColor::Line
2545  (*dc, rect.x, buttonRect.y + buttonRect.height,
2546  rect.width - 1, buttonRect.y + buttonRect.height);
2547  }
2548 
2549  // left of and above minimize button
2550  wxRect minimizeRect;
2551  this->GetMinimizeRect(rect, minimizeRect);
2552  AColor::Line
2553  (*dc, minimizeRect.x - 1, minimizeRect.y,
2554  minimizeRect.x - 1, minimizeRect.y + minimizeRect.height - 1);
2555  AColor::Line
2556  (*dc, minimizeRect.x, minimizeRect.y - 1,
2557  minimizeRect.x + minimizeRect.width - 1, minimizeRect.y - 1);
2558 }
2559 #endif
2560 
2561 //#define USE_BEVELS
2562 
2563 // Paint the whole given rectangle some fill color
2565  wxDC * dc, const wxRect & rect, bool bSelected, const int vrul)
2566 {
2567  // fill in label
2568  wxRect fill = rect;
2569  fill.width = vrul - kLeftMargin;
2570  AColor::MediumTrackInfo(dc, bSelected);
2571  dc->DrawRectangle(fill);
2572 
2573 #ifdef USE_BEVELS
2574  // This branch is not now used
2575  // PRL: todo: banish magic numbers.
2576  // PRL: vrul was the x coordinate of left edge of the vertical ruler.
2577  // PRL: bHasMuteSolo was true iff the track was WaveTrack.
2578  if( bHasMuteSolo )
2579  {
2580  int ylast = rect.height-20;
2581  int ybutton = wxMin(32,ylast-17);
2582  int ybuttonEnd = 67;
2583 
2584  fill=wxRect( rect.x+1, rect.y+17, vrul-6, ybutton);
2585  AColor::BevelTrackInfo( *dc, true, fill );
2586 
2587  if( ybuttonEnd < ylast ){
2588  fill=wxRect( rect.x+1, rect.y+ybuttonEnd, fill.width, ylast - ybuttonEnd);
2589  AColor::BevelTrackInfo( *dc, true, fill );
2590  }
2591  }
2592  else
2593  {
2594  fill=wxRect( rect.x+1, rect.y+17, vrul-6, rect.height-37);
2595  AColor::BevelTrackInfo( *dc, true, fill );
2596  }
2597 #endif
2598 }
2599 
2600 namespace {
2601 unsigned DefaultTrackHeight( const TCPLines &topLines )
2602 {
2603  int needed =
2605  totalTCPLines( topLines, true ) +
2606  totalTCPLines( commonTrackTCPBottomLines, false ) + 1;
2607  return (unsigned) std::max( needed, (int) Track::DefaultHeight );
2608 }
2609 }
2610 
2612 {
2613  return DefaultTrackHeight( noteTrackTCPLines );
2614 }
2615 
2617 {
2618  return DefaultTrackHeight( waveTrackTCPLines );
2619 }
2620 
2622 (const wxRect &sliderRect, const WaveTrack *t, bool captured, wxWindow *pParent)
2623 {
2624  wxPoint pos = sliderRect.GetPosition();
2625  float gain = t ? t->GetGain() : 1.0;
2626 
2627  gGain->Move(pos);
2628  gGain->Set(gain);
2629  gGainCaptured->Move(pos);
2630  gGainCaptured->Set(gain);
2631 
2632  auto slider = (captured ? gGainCaptured : gGain).get();
2633  slider->SetParent( pParent ? pParent : ::GetActiveProject() );
2634  return slider;
2635 }
2636 
2638 (const wxRect &sliderRect, const WaveTrack *t, bool captured, wxWindow *pParent)
2639 {
2640  wxPoint pos = sliderRect.GetPosition();
2641  float pan = t ? t->GetPan() : 0.0;
2642 
2643  gPan->Move(pos);
2644  gPan->Set(pan);
2645  gPanCaptured->Move(pos);
2646  gPanCaptured->Set(pan);
2647 
2648  auto slider = (captured ? gPanCaptured : gPan).get();
2649  slider->SetParent( pParent ? pParent : ::GetActiveProject() );
2650  return slider;
2651 }
2652 
2653 #ifdef EXPERIMENTAL_MIDI_OUT
2655 (const wxRect &sliderRect, const NoteTrack *t, bool captured, wxWindow *pParent)
2656 {
2657  wxPoint pos = sliderRect.GetPosition();
2658  float velocity = t ? t->GetVelocity() : 0.0;
2659 
2660  gVelocity->Move(pos);
2661  gVelocity->Set(velocity);
2662  gVelocityCaptured->Move(pos);
2663  gVelocityCaptured->Set(velocity);
2664 
2665  auto slider = (captured ? gVelocityCaptured : gVelocity).get();
2666  slider->SetParent( pParent ? pParent : ::GetActiveProject() );
2667  return slider;
2668 }
2669 #endif
2670 
2671 void TrackInfo::UpdatePrefs( wxWindow *pParent )
2672 {
2673  // Calculation of best font size depends on language, so it should be redone in case
2674  // the language preference changed.
2675 
2676  int fontSize = 10;
2677  gFont.Create(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
2678 
2679  int allowableWidth =
2680  // PRL: was it correct to include the margin?
2682  - 2; // 2 to allow for left/right borders
2683  int textWidth, textHeight;
2684  do {
2685  gFont.SetPointSize(fontSize);
2686  pParent->GetTextExtent(_("Stereo, 999999Hz"),
2687  &textWidth,
2688  &textHeight,
2689  NULL,
2690  NULL,
2691  &gFont);
2692  fontSize--;
2693  } while (textWidth >= allowableWidth);
2694 }
2695 
2696 static TrackPanel * TrackPanelFactory(wxWindow * parent,
2697  wxWindowID id,
2698  const wxPoint & pos,
2699  const wxSize & size,
2700  const std::shared_ptr<TrackList> &tracks,
2701  ViewInfo * viewInfo,
2702  TrackPanelListener * listener,
2703  AdornedRulerPanel * ruler)
2704 {
2705  wxASSERT(parent); // to justify safenew
2706  return safenew TrackPanel(
2707  parent,
2708  id,
2709  pos,
2710  size,
2711  tracks,
2712  viewInfo,
2713  listener,
2714  ruler);
2715 }
2716 
2717 
2718 // Declare the static factory function.
2719 // We defined it in the class.
2720 TrackPanel *(*TrackPanel::FactoryFunction)(
2721  wxWindow * parent,
2722  wxWindowID id,
2723  const wxPoint & pos,
2724  const wxSize & size,
2725  const std::shared_ptr<TrackList> &tracks,
2726  ViewInfo * viewInfo,
2727  TrackPanelListener * listener,
2729 
2731 {
2732 }
2733 
2735 {
2736 }
2737 
2739 {
2740 }
2741 
2743 {
2744 }
2745 
2747 {
2748 }
2749 
2752 {
2753  return {};
2754 }
2755 
2758 {
2759  return RefreshCode::Cancelled;
2760 }
2761 
2763  (const wxRect &, wxWindow*, wxPoint *)
2764 {
2765  return RefreshCode::RefreshNone;
2766 }
2767 
2768 unsigned TrackPanelCell::CaptureKey(wxKeyEvent &event, ViewInfo &, wxWindow *)
2769 {
2770  event.Skip();
2771  return RefreshCode::RefreshNone;
2772 }
2773 
2774 unsigned TrackPanelCell::KeyDown(wxKeyEvent &event, ViewInfo &, wxWindow *)
2775 {
2776  event.Skip();
2777  return RefreshCode::RefreshNone;
2778 }
2779 
2780 unsigned TrackPanelCell::KeyUp(wxKeyEvent &event, ViewInfo &, wxWindow *)
2781 {
2782  event.Skip();
2783  return RefreshCode::RefreshNone;
2784 }
2785 
2786 unsigned TrackPanelCell::Char(wxKeyEvent &event, ViewInfo &, wxWindow *)
2787 {
2788  event.Skip();
2789  return RefreshCode::RefreshNone;
2790 }
2791 
2793  : mPanelRect {
2794  wxPoint{ 0, project->mViewInfo.vpos },
2795  project->GetTPTracksUsableArea()
2796  }
2797 {}
2798 
2799 bool IsVisibleTrack::operator () (const Track *pTrack) const
2800 {
2801  // Need to return true if this track or a later channel intersects
2802  // the view
2803  return
2804  TrackList::Channels(pTrack).StartingWith(pTrack).any_of(
2805  [this]( const Track *pT ) {
2806  wxRect r(0, pT->GetY(), 1, pT->GetHeight());
2807  return r.Intersects(mPanelRect);
2808  }
2809  );
2810 }
LWSlider * VelocitySlider(const NoteTrack *nt)
Definition: TrackPanel.cpp:290
void SetZoom(double pixelsPerSecond)
Definition: ViewInfo.cpp:94
#define MUTE_SOLO_ITEMS(extra)
Definition: TrackPanel.cpp:796
void RefreshTrack(Track *trk, bool refreshbacking=true)
Definition: TrackPanel.cpp:990
LWSlider * VelocitySlider(const wxRect &sliderRect, const NoteTrack *t, bool captured, wxWindow *pParent)
size_t GetTrackCount() const
Definition: TrackPanel.cpp:664
void DrawVRuler(TrackPanelDrawingContext &context, const Track *t, const wxRect &rect, bool bSelected)
bool GetSolo() const
Definition: Track.h:777
void GetSyncLockHorizontalBounds(const wxRect &rect, wxRect &dest)
int GetLabelWidth() const
Definition: TrackPanel.h:357
void RedrawProject(const bool bForceWaveTracks=false)
Definition: Project.cpp:1466
AudacityPrefs * gPrefs
Definition: Prefs.cpp:73
void WideSoloDrawFunction(TrackPanelDrawingContext &context, const wxRect &rect, const Track *pTrack)
AUDACITY_DLL_API Theme theTheme
Definition: Theme.cpp:209
void GetTitleBarRect(const wxRect &rect, wxRect &dest)
ViewInfo * mViewInfo
void UpdateViewIfNoTracks()
Definition: TrackPanel.cpp:681
void GetMuteSoloRect(const wxRect &rect, wxRect &dest, bool solo, bool bHasSoloButton, const Track *pTrack)
const TrackList * GetTracks() const
Definition: TrackPanel.h:362
void CloseTitleDrawFunction(TrackPanelDrawingContext &context, const wxRect &rect, const Track *pTrack)
bool bUpdateTrackIndicator
Definition: ViewInfo.h:184
void DrawOutside(TrackPanelDrawingContext &context, const Track *leaderTrack, const wxRect &teamRect)
void SliderDrawFunction(LWSlider *(*Selector)(const wxRect &sliderRect, const TrackClass *t, bool captured, wxWindow *), wxDC *dc, const wxRect &rect, const Track *pTrack, bool captured, bool highlight)
ViewInfo is used mainly to hold the zooming, selection and scroll information. It also has some statu...
Definition: ViewInfo.h:143
void EnsureVisible(Track *t)
SelectedRegion selectedRegion
Definition: ViewInfo.h:162
R TypeSwitch(const Functions &...functions)
Definition: Track.h:630
static void CaptureKeyboard(wxWindow *handler)
Definition: Project.cpp:5350
std::weak_ptr< Track > mpTrack
Definition: Track.h:1136
void DrawEverythingElse(TrackPanelDrawingContext &context, const wxRegion &region, const wxRect &clip)
std::shared_ptr< TrackList > mTracks
Definition: TrackPanel.h:422
bool GetSelected() const
Definition: Track.h:381
void ClearTopMargin(TrackPanelDrawingContext &context, const wxRect &clip)
void DrawItems(TrackPanelDrawingContext &context, const wxRect &rect, const Track &track)
static void TrackPanelBackground(wxDC *dc, bool selected)
Definition: AColor.cpp:349
static void Arrow(wxDC &dc, wxCoord x, wxCoord y, int width, bool down=true)
Definition: AColor.cpp:96
virtual unsigned Char(wxKeyEvent &event, ViewInfo &viewInfo, wxWindow *pParent)
virtual void TP_ModifyState(bool bWantsAutoSave)=0
auto SelectedLeaders() -> TrackIterRange< TrackType >
Definition: Track.h:1297
unsigned MinimumTrackHeight()
Definition: TrackPanel.cpp:917
static void Bevel2(wxDC &dc, bool up, const wxRect &r, bool bSel=false, bool bHighlight=false)
Definition: AColor.cpp:222
TrackPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, const std::shared_ptr< TrackList > &tracks, ViewInfo *viewInfo, TrackPanelListener *listener, AdornedRulerPanel *ruler)
Definition: TrackPanel.cpp:200
void OnTrackMenu(Track *t=NULL)
double GetScreenEndTime() const
Definition: TrackPanel.cpp:455
void OnTrackListResizing(TrackListEvent &event)
Definition: TrackPanel.cpp:714
void ClearLeftAndRightMargins(TrackPanelDrawingContext &context, const wxRect &clip)
void MuteAndSoloDrawFunction(TrackPanelDrawingContext &context, const wxRect &rect, const Track *pTrack)
float GetPan() const
Definition: WaveTrack.cpp:431
void GetNarrowSoloHorizontalBounds(const wxRect &rect, wxRect &dest)
auto Leaders() -> TrackIterRange< TrackType >
Definition: Track.h:1280
void HandlePageDownKey()
Definition: TrackPanel.cpp:619
bool IsSyncLockSelected() const
Definition: Track.cpp:318
#define STATUS_ITEMS
Definition: TrackPanel.cpp:799
void DisplaySelection()
Displays the bounds of the selection in the status bar.
void SetTrackInfoFont(wxDC *dc)
virtual void TP_HandleResize()=0
wxRect FindTrackRect(const Track *target, bool label)
virtual unsigned HandleWheelRotation(const TrackPanelMouseEvent &event, AudacityProject *pProject)
LWSlider * GainSlider(const wxRect &sliderRect, const WaveTrack *t, bool captured, wxWindow *pParent)
void UpdatePrefs()
Definition: TrackPanel.cpp:301
void HandlePageUpKey()
Definition: TrackPanel.cpp:614
void SetAudioIOToken(int token)
Definition: Project.cpp:1528
unsigned GetNumCaptureChannels() const
Definition: AudioIO.h:1028
static void DrawLabelControls(const NoteTrack *pTrack, wxDC &dc, const wxRect &rect, int highlightedChannel=-1)
Definition: NoteTrack.cpp:268
std::shared_ptr< TrackPanelNode > Root() override
wxString label
Definition: Tags.cpp:733
UIHandlePtr Target()
TrackPanel::AudacityTimer mTimer
double PositionToTime(wxInt64 position, wxInt64 origin=0, bool ignoreFisheye=false) const
Definition: ViewInfo.cpp:49
static bool HasSoloButton()
Definition: TrackPanel.h:409
double OffsetTimeByPixels(double time, wxInt64 offset, bool ignoreFisheye=false) const
Definition: ViewInfo.h:77
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:1246
wxRect mPanelRect
Definition: TrackPanel.h:505
SelectedRegion mLastDrawnSelectedRegion
Definition: TrackPanel.h:474
std::shared_ptr< const Track > SubstitutePendingChangedTrack() const
Definition: Track.cpp:1370
void HighlightFocusedTrack(wxDC *dc, const wxRect &rect)
Draw a three-level highlight gradient around the focused track.
void DrawSash(wxDC *dc, const wxRect &rect, int labelw, bool bSelected)
void GetMinimizeHorizontalBounds(const wxRect &rect, wxRect &dest)
TrackIter< Track > FindLeader(Track *pTrack)
Definition: Track.cpp:697
void StopPlaying(bool stopStream=true)
int GetAudioIOToken() const
Definition: Project.cpp:1523
int GetVRulerWidth() const
static void MediumTrackInfo(wxDC *dc, bool selected)
Definition: AColor.cpp:328
bool GetMinimized() const
Definition: Track.cpp:217
bool IsStreamActive() const
Returns true if the audio i/o is running at all, but not during cleanup.
Definition: AudioIO.cpp:2749
void GetSyncLockIconRect(const wxRect &rect, wxRect &dest)
size_t GetSelectedTrackCount() const
Definition: TrackPanel.cpp:670
bool IsAudioTokenActive(int token) const
Returns true if the stream is active, or even if audio I/O is busy cleaning up its data or writing to...
Definition: AudioIO.cpp:2768
virtual ChannelType GetChannel() const
Definition: Track.h:387
virtual void TP_ScrollWindow(double scrollto)=0
void StatusDrawFunction(const wxString &string, wxDC *dc, const wxRect &rect)
double h
Definition: ViewInfo.h:47
void DoContextMenu(TrackPanelCell *pCell=nullptr)
void DrawBackground(wxDC *dc, const wxRect &rect, bool bSelected, const int vrul)
static wxString gSoloPref
Definition: TrackPanel.h:469
virtual ~TrackPanel()
Definition: TrackPanel.cpp:263
void UpdateSelectionDisplay()
Definition: TrackPanel.cpp:644
LWSlider * PanSlider(const wxRect &sliderRect, const WaveTrack *t, bool captured, wxWindow *pParent)
void PanSliderDrawFunction(TrackPanelDrawingContext &context, const wxRect &rect, const Track *pTrack)
static void Dark(wxDC *dc, bool selected, bool highlight=false)
Definition: AColor.cpp:338
void GetWideMuteSoloHorizontalBounds(const wxRect &rect, wxRect &dest)
#define RANGE(array)
Definition: TrackPanel.cpp:756
void ScrollIntoView(double pos)
virtual unsigned CaptureKey(wxKeyEvent &event, ViewInfo &viewInfo, wxWindow *pParent)
auto Selected() -> TrackIterRange< TrackType >
Definition: Track.h:1263
#define safenew
Definition: Audacity.h:230
void GetMidiControlsRect(const wxRect &rect, wxRect &dest)
void WideMuteDrawFunction(TrackPanelDrawingContext &context, const wxRect &rect, const Track *pTrack)
virtual unsigned DoContextMenu(const wxRect &rect, wxWindow *pParent, wxPoint *pPosition)
void UpdatePrefs()
Definition: ViewInfo.cpp:150
wxCoord MostRecentXCoord() const
std::shared_ptr< Track > GetTrack() const
Definition: ButtonHandle.h:31
void UpdateAccessibility()
Definition: TrackPanel.cpp:657
void SetFocusedTrack(Track *t)
void GetTracksUsableArea(int *width, int *height) const
Definition: TrackPanel.cpp:328
std::shared_ptr< NoteTrack > GetTrack() const
bool HideTopItem(const wxRect &rect, const wxRect &subRect, int allowance=0)
Definition: TrackPanel.cpp:929
void DisplayBitmap(wxDC &dc)
Definition: BackedPanel.cpp:65
virtual ~TrackPanelNode()=0
TrackList is a flat linked list of tracks supporting Add, Remove, Clear, and Contains, plus serialization of the list of tracks.
Definition: Track.h:1121
void FixScrollbars()
Definition: Project.cpp:1973
AudacityProject provides the main window, with tools and tracks contained within it.
Definition: Project.h:174
void DrawShadow(wxDC *dc, const wxRect &rect)
wxSize vrulerSize
Definition: TrackPanel.h:477
unsigned Result
Definition: UIHandle.h:37
void GetTitleBarHorizontalBounds(const wxRect &rect, wxRect &dest)
Track * GetFirstSelectedTrack()
#define PAN_SLIDER
Definition: ASlider.h:46
static void Mute(wxDC *dc, bool on, bool selected, bool soloing)
Definition: AColor.cpp:398
int format
Definition: ExportPCM.cpp:56
bool TakesFocus() const override
Definition: TrackPanel.cpp:639
Defines a selected portion of a project.
void OnTimer(wxTimerEvent &event)
AS: This gets called on our wx timer events.
Definition: TrackPanel.cpp:379
#define COMMON_ITEMS
Definition: TrackPanel.cpp:807
void MuteOrSoloDrawFunction(wxDC *dc, const wxRect &rect, const Track *pTrack, bool down, bool captured, bool solo, bool hit)
The TrackPanel class coordinates updates and operations on the main part of the screen which contains...
Definition: TrackPanel.h:248
static void Solo(wxDC *dc, bool on, bool selected)
Definition: AColor.cpp:413
const int kTimerInterval
void RepairBitmap(wxDC &dc, wxCoord x, wxCoord y, wxCoord width, wxCoord height)
Definition: BackedPanel.cpp:60
static const int TitleSoloBorderOverlap
Lightweight version of ASlider. In other words it does not have a window permanently associated with ...
Definition: ASlider.h:73
void DrawBordersAroundTrack(wxDC *dc, const wxRect &rect)
void SetFocusedCell() override
wxDEFINE_EVENT(EVT_TRACK_PANEL_TIMER, wxCommandEvent)
void GetMinimizeRect(const wxRect &rect, wxRect &dest)
virtual void TP_DisplayStatusMessage(const wxString &msg)=0
bool IsAudioActive()
Definition: TrackPanel.cpp:624
TrackPanelCell * GetFocusedCell() override
A Track that contains audio waveform data.
Definition: WaveTrack.h:60
void DrawTracks(wxDC *dc)
std::shared_ptr< Track > GetTrack() const
Definition: SliderHandle.h:35
void ProcessUIHandleResult(TrackPanelCell *pClickedTrack, TrackPanelCell *pLatestCell, unsigned refreshResult) override
Definition: TrackPanel.cpp:534
Fundamental data object of Audacity, placed in the TrackPanel. Classes derived form it include the Wa...
Definition: Track.h:191
unsigned DefaultNoteTrackHeight()
void DrawOverlays(bool repaint_all, wxDC *pDC=nullptr)
DrawFunction drawFunction
Definition: TrackPanel.cpp:751
wxImage & Image(int iIndex)
Definition: Theme.cpp:1251
ViewInfo mViewInfo
Definition: Project.h:555
wxInt64 TimeToPosition(double time, wxInt64 origin=0, bool ignoreFisheye=false) const
STM: Converts a project time to screen x position.
Definition: ViewInfo.cpp:59
void UpdatePrefs(wxWindow *pParent)
unsigned DefaultWaveTrackHeight()
void Refresh(bool eraseBackground=true, const wxRect *rect=(const wxRect *) NULL) override
wxString GetName() const
Definition: Track.h:376
double GetMostRecentXPos()
Definition: TrackPanel.cpp:985
void GetCloseBoxHorizontalBounds(const wxRect &rect, wxRect &dest)
LWSlider * PanSlider(const WaveTrack *wt)
Definition: TrackPanel.cpp:281
virtual HitTestPreview DefaultPreview(const TrackPanelMouseState &state, const AudacityProject *pProject)
virtual void TP_RedrawScrollbars()=0
void Status1DrawFunction(TrackPanelDrawingContext &context, const wxRect &rect, const Track *pTrack)
bool IsAudioActive() const
Definition: Project.cpp:1533
int scrollStep
Definition: ViewInfo.h:179
bool within(A a, B b, DIST d)
Definition: TrackPanel.cpp:161
wxMouseState mLastMouseState
void UpdateTrackVRuler(const Track *t)
void GetSliderHorizontalBounds(const wxPoint &topleft, wxRect &dest)
void UpdateStatusMessage(const wxString &status) override
Definition: TrackPanel.cpp:630
void SetBackgroundCell(const std::shared_ptr< TrackPanelCell > &pCell)
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:122
void GetPanRect(const wxPoint &topLeft, wxRect &dest)
virtual ~TrackPanelCell()=0
An AudioTrack that can be played and stopped.
Definition: Track.h:769
wxDC & GetBackingDCForRepaint()
Definition: BackedPanel.cpp:35
void VerticalScroll(float fracPosition)
FoundCell FindCell(int mouseX, int mouseY)
int vpos
Definition: ViewInfo.h:45
TrackPanelListener * mListener
Definition: TrackPanel.h:420
virtual unsigned KeyUp(wxKeyEvent &event, ViewInfo &viewInfo, wxWindow *pParent)
const wxChar * GetSampleFormatStr(sampleFormat format)
std::shared_ptr< TrackPanelCell > GetBackgroundCell()
#define DB_SLIDER
Definition: ASlider.h:45
virtual ~TrackPanelGroup()
#define VEL_SLIDER
Definition: ASlider.h:49
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom")).Raw()), OnMoveTrack)#define SET_TRACK_NAME_PLUGIN_SYMBOLclass SetTrackNameCommand:public AudacityCommand
void HandleCursorForPresentMouseState(bool doHit=true)
void UpdateVRuler(Track *t)
AudioIO * gAudioIO
Definition: AudioIO.cpp:491
int mTimeCount
Definition: TrackPanel.h:444
static void TrackFocusPen(wxDC *dc, int level)
Definition: AColor.cpp:384
ControlToolBar * GetControlToolBar()
Definition: Project.cpp:4824
void MessageForScreenReader(const wxString &message)
Definition: TrackPanel.cpp:675
void ClearSeparator(TrackPanelDrawingContext &context, const wxRect &rect)
bool IsDown(int tool) const
float GetVelocity() const
Definition: NoteTrack.h:116
void GetGainRect(const wxPoint &topLeft, wxRect &dest)
void MakeParentModifyState(bool bWantsAutoSave)
Definition: TrackPanel.cpp:514
std::unique_ptr< TrackArtist > mTrackArtist
Definition: TrackPanel.h:426
AUDACITY_DLL_API AudacityProject * GetActiveProject()
Definition: Project.cpp:311
void MinimizeSyncLockDrawFunction(TrackPanelDrawingContext &context, const wxRect &rect, const Track *pTrack)
AudacityProject * GetProject() const override
Definition: TrackPanel.cpp:340
static double GetDefaultZoom()
Definition: ViewInfo.h:85
void MidiControlsDrawFunction(TrackPanelDrawingContext &context, const wxRect &rect, const Track *pTrack)
void GetMidiControlsHorizontalBounds(const wxRect &rect, wxRect &dest)
void GainSliderDrawFunction(TrackPanelDrawingContext &context, const wxRect &rect, const Track *pTrack)
int GetY() const
Definition: Track.cpp:157
bool mRefreshBacking
Definition: TrackPanel.h:446
A kind of ToolBar with Tools on it.
Definition: ToolsToolBar.h:49
void ApplyUpdatedTheme()
Definition: TrackPanel.cpp:322
void(*)(TrackPanelDrawingContext &context, const wxRect &rect, const Track *maybeNULL) DrawFunction
Definition: TrackPanel.cpp:746
Track * GetFocusedTrack()
virtual bool TP_ScrollUpDown(int delta)=0
bool mRedrawAfterStop
Definition: TrackPanel.h:454
void DrawTracks(TrackPanelDrawingContext &context, const TrackList *tracks, const wxRegion &reg, const wxRect &clip)
static void BevelTrackInfo(wxDC &dc, bool up, const wxRect &r, bool highlight=false)
Definition: AColor.cpp:262
float GetGain() const
Definition: WaveTrack.cpp:418
std::shared_ptr< TrackPanelCell > mpBackground
Definition: TrackPanel.h:481
LWSlider * GainSlider(const WaveTrack *wt)
Definition: TrackPanel.cpp:273
std::unique_ptr< wxCursor > MakeCursor(int WXUNUSED(CursorId), const char *const pXpm[36], int HotX, int HotY)
Definition: TrackPanel.cpp:177
This is an Audacity Specific ruler panel which additionally has border, selection markers...
wxColour & Colour(int iIndex)
Definition: Theme.cpp:1225
void OnPlayback(wxEvent &)
Definition: TrackPanel.cpp:703
void OnIdle(wxIdleEvent &event)
Definition: TrackPanel.cpp:360
int GetLeftOffset() const
Definition: TrackPanel.h:279
void GetCloseBoxRect(const wxRect &rect, wxRect &dest)
END_EVENT_TABLE()
double GetRate() const
Definition: WaveTrack.cpp:401
void OnPaint(wxPaintEvent &event)
Definition: TrackPanel.cpp:464
std::unique_ptr< TrackPanelAx > mAx
Definition: TrackPanel.h:461
void UpdateVRulers()
void OnKeyDown(wxKeyEvent &event)
Definition: TrackPanel.cpp:938
static std::shared_ptr< Subclass > Pointer(Track *t)
Definition: Track.h:233
AdornedRulerPanel * mRuler
Definition: TrackPanel.h:424
Formerly part of TrackPanel, this abstract base class has no special knowledge of Track objects and i...
Definition: CellularPanel.h:33
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1356
void MakeParentRedrawScrollbars()
Definition: TrackPanel.cpp:519
virtual void TP_DisplaySelection()=0
static TrackPanel * TrackPanelFactory(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, const std::shared_ptr< TrackList > &tracks, ViewInfo *viewInfo, TrackPanelListener *listener, AdornedRulerPanel *ruler)
wxSize vrulerSize
Definition: Track.h:284
void OnMouseEvent(wxMouseEvent &event)
Definition: TrackPanel.cpp:958
IsVisibleTrack(AudacityProject *project)
void GetNarrowMuteHorizontalBounds(const wxRect &rect, wxRect &dest)
virtual ToolsToolBar * TP_GetToolsToolBar()=0
void Status2DrawFunction(TrackPanelDrawingContext &context, const wxRect &rect, const Track *pTrack)
void SetLeftOffset(int offset)
wxSize GetTPTracksUsableArea()
Definition: Project.cpp:5052
void ReCreateSliders(wxWindow *pParent)
void GetVelocityRect(const wxPoint &topLeft, wxRect &dest)
Makes temporary drawing context changes that you back out of, RAII style.
Definition: AColor.h:42
bool operator()(const Track *pTrack) const
int GetHeight() const
Definition: Track.cpp:183
int GetVRulerOffset() const
Definition: TrackPanel.h:354
virtual unsigned KeyDown(wxKeyEvent &event, ViewInfo &viewInfo, wxWindow *pParent)
ViewInfo * GetViewInfo()
Definition: TrackPanel.h:364
A Track that is used for Midi notes. (Somewhat old code).
Definition: NoteTrack.h:66
void OnTrackListDeletion(wxEvent &event)
Definition: TrackPanel.cpp:724
void UpdateVRulerSize()
void VelocitySliderDrawFunction(TrackPanelDrawingContext &context, const wxRect &rect, const Track *pTrack)