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