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