Audacity 3.2.0
TrackInfo.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3Audacity: A Digital Audio Editor
4
5TrackInfo.cpp
6
7Paul Licameli split from TrackPanel.cpp
8
9********************************************************************/
29#include "TrackInfo.h"
30
31#include <wx/app.h>
32#include <wx/dc.h>
33#include <wx/frame.h>
34
35#include "AColor.h"
36#include "AllThemeResources.h"
37#include "PlayableTrack.h"
38#include "Prefs.h"
39#include "Project.h"
40#include "SyncLock.h"
41#include "Theme.h"
43#include "ViewInfo.h"
45
46// Subscribe to preference changes to update static variables
48 wxFont gFont;
49
50 bool mInitialized{ false };
51
52 void UpdatePrefs() override
53 {
54 // Calculation of best font size depends on language, so it should be redone in case
55 // the language preference changed.
56
57 // wxWidgets seems to need a window to do this portably.
58 if ( !wxTheApp )
59 return;
60 auto window = wxTheApp->GetTopWindow();
61 if ( !window )
62 return;
63
64 int fontSize = 10;
65 gFont.Create(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
66
67 int allowableWidth =
68 // PRL: was it correct to include the margin?
70 - 2; // 2 to allow for left/right borders
71 int textWidth;
72 do {
73 gFont.SetPointSize(fontSize);
74 window->GetTextExtent(_("Stereo, 999999Hz"),
75 &textWidth, nullptr, nullptr, nullptr, &gFont);
76 fontSize--;
77 } while (textWidth >= allowableWidth);
78
79 mInitialized = true;
80 }
81};
82
84{
85 static Settings theSettings;
86 if ( !theSettings.mInitialized )
87 theSettings.UpdatePrefs();
88 return theSettings;
89}
90
92{
93 return TracksBehaviorsSolo.ReadEnum() != SoloBehaviorNone;
94}
95
96#define RANGE(array) (array), (array) + sizeof(array)/sizeof(*(array))
99
101{
102 static const TCPLines theLines{
103#ifdef EXPERIMENTAL_DA
104
107
108#else
109
112
113#endif
114 };
115 return theLines;
116}
117
120{
121 return commonTrackTCPLines();
122}
123
124namespace {
125
126int totalTCPLines( const TCPLines &lines, bool omitLastExtra )
127{
128 int total = 0;
129 int lastExtra = 0;
130 for ( const auto line : lines ) {
131 lastExtra = line.extraSpace;
132 total += line.height + lastExtra;
133 }
134 if (omitLastExtra)
135 total -= lastExtra;
136 return total;
137}
138}
139
140// return y value and height
141std::pair< int, int >
142TrackInfo::CalcItemY( const TCPLines &lines, unsigned iItem )
143{
144 int y = 0;
145 auto pLines = lines.begin();
146 while ( pLines != lines.end() &&
147 0 == (pLines->items & iItem) ) {
148 y += pLines->height + pLines->extraSpace;
149 ++pLines;
150 }
151 int height = 0;
152 if ( pLines != lines.end() )
153 height = pLines->height;
154 return { y, height };
155}
156
157namespace {
158
159// Items for the bottom of the panel, listed bottom-upwards
160// As also with the top items, the extra space is below the item
162 // The '0' avoids impinging on bottom line of TCP
163 // Use -1 if you do want to do so.
166};
168
169// return y value and height
170std::pair< int, int > CalcBottomItemY
171 ( const TCPLines &lines, unsigned iItem, int height )
172{
173 int y = height;
174 auto pLines = lines.begin();
175 while ( pLines != lines.end() &&
176 0 == (pLines->items & iItem) ) {
177 y -= pLines->height + pLines->extraSpace;
178 ++pLines;
179 }
180 if (pLines != lines.end())
181 y -= (pLines->height + pLines->extraSpace );
182 return { y, pLines->height };
183}
184
185}
186
188{
189 return commonTrackTCPLines();
190}
191
193{
194 unsigned height = 0;
195 if (!commonTrackTCPLines().empty())
196 height += commonTrackTCPLines().front().height;
197 if (!commonTrackTCPBottomLines.empty())
198 height += commonTrackTCPBottomLines.front().height;
199 // + 1 prevents the top item from disappearing for want of enough space,
200 // according to the rules in HideTopItem.
201 return height + kVerticalPadding + 1;
202}
203
204bool TrackInfo::HideTopItem( const wxRect &rect, const wxRect &subRect,
205 int allowance ) {
206 auto limit = CalcBottomItemY
208 // Return true if the rectangle is even touching the limit
209 // without an overlap. That was the behavior as of 2.1.3.
210 return subRect.y + subRect.height - allowance >= rect.y + limit;
211}
212
214( TrackPanelDrawingContext &context,
215 const wxRect &rect, const Track &track )
216{
217 auto &trackControl = static_cast<const CommonTrackControls&>(
218 TrackControls::Get( track ) );
219 const auto &topLines = trackControl.GetTCPLines();
220 const auto &bottomLines = commonTrackTCPBottomLines;
222 ( context, rect, &track, topLines, bottomLines );
223}
224
226( TrackPanelDrawingContext &context,
227 const wxRect &rect, const Track *pTrack,
228 const std::vector<TCPLine> &topLines, const std::vector<TCPLine> &bottomLines )
229{
230 auto dc = &context.dc;
232 dc->SetTextForeground(theTheme.Colour(clrTrackPanelText));
233
234 {
235 int yy = 0;
236 for ( const auto &line : topLines ) {
237 wxRect itemRect{
238 rect.x, rect.y + yy,
239 rect.width, line.height
240 };
241 if ( !TrackInfo::HideTopItem( rect, itemRect ) &&
242 line.drawFunction )
243 line.drawFunction( context, itemRect, pTrack );
244 yy += line.height + line.extraSpace;
245 }
246 }
247 {
248 int yy = rect.height;
249 for ( const auto &line : bottomLines ) {
250 yy -= line.height + line.extraSpace;
251 if ( line.drawFunction ) {
252 wxRect itemRect{
253 rect.x, rect.y + yy,
254 rect.width, line.height
255 };
256 line.drawFunction( context, itemRect, pTrack );
257 }
258 }
259 }
260}
261
264 TrackPanelDrawingContext &context, const wxRect &bev,
265 const Track *pTrack, ButtonHandle *target )
266{
267 auto dc = &context.dc;
268 bool selected = pTrack ? pTrack->GetSelected() : true;
269 bool hit = target && target->GetTrack().get() == pTrack;
270 bool captured = hit && target->IsClicked();
271 bool down = captured && bev.Contains( context.lastState.GetPosition());
272 AColor::Bevel2(*dc, !down, bev, selected, hit );
273
274#ifdef EXPERIMENTAL_THEMING
275 wxPen pen( theTheme.Colour( clrTrackPanelText ));
276 dc->SetPen( pen );
277#else
278 dc->SetPen(*wxBLACK_PEN);
279#endif
280 bev.Inflate( -1, -1 );
281 // Draw the "X"
282 const int s = 6;
283
284 int ls = bev.x + ((bev.width - s) / 2);
285 int ts = bev.y + ((bev.height - s) / 2);
286 int rs = ls + s;
287 int bs = ts + s;
288
289 AColor::Line(*dc, ls, ts, rs, bs);
290 AColor::Line(*dc, ls + 1, ts, rs + 1, bs);
291 AColor::Line(*dc, rs, ts, ls, bs);
292 AColor::Line(*dc, rs + 1, ts, ls + 1, bs);
293 // bev.Inflate(-1, -1);
294}
295
297( TrackPanelDrawingContext &context,
298 const wxRect &rect, const Track *pTrack )
299{
300 auto dc = &context.dc;
301 bool selected = pTrack ? pTrack->GetSelected() : true;
302 {
303 wxRect bev = rect;
304 GetCloseBoxHorizontalBounds( rect, bev );
305 auto target = dynamic_cast<CloseButtonHandle*>( context.target.get() );
306 DrawCloseButton( context, bev, pTrack, target );
307 }
308
309 {
310 wxRect bev = rect;
311 GetTitleBarHorizontalBounds( rect, bev );
312 auto target = dynamic_cast<MenuButtonHandle*>( context.target.get() );
313 bool hit = target && target->GetTrack().get() == pTrack;
314 bool captured = hit && target->IsClicked();
315 bool down = captured && bev.Contains( context.lastState.GetPosition());
316 wxString titleStr =
317 pTrack ? pTrack->GetName() : _("Name");
318
319 //bev.Inflate(-1, -1);
320 AColor::Bevel2(*dc, !down, bev, selected, hit);
321
322 // Draw title text
324
325 // Bug 1660 The 'k' of 'Audio Track' was being truncated.
326 // Constant of 32 found by counting pixels on a windows machine.
327 // I believe it's the size of the X close button + the size of the
328 // drop down arrow.
329 int allowableWidth = rect.width - 32;
330
331 wxCoord textWidth, textHeight;
332 dc->GetTextExtent(titleStr, &textWidth, &textHeight);
333 while (textWidth > allowableWidth) {
334 titleStr = titleStr.Left(titleStr.length() - 1);
335 dc->GetTextExtent(titleStr, &textWidth, &textHeight);
336 }
337
338 // Pop-up triangle
339 #ifdef EXPERIMENTAL_THEMING
340 wxColour c = theTheme.Colour( clrTrackPanelText );
341 #else
342 wxColour c = *wxBLACK;
343 #endif
344
345 // wxGTK leaves little scraps (antialiasing?) of the
346 // characters if they are repeatedly drawn. This
347 // happens when holding down mouse button and moving
348 // in and out of the title bar. So clear it first.
349 // AColor::MediumTrackInfo(dc, t->GetSelected());
350 // dc->DrawRectangle(bev);
351
352 dc->SetTextForeground( c );
353 dc->SetTextBackground( wxTRANSPARENT );
354 dc->DrawText(titleStr, bev.x + 2, bev.y + (bev.height - textHeight) / 2);
355
356
357
358 dc->SetPen(c);
359 dc->SetBrush(c);
360
361 int s = 10; // Width of dropdown arrow...height is half of width
362 AColor::Arrow(*dc,
363 bev.GetRight() - s - 3, // 3 to offset from right border
364 bev.y + ((bev.height - (s / 2)) / 2),
365 s);
366
367 }
368}
369
371( TrackPanelDrawingContext &context,
372 const wxRect &rect, const Track *pTrack )
373{
374 auto dc = &context.dc;
375 bool selected = pTrack ? pTrack->GetSelected() : true;
376 bool syncLockSelected = pTrack ? SyncLock::IsSyncLockSelected(pTrack) : true;
377 bool minimized =
378 pTrack ? ChannelView::Get(*pTrack->GetChannel(0)).GetMinimized() : false;
379 {
380 wxRect bev = rect;
382 auto target = dynamic_cast<MinimizeButtonHandle*>( context.target.get() );
383 bool hit = target && target->GetTrack().get() == pTrack;
384 bool captured = hit && target->IsClicked();
385 bool down = captured && bev.Contains( context.lastState.GetPosition());
386
387 // Clear background to get rid of previous arrow
388 //AColor::MediumTrackInfo(dc, t->GetSelected());
389 //dc->DrawRectangle(bev);
390
391 AColor::Bevel2(*dc, !down, bev, selected, hit);
392
393#ifdef EXPERIMENTAL_THEMING
394 wxColour c = theTheme.Colour(clrTrackPanelText);
395 dc->SetBrush(c);
396 dc->SetPen(c);
397#else
398 AColor::Dark(dc, selected);
399#endif
400
401 AColor::Arrow(*dc,
402 bev.x - 5 + bev.width / 2,
403 bev.y - 2 + bev.height / 2,
404 10,
405 minimized);
406 }
407
408 {
409 wxRect bev = rect;
411 auto target = dynamic_cast<SelectButtonHandle*>( context.target.get() );
412 bool hit = target && target->GetTrack().get() == pTrack;
413 bool captured = hit && target->IsClicked();
414 bool down = captured && bev.Contains( context.lastState.GetPosition());
415
416 AColor::Bevel2(*dc, !down, bev, selected, hit);
417
418#ifdef EXPERIMENTAL_THEMING
419 wxColour c = theTheme.Colour(clrTrackPanelText);
420 dc->SetBrush(c);
421 dc->SetPen(c);
422#else
423 AColor::Dark(dc, selected);
424#endif
425
426 wxString str = _("Select");
427 wxCoord textWidth;
428 wxCoord textHeight;
430 dc->GetTextExtent(str, &textWidth, &textHeight);
431
432 dc->SetTextForeground( c );
433 dc->SetTextBackground( wxTRANSPARENT );
434 dc->DrawText(str, bev.x + 2 + (bev.width-textWidth)/2, bev.y + (bev.height - textHeight) / 2);
435 }
436
437
438 // Draw the sync-lock indicator if this track is in a sync-lock selected group.
439 if (syncLockSelected)
440 {
441 wxRect syncLockIconRect = rect;
442
443 GetSyncLockHorizontalBounds( rect, syncLockIconRect );
444 wxBitmap syncLockBitmap(theTheme.Image(bmpSyncLockIcon));
445 // Icon is 12x12 and syncLockIconRect is 16x16.
446 dc->DrawBitmap(syncLockBitmap,
447 syncLockIconRect.x + 3,
448 syncLockIconRect.y + 2,
449 true);
450 }
451}
452
453void TrackInfo::GetCloseBoxHorizontalBounds( const wxRect & rect, wxRect &dest )
454{
455 dest.x = rect.x;
456 dest.width = kTrackInfoBtnSize;
457}
458
459void TrackInfo::GetCloseBoxRect(const wxRect & rect, wxRect & dest)
460{
461 GetCloseBoxHorizontalBounds( rect, dest );
463 dest.y = rect.y + results.first;
464 dest.height = results.second;
465}
466
467void TrackInfo::GetTitleBarHorizontalBounds( const wxRect & rect, wxRect &dest )
468{
469 // to right of CloseBoxRect, plus a little more
470 wxRect closeRect;
471 GetCloseBoxHorizontalBounds( rect, closeRect );
472 dest.x = rect.x + closeRect.width + 1;
473 dest.width = rect.x + rect.width - dest.x + TitleSoloBorderOverlap;
474}
475
476void TrackInfo::GetTitleBarRect(const wxRect & rect, wxRect & dest)
477{
478 GetTitleBarHorizontalBounds( rect, dest );
480 dest.y = rect.y + results.first;
481 dest.height = results.second;
482}
483
484void TrackInfo::GetSliderHorizontalBounds( const wxPoint &topleft, wxRect &dest )
485{
486 dest.x = topleft.x + 6;
487 dest.width = kTrackInfoSliderWidth;
488}
489
490void TrackInfo::GetMinimizeHorizontalBounds( const wxRect &rect, wxRect &dest )
491{
492 const int space = 0;// was 3.
493 dest.x = rect.x + space;
494
495 wxRect syncLockRect;
496 GetSyncLockHorizontalBounds( rect, syncLockRect );
497
498 // Width is rect.width less space on left for track select
499 // and on right for sync-lock icon.
500 dest.width = kTrackInfoBtnSize;
501// rect.width - (space + syncLockRect.width);
502}
503
504void TrackInfo::GetMinimizeRect(const wxRect & rect, wxRect &dest)
505{
506 GetMinimizeHorizontalBounds( rect, dest );
507 auto results = CalcBottomItemY
509 dest.y = rect.y + results.first;
510 dest.height = results.second;
511}
512
513void TrackInfo::GetSelectButtonHorizontalBounds( const wxRect &rect, wxRect &dest )
514{
515 const int space = 0;// was 3.
516 dest.x = rect.x + space;
517
518 wxRect syncLockRect;
519 GetSyncLockHorizontalBounds( rect, syncLockRect );
520 wxRect minimizeRect;
521 GetMinimizeHorizontalBounds( rect, minimizeRect );
522
523 dest.x = dest.x + space + minimizeRect.width;
524 // Width is rect.width less space on left for track select
525 // and on right for sync-lock icon.
526 dest.width = rect.width - (space + syncLockRect.width) - (space + minimizeRect.width);
527}
528
529
530void TrackInfo::GetSelectButtonRect(const wxRect & rect, wxRect &dest)
531{
533 auto results = CalcBottomItemY
535 dest.y = rect.y + results.first;
536 dest.height = results.second;
537}
538
539void TrackInfo::GetSyncLockHorizontalBounds( const wxRect &rect, wxRect &dest )
540{
541 dest.width = kTrackInfoBtnSize;
542 dest.x = rect.x + rect.width - dest.width;
543}
544
545void TrackInfo::GetSyncLockIconRect(const wxRect & rect, wxRect &dest)
546{
547 GetSyncLockHorizontalBounds( rect, dest );
548 auto results = CalcBottomItemY
550 dest.y = rect.y + results.first;
551 dest.height = results.second;
552}
553
556{
557 dc->SetFont(settings().gFont);
558}
559
560//#define USE_BEVELS
561
562unsigned TrackInfo::DefaultTrackHeight( const TCPLines &topLines )
563{
564 int needed =
566 totalTCPLines( topLines, true ) +
568 return (unsigned) std::max(needed, (int) ChannelView::DefaultHeight);
569}
#define str(a)
#define _(s)
Definition: Internat.h:73
EnumSetting< SoloBehavior > TracksBehaviorsSolo
Extends Track with notions of mute and solo setting.
@ SoloBehaviorNone
Definition: PlayableTrack.h:72
THEME_API Theme theTheme
Definition: Theme.cpp:82
TrackInfo::TCPLines TCPLines
Definition: TrackInfo.cpp:98
TrackInfo::TCPLine TCPLine
Definition: TrackInfo.cpp:97
static Settings & settings()
Definition: TrackInfo.cpp:83
static const TCPLines & commonTrackTCPLines()
Definition: TrackInfo.cpp:100
#define RANGE(array)
Definition: TrackInfo.cpp:96
static const int TitleSoloBorderOverlap
Definition: TrackInfo.h:29
@ kTrackInfoSliderWidth
Definition: ViewInfo.h:99
@ kTrackInfoBtnSize
Definition: ViewInfo.h:96
@ kVerticalPadding
Definition: ViewInfo.h:92
@ kTrackInfoWidth
Definition: ZoomInfo.h:30
@ kLeftMargin
Definition: ZoomInfo.h:27
static void Arrow(wxDC &dc, wxCoord x, wxCoord y, int width, bool down=true)
Definition: AColor.cpp:160
static void Bevel2(wxDC &dc, bool up, const wxRect &r, bool bSel=false, bool bHighlight=false)
Definition: AColor.cpp:294
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:187
static void Dark(wxDC *dc, bool selected, bool highlight=false)
Definition: AColor.cpp:443
A UIHandle for a TrackPanel button, such as the Mute and Solo buttons.
Definition: ButtonHandle.h:26
std::shared_ptr< Track > GetTrack() const
Definition: ButtonHandle.h:30
bool IsClicked() const
Definition: ButtonHandle.h:31
std::shared_ptr< ChannelType > GetChannel(size_t iChannel)
Retrieve a channel, cast to the given type.
Definition: Channel.h:344
static ChannelView & Get(Channel &channel)
bool GetMinimized() const
Definition: ChannelView.h:68
virtual const TCPLines & GetTCPLines() const
Definition: TrackInfo.cpp:187
static const TCPLines & StaticTCPLines()
Definition: TrackInfo.cpp:119
A listener notified of changes in preferences.
Definition: Prefs.h:561
static bool IsSyncLockSelected(const Track *pTrack)
Definition: SyncLock.cpp:82
wxColour & Colour(int iIndex)
wxImage & Image(int iIndex)
static TrackControls & Get(Track &track)
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:123
bool GetSelected() const
Selectedness is always the same for all channels of a group.
Definition: Track.cpp:70
const wxString & GetName() const
Name is always the same for all channels of a group.
Definition: Track.cpp:56
AUDACITY_DLL_API void GetSliderHorizontalBounds(const wxPoint &topleft, wxRect &dest)
Definition: TrackInfo.cpp:484
AUDACITY_DLL_API bool HideTopItem(const wxRect &rect, const wxRect &subRect, int allowance=0)
Definition: TrackInfo.cpp:204
AUDACITY_DLL_API void GetCloseBoxRect(const wxRect &rect, wxRect &dest)
Definition: TrackInfo.cpp:459
AUDACITY_DLL_API void GetTitleBarRect(const wxRect &rect, wxRect &dest)
Definition: TrackInfo.cpp:476
AUDACITY_DLL_API void GetSyncLockHorizontalBounds(const wxRect &rect, wxRect &dest)
Definition: TrackInfo.cpp:539
AUDACITY_DLL_API void SetTrackInfoFont(wxDC *dc)
Definition: TrackInfo.cpp:555
AUDACITY_DLL_API void GetCloseBoxHorizontalBounds(const wxRect &rect, wxRect &dest)
Definition: TrackInfo.cpp:453
std::vector< TCPLine > TCPLines
Definition: TrackInfo.h:67
AUDACITY_DLL_API void GetMinimizeHorizontalBounds(const wxRect &rect, wxRect &dest)
Definition: TrackInfo.cpp:490
AUDACITY_DLL_API void GetMinimizeRect(const wxRect &rect, wxRect &dest)
Definition: TrackInfo.cpp:504
AUDACITY_DLL_API void GetSelectButtonRect(const wxRect &rect, wxRect &dest)
Definition: TrackInfo.cpp:530
AUDACITY_DLL_API bool HasSoloButton()
Definition: TrackInfo.cpp:91
AUDACITY_DLL_API void GetTitleBarHorizontalBounds(const wxRect &rect, wxRect &dest)
Definition: TrackInfo.cpp:467
AUDACITY_DLL_API void GetSyncLockIconRect(const wxRect &rect, wxRect &dest)
Definition: TrackInfo.cpp:545
AUDACITY_DLL_API unsigned DefaultTrackHeight(const TCPLines &topLines)
Definition: TrackInfo.cpp:562
AUDACITY_DLL_API void DrawCloseButton(TrackPanelDrawingContext &context, const wxRect &bev, const Track *pTrack, ButtonHandle *target)
Definition: TrackInfo.cpp:263
AUDACITY_DLL_API void MinimizeSyncLockDrawFunction(TrackPanelDrawingContext &context, const wxRect &rect, const Track *pTrack)
Definition: TrackInfo.cpp:371
AUDACITY_DLL_API void DrawItems(TrackPanelDrawingContext &context, const wxRect &rect, const Track &track)
Definition: TrackInfo.cpp:214
AUDACITY_DLL_API std::pair< int, int > CalcItemY(const TCPLines &lines, unsigned iItem)
Definition: TrackInfo.cpp:142
AUDACITY_DLL_API void GetSelectButtonHorizontalBounds(const wxRect &rect, wxRect &dest)
Definition: TrackInfo.cpp:513
AUDACITY_DLL_API void CloseTitleDrawFunction(TrackPanelDrawingContext &context, const wxRect &rect, const Track *pTrack)
Definition: TrackInfo.cpp:297
AUDACITY_DLL_API unsigned MinimumTrackHeight()
Definition: TrackInfo.cpp:192
const TrackInfo::TCPLine defaultCommonTrackTCPBottomLines[]
Definition: TrackInfo.cpp:161
int totalTCPLines(const TCPLines &lines, bool omitLastExtra)
Definition: TrackInfo.cpp:126
std::pair< int, int > CalcBottomItemY(const TCPLines &lines, unsigned iItem, int height)
Definition: TrackInfo.cpp:171
bool mInitialized
Definition: TrackInfo.cpp:50
void UpdatePrefs() override
Definition: TrackInfo.cpp:52
wxFont gFont
Definition: TrackInfo.cpp:48