2010-01-23 19:44:49 +00:00
|
|
|
/**********************************************************************
|
|
|
|
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
|
|
|
|
TrackPanel.cpp
|
|
|
|
|
|
|
|
Dominic Mazzoni
|
|
|
|
and lots of other contributors
|
|
|
|
|
|
|
|
Implements TrackPanel and TrackInfo.
|
|
|
|
|
2014-06-03 20:30:19 +00:00
|
|
|
********************************************************************//*!
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
\todo
|
2014-06-03 20:30:19 +00:00
|
|
|
Refactoring of the TrackPanel, possibly as described
|
2010-01-23 19:44:49 +00:00
|
|
|
in \ref TrackPanelRefactor
|
|
|
|
|
|
|
|
*//*****************************************************************//*!
|
|
|
|
|
|
|
|
\file TrackPanel.cpp
|
2014-06-03 20:30:19 +00:00
|
|
|
\brief
|
2010-01-23 19:44:49 +00:00
|
|
|
Implements TrackPanel and TrackInfo.
|
|
|
|
|
2014-06-03 20:30:19 +00:00
|
|
|
TrackPanel.cpp is currently some of the worst code in Audacity.
|
2010-01-23 19:44:49 +00:00
|
|
|
It's not really unreadable, there's just way too much stuff in this
|
|
|
|
one file. Rather than apply a quick fix, the long-term plan
|
|
|
|
is to create a GUITrack class that knows how to draw itself
|
|
|
|
and handle events. Then this class just helps coordinate
|
|
|
|
between tracks.
|
|
|
|
|
|
|
|
Plans under discussion are described in \ref TrackPanelRefactor
|
|
|
|
|
|
|
|
*//********************************************************************/
|
|
|
|
|
2014-06-03 20:30:19 +00:00
|
|
|
// Documentation: Rather than have a lengthy \todo section, having
|
|
|
|
// a \todo a \file and a \page in EXACTLY that order gets Doxygen to
|
2016-02-13 15:43:16 +00:00
|
|
|
// put the following lengthy description of refactoring on a NEW page
|
2010-01-23 19:44:49 +00:00
|
|
|
// and link to it from the docs.
|
|
|
|
|
|
|
|
/*****************************************************************//**
|
|
|
|
|
|
|
|
\class TrackPanel
|
|
|
|
\brief
|
|
|
|
The TrackPanel class coordinates updates and operations on the
|
|
|
|
main part of the screen which contains multiple tracks.
|
|
|
|
|
|
|
|
It uses many other classes, but in particular it uses the
|
(Sync-Lock)
Commented out the one call to TrackInfo::DrawBordersWithin(). This eliminates all the dark lines within the TrackInfo, in an effort to make the sync-lock icon not look like a button. It leaves some lighter borders, and I think that's an aesthetic improvement, though it make be worse in terms of accessibility. I can also remove the light border above the sync-lock icon, but I think this looks best overall.
In Track::IsSyncLockSelected(), for the "// Not in a sync-locked group." conditional, it returned true if the track was selected. I made it do so only if track kind is Wave or Label. Among other things, this means Time and Note tracks will never show the sync-lock icon. I think this is correct by definition, but Al, please let me know if this will have negative repercussions elsewhere. There are *lots* of calls to that method and I can move the track-type check to the code that draws the sync-lock icon..
Fixed the bug Gale pointed out where, if a WaveTrack is shrunk such that the sync-lock icon is over a TrackInfo control, such as pan slider, it didn't intercept the mouse event, and passed it on to the control. Now, clicking on the sync-lock icon does nothing.
Fixed a bug where the sync-lock icon was redrawn dark when the minimize button is down. Now not redrawn at all in that case.
Added some clarifying comments, especially about the term "Label" as used in TrackPanel.*.
2010-09-09 00:46:40 +00:00
|
|
|
TrackInfo class to draw the controls area on the left of a track,
|
2010-01-23 19:44:49 +00:00
|
|
|
and the TrackArtist class to draw the actual waveforms.
|
|
|
|
|
2014-06-03 20:30:19 +00:00
|
|
|
Note that in some of the older code here, e.g., GetLabelWidth(),
|
|
|
|
"Label" means the TrackInfo plus the vertical ruler.
|
(Sync-Lock)
Commented out the one call to TrackInfo::DrawBordersWithin(). This eliminates all the dark lines within the TrackInfo, in an effort to make the sync-lock icon not look like a button. It leaves some lighter borders, and I think that's an aesthetic improvement, though it make be worse in terms of accessibility. I can also remove the light border above the sync-lock icon, but I think this looks best overall.
In Track::IsSyncLockSelected(), for the "// Not in a sync-locked group." conditional, it returned true if the track was selected. I made it do so only if track kind is Wave or Label. Among other things, this means Time and Note tracks will never show the sync-lock icon. I think this is correct by definition, but Al, please let me know if this will have negative repercussions elsewhere. There are *lots* of calls to that method and I can move the track-type check to the code that draws the sync-lock icon..
Fixed the bug Gale pointed out where, if a WaveTrack is shrunk such that the sync-lock icon is over a TrackInfo control, such as pan slider, it didn't intercept the mouse event, and passed it on to the control. Now, clicking on the sync-lock icon does nothing.
Fixed a bug where the sync-lock icon was redrawn dark when the minimize button is down. Now not redrawn at all in that case.
Added some clarifying comments, especially about the term "Label" as used in TrackPanel.*.
2010-09-09 00:46:40 +00:00
|
|
|
Confusing relative to LabelTrack labels.
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
The TrackPanel manages multiple tracks and their TrackInfos.
|
|
|
|
|
|
|
|
Note that with stereo tracks there will be one TrackInfo
|
|
|
|
being used by two wavetracks.
|
|
|
|
|
|
|
|
*//*****************************************************************//**
|
|
|
|
|
|
|
|
\class TrackInfo
|
|
|
|
\brief
|
2014-06-03 20:30:19 +00:00
|
|
|
The TrackInfo is shown to the side of a track
|
2010-01-23 19:44:49 +00:00
|
|
|
It has the menus, pan and gain controls displayed in it.
|
(Sync-Lock)
Commented out the one call to TrackInfo::DrawBordersWithin(). This eliminates all the dark lines within the TrackInfo, in an effort to make the sync-lock icon not look like a button. It leaves some lighter borders, and I think that's an aesthetic improvement, though it make be worse in terms of accessibility. I can also remove the light border above the sync-lock icon, but I think this looks best overall.
In Track::IsSyncLockSelected(), for the "// Not in a sync-locked group." conditional, it returned true if the track was selected. I made it do so only if track kind is Wave or Label. Among other things, this means Time and Note tracks will never show the sync-lock icon. I think this is correct by definition, but Al, please let me know if this will have negative repercussions elsewhere. There are *lots* of calls to that method and I can move the track-type check to the code that draws the sync-lock icon..
Fixed the bug Gale pointed out where, if a WaveTrack is shrunk such that the sync-lock icon is over a TrackInfo control, such as pan slider, it didn't intercept the mouse event, and passed it on to the control. Now, clicking on the sync-lock icon does nothing.
Fixed a bug where the sync-lock icon was redrawn dark when the minimize button is down. Now not redrawn at all in that case.
Added some clarifying comments, especially about the term "Label" as used in TrackPanel.*.
2010-09-09 00:46:40 +00:00
|
|
|
So "Info" is somewhat a misnomer. Should possibly be "TrackControls".
|
2013-10-29 21:49:45 +00:00
|
|
|
|
2014-06-03 20:30:19 +00:00
|
|
|
TrackPanel and not TrackInfo takes care of the functionality for
|
2010-01-23 19:44:49 +00:00
|
|
|
each of the buttons in that panel.
|
|
|
|
|
|
|
|
In its current implementation TrackInfo is not derived from a
|
2014-06-03 20:30:19 +00:00
|
|
|
wxWindow. Following the original coding style, it has
|
|
|
|
been coded as a 'flyweight' class, which is passed
|
2010-01-23 19:44:49 +00:00
|
|
|
state as needed, except for the array of gains and pans.
|
2013-10-29 21:49:45 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
If we'd instead coded it as a wxWindow, we would have an instance
|
|
|
|
of this class for each instance displayed.
|
|
|
|
|
|
|
|
*//**************************************************************//**
|
|
|
|
|
|
|
|
\class TrackPanelListener
|
|
|
|
\brief A now badly named class which is used to give access to a
|
2010-09-12 05:11:43 +00:00
|
|
|
subset of the TrackPanel methods from all over the place.
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
*//**************************************************************//**
|
|
|
|
|
|
|
|
\class TrackList
|
|
|
|
\brief A list of TrackListNode items.
|
|
|
|
|
|
|
|
*//**************************************************************//**
|
|
|
|
|
|
|
|
\class TrackListIterator
|
|
|
|
\brief An iterator for a TrackList.
|
|
|
|
|
|
|
|
*//**************************************************************//**
|
|
|
|
|
|
|
|
\class TrackListNode
|
|
|
|
\brief Used by TrackList, points to a Track.
|
|
|
|
|
|
|
|
*//**************************************************************//**
|
|
|
|
|
|
|
|
\class TrackPanel::AudacityTimer
|
2014-06-03 20:30:19 +00:00
|
|
|
\brief Timer class dedicated to infomring the TrackPanel that it
|
2010-01-23 19:44:49 +00:00
|
|
|
is time to refresh some aspect of the screen.
|
|
|
|
|
|
|
|
*//*****************************************************************//**
|
|
|
|
|
|
|
|
\page TrackPanelRefactor Track Panel Refactor
|
|
|
|
\brief Planned refactoring of TrackPanel.cpp
|
|
|
|
|
|
|
|
- Move menus from current TrackPanel into TrackInfo.
|
|
|
|
- Convert TrackInfo from 'flyweight' to heavyweight.
|
|
|
|
- Split GuiStereoTrack and GuiWaveTrack out from TrackPanel.
|
|
|
|
|
|
|
|
JKC: Incremental refactoring started April/2003
|
|
|
|
|
|
|
|
Possibly aiming for Gui classes something like this - it's under
|
|
|
|
discussion:
|
|
|
|
|
|
|
|
<pre>
|
|
|
|
+----------------------------------------------------+
|
|
|
|
| AdornedRulerPanel |
|
|
|
|
+----------------------------------------------------+
|
|
|
|
+----------------------------------------------------+
|
|
|
|
|+------------+ +-----------------------------------+|
|
|
|
|
|| | | (L) GuiWaveTrack ||
|
2014-11-14 03:03:17 +00:00
|
|
|
|| TrackInfo | +-----------------------------------+|
|
2010-01-23 19:44:49 +00:00
|
|
|
|| | +-----------------------------------+|
|
|
|
|
|| | | (R) GuiWaveTrack ||
|
|
|
|
|+------------+ +-----------------------------------+|
|
|
|
|
+-------- GuiStereoTrack ----------------------------+
|
|
|
|
+----------------------------------------------------+
|
|
|
|
|+------------+ +-----------------------------------+|
|
|
|
|
|| | | (L) GuiWaveTrack ||
|
2014-11-14 03:03:17 +00:00
|
|
|
|| TrackInfo | +-----------------------------------+|
|
2010-01-23 19:44:49 +00:00
|
|
|
|| | +-----------------------------------+|
|
|
|
|
|| | | (R) GuiWaveTrack ||
|
|
|
|
|+------------+ +-----------------------------------+|
|
|
|
|
+-------- GuiStereoTrack ----------------------------+
|
|
|
|
</pre>
|
2013-10-29 21:49:45 +00:00
|
|
|
|
2014-06-03 20:30:19 +00:00
|
|
|
With the whole lot sitting in a TrackPanel which forwards
|
2010-01-23 19:44:49 +00:00
|
|
|
events to the sub objects.
|
|
|
|
|
|
|
|
The GuiStereoTrack class will do the special logic for
|
2014-06-03 20:30:19 +00:00
|
|
|
Stereo channel grouping.
|
2013-10-29 21:49:45 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
The precise names of the classes are subject to revision.
|
2016-08-08 13:50:55 +00:00
|
|
|
Have deliberately not created NEW files for the NEW classes
|
2010-01-23 19:44:49 +00:00
|
|
|
such as AdornedRulerPanel and TrackInfo - yet.
|
|
|
|
|
|
|
|
*//*****************************************************************/
|
|
|
|
|
|
|
|
#include "Audacity.h"
|
|
|
|
#include "Experimental.h"
|
|
|
|
#include "TrackPanel.h"
|
2015-09-09 01:15:35 +00:00
|
|
|
#include "Project.h"
|
2015-08-25 17:35:32 +00:00
|
|
|
#include "TrackPanelCellIterator.h"
|
2015-07-07 03:12:16 +00:00
|
|
|
#include "TrackPanelMouseEvent.h"
|
2015-08-10 03:41:08 +00:00
|
|
|
#include "TrackPanelResizeHandle.h"
|
2010-01-23 19:44:49 +00:00
|
|
|
//#define DEBUG_DRAW_TIMING 1
|
2014-10-18 14:19:38 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
#include "AColor.h"
|
|
|
|
#include "AllThemeResources.h"
|
|
|
|
#include "AudioIO.h"
|
|
|
|
#include "float_cast.h"
|
|
|
|
|
|
|
|
#include "Prefs.h"
|
2015-07-07 03:12:16 +00:00
|
|
|
#include "RefreshCode.h"
|
2010-01-23 19:44:49 +00:00
|
|
|
#include "TrackArtist.h"
|
|
|
|
#include "TrackPanelAx.h"
|
2015-07-07 03:12:16 +00:00
|
|
|
#include "UIHandle.h"
|
|
|
|
#include "HitTestResult.h"
|
2010-01-23 19:44:49 +00:00
|
|
|
#include "WaveTrack.h"
|
2015-09-09 01:15:35 +00:00
|
|
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
|
|
|
#include "NoteTrack.h"
|
|
|
|
#endif
|
2015-06-04 04:05:46 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
#include "toolbars/ControlToolBar.h"
|
|
|
|
#include "toolbars/ToolsToolBar.h"
|
|
|
|
|
|
|
|
//This loads the appropriate set of cursors, depending on platform.
|
|
|
|
#include "../images/Cursors.h"
|
|
|
|
|
2015-08-09 17:48:27 +00:00
|
|
|
#include "widgets/ASlider.h"
|
2015-09-09 01:15:35 +00:00
|
|
|
#include "widgets/Ruler.h"
|
|
|
|
#include <algorithm>
|
2015-08-09 17:48:27 +00:00
|
|
|
|
2015-07-12 12:47:56 +00:00
|
|
|
DEFINE_EVENT_TYPE(EVT_TRACK_PANEL_TIMER)
|
|
|
|
|
2015-08-20 19:52:56 +00:00
|
|
|
/*
|
|
|
|
|
|
|
|
This is a diagram of TrackPanel's division of one (non-stereo) track rectangle.
|
|
|
|
Total height equals Track::GetHeight()'s value. Total width is the wxWindow's width.
|
|
|
|
Each charater that is not . represents one pixel.
|
|
|
|
|
|
|
|
Inset space of this track, and top inset of the next track, are used to draw the focus highlight.
|
|
|
|
|
|
|
|
Top inset of the right channel of a stereo track, and bottom shadow line of the
|
|
|
|
left channel, are used for the channel separator.
|
|
|
|
|
2017-06-06 15:24:03 +00:00
|
|
|
"Margin" is a term used for inset plus border (top and left) or inset plus
|
|
|
|
shadow plus border (right and bottom).
|
|
|
|
|
2015-08-20 19:52:56 +00:00
|
|
|
TrackInfo::GetTrackInfoWidth() == GetVRulerOffset()
|
|
|
|
counts columns from the left edge up to and including controls, and is a constant.
|
|
|
|
|
|
|
|
GetVRulerWidth() is variable -- all tracks have the same ruler width at any time,
|
|
|
|
but that width may be adjusted when tracks change their vertical scales.
|
|
|
|
|
|
|
|
GetLabelWidth() counts columns up to and including the VRuler.
|
|
|
|
GetLeftOffset() is yet one more -- it counts the "one pixel" column.
|
|
|
|
|
2017-06-06 15:43:58 +00:00
|
|
|
FindCell() for label returns a rectangle that OMITS left, top, and bottom
|
|
|
|
margins
|
2017-06-06 15:43:34 +00:00
|
|
|
|
2017-06-15 02:10:26 +00:00
|
|
|
FindCell() for vruler returns a rectangle right of the label,
|
|
|
|
up to and including the One Pixel column, and OMITS top and bottom margins
|
2015-08-20 19:52:56 +00:00
|
|
|
|
2017-06-15 02:10:26 +00:00
|
|
|
FindCell() for track returns a rectangle with x == GetLeftOffset(), and OMITS
|
|
|
|
right top, and bottom margins
|
2015-08-20 19:52:56 +00:00
|
|
|
|
|
|
|
+--------------- ... ------ ... --------------------- ... ... -------------+
|
|
|
|
| Top Inset |
|
|
|
|
| |
|
|
|
|
| +------------ ... ------ ... --------------------- ... ... ----------+ |
|
|
|
|
| L|+-Border---- ... ------ ... --------------------- ... ... -Border-+ |R |
|
|
|
|
| e||+---------- ... -++--- ... -+++----------------- ... ... -------+| |i |
|
|
|
|
| f|B| || ||| |BS|g |
|
|
|
|
| t|o| Controls || V |O| The good stuff |oh|h |
|
|
|
|
| |r| || R |n| |ra|t |
|
|
|
|
| I|d| || u |e| |dd| |
|
|
|
|
| n|e| || l | | |eo|I |
|
|
|
|
| s|r| || e |P| |rw|n |
|
|
|
|
| e||| || r |i| ||||s |
|
|
|
|
| t||| || |x| ||||e |
|
|
|
|
| ||| || |e| ||||t |
|
|
|
|
| ||| || |l| |||| |
|
|
|
|
| ||| || ||| |||| |
|
|
|
|
|
|
|
|
. ... .. ... .... .
|
|
|
|
. ... .. ... .... .
|
|
|
|
. ... .. ... .... .
|
|
|
|
|
|
|
|
| ||| || ||| |||| |
|
|
|
|
| ||+---------- -++-- ... -+++----------------- ... ... -------+||| |
|
|
|
|
| |+-Border---- ... ----- ... --------------------- ... ... -Border-+|| |
|
|
|
|
| | Shadow---- ... ----- ... --------------------- ... ... --Shadow-+| |
|
|
|
|
*/
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
// Is the distance between A and B less than D?
|
|
|
|
template < class A, class B, class DIST > bool within(A a, B b, DIST d)
|
|
|
|
{
|
|
|
|
return (a > b - d) && (a < b + d);
|
|
|
|
}
|
|
|
|
|
2016-05-07 17:00:48 +00:00
|
|
|
BEGIN_EVENT_TABLE(TrackPanel, OverlayPanel)
|
2010-01-23 19:44:49 +00:00
|
|
|
EVT_MOUSE_EVENTS(TrackPanel::OnMouseEvent)
|
|
|
|
EVT_MOUSE_CAPTURE_LOST(TrackPanel::OnCaptureLost)
|
|
|
|
EVT_COMMAND(wxID_ANY, EVT_CAPTURE_KEY, TrackPanel::OnCaptureKey)
|
|
|
|
EVT_KEY_DOWN(TrackPanel::OnKeyDown)
|
2015-08-10 18:15:15 +00:00
|
|
|
EVT_KEY_UP(TrackPanel::OnKeyUp)
|
2010-01-23 19:44:49 +00:00
|
|
|
EVT_CHAR(TrackPanel::OnChar)
|
|
|
|
EVT_PAINT(TrackPanel::OnPaint)
|
|
|
|
EVT_SET_FOCUS(TrackPanel::OnSetFocus)
|
|
|
|
EVT_KILL_FOCUS(TrackPanel::OnKillFocus)
|
|
|
|
EVT_CONTEXT_MENU(TrackPanel::OnContextMenu)
|
|
|
|
|
2015-08-26 03:50:23 +00:00
|
|
|
EVT_TIMER(wxID_ANY, TrackPanel::OnTimer)
|
2010-01-23 19:44:49 +00:00
|
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
|
|
/// Makes a cursor from an XPM, uses CursorId as a fallback.
|
2015-07-07 23:51:48 +00:00
|
|
|
/// TODO: Move this function to some other source file for reuse elsewhere.
|
2016-04-10 03:06:10 +00:00
|
|
|
std::unique_ptr<wxCursor> MakeCursor( int WXUNUSED(CursorId), const char * pXpm[36], int HotX, int HotY )
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
|
|
|
#ifdef CURSORS_SIZE32
|
|
|
|
const int HotAdjust =0;
|
|
|
|
#else
|
|
|
|
const int HotAdjust =8;
|
|
|
|
#endif
|
|
|
|
|
2013-10-29 21:49:45 +00:00
|
|
|
wxImage Image = wxImage(wxBitmap(pXpm).ConvertToImage());
|
2010-01-23 19:44:49 +00:00
|
|
|
Image.SetMaskColour(255,0,0);
|
|
|
|
Image.SetMask();// Enable mask.
|
|
|
|
|
|
|
|
Image.SetOption( wxIMAGE_OPTION_CUR_HOTSPOT_X, HotX-HotAdjust );
|
|
|
|
Image.SetOption( wxIMAGE_OPTION_CUR_HOTSPOT_Y, HotY-HotAdjust );
|
2016-04-10 03:06:10 +00:00
|
|
|
return std::make_unique<wxCursor>( Image );
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Don't warn us about using 'this' in the base member initializer list.
|
|
|
|
#ifndef __WXGTK__ //Get rid if this pragma for gtk
|
|
|
|
#pragma warning( disable: 4355 )
|
|
|
|
#endif
|
|
|
|
TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id,
|
|
|
|
const wxPoint & pos,
|
|
|
|
const wxSize & size,
|
2016-08-20 17:58:56 +00:00
|
|
|
const std::shared_ptr<TrackList> &tracks,
|
2010-01-23 19:44:49 +00:00
|
|
|
ViewInfo * viewInfo,
|
|
|
|
TrackPanelListener * listener,
|
|
|
|
AdornedRulerPanel * ruler)
|
2016-05-07 17:00:48 +00:00
|
|
|
: OverlayPanel(parent, id, pos, size, wxWANTS_CHARS | wxNO_BORDER),
|
2010-01-23 19:44:49 +00:00
|
|
|
mTrackInfo(this),
|
|
|
|
mListener(listener),
|
|
|
|
mTracks(tracks),
|
|
|
|
mViewInfo(viewInfo),
|
|
|
|
mRuler(ruler),
|
2016-08-03 01:25:05 +00:00
|
|
|
mTrackArtist(nullptr),
|
2010-01-23 19:44:49 +00:00
|
|
|
mRefreshBacking(false),
|
2010-02-02 19:43:52 +00:00
|
|
|
vrulerSize(36,0)
|
2010-01-23 19:44:49 +00:00
|
|
|
#ifndef __WXGTK__ //Get rid if this pragma for gtk
|
|
|
|
#pragma warning( default: 4355 )
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
SetLabel(_("Track Panel"));
|
|
|
|
SetName(_("Track Panel"));
|
2015-08-24 01:25:01 +00:00
|
|
|
SetBackgroundStyle(wxBG_STYLE_PAINT);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2016-08-03 01:24:53 +00:00
|
|
|
{
|
|
|
|
auto pAx = std::make_unique <TrackPanelAx>( this );
|
2010-01-23 19:44:49 +00:00
|
|
|
#if wxUSE_ACCESSIBILITY
|
2016-08-03 01:24:53 +00:00
|
|
|
// wxWidgets owns the accessible object
|
|
|
|
SetAccessible(mAx = pAx.release());
|
|
|
|
#else
|
2017-03-14 23:54:11 +00:00
|
|
|
// wxWidgets does not own the object, but we need to retain it
|
2016-08-03 01:24:53 +00:00
|
|
|
mAx = std::move(pAx);
|
2010-01-23 19:44:49 +00:00
|
|
|
#endif
|
2016-08-03 01:24:53 +00:00
|
|
|
}
|
2015-08-24 01:25:01 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
mRedrawAfterStop = false;
|
|
|
|
|
2016-08-03 01:25:05 +00:00
|
|
|
mTrackArtist = std::make_unique<TrackArtist>();
|
2015-06-23 03:56:36 +00:00
|
|
|
|
2017-06-06 16:34:57 +00:00
|
|
|
mTrackArtist->SetMargins(1, kTopMargin, kRightMargin, kBottomMargin);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
mTimeCount = 0;
|
|
|
|
mTimer.parent = this;
|
2017-02-25 21:16:42 +00:00
|
|
|
// Timer is started after the window is visible
|
|
|
|
GetProject()->Bind(wxEVT_IDLE, &TrackPanel::OnIdle, this);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
// Register for tracklist updates
|
2017-06-25 05:33:31 +00:00
|
|
|
mTracks->Connect(EVT_TRACKLIST_RESIZING,
|
|
|
|
wxCommandEventHandler(TrackPanel::OnTrackListResizing),
|
2010-01-23 19:44:49 +00:00
|
|
|
NULL,
|
|
|
|
this);
|
2017-06-25 05:33:31 +00:00
|
|
|
mTracks->Connect(EVT_TRACKLIST_DELETION,
|
|
|
|
wxCommandEventHandler(TrackPanel::OnTrackListDeletion),
|
2010-01-23 19:44:49 +00:00
|
|
|
NULL,
|
|
|
|
this);
|
2017-06-16 19:39:38 +00:00
|
|
|
wxTheApp->Connect(EVT_AUDIOIO_PLAYBACK,
|
|
|
|
wxCommandEventHandler(TrackPanel::OnPlayback),
|
|
|
|
NULL,
|
|
|
|
this);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2015-08-18 15:03:56 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
TrackPanel::~TrackPanel()
|
|
|
|
{
|
|
|
|
mTimer.Stop();
|
|
|
|
|
|
|
|
// Unregister for tracklist updates
|
2017-06-25 05:33:31 +00:00
|
|
|
mTracks->Disconnect(EVT_TRACKLIST_DELETION,
|
|
|
|
wxCommandEventHandler(TrackPanel::OnTrackListDeletion),
|
2010-01-23 19:44:49 +00:00
|
|
|
NULL,
|
|
|
|
this);
|
2017-06-25 05:33:31 +00:00
|
|
|
mTracks->Disconnect(EVT_TRACKLIST_RESIZING,
|
|
|
|
wxCommandEventHandler(TrackPanel::OnTrackListResizing),
|
2010-01-23 19:44:49 +00:00
|
|
|
NULL,
|
|
|
|
this);
|
2017-06-16 19:39:38 +00:00
|
|
|
wxTheApp->Disconnect(EVT_AUDIOIO_PLAYBACK,
|
|
|
|
wxCommandEventHandler(TrackPanel::OnPlayback),
|
|
|
|
NULL,
|
|
|
|
this);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
// This can happen if a label is being edited and the user presses
|
|
|
|
// ALT+F4 or Command+Q
|
|
|
|
if (HasCapture())
|
|
|
|
ReleaseMouse();
|
|
|
|
}
|
|
|
|
|
2017-06-12 20:54:12 +00:00
|
|
|
LWSlider *TrackPanel::GainSlider( const WaveTrack *wt )
|
|
|
|
{
|
|
|
|
auto rect = FindTrackRect( wt, true );
|
|
|
|
wxRect sliderRect;
|
|
|
|
TrackInfo::GetGainRect( rect.GetTopLeft(), sliderRect );
|
|
|
|
return TrackInfo::GainSlider(sliderRect, wt, false, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
LWSlider *TrackPanel::PanSlider( const WaveTrack *wt )
|
|
|
|
{
|
|
|
|
auto rect = FindTrackRect( wt, true );
|
|
|
|
wxRect sliderRect;
|
|
|
|
TrackInfo::GetPanRect( rect.GetTopLeft(), sliderRect );
|
|
|
|
return TrackInfo::PanSlider(sliderRect, wt, false, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
|
|
|
LWSlider *TrackPanel::VelocitySlider( const NoteTrack *nt )
|
|
|
|
{
|
|
|
|
auto rect = FindTrackRect( nt, true );
|
|
|
|
wxRect sliderRect;
|
|
|
|
TrackInfo::GetVelocityRect( rect.GetTopLeft(), sliderRect );
|
|
|
|
return TrackInfo::VelocitySlider(sliderRect, nt, false, this);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2013-05-30 23:14:25 +00:00
|
|
|
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
|
|
|
|
void TrackPanel::UpdateVirtualStereoOrder()
|
|
|
|
{
|
2017-01-06 16:23:02 +00:00
|
|
|
TrackListOfKindIterator iter(Track::Wave, GetTracks());
|
2013-05-30 23:14:25 +00:00
|
|
|
Track *t;
|
|
|
|
int temp;
|
2013-10-29 21:49:45 +00:00
|
|
|
|
2013-05-30 23:14:25 +00:00
|
|
|
for (t = iter.First(); t; t = iter.Next()) {
|
2016-09-15 13:54:38 +00:00
|
|
|
const auto wt = static_cast<WaveTrack*>(t);
|
|
|
|
if(t->GetChannel() == Track::MonoChannel){
|
2013-05-30 23:14:25 +00:00
|
|
|
|
|
|
|
if(WaveTrack::mMonoAsVirtualStereo && wt->GetPan() != 0){
|
|
|
|
temp = wt->GetHeight();
|
|
|
|
wt->SetHeight(temp*wt->GetVirtualTrackPercentage());
|
|
|
|
wt->SetHeight(temp - wt->GetHeight(),true);
|
|
|
|
}else if(!WaveTrack::mMonoAsVirtualStereo && wt->GetPan() != 0){
|
|
|
|
wt->SetHeight(wt->GetHeight() + wt->GetHeight(true));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
t = iter.First();
|
|
|
|
if(t){
|
|
|
|
t->ReorderList(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-06-13 12:05:17 +00:00
|
|
|
wxString TrackPanel::gSoloPref;
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
void TrackPanel::UpdatePrefs()
|
|
|
|
{
|
|
|
|
gPrefs->Read(wxT("/GUI/AutoScroll"), &mViewInfo->bUpdateTrackIndicator,
|
2015-04-19 21:12:06 +00:00
|
|
|
true);
|
2017-06-13 12:05:17 +00:00
|
|
|
gPrefs->Read(wxT("/GUI/Solo"), &gSoloPref, wxT("Simple"));
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2013-05-30 23:14:25 +00:00
|
|
|
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
|
|
|
|
bool temp = WaveTrack::mMonoAsVirtualStereo;
|
|
|
|
gPrefs->Read(wxT("/GUI/MonoAsVirtualStereo"), &WaveTrack::mMonoAsVirtualStereo,
|
2015-04-19 21:12:06 +00:00
|
|
|
false);
|
2013-05-30 23:14:25 +00:00
|
|
|
|
|
|
|
if(WaveTrack::mMonoAsVirtualStereo != temp)
|
|
|
|
UpdateVirtualStereoOrder();
|
|
|
|
#endif
|
|
|
|
|
2015-07-07 04:51:14 +00:00
|
|
|
mViewInfo->UpdatePrefs();
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
if (mTrackArtist) {
|
|
|
|
mTrackArtist->UpdatePrefs();
|
|
|
|
}
|
|
|
|
|
|
|
|
// All vertical rulers must be recalculated since the minimum and maximum
|
|
|
|
// frequences may have been changed.
|
|
|
|
UpdateVRulers();
|
2016-05-01 21:55:48 +00:00
|
|
|
|
|
|
|
mTrackInfo.UpdatePrefs();
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
Refresh();
|
|
|
|
}
|
|
|
|
|
2017-04-05 19:16:12 +00:00
|
|
|
void TrackPanel::ApplyUpdatedTheme()
|
|
|
|
{
|
|
|
|
mTrackInfo.ReCreateSliders();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
void TrackPanel::GetTracksUsableArea(int *width, int *height) const
|
|
|
|
{
|
|
|
|
GetSize(width, height);
|
2015-08-21 16:02:48 +00:00
|
|
|
if (width) {
|
|
|
|
*width -= GetLeftOffset();
|
|
|
|
*width -= kRightMargin;
|
|
|
|
*width = std::max(0, *width);
|
|
|
|
}
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Gets the pointer to the AudacityProject that
|
|
|
|
/// goes with this track panel.
|
|
|
|
AudacityProject * TrackPanel::GetProject() const
|
|
|
|
{
|
|
|
|
//JKC casting away constness here.
|
|
|
|
//Do it in two stages in case 'this' is not a wxWindow.
|
|
|
|
//when the compiler will flag the error.
|
|
|
|
wxWindow const * const pConstWind = this;
|
|
|
|
wxWindow * pWind=(wxWindow*)pConstWind;
|
|
|
|
#ifdef EXPERIMENTAL_NOTEBOOK
|
|
|
|
pWind = pWind->GetParent(); //Page
|
|
|
|
wxASSERT( pWind );
|
|
|
|
pWind = pWind->GetParent(); //Notebook
|
|
|
|
wxASSERT( pWind );
|
|
|
|
#endif
|
|
|
|
pWind = pWind->GetParent(); //MainPanel
|
|
|
|
wxASSERT( pWind );
|
|
|
|
pWind = pWind->GetParent(); //Project
|
|
|
|
wxASSERT( pWind );
|
|
|
|
return (AudacityProject*)pWind;
|
|
|
|
}
|
|
|
|
|
2017-02-25 21:16:42 +00:00
|
|
|
void TrackPanel::OnIdle(wxIdleEvent& event)
|
|
|
|
{
|
|
|
|
// The window must be ready when the timer fires (#1401)
|
|
|
|
if (IsShownOnScreen())
|
|
|
|
{
|
|
|
|
mTimer.Start(kTimerInterval, FALSE);
|
|
|
|
|
|
|
|
// Timer is started, we don't need the event anymore
|
|
|
|
GetProject()->Unbind(wxEVT_IDLE, &TrackPanel::OnIdle, this);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Get another idle event, wx only guarantees we get one
|
|
|
|
// event after "some other normal events occur"
|
|
|
|
event.RequestMore();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
/// AS: This gets called on our wx timer events.
|
2015-09-06 22:09:16 +00:00
|
|
|
void TrackPanel::OnTimer(wxTimerEvent& )
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2016-06-29 19:13:38 +00:00
|
|
|
#ifdef __WXMAC__
|
|
|
|
// Unfortunate part of fix for bug 1431
|
|
|
|
// Without this, the toolbars hide only every other time that you press
|
|
|
|
// the yellow title bar button. For some reason, not every press sends
|
|
|
|
// us a deactivate event for the application.
|
|
|
|
{
|
|
|
|
auto project = GetProject();
|
|
|
|
if (project->IsIconized())
|
|
|
|
project->MacShowUndockedToolbars(false);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
mTimeCount++;
|
|
|
|
|
2015-08-26 21:57:02 +00:00
|
|
|
AudacityProject *const p = GetProject();
|
2015-04-17 18:09:35 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
// Check whether we were playing or recording, but the stream has stopped.
|
2015-07-06 20:55:04 +00:00
|
|
|
if (p->GetAudioIOToken()>0 && !IsAudioActive())
|
2014-06-03 20:30:19 +00:00
|
|
|
{
|
2010-01-23 19:44:49 +00:00
|
|
|
//the stream may have been started up after this one finished (by some other project)
|
|
|
|
//in that case reset the buttons don't stop the stream
|
|
|
|
p->GetControlToolBar()->StopPlaying(!gAudioIO->IsStreamActive());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Next, check to see if we were playing or recording
|
|
|
|
// audio, but now Audio I/O is completely finished.
|
|
|
|
if (p->GetAudioIOToken()>0 &&
|
2014-06-03 20:30:19 +00:00
|
|
|
!gAudioIO->IsAudioTokenActive(p->GetAudioIOToken()))
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2014-12-29 22:28:32 +00:00
|
|
|
p->FixScrollbars();
|
2014-12-28 08:53:57 +00:00
|
|
|
p->SetAudioIOToken(0);
|
2014-12-29 22:28:32 +00:00
|
|
|
p->RedrawProject();
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
mRedrawAfterStop = false;
|
|
|
|
|
2013-02-20 23:42:58 +00:00
|
|
|
//ANSWER-ME: Was DisplaySelection added to solve a repaint problem?
|
2010-01-23 19:44:49 +00:00
|
|
|
DisplaySelection();
|
|
|
|
}
|
2016-05-23 21:56:06 +00:00
|
|
|
if (mLastDrawnSelectedRegion != mViewInfo->selectedRegion) {
|
|
|
|
UpdateSelectionDisplay();
|
|
|
|
}
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-08-26 21:57:02 +00:00
|
|
|
// Notify listeners for timer ticks
|
|
|
|
{
|
|
|
|
wxCommandEvent e(EVT_TRACK_PANEL_TIMER);
|
|
|
|
p->GetEventHandler()->ProcessEvent(e);
|
|
|
|
}
|
|
|
|
|
2015-08-13 14:59:42 +00:00
|
|
|
DrawOverlays(false);
|
2016-05-08 19:48:51 +00:00
|
|
|
mRuler->DrawOverlays(false);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-07-06 20:55:04 +00:00
|
|
|
if(IsAudioActive() && gAudioIO->GetNumCaptureChannels()) {
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
// Periodically update the display while recording
|
2013-10-29 21:49:45 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
if (!mRedrawAfterStop) {
|
|
|
|
mRedrawAfterStop = true;
|
|
|
|
MakeParentRedrawScrollbars();
|
|
|
|
mListener->TP_ScrollUpDown( 99999999 );
|
|
|
|
Refresh( false );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if ((mTimeCount % 5) == 0) {
|
|
|
|
// Must tell OnPaint() to recreate the backing bitmap
|
|
|
|
// since we've not done a full refresh.
|
|
|
|
mRefreshBacking = true;
|
|
|
|
Refresh( false );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(mTimeCount > 1000)
|
|
|
|
mTimeCount = 0;
|
|
|
|
}
|
|
|
|
|
2015-08-21 17:22:44 +00:00
|
|
|
double TrackPanel::GetScreenEndTime() const
|
|
|
|
{
|
|
|
|
int width;
|
|
|
|
GetTracksUsableArea(&width, NULL);
|
2016-05-18 18:55:03 +00:00
|
|
|
return mViewInfo->PositionToTime(width, 0, true);
|
2015-08-21 17:22:44 +00:00
|
|
|
}
|
|
|
|
|
2014-06-03 20:30:19 +00:00
|
|
|
/// AS: OnPaint( ) is called during the normal course of
|
2010-01-23 19:44:49 +00:00
|
|
|
/// completing a repaint operation.
|
|
|
|
void TrackPanel::OnPaint(wxPaintEvent & /* event */)
|
|
|
|
{
|
2016-05-23 21:56:06 +00:00
|
|
|
mLastDrawnSelectedRegion = mViewInfo->selectedRegion;
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
#if DEBUG_DRAW_TIMING
|
|
|
|
wxStopWatch sw;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
{
|
2016-02-01 01:39:24 +00:00
|
|
|
wxPaintDC dc(this);
|
|
|
|
|
|
|
|
// Retrieve the damage rectangle
|
|
|
|
wxRect box = GetUpdateRegion().GetBox();
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2016-02-01 01:39:24 +00:00
|
|
|
// Recreate the backing bitmap if we have a full refresh
|
|
|
|
// (See TrackPanel::Refresh())
|
|
|
|
if (mRefreshBacking || (box == GetRect()))
|
2015-08-24 01:25:01 +00:00
|
|
|
{
|
2016-02-01 01:39:24 +00:00
|
|
|
// Reset (should a mutex be used???)
|
|
|
|
mRefreshBacking = false;
|
2015-08-24 01:25:01 +00:00
|
|
|
|
2016-02-01 01:39:24 +00:00
|
|
|
// Redraw the backing bitmap
|
2016-05-07 17:00:48 +00:00
|
|
|
DrawTracks(&GetBackingDCForRepaint());
|
2015-08-24 01:25:01 +00:00
|
|
|
|
2016-02-01 01:39:24 +00:00
|
|
|
// Copy it to the display
|
2016-05-07 17:00:48 +00:00
|
|
|
DisplayBitmap(dc);
|
2016-02-01 01:39:24 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Copy full, possibly clipped, damage rectangle
|
2016-05-07 17:00:48 +00:00
|
|
|
RepairBitmap(dc, box.x, box.y, box.width, box.height);
|
2016-02-01 01:39:24 +00:00
|
|
|
}
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2016-02-01 01:39:24 +00:00
|
|
|
// Done with the clipped DC
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2016-05-07 17:00:48 +00:00
|
|
|
// Drawing now goes directly to the client area.
|
|
|
|
// DrawOverlays() may need to draw outside the clipped region.
|
2016-08-08 13:50:55 +00:00
|
|
|
// (Used to make a NEW, separate wxClientDC, but that risks flashing
|
2016-05-07 17:00:48 +00:00
|
|
|
// problems on Mac.)
|
|
|
|
dc.DestroyClippingRegion();
|
|
|
|
DrawOverlays(true, &dc);
|
|
|
|
}
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
#if DEBUG_DRAW_TIMING
|
2015-08-24 01:25:01 +00:00
|
|
|
sw.Pause();
|
|
|
|
wxLogDebug(wxT("Total: %ld milliseconds"), sw.Time());
|
|
|
|
wxPrintf(wxT("Total: %ld milliseconds\n"), sw.Time());
|
2010-01-23 19:44:49 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-12-30 00:41:18 +00:00
|
|
|
void TrackPanel::MakeParentModifyState(bool bWantsAutoSave)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2013-12-30 00:41:18 +00:00
|
|
|
mListener->TP_ModifyState(bWantsAutoSave);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TrackPanel::MakeParentRedrawScrollbars()
|
|
|
|
{
|
|
|
|
mListener->TP_RedrawScrollbars();
|
|
|
|
}
|
|
|
|
|
2016-05-22 03:19:09 +00:00
|
|
|
void TrackPanel::HandleInterruptedDrag()
|
|
|
|
{
|
2017-06-08 21:27:41 +00:00
|
|
|
if (mUIHandle && mUIHandle->StopsOnKeystroke() ) {
|
2015-07-07 03:12:16 +00:00
|
|
|
// The bogus id isn't used anywhere, but may help with debugging.
|
|
|
|
// as this is sending a bogus mouse up. The mouse button is still actually down
|
|
|
|
// and may go up again.
|
|
|
|
const int idBogusUp = 2;
|
|
|
|
wxMouseEvent evt { wxEVT_LEFT_UP };
|
|
|
|
evt.SetId( idBogusUp );
|
|
|
|
evt.SetPosition(this->ScreenToClient(::wxGetMousePosition()));
|
|
|
|
this->ProcessEvent(evt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
void ProcessUIHandleResult
|
|
|
|
(TrackPanel *panel, AdornedRulerPanel *ruler,
|
|
|
|
Track *pClickedTrack, Track *pLatestTrack,
|
|
|
|
UIHandle::Result refreshResult)
|
|
|
|
{
|
|
|
|
// TODO: make a finer distinction between refreshing the track control area,
|
|
|
|
// and the waveform area. As it is, redraw both whenever you must redraw either.
|
|
|
|
|
|
|
|
using namespace RefreshCode;
|
|
|
|
|
|
|
|
panel->UpdateViewIfNoTracks();
|
|
|
|
|
|
|
|
if (refreshResult & DestroyedCell) {
|
|
|
|
// Beware stale pointer!
|
|
|
|
if (pLatestTrack == pClickedTrack)
|
|
|
|
pLatestTrack = NULL;
|
|
|
|
pClickedTrack = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pClickedTrack && (refreshResult & UpdateVRuler))
|
|
|
|
panel->UpdateVRuler(pClickedTrack);
|
|
|
|
|
|
|
|
if (refreshResult & DrawOverlays) {
|
|
|
|
panel->DrawOverlays(false);
|
|
|
|
ruler->DrawOverlays(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Refresh all if told to do so, or if told to refresh a track that
|
|
|
|
// is not known.
|
|
|
|
const bool refreshAll =
|
|
|
|
( (refreshResult & RefreshAll)
|
|
|
|
|| ((refreshResult & RefreshCell) && !pClickedTrack)
|
|
|
|
|| ((refreshResult & RefreshLatestCell) && !pLatestTrack));
|
|
|
|
|
|
|
|
if (refreshAll)
|
|
|
|
panel->Refresh(false);
|
|
|
|
else {
|
|
|
|
if (refreshResult & RefreshCell)
|
|
|
|
panel->RefreshTrack(pClickedTrack);
|
|
|
|
if (refreshResult & RefreshLatestCell)
|
|
|
|
panel->RefreshTrack(pLatestTrack);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (refreshResult & FixScrollbars)
|
|
|
|
panel->MakeParentRedrawScrollbars();
|
|
|
|
|
|
|
|
if (refreshResult & Resize)
|
|
|
|
panel->GetListener()->TP_HandleResize();
|
|
|
|
|
|
|
|
// This flag is superfluous if you do full refresh,
|
|
|
|
// because TrackPanel::Refresh() does this too
|
|
|
|
if (refreshResult & UpdateSelection) {
|
|
|
|
panel->DisplaySelection();
|
|
|
|
|
|
|
|
{
|
|
|
|
// Formerly in TrackPanel::UpdateSelectionDisplay():
|
|
|
|
|
|
|
|
// Make sure the ruler follows suit.
|
|
|
|
// mRuler->DrawSelection();
|
|
|
|
|
|
|
|
// ... but that too is superfluous it does nothing but refresh
|
|
|
|
// the ruler, while DisplaySelection calls TP_DisplaySelection which
|
|
|
|
// also always refreshes the ruler.
|
2015-09-09 01:15:35 +00:00
|
|
|
}
|
2014-10-18 14:19:38 +00:00
|
|
|
}
|
|
|
|
|
2015-09-09 01:15:35 +00:00
|
|
|
if ((refreshResult & EnsureVisible) && pClickedTrack)
|
|
|
|
panel->EnsureVisible(pClickedTrack);
|
2014-10-18 14:19:38 +00:00
|
|
|
}
|
|
|
|
}
|
2014-10-25 22:05:45 +00:00
|
|
|
|
2017-06-08 21:27:41 +00:00
|
|
|
void TrackPanel::Uncapture(wxMouseEvent *pEvent)
|
|
|
|
{
|
|
|
|
if (HasCapture())
|
|
|
|
ReleaseMouse();
|
|
|
|
HandleCursor( pEvent );
|
|
|
|
}
|
|
|
|
|
2015-09-09 01:15:35 +00:00
|
|
|
void TrackPanel::CancelDragging()
|
2014-10-25 22:05:45 +00:00
|
|
|
{
|
2015-09-09 01:15:35 +00:00
|
|
|
if (mUIHandle) {
|
|
|
|
UIHandle::Result refreshResult = mUIHandle->Cancel(GetProject());
|
2014-10-25 22:05:45 +00:00
|
|
|
{
|
2015-09-09 01:15:35 +00:00
|
|
|
// TODO: avoid dangling pointers to mpClickedTrack
|
|
|
|
// when the undo stack management of the typical Cancel override
|
|
|
|
// causes it to relocate. That is implement some means to
|
|
|
|
// re-fetch the track according to its position in the list.
|
2017-06-08 21:27:41 +00:00
|
|
|
// (Or should all Tracks be managed always by std::shared_ptr?)
|
2015-09-09 01:15:35 +00:00
|
|
|
mpClickedTrack = NULL;
|
2014-10-25 22:05:45 +00:00
|
|
|
}
|
2015-09-09 01:15:35 +00:00
|
|
|
ProcessUIHandleResult(this, mRuler, mpClickedTrack, NULL, refreshResult);
|
|
|
|
mpClickedTrack = NULL;
|
|
|
|
mUIHandle = NULL;
|
2017-06-08 21:27:41 +00:00
|
|
|
Uncapture();
|
2014-10-25 22:05:45 +00:00
|
|
|
}
|
|
|
|
}
|
2017-05-11 18:53:03 +00:00
|
|
|
|
2015-09-09 01:15:35 +00:00
|
|
|
bool TrackPanel::HandleEscapeKey(bool down)
|
2017-05-11 18:53:03 +00:00
|
|
|
{
|
2015-09-09 01:15:35 +00:00
|
|
|
if (!down)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (mUIHandle) {
|
|
|
|
// UIHANDLE CANCEL
|
|
|
|
CancelDragging();
|
|
|
|
return true;
|
2017-05-11 18:53:03 +00:00
|
|
|
}
|
2015-09-09 01:15:35 +00:00
|
|
|
|
|
|
|
// Not escaping from a mouse drag
|
|
|
|
return false;
|
2017-05-11 18:53:03 +00:00
|
|
|
}
|
|
|
|
|
2015-09-09 01:15:35 +00:00
|
|
|
void TrackPanel::HandleAltKey(bool down)
|
2017-05-11 18:53:03 +00:00
|
|
|
{
|
2015-09-09 01:15:35 +00:00
|
|
|
mLastMouseEvent.m_altDown = down;
|
|
|
|
HandleCursorForLastMouseEvent();
|
|
|
|
}
|
2017-05-11 18:53:03 +00:00
|
|
|
|
2015-09-09 01:15:35 +00:00
|
|
|
void TrackPanel::HandleShiftKey(bool down)
|
|
|
|
{
|
|
|
|
mLastMouseEvent.m_shiftDown = down;
|
|
|
|
HandleCursorForLastMouseEvent();
|
|
|
|
}
|
2017-05-11 18:53:03 +00:00
|
|
|
|
2015-09-09 01:15:35 +00:00
|
|
|
void TrackPanel::HandleControlKey(bool down)
|
|
|
|
{
|
|
|
|
mLastMouseEvent.m_controlDown = down;
|
|
|
|
HandleCursorForLastMouseEvent();
|
|
|
|
}
|
2017-05-11 18:53:03 +00:00
|
|
|
|
2015-09-09 01:15:35 +00:00
|
|
|
void TrackPanel::HandlePageUpKey()
|
|
|
|
{
|
|
|
|
mListener->TP_ScrollWindow(2 * mViewInfo->h - GetScreenEndTime());
|
|
|
|
}
|
2017-05-11 18:53:03 +00:00
|
|
|
|
2015-09-09 01:15:35 +00:00
|
|
|
void TrackPanel::HandlePageDownKey()
|
|
|
|
{
|
|
|
|
mListener->TP_ScrollWindow(GetScreenEndTime());
|
|
|
|
}
|
2017-05-11 18:53:03 +00:00
|
|
|
|
2015-09-09 01:15:35 +00:00
|
|
|
void TrackPanel::HandleCursorForLastMouseEvent()
|
|
|
|
{
|
2017-06-17 02:57:03 +00:00
|
|
|
// Come here on modifier key transitions,
|
|
|
|
// or on starting or stopping of play or record,
|
|
|
|
// and change the cursor appropriately.
|
2017-06-08 21:27:41 +00:00
|
|
|
HandleCursor( &mLastMouseEvent );
|
2017-05-11 18:53:03 +00:00
|
|
|
}
|
|
|
|
|
2015-09-09 01:15:35 +00:00
|
|
|
bool TrackPanel::IsAudioActive()
|
|
|
|
{
|
|
|
|
AudacityProject *p = GetProject();
|
|
|
|
return p->IsAudioActive();
|
|
|
|
}
|
2010-09-18 21:02:36 +00:00
|
|
|
|
2017-05-11 17:41:26 +00:00
|
|
|
|
2015-09-09 01:15:35 +00:00
|
|
|
/// TrackPanel::HandleCursor( ) sets the cursor drawn at the mouse location.
|
|
|
|
/// As this procedure checks which region the mouse is over, it is
|
|
|
|
/// appropriate to establish the message in the status bar.
|
2017-06-08 21:27:41 +00:00
|
|
|
void TrackPanel::HandleCursor( wxMouseEvent *pEvent )
|
2015-09-09 01:15:35 +00:00
|
|
|
{
|
2017-06-08 21:27:41 +00:00
|
|
|
wxMouseEvent dummy;
|
|
|
|
if (!pEvent)
|
|
|
|
pEvent = &dummy;
|
|
|
|
else
|
|
|
|
mLastMouseEvent = *pEvent;
|
|
|
|
auto &event = *pEvent;
|
2017-05-11 17:41:26 +00:00
|
|
|
|
2015-09-09 01:15:35 +00:00
|
|
|
const auto foundCell = FindCell( event.m_x, event.m_y );
|
|
|
|
auto &track = foundCell.pTrack;
|
|
|
|
auto &rect = foundCell.rect;
|
|
|
|
auto &pCell = foundCell.pCell;
|
2017-06-08 21:27:41 +00:00
|
|
|
const auto size = GetSize();
|
|
|
|
const TrackPanelMouseEvent tpmEvent{ event, rect, size, pCell };
|
2017-06-16 20:49:49 +00:00
|
|
|
HandleCursor( tpmEvent );
|
2017-06-08 21:27:41 +00:00
|
|
|
}
|
2017-05-12 21:41:57 +00:00
|
|
|
|
2017-06-16 20:49:49 +00:00
|
|
|
void TrackPanel::HandleCursor( const TrackPanelMouseEvent &tpmEvent )
|
2017-06-08 21:27:41 +00:00
|
|
|
{
|
|
|
|
if ( mUIHandle ) {
|
|
|
|
// UIHANDLE PREVIEW
|
|
|
|
// Update status message and cursor during drag
|
|
|
|
HitTestPreview preview = mUIHandle->Preview( tpmEvent, GetProject() );
|
|
|
|
mListener->TP_DisplayStatusMessage( preview.message );
|
|
|
|
if ( preview.cursor )
|
|
|
|
SetCursor( *preview.cursor );
|
2010-09-18 21:02:36 +00:00
|
|
|
}
|
2017-06-08 21:27:41 +00:00
|
|
|
else {
|
|
|
|
wxCursor *pCursor = NULL;
|
|
|
|
|
|
|
|
wxString tip;
|
|
|
|
|
|
|
|
// Are we within the vertical resize area?
|
|
|
|
// (Add margin back to bottom of the rectangle)
|
|
|
|
auto pCell = tpmEvent.pCell;
|
|
|
|
auto track = static_cast<CommonTrackPanelCell*>( pCell )->FindTrack();
|
|
|
|
if (pCell && pCursor == NULL && tip == wxString()) {
|
|
|
|
const auto size = GetSize();
|
|
|
|
HitTestResult hitTest( pCell->HitTest(tpmEvent, GetProject()) );
|
|
|
|
tip = hitTest.preview.message;
|
|
|
|
ProcessUIHandleResult(this, mRuler, track, track, hitTest.preview.refreshCode);
|
|
|
|
pCursor = hitTest.preview.cursor;
|
|
|
|
if (pCursor)
|
|
|
|
SetCursor(*pCursor);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pCursor != NULL || tip != wxString())
|
|
|
|
mListener->TP_DisplayStatusMessage(tip);
|
2010-09-18 21:02:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-09 01:15:35 +00:00
|
|
|
void TrackPanel::UpdateSelectionDisplay()
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2015-09-09 01:15:35 +00:00
|
|
|
// Full refresh since the label area may need to indicate
|
|
|
|
// newly selected tracks.
|
|
|
|
Refresh(false);
|
2014-10-18 14:19:38 +00:00
|
|
|
|
2015-09-09 01:15:35 +00:00
|
|
|
// Make sure the ruler follows suit.
|
|
|
|
mRuler->DrawSelection();
|
2014-10-18 14:19:38 +00:00
|
|
|
|
2015-09-09 01:15:35 +00:00
|
|
|
// As well as the SelectionBar.
|
|
|
|
DisplaySelection();
|
2014-10-18 14:19:38 +00:00
|
|
|
}
|
|
|
|
|
2015-09-09 01:15:35 +00:00
|
|
|
void TrackPanel::UpdateAccessibility()
|
2014-10-25 22:05:45 +00:00
|
|
|
{
|
2015-09-09 01:15:35 +00:00
|
|
|
if (mAx)
|
|
|
|
mAx->Updated();
|
2014-10-25 22:05:45 +00:00
|
|
|
}
|
|
|
|
|
2015-09-09 01:15:35 +00:00
|
|
|
// Counts tracks, counting stereo tracks as one track.
|
|
|
|
size_t TrackPanel::GetTrackCount() const
|
2014-11-29 22:09:57 +00:00
|
|
|
{
|
2015-09-09 01:15:35 +00:00
|
|
|
size_t count = 0;
|
|
|
|
TrackListConstIterator iter(GetTracks());
|
|
|
|
for (auto t = iter.First(); t; t = iter.Next()) {
|
|
|
|
count += 1;
|
|
|
|
if( t->GetLinked() ){
|
|
|
|
t = iter.Next();
|
|
|
|
if( !t )
|
|
|
|
break;
|
|
|
|
}
|
2014-11-29 22:09:57 +00:00
|
|
|
}
|
2015-09-09 01:15:35 +00:00
|
|
|
return count;
|
|
|
|
}
|
2014-11-29 22:09:57 +00:00
|
|
|
|
2015-09-09 01:15:35 +00:00
|
|
|
// Counts selected tracks, counting stereo tracks as one track.
|
|
|
|
size_t TrackPanel::GetSelectedTrackCount() const
|
|
|
|
{
|
|
|
|
size_t count = 0;
|
2014-11-29 22:09:57 +00:00
|
|
|
|
2015-09-09 01:15:35 +00:00
|
|
|
TrackListConstIterator iter(GetTracks());
|
|
|
|
for (auto t = iter.First(); t; t = iter.Next()) {
|
|
|
|
count += t->GetSelected() ? 1:0;
|
|
|
|
if( t->GetLinked() ){
|
|
|
|
t = iter.Next();
|
|
|
|
if( !t )
|
|
|
|
break;
|
2014-10-25 22:05:45 +00:00
|
|
|
}
|
|
|
|
}
|
2015-09-09 01:15:35 +00:00
|
|
|
return count;
|
|
|
|
}
|
2014-10-25 22:05:45 +00:00
|
|
|
|
2015-09-09 01:15:35 +00:00
|
|
|
void TrackPanel::MessageForScreenReader(const wxString& message)
|
|
|
|
{
|
|
|
|
if (mAx)
|
|
|
|
mAx->MessageForScreenReader(message);
|
2014-10-25 22:05:45 +00:00
|
|
|
}
|
|
|
|
|
2017-06-08 21:27:41 +00:00
|
|
|
/// Determines if a modal tool is active
|
2012-12-04 16:41:43 +00:00
|
|
|
bool TrackPanel::IsMouseCaptured()
|
|
|
|
{
|
2015-09-09 01:15:35 +00:00
|
|
|
return mUIHandle != NULL;
|
2012-12-04 16:41:43 +00:00
|
|
|
}
|
|
|
|
|
2012-08-16 23:41:09 +00:00
|
|
|
void TrackPanel::UpdateViewIfNoTracks()
|
|
|
|
{
|
2014-06-03 20:30:19 +00:00
|
|
|
if (mTracks->IsEmpty())
|
2012-08-16 23:41:09 +00:00
|
|
|
{
|
|
|
|
// BG: There are no more tracks on screen
|
2010-01-23 19:44:49 +00:00
|
|
|
//BG: Set zoom to normal
|
2015-04-19 23:26:36 +00:00
|
|
|
mViewInfo->SetZoom(ZoomInfo::GetDefaultZoom());
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
//STM: Set selection to 0,0
|
2014-10-05 17:10:09 +00:00
|
|
|
//PRL: and default the rest of the selection information
|
|
|
|
mViewInfo->selectedRegion = SelectedRegion();
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-05-27 00:20:04 +00:00
|
|
|
// PRL: Following causes the time ruler to align 0 with left edge.
|
|
|
|
// Bug 972
|
|
|
|
mViewInfo->h = 0;
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
mListener->TP_RedrawScrollbars();
|
2015-07-18 20:07:46 +00:00
|
|
|
mListener->TP_HandleResize();
|
2010-01-23 19:44:49 +00:00
|
|
|
mListener->TP_DisplayStatusMessage(wxT("")); //STM: Clear message if all tracks are removed
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-16 19:39:38 +00:00
|
|
|
void TrackPanel::OnPlayback(wxCommandEvent &e)
|
|
|
|
{
|
|
|
|
e.Skip();
|
|
|
|
CallAfter( [this] { HandleCursorForLastMouseEvent(); } );
|
|
|
|
}
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
// The tracks positions within the list have changed, so update the vertical
|
|
|
|
// ruler size for the track that triggered the event.
|
2017-06-25 05:33:31 +00:00
|
|
|
void TrackPanel::OnTrackListResizing(wxCommandEvent & e)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
|
|
|
Track *t = (Track *) e.GetClientData();
|
|
|
|
UpdateVRuler(t);
|
|
|
|
e.Skip();
|
|
|
|
}
|
|
|
|
|
2017-06-25 05:33:31 +00:00
|
|
|
// Tracks have been removed from the list.
|
|
|
|
void TrackPanel::OnTrackListDeletion(wxCommandEvent & e)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2015-07-07 03:12:16 +00:00
|
|
|
if (mUIHandle)
|
|
|
|
mUIHandle->OnProjectChange(GetProject());
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
// Tracks may have been deleted, so check to see if the focused track was on of them.
|
|
|
|
if (!mTracks->Contains(GetFocusedTrack())) {
|
|
|
|
SetFocusedTrack(NULL);
|
|
|
|
}
|
|
|
|
|
2017-06-25 05:33:31 +00:00
|
|
|
UpdateVRulerSize();
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
e.Skip();
|
|
|
|
}
|
|
|
|
|
2013-08-25 21:51:26 +00:00
|
|
|
void TrackPanel::OnContextMenu(wxContextMenuEvent & WXUNUSED(event))
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
|
|
|
OnTrackMenu();
|
|
|
|
}
|
|
|
|
|
2017-06-12 16:28:20 +00:00
|
|
|
struct TrackInfo::TCPLine {
|
|
|
|
using DrawFunction = void (*)(
|
|
|
|
wxDC *dc,
|
|
|
|
const wxRect &rect,
|
|
|
|
const Track *maybeNULL,
|
|
|
|
int pressed, // a value from MouseCaptureEnum; TODO: make it bool
|
|
|
|
bool captured
|
|
|
|
);
|
|
|
|
|
|
|
|
unsigned items; // a bitwise OR of values of the enum above
|
|
|
|
int height;
|
|
|
|
int extraSpace;
|
|
|
|
DrawFunction drawFunction;
|
|
|
|
};
|
|
|
|
|
2017-06-05 00:13:48 +00:00
|
|
|
namespace {
|
|
|
|
|
2017-06-13 17:34:12 +00:00
|
|
|
#define RANGE(array) (array), (array) + sizeof(array)/sizeof(*(array))
|
|
|
|
using TCPLines = std::vector< TrackInfo::TCPLine >;
|
|
|
|
|
2017-06-05 00:13:48 +00:00
|
|
|
enum : unsigned {
|
|
|
|
// The sequence is not significant, just keep bits distinct
|
|
|
|
kItemBarButtons = 1 << 0,
|
2017-06-05 19:45:47 +00:00
|
|
|
kItemStatusInfo1 = 1 << 1,
|
2017-06-05 00:13:48 +00:00
|
|
|
kItemMute = 1 << 2,
|
|
|
|
kItemSolo = 1 << 3,
|
|
|
|
kItemGain = 1 << 4,
|
|
|
|
kItemPan = 1 << 5,
|
2017-06-05 00:47:43 +00:00
|
|
|
kItemVelocity = 1 << 6,
|
|
|
|
kItemMidiControlsRect = 1 << 7,
|
2017-06-05 15:29:21 +00:00
|
|
|
kItemMinimize = 1 << 8,
|
|
|
|
kItemSyncLock = 1 << 9,
|
2017-06-05 19:45:47 +00:00
|
|
|
kItemStatusInfo2 = 1 << 10,
|
2017-06-05 15:32:19 +00:00
|
|
|
|
|
|
|
kHighestBottomItem = kItemMinimize,
|
2017-06-05 00:13:48 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef EXPERIMENTAL_DA
|
|
|
|
|
2017-06-13 02:04:09 +00:00
|
|
|
#define TITLE_ITEMS \
|
2017-06-12 17:23:00 +00:00
|
|
|
{ kItemBarButtons, kTrackInfoBtnSize, 4, \
|
|
|
|
&TrackInfo::CloseTitleDrawFunction },
|
2017-06-05 00:13:48 +00:00
|
|
|
// DA: Has Mute and Solo on separate lines.
|
|
|
|
#define MUTE_SOLO_ITEMS(extra) \
|
2017-06-13 12:02:44 +00:00
|
|
|
{ kItemMute, kTrackInfoBtnSize + 1, 1, \
|
|
|
|
&TrackInfo::WideMuteDrawFunction }, \
|
|
|
|
{ kItemSolo, kTrackInfoBtnSize + 1, extra, \
|
|
|
|
&TrackInfo::WideSoloDrawFunction },
|
2017-06-12 09:46:20 +00:00
|
|
|
// DA: Does not have status information for a track.
|
|
|
|
#define STATUS_ITEMS
|
2017-06-05 00:13:48 +00:00
|
|
|
|
|
|
|
#else
|
|
|
|
|
2017-06-13 02:04:09 +00:00
|
|
|
#define TITLE_ITEMS \
|
2017-06-12 17:23:00 +00:00
|
|
|
{ kItemBarButtons, kTrackInfoBtnSize, 0, \
|
|
|
|
&TrackInfo::CloseTitleDrawFunction },
|
2017-06-05 00:13:48 +00:00
|
|
|
#define MUTE_SOLO_ITEMS(extra) \
|
2017-06-13 12:02:44 +00:00
|
|
|
{ kItemMute | kItemSolo, kTrackInfoBtnSize + 1, extra, \
|
|
|
|
&TrackInfo::MuteAndSoloDrawFunction },
|
2017-06-12 09:46:20 +00:00
|
|
|
#define STATUS_ITEMS \
|
2017-06-13 13:14:58 +00:00
|
|
|
{ kItemStatusInfo1, 12, 0, \
|
|
|
|
&TrackInfo::Status1DrawFunction }, \
|
|
|
|
{ kItemStatusInfo2, 12, 0, \
|
|
|
|
&TrackInfo::Status2DrawFunction },
|
2017-06-05 00:13:48 +00:00
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2017-06-13 02:04:09 +00:00
|
|
|
#define COMMON_ITEMS \
|
|
|
|
TITLE_ITEMS
|
2017-06-05 00:13:48 +00:00
|
|
|
|
2017-06-13 17:34:12 +00:00
|
|
|
const TrackInfo::TCPLine defaultCommonTrackTCPLines[] = {
|
2017-06-13 02:04:09 +00:00
|
|
|
COMMON_ITEMS
|
2017-06-05 00:13:48 +00:00
|
|
|
};
|
2017-06-13 17:34:12 +00:00
|
|
|
TCPLines commonTrackTCPLines{ RANGE(defaultCommonTrackTCPLines) };
|
2017-06-05 00:13:48 +00:00
|
|
|
|
2017-06-13 17:34:12 +00:00
|
|
|
const TrackInfo::TCPLine defaultWaveTrackTCPLines[] = {
|
2017-06-13 02:04:09 +00:00
|
|
|
COMMON_ITEMS
|
2017-06-05 00:13:48 +00:00
|
|
|
MUTE_SOLO_ITEMS(2)
|
2017-06-12 20:10:03 +00:00
|
|
|
{ kItemGain, kTrackInfoSliderHeight, kTrackInfoSliderExtra,
|
|
|
|
&TrackInfo::GainSliderDrawFunction },
|
|
|
|
{ kItemPan, kTrackInfoSliderHeight, kTrackInfoSliderExtra,
|
|
|
|
&TrackInfo::PanSliderDrawFunction },
|
2017-06-12 09:46:20 +00:00
|
|
|
STATUS_ITEMS
|
2017-06-05 00:13:48 +00:00
|
|
|
};
|
2017-06-13 17:34:12 +00:00
|
|
|
TCPLines waveTrackTCPLines{ RANGE(defaultWaveTrackTCPLines) };
|
2017-06-05 00:13:48 +00:00
|
|
|
|
2017-06-13 17:34:12 +00:00
|
|
|
const TrackInfo::TCPLine defaultNoteTrackTCPLines[] = {
|
2017-06-13 02:04:09 +00:00
|
|
|
COMMON_ITEMS
|
2017-06-07 01:47:13 +00:00
|
|
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
2017-06-05 00:13:48 +00:00
|
|
|
MUTE_SOLO_ITEMS(0)
|
2017-06-12 19:51:06 +00:00
|
|
|
{ kItemMidiControlsRect, kMidiCellHeight * 4, 0,
|
|
|
|
&TrackInfo::MidiControlsDrawFunction },
|
2017-06-12 20:10:03 +00:00
|
|
|
{ kItemVelocity, kTrackInfoSliderHeight, kTrackInfoSliderExtra,
|
|
|
|
&TrackInfo::VelocitySliderDrawFunction },
|
2017-06-07 01:47:13 +00:00
|
|
|
#endif
|
2017-06-05 00:13:48 +00:00
|
|
|
};
|
2017-06-13 17:34:12 +00:00
|
|
|
TCPLines noteTrackTCPLines{ RANGE(defaultNoteTrackTCPLines) };
|
2017-06-05 00:13:48 +00:00
|
|
|
|
2017-06-13 17:34:12 +00:00
|
|
|
int totalTCPLines( const TCPLines &lines, bool omitLastExtra )
|
2017-06-07 01:40:58 +00:00
|
|
|
{
|
|
|
|
int total = 0;
|
2017-06-12 23:03:09 +00:00
|
|
|
int lastExtra = 0;
|
2017-06-13 17:34:12 +00:00
|
|
|
for ( const auto line : lines ) {
|
|
|
|
lastExtra = line.extraSpace;
|
|
|
|
total += line.height + lastExtra;
|
2017-06-07 01:40:58 +00:00
|
|
|
}
|
2017-06-12 23:03:09 +00:00
|
|
|
if (omitLastExtra)
|
|
|
|
total -= lastExtra;
|
2017-06-07 01:40:58 +00:00
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
2017-06-13 17:34:12 +00:00
|
|
|
const TCPLines &getTCPLines( const Track &track )
|
2017-06-05 00:13:48 +00:00
|
|
|
{
|
|
|
|
#ifdef USE_MIDI
|
|
|
|
if ( track.GetKind() == Track::Note )
|
|
|
|
return noteTrackTCPLines;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if ( track.GetKind() == Track::Wave )
|
|
|
|
return waveTrackTCPLines;
|
|
|
|
|
|
|
|
return commonTrackTCPLines;
|
|
|
|
}
|
|
|
|
|
|
|
|
// return y value and height
|
2017-06-13 17:34:12 +00:00
|
|
|
std::pair< int, int > CalcItemY( const TCPLines &lines, unsigned iItem )
|
2017-06-05 00:13:48 +00:00
|
|
|
{
|
2017-06-06 15:43:58 +00:00
|
|
|
int y = 0;
|
2017-06-13 17:34:12 +00:00
|
|
|
auto pLines = lines.begin();
|
|
|
|
while ( pLines != lines.end() &&
|
2017-06-05 00:13:48 +00:00
|
|
|
0 == (pLines->items & iItem) ) {
|
|
|
|
y += pLines->height + pLines->extraSpace;
|
|
|
|
++pLines;
|
|
|
|
}
|
2017-06-13 17:34:12 +00:00
|
|
|
int height = 0;
|
|
|
|
if ( pLines != lines.end() )
|
|
|
|
height = pLines->height;
|
|
|
|
return { y, height };
|
2017-06-05 00:13:48 +00:00
|
|
|
}
|
|
|
|
|
2017-06-05 15:29:21 +00:00
|
|
|
// Items for the bottom of the panel, listed bottom-upwards
|
|
|
|
// As also with the top items, the extra space is below the item
|
2017-06-13 17:34:12 +00:00
|
|
|
const TrackInfo::TCPLine defaultCommonTrackTCPBottomLines[] = {
|
2017-06-10 09:10:38 +00:00
|
|
|
// The '0' avoids impinging on bottom line of TCP
|
|
|
|
// Use -1 if you do want to do so.
|
2017-06-12 19:17:26 +00:00
|
|
|
{ kItemSyncLock | kItemMinimize, kTrackInfoBtnSize, 0,
|
|
|
|
&TrackInfo::MinimizeSyncLockDrawFunction },
|
2017-06-05 15:29:21 +00:00
|
|
|
};
|
2017-06-13 17:34:12 +00:00
|
|
|
TCPLines commonTrackTCPBottomLines{ RANGE(defaultCommonTrackTCPBottomLines) };
|
2017-06-05 15:29:21 +00:00
|
|
|
|
|
|
|
// return y value and height
|
|
|
|
std::pair< int, int > CalcBottomItemY
|
2017-06-13 17:34:12 +00:00
|
|
|
( const TCPLines &lines, unsigned iItem, int height )
|
2017-06-05 15:29:21 +00:00
|
|
|
{
|
|
|
|
int y = height;
|
2017-06-13 17:34:12 +00:00
|
|
|
auto pLines = lines.begin();
|
|
|
|
while ( pLines != lines.end() &&
|
2017-06-05 15:29:21 +00:00
|
|
|
0 == (pLines->items & iItem) ) {
|
|
|
|
y -= pLines->height + pLines->extraSpace;
|
|
|
|
++pLines;
|
|
|
|
}
|
2017-06-13 17:34:12 +00:00
|
|
|
if (pLines != lines.end())
|
|
|
|
y -= (pLines->height + pLines->extraSpace );
|
|
|
|
return { y, pLines->height };
|
2017-06-05 15:29:21 +00:00
|
|
|
}
|
|
|
|
|
2017-06-07 14:39:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TrackInfo::HideTopItem( const wxRect &rect, const wxRect &subRect,
|
|
|
|
int allowance ) {
|
2017-06-05 15:32:19 +00:00
|
|
|
auto limit = CalcBottomItemY
|
2017-06-07 14:39:47 +00:00
|
|
|
( commonTrackTCPBottomLines, kHighestBottomItem, rect.height).first;
|
2017-06-05 15:32:19 +00:00
|
|
|
// Return true if the rectangle is even touching the limit
|
|
|
|
// without an overlap. That was the behavior as of 2.1.3.
|
2017-06-07 01:19:57 +00:00
|
|
|
return subRect.y + subRect.height - allowance >= rect.y + limit;
|
2017-06-05 15:32:19 +00:00
|
|
|
}
|
|
|
|
|
2012-02-13 20:38:24 +00:00
|
|
|
/// Handle mouse wheel rotation (for zoom in/out, vertical and horizontal scrolling)
|
2017-06-08 21:27:41 +00:00
|
|
|
void TrackPanel::HandleWheelRotation( TrackPanelMouseEvent &tpmEvent )
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2017-06-08 21:27:41 +00:00
|
|
|
auto pCell = tpmEvent.pCell;
|
|
|
|
if (!pCell)
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto &event = tpmEvent.event;
|
2016-07-03 16:14:08 +00:00
|
|
|
double steps {};
|
|
|
|
#if defined(__WXMAC__) && defined(EVT_MAGNIFY)
|
|
|
|
// PRL:
|
|
|
|
// Pinch and spread implemented in wxWidgets 3.1.0, or cherry-picked from
|
|
|
|
// the future in custom build of 3.0.2
|
|
|
|
if (event.Magnify()) {
|
|
|
|
event.SetControlDown(true);
|
|
|
|
steps = 2 * event.GetMagnification();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
steps = event.m_wheelRotation /
|
|
|
|
(event.m_wheelDelta > 0 ? (double)event.m_wheelDelta : 120.0);
|
|
|
|
}
|
|
|
|
|
2016-05-25 16:31:30 +00:00
|
|
|
if(event.GetWheelAxis() == wxMOUSE_WHEEL_HORIZONTAL) {
|
|
|
|
// Two-fingered horizontal swipe on mac is treated like shift-mousewheel
|
|
|
|
event.SetShiftDown(true);
|
|
|
|
// This makes the wave move in the same direction as the fingers, and the scrollbar
|
|
|
|
// thumb moves oppositely
|
2016-07-03 16:14:08 +00:00
|
|
|
steps *= -1;
|
2016-05-25 16:31:30 +00:00
|
|
|
}
|
|
|
|
|
2017-06-08 21:27:41 +00:00
|
|
|
tpmEvent.steps = steps;
|
|
|
|
|
2016-04-22 00:59:38 +00:00
|
|
|
if(!event.HasAnyModifiers()) {
|
|
|
|
// We will later un-skip if we do anything, but if we don't,
|
|
|
|
// propagate the event up for the sake of the scrubber
|
|
|
|
event.Skip();
|
|
|
|
event.ResumePropagation(wxEVENT_PROPAGATE_MAX);
|
|
|
|
}
|
|
|
|
|
2017-06-08 21:27:41 +00:00
|
|
|
unsigned result =
|
|
|
|
pCell->HandleWheelRotation( tpmEvent, GetProject() );
|
|
|
|
auto pTrack = static_cast<CommonTrackPanelCell*>(pCell)->FindTrack();
|
|
|
|
ProcessUIHandleResult(this, mRuler, pTrack, pTrack, result);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Filter captured keys typed into LabelTracks.
|
|
|
|
void TrackPanel::OnCaptureKey(wxCommandEvent & event)
|
|
|
|
{
|
2017-06-07 16:21:33 +00:00
|
|
|
wxKeyEvent *kevent = static_cast<wxKeyEvent *>(event.GetEventObject());
|
|
|
|
if ( WXK_ESCAPE != kevent->GetKeyCode() )
|
|
|
|
HandleInterruptedDrag();
|
2016-05-22 03:19:09 +00:00
|
|
|
|
2015-07-07 03:12:16 +00:00
|
|
|
Track * const t = GetFocusedTrack();
|
|
|
|
if (t) {
|
|
|
|
const unsigned refreshResult =
|
|
|
|
((TrackPanelCell*)t)->CaptureKey(*kevent, *mViewInfo, this);
|
|
|
|
ProcessUIHandleResult(this, mRuler, t, t, refreshResult);
|
2017-06-16 01:22:14 +00:00
|
|
|
event.Skip(kevent->GetSkipped());
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
2015-07-07 03:12:16 +00:00
|
|
|
else
|
|
|
|
event.Skip();
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TrackPanel::OnKeyDown(wxKeyEvent & event)
|
|
|
|
{
|
2015-08-10 18:15:15 +00:00
|
|
|
switch (event.GetKeyCode())
|
|
|
|
{
|
|
|
|
case WXK_ESCAPE:
|
2016-04-20 19:34:49 +00:00
|
|
|
if(HandleEscapeKey(true))
|
|
|
|
// Don't skip the event, eat it so that
|
|
|
|
// AudacityApp does not also stop any playback.
|
|
|
|
return;
|
|
|
|
else
|
|
|
|
break;
|
2015-08-09 23:26:29 +00:00
|
|
|
|
2015-08-10 18:15:15 +00:00
|
|
|
case WXK_ALT:
|
2015-08-09 23:26:29 +00:00
|
|
|
HandleAltKey(true);
|
2015-08-10 18:15:15 +00:00
|
|
|
break;
|
2015-08-09 23:26:29 +00:00
|
|
|
|
2015-08-10 18:15:15 +00:00
|
|
|
case WXK_SHIFT:
|
2015-08-09 23:26:29 +00:00
|
|
|
HandleShiftKey(true);
|
2015-08-10 18:15:15 +00:00
|
|
|
break;
|
2015-08-09 23:26:29 +00:00
|
|
|
|
2015-08-10 18:15:15 +00:00
|
|
|
case WXK_CONTROL:
|
2015-08-09 23:26:29 +00:00
|
|
|
HandleControlKey(true);
|
2015-08-10 18:15:15 +00:00
|
|
|
break;
|
2015-08-09 23:26:29 +00:00
|
|
|
|
2015-07-07 03:12:16 +00:00
|
|
|
// Allow PageUp and PageDown keys to
|
|
|
|
//scroll the Track Panel left and right
|
2015-08-10 18:15:15 +00:00
|
|
|
case WXK_PAGEUP:
|
2015-08-09 23:26:29 +00:00
|
|
|
HandlePageUpKey();
|
2015-08-10 18:15:15 +00:00
|
|
|
return;
|
2015-08-09 23:26:29 +00:00
|
|
|
|
2015-08-10 18:15:15 +00:00
|
|
|
case WXK_PAGEDOWN:
|
2015-08-09 23:26:29 +00:00
|
|
|
HandlePageDownKey();
|
2015-08-10 18:15:15 +00:00
|
|
|
return;
|
|
|
|
}
|
2015-08-09 23:26:29 +00:00
|
|
|
|
2015-07-07 03:12:16 +00:00
|
|
|
Track *const t = GetFocusedTrack();
|
2014-10-18 14:19:38 +00:00
|
|
|
|
2015-07-07 03:12:16 +00:00
|
|
|
if (t) {
|
|
|
|
const unsigned refreshResult =
|
|
|
|
((TrackPanelCell*)t)->KeyDown(event, *mViewInfo, this);
|
|
|
|
ProcessUIHandleResult(this, mRuler, t, t, refreshResult);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
event.Skip();
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TrackPanel::OnChar(wxKeyEvent & event)
|
|
|
|
{
|
2015-08-10 18:15:15 +00:00
|
|
|
switch (event.GetKeyCode())
|
|
|
|
{
|
|
|
|
case WXK_ESCAPE:
|
|
|
|
case WXK_ALT:
|
|
|
|
case WXK_SHIFT:
|
|
|
|
case WXK_CONTROL:
|
|
|
|
case WXK_PAGEUP:
|
|
|
|
case WXK_PAGEDOWN:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-07-07 03:12:16 +00:00
|
|
|
Track *const t = GetFocusedTrack();
|
|
|
|
if (t) {
|
|
|
|
const unsigned refreshResult =
|
|
|
|
((TrackPanelCell*)t)->Char(event, *mViewInfo, this);
|
|
|
|
ProcessUIHandleResult(this, mRuler, t, t, refreshResult);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
event.Skip();
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2015-08-09 23:26:29 +00:00
|
|
|
void TrackPanel::OnKeyUp(wxKeyEvent & event)
|
|
|
|
{
|
2016-04-20 19:34:49 +00:00
|
|
|
bool didSomething = false;
|
2015-08-10 18:15:15 +00:00
|
|
|
switch (event.GetKeyCode())
|
|
|
|
{
|
|
|
|
case WXK_ESCAPE:
|
2016-04-20 19:34:49 +00:00
|
|
|
didSomething = HandleEscapeKey(false);
|
2015-08-10 18:15:15 +00:00
|
|
|
break;
|
|
|
|
case WXK_ALT:
|
2015-08-09 23:26:29 +00:00
|
|
|
HandleAltKey(false);
|
2015-08-10 18:15:15 +00:00
|
|
|
break;
|
2015-08-09 23:26:29 +00:00
|
|
|
|
2015-08-10 18:15:15 +00:00
|
|
|
case WXK_SHIFT:
|
2015-08-09 23:26:29 +00:00
|
|
|
HandleShiftKey(false);
|
2015-08-10 18:15:15 +00:00
|
|
|
break;
|
2015-08-09 23:26:29 +00:00
|
|
|
|
2015-08-10 18:15:15 +00:00
|
|
|
case WXK_CONTROL:
|
2015-08-09 23:26:29 +00:00
|
|
|
HandleControlKey(false);
|
2015-08-10 18:15:15 +00:00
|
|
|
break;
|
|
|
|
}
|
2015-08-09 23:26:29 +00:00
|
|
|
|
2015-07-07 03:12:16 +00:00
|
|
|
if (didSomething)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Track * const t = GetFocusedTrack();
|
|
|
|
if (t) {
|
|
|
|
const unsigned refreshResult =
|
|
|
|
((TrackPanelCell*)t)->KeyUp(event, *mViewInfo, this);
|
|
|
|
ProcessUIHandleResult(this, mRuler, t, t, refreshResult);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
event.Skip();
|
2015-08-09 23:26:29 +00:00
|
|
|
}
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
/// Should handle the case when the mouse capture is lost.
|
2013-08-25 21:51:26 +00:00
|
|
|
void TrackPanel::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(event))
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
|
|
|
wxMouseEvent e(wxEVT_LEFT_UP);
|
|
|
|
|
|
|
|
e.m_x = mMouseMostRecentX;
|
|
|
|
e.m_y = mMouseMostRecentY;
|
|
|
|
|
|
|
|
OnMouseEvent(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This handles just generic mouse events. Then, based
|
|
|
|
/// on our current state, we forward the mouse events to
|
|
|
|
/// various interested parties.
|
|
|
|
void TrackPanel::OnMouseEvent(wxMouseEvent & event)
|
2016-12-18 20:57:03 +00:00
|
|
|
try
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2017-06-08 21:27:41 +00:00
|
|
|
const auto foundCell = FindCell( event.m_x, event.m_y );
|
|
|
|
auto &rect = foundCell.rect;
|
|
|
|
auto &pCell = foundCell.pCell;
|
|
|
|
auto &pTrack = foundCell.pTrack;
|
|
|
|
|
|
|
|
const auto size = GetSize();
|
|
|
|
TrackPanelMouseEvent tpmEvent{ event, rect, size, pCell };
|
|
|
|
|
2016-07-03 16:14:08 +00:00
|
|
|
#if defined(__WXMAC__) && defined(EVT_MAGNIFY)
|
|
|
|
// PRL:
|
|
|
|
// Pinch and spread implemented in wxWidgets 3.1.0, or cherry-picked from
|
|
|
|
// the future in custom build of 3.0.2
|
|
|
|
if (event.Magnify()) {
|
2017-06-08 21:27:41 +00:00
|
|
|
HandleWheelRotation( tpmEvent );
|
2016-07-03 16:14:08 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-03-20 13:23:59 +00:00
|
|
|
// If a mouse event originates from a keyboard context menu event then
|
|
|
|
// event.GetPosition() == wxDefaultPosition. wxContextMenu events are handled in
|
|
|
|
// TrackPanel::OnContextMenu(), and therefore associated mouse events are ignored here.
|
|
|
|
// Not ignoring them was causing bug 613: the mouse events were interpreted as clicking
|
|
|
|
// outside the tracks.
|
|
|
|
if (event.GetPosition() == wxDefaultPosition && (event.RightDown() || event.RightUp())) {
|
|
|
|
event.Skip();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
if (event.m_wheelRotation != 0)
|
2017-06-08 21:27:41 +00:00
|
|
|
HandleWheelRotation( tpmEvent );
|
2013-10-29 21:49:45 +00:00
|
|
|
|
2016-04-22 00:59:38 +00:00
|
|
|
if (event.LeftDown() || event.LeftIsDown() || event.Moving()) {
|
|
|
|
// Skip, even if we do something, so that the left click or drag
|
|
|
|
// may have an additional effect in the scrubber.
|
|
|
|
event.Skip();
|
|
|
|
event.ResumePropagation(wxEVENT_PROPAGATE_MAX);
|
|
|
|
}
|
|
|
|
|
2015-09-09 01:15:35 +00:00
|
|
|
mMouseMostRecentX = event.m_x;
|
|
|
|
mMouseMostRecentY = event.m_y;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
if (event.LeftDown()) {
|
2014-06-03 20:30:19 +00:00
|
|
|
// The activate event is used to make the
|
2010-01-23 19:44:49 +00:00
|
|
|
// parent window 'come alive' if it didn't have focus.
|
|
|
|
wxActivateEvent e;
|
2014-10-06 08:10:50 +00:00
|
|
|
GetParent()->GetEventHandler()->ProcessEvent(e);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
// wxTimers seem to be a little unreliable, so this
|
|
|
|
// "primes" it to make sure it keeps going for a while...
|
|
|
|
|
|
|
|
// When this timer fires, we call TrackPanel::OnTimer and
|
|
|
|
// possibly update the screen for offscreen scrolling.
|
|
|
|
mTimer.Stop();
|
|
|
|
mTimer.Start(kTimerInterval, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event.ButtonDown()) {
|
|
|
|
SetFocus();
|
|
|
|
}
|
|
|
|
|
2016-05-03 18:46:24 +00:00
|
|
|
if (event.Leaving())
|
2014-12-06 10:15:18 +00:00
|
|
|
{
|
2016-05-03 18:46:24 +00:00
|
|
|
auto buttons =
|
|
|
|
// Bug 1325: button state in Leaving events is unreliable on Mac.
|
|
|
|
// Poll the global state instead.
|
|
|
|
// event.ButtonIsDown(wxMOUSE_BTN_ANY);
|
|
|
|
::wxGetMouseState().ButtonIsDown(wxMOUSE_BTN_ANY);
|
2016-05-03 18:18:00 +00:00
|
|
|
|
2016-05-03 18:46:24 +00:00
|
|
|
if(!buttons) {
|
2015-07-07 03:12:16 +00:00
|
|
|
HandleEscapeKey( true );
|
2016-05-03 18:18:00 +00:00
|
|
|
|
2016-05-03 18:46:24 +00:00
|
|
|
#if defined(__WXMAC__)
|
2016-05-03 18:18:00 +00:00
|
|
|
|
2016-05-03 18:46:24 +00:00
|
|
|
// We must install the cursor ourselves since the window under
|
|
|
|
// the mouse is no longer this one and wx2.8.12 makes that check.
|
|
|
|
// Should re-evaluate with wx3.
|
|
|
|
wxSTANDARD_CURSOR->MacInstall();
|
2014-12-12 21:56:49 +00:00
|
|
|
#endif
|
2016-05-03 18:46:24 +00:00
|
|
|
}
|
2014-12-06 10:15:18 +00:00
|
|
|
}
|
|
|
|
|
2015-07-07 03:12:16 +00:00
|
|
|
if (mUIHandle) {
|
|
|
|
if (event.Dragging()) {
|
|
|
|
// UIHANDLE DRAG
|
2017-06-08 21:27:41 +00:00
|
|
|
const UIHandle::Result refreshResult =
|
|
|
|
mUIHandle->Drag( tpmEvent, GetProject() );
|
2015-07-07 03:12:16 +00:00
|
|
|
ProcessUIHandleResult(this, mRuler, mpClickedTrack, pTrack, refreshResult);
|
|
|
|
if (refreshResult & RefreshCode::Cancelled) {
|
|
|
|
// Drag decided to abort itself
|
|
|
|
mUIHandle = NULL;
|
|
|
|
mpClickedTrack = NULL;
|
2017-06-08 21:27:41 +00:00
|
|
|
Uncapture( &event );
|
2015-07-07 03:12:16 +00:00
|
|
|
}
|
2017-06-08 21:27:41 +00:00
|
|
|
else
|
|
|
|
HandleCursor( tpmEvent );
|
2015-07-07 03:12:16 +00:00
|
|
|
}
|
|
|
|
else if (event.ButtonUp()) {
|
|
|
|
// UIHANDLE RELEASE
|
2017-06-17 23:53:56 +00:00
|
|
|
auto uiHandle = mUIHandle;
|
|
|
|
// Null this pointer out first before calling Release -- because on Windows, we can
|
|
|
|
// come back recursively to this place during handling of the context menu,
|
|
|
|
// because of a capture lost event.
|
|
|
|
mUIHandle = nullptr;
|
2017-06-08 21:27:41 +00:00
|
|
|
UIHandle::Result refreshResult =
|
2017-06-17 23:53:56 +00:00
|
|
|
uiHandle->Release( tpmEvent, GetProject(), this );
|
2015-07-07 03:12:16 +00:00
|
|
|
ProcessUIHandleResult(this, mRuler, mpClickedTrack, pTrack, refreshResult);
|
2017-06-08 21:27:41 +00:00
|
|
|
mpClickedTrack = NULL;
|
|
|
|
// will also Uncapture() below
|
2015-07-07 03:12:16 +00:00
|
|
|
}
|
|
|
|
}
|
2017-06-08 21:27:41 +00:00
|
|
|
else if ( event.GetEventType() == wxEVT_MOTION )
|
|
|
|
// Update status message and cursor, not during drag
|
2017-06-17 15:45:42 +00:00
|
|
|
// consider it not a drag, even if button is down during motion, if
|
2017-06-08 21:27:41 +00:00
|
|
|
// mUIHandle is null, as it becomes during interrupted drag
|
|
|
|
// (e.g. by hitting space to play while dragging an envelope point)
|
|
|
|
HandleCursor( &event );
|
2017-06-17 15:45:42 +00:00
|
|
|
else if ( event.ButtonDown() || event.ButtonDClick() )
|
2017-06-08 21:27:41 +00:00
|
|
|
HandleClick( tpmEvent );
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-07-08 05:37:06 +00:00
|
|
|
if (event.ButtonDown() && IsMouseCaptured()) {
|
|
|
|
if (!HasCapture())
|
|
|
|
CaptureMouse();
|
|
|
|
}
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
//EnsureVisible should be called after the up-click.
|
|
|
|
if (event.ButtonUp()) {
|
2017-06-08 21:27:41 +00:00
|
|
|
Uncapture( &event );
|
|
|
|
|
2015-08-03 17:49:53 +00:00
|
|
|
wxRect rect;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2017-01-10 20:53:19 +00:00
|
|
|
const auto foundCell = FindCell(event.m_x, event.m_y);
|
|
|
|
auto t = foundCell.pTrack;
|
2017-06-16 20:49:49 +00:00
|
|
|
if ( t )
|
2010-01-23 19:44:49 +00:00
|
|
|
EnsureVisible(t);
|
|
|
|
}
|
|
|
|
}
|
2016-12-18 20:57:03 +00:00
|
|
|
catch( ... )
|
|
|
|
{
|
|
|
|
// Abort any dragging, as if by hitting Esc
|
|
|
|
if ( HandleEscapeKey( true ) )
|
|
|
|
;
|
|
|
|
else {
|
2017-06-08 21:27:41 +00:00
|
|
|
Uncapture();
|
2016-12-18 20:57:03 +00:00
|
|
|
Refresh(false);
|
|
|
|
}
|
|
|
|
throw;
|
|
|
|
}
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2017-06-08 21:27:41 +00:00
|
|
|
void TrackPanel::HandleClick( const TrackPanelMouseEvent &tpmEvent )
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2017-06-08 21:27:41 +00:00
|
|
|
const auto &event = tpmEvent.event;
|
|
|
|
auto pCell = tpmEvent.pCell;
|
|
|
|
const auto &rect = tpmEvent.rect;
|
|
|
|
auto pTrack = static_cast<CommonTrackPanelCell *>( pCell )->FindTrack();
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2017-06-08 21:27:41 +00:00
|
|
|
if ( !mUIHandle && pCell )
|
|
|
|
mUIHandle =
|
|
|
|
pCell->HitTest( tpmEvent, GetProject() ).handle;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2017-06-08 21:27:41 +00:00
|
|
|
if (mUIHandle) {
|
|
|
|
// UIHANDLE CLICK
|
|
|
|
UIHandle::Result refreshResult =
|
|
|
|
mUIHandle->Click( tpmEvent, GetProject() );
|
|
|
|
if (refreshResult & RefreshCode::Cancelled)
|
|
|
|
mUIHandle = NULL;
|
|
|
|
else
|
|
|
|
mpClickedTrack = pTrack;
|
|
|
|
ProcessUIHandleResult(this, mRuler, pTrack, pTrack, refreshResult);
|
|
|
|
HandleCursor( tpmEvent );
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
2010-09-18 21:02:36 +00:00
|
|
|
}
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
double TrackPanel::GetMostRecentXPos()
|
|
|
|
{
|
2015-04-19 21:12:06 +00:00
|
|
|
return mViewInfo->PositionToTime(mMouseMostRecentX, GetLabelWidth());
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TrackPanel::RefreshTrack(Track *trk, bool refreshbacking)
|
|
|
|
{
|
2011-06-04 02:41:16 +00:00
|
|
|
Track *link = trk->GetLink();
|
2013-10-29 21:49:45 +00:00
|
|
|
|
2011-06-04 02:41:16 +00:00
|
|
|
if (link && !trk->GetLinked()) {
|
|
|
|
trk = link;
|
|
|
|
link = trk->GetLink();
|
|
|
|
}
|
2013-10-29 21:49:45 +00:00
|
|
|
|
2015-08-21 00:57:24 +00:00
|
|
|
// subtract insets and shadows from the rectangle, but not border
|
|
|
|
// This matters because some separators do paint over the border
|
2015-08-03 17:49:53 +00:00
|
|
|
wxRect rect(kLeftInset,
|
2011-06-04 02:41:16 +00:00
|
|
|
-mViewInfo->vpos + trk->GetY() + kTopInset,
|
2015-08-21 00:57:24 +00:00
|
|
|
GetRect().GetWidth() - kLeftInset - kRightInset - kShadowThickness,
|
|
|
|
trk->GetHeight() - kTopInset - kShadowThickness);
|
2013-10-29 21:49:45 +00:00
|
|
|
|
2011-06-04 02:41:16 +00:00
|
|
|
if (link) {
|
2015-08-03 17:49:53 +00:00
|
|
|
rect.height += link->GetHeight();
|
2011-06-04 02:41:16 +00:00
|
|
|
}
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2013-05-30 23:14:25 +00:00
|
|
|
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
|
|
|
|
else if(MONO_WAVE_PAN(trk)){
|
2015-08-03 17:49:53 +00:00
|
|
|
rect.height += trk->GetHeight(true);
|
2013-05-30 23:14:25 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
if( refreshbacking )
|
|
|
|
{
|
|
|
|
mRefreshBacking = true;
|
|
|
|
}
|
|
|
|
|
2015-08-03 17:49:53 +00:00
|
|
|
Refresh( false, &rect );
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-03 20:30:19 +00:00
|
|
|
/// This method overrides Refresh() of wxWindow so that the
|
2011-06-04 02:41:16 +00:00
|
|
|
/// boolean play indictaor can be set to false, so that an old play indicator that is
|
2014-06-03 20:30:19 +00:00
|
|
|
/// no longer there won't get XORed (to erase it), thus redrawing it on the
|
2010-01-23 19:44:49 +00:00
|
|
|
/// TrackPanel
|
|
|
|
void TrackPanel::Refresh(bool eraseBackground /* = TRUE */,
|
|
|
|
const wxRect *rect /* = NULL */)
|
|
|
|
{
|
|
|
|
// Tell OnPaint() to refresh the backing bitmap.
|
|
|
|
//
|
|
|
|
// Originally I had the check within the OnPaint() routine and it
|
|
|
|
// was working fine. That was until I found that, even though a full
|
|
|
|
// refresh was requested, Windows only set the onscreen portion of a
|
|
|
|
// window as damaged.
|
|
|
|
//
|
|
|
|
// So, if any part of the trackpanel was off the screen, full refreshes
|
|
|
|
// didn't work and the display got corrupted.
|
|
|
|
if( !rect || ( *rect == GetRect() ) )
|
|
|
|
{
|
|
|
|
mRefreshBacking = true;
|
|
|
|
}
|
|
|
|
wxWindow::Refresh(eraseBackground, rect);
|
|
|
|
DisplaySelection();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Draw the actual track areas. We only draw the borders
|
|
|
|
/// and the little buttons and menues and whatnot here, the
|
|
|
|
/// actual contents of each track are drawn by the TrackArtist.
|
|
|
|
void TrackPanel::DrawTracks(wxDC * dc)
|
|
|
|
{
|
|
|
|
wxRegion region = GetUpdateRegion();
|
|
|
|
|
2017-06-04 15:52:56 +00:00
|
|
|
const wxRect clip = GetRect();
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
wxRect panelRect = clip;
|
|
|
|
panelRect.y = -mViewInfo->vpos;
|
|
|
|
|
|
|
|
wxRect tracksRect = panelRect;
|
|
|
|
tracksRect.x += GetLabelWidth();
|
|
|
|
tracksRect.width -= GetLabelWidth();
|
|
|
|
|
|
|
|
ToolsToolBar *pTtb = mListener->TP_GetToolsToolBar();
|
|
|
|
bool bMultiToolDown = pTtb->IsDown(multiTool);
|
|
|
|
bool envelopeFlag = pTtb->IsDown(envelopeTool) || bMultiToolDown;
|
2015-06-09 14:45:14 +00:00
|
|
|
bool bigPointsFlag = pTtb->IsDown(drawTool) || bMultiToolDown;
|
2010-01-23 19:44:49 +00:00
|
|
|
bool sliderFlag = bMultiToolDown;
|
|
|
|
|
|
|
|
// The track artist actually draws the stuff inside each track
|
2016-08-20 17:58:56 +00:00
|
|
|
mTrackArtist->DrawTracks(GetTracks(), GetProject()->GetFirstVisible(),
|
2015-06-09 14:45:14 +00:00
|
|
|
*dc, region, tracksRect, clip,
|
|
|
|
mViewInfo->selectedRegion, *mViewInfo,
|
|
|
|
envelopeFlag, bigPointsFlag, sliderFlag);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-04-23 16:30:38 +00:00
|
|
|
DrawEverythingElse(dc, region, clip);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Draws 'Everything else'. In particular it draws:
|
|
|
|
/// - Drop shadow for tracks and vertical rulers.
|
|
|
|
/// - Zooming Indicators.
|
2014-06-03 20:30:19 +00:00
|
|
|
/// - Fills in space below the tracks.
|
2010-01-23 19:44:49 +00:00
|
|
|
void TrackPanel::DrawEverythingElse(wxDC * dc,
|
2015-04-23 14:07:25 +00:00
|
|
|
const wxRegion ®ion,
|
|
|
|
const wxRect & clip)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
|
|
|
// We draw everything else
|
|
|
|
|
2014-06-03 20:30:19 +00:00
|
|
|
wxRect focusRect(-1, -1, 0, 0);
|
2010-01-23 19:44:49 +00:00
|
|
|
wxRect trackRect = clip;
|
|
|
|
trackRect.height = 0; // for drawing background in no tracks case.
|
|
|
|
|
|
|
|
VisibleTrackIterator iter(GetProject());
|
|
|
|
for (Track *t = iter.First(); t; t = iter.Next()) {
|
|
|
|
trackRect.y = t->GetY() - mViewInfo->vpos;
|
|
|
|
trackRect.height = t->GetHeight();
|
|
|
|
|
|
|
|
// If this track is linked to the next one, display a common
|
|
|
|
// border for both, otherwise draw a normal border
|
2015-08-03 17:49:53 +00:00
|
|
|
wxRect rect = trackRect;
|
2010-01-23 19:44:49 +00:00
|
|
|
bool skipBorder = false;
|
|
|
|
Track *l = t->GetLink();
|
|
|
|
|
|
|
|
if (t->GetLinked()) {
|
2015-08-03 17:49:53 +00:00
|
|
|
rect.height += l->GetHeight();
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
else if (l && trackRect.y >= 0) {
|
|
|
|
skipBorder = true;
|
|
|
|
}
|
|
|
|
|
2013-05-30 23:14:25 +00:00
|
|
|
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
|
|
|
|
if(MONO_WAVE_PAN(t)){
|
2015-08-03 17:49:53 +00:00
|
|
|
rect.height += t->GetHeight(true);
|
2013-05-30 23:14:25 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
// If the previous track is linked to this one but isn't on the screen
|
|
|
|
// (and thus would have been skipped by VisibleTrackIterator) we need to
|
|
|
|
// draw that track's border instead.
|
|
|
|
Track *borderTrack = t;
|
2017-06-05 22:16:55 +00:00
|
|
|
wxRect borderRect = rect;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
if (l && !t->GetLinked() && trackRect.y < 0)
|
|
|
|
{
|
|
|
|
borderTrack = l;
|
|
|
|
|
2017-06-05 22:16:55 +00:00
|
|
|
borderRect = trackRect;
|
|
|
|
borderRect.y = l->GetY() - mViewInfo->vpos;
|
|
|
|
borderRect.height = l->GetHeight();
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
borderRect.height += t->GetHeight();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!skipBorder) {
|
|
|
|
if (mAx->IsFocused(t)) {
|
|
|
|
focusRect = borderRect;
|
|
|
|
}
|
2017-06-05 22:16:55 +00:00
|
|
|
DrawOutside(borderTrack, dc, borderRect);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Believe it or not, we can speed up redrawing if we don't
|
|
|
|
// redraw the vertical ruler when only the waveform data has
|
|
|
|
// changed. An example is during recording.
|
|
|
|
|
|
|
|
#if DEBUG_DRAW_TIMING
|
|
|
|
// wxRect rbox = region.GetBox();
|
|
|
|
// wxPrintf(wxT("Update Region: %d %d %d %d\n"),
|
|
|
|
// rbox.x, rbox.y, rbox.width, rbox.height);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (region.Contains(0, trackRect.y, GetLeftOffset(), trackRect.height)) {
|
2015-08-03 17:49:53 +00:00
|
|
|
wxRect rect = trackRect;
|
|
|
|
rect.x += GetVRulerOffset();
|
2015-08-21 15:04:30 +00:00
|
|
|
rect.y += kTopMargin;
|
2015-08-03 17:49:53 +00:00
|
|
|
rect.width = GetVRulerWidth();
|
2015-08-21 15:04:30 +00:00
|
|
|
rect.height -= (kTopMargin + kBottomMargin);
|
2015-08-03 17:49:53 +00:00
|
|
|
mTrackArtist->DrawVRuler(t, dc, rect);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
2013-05-30 23:14:25 +00:00
|
|
|
|
|
|
|
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
|
|
|
|
if(MONO_WAVE_PAN(t)){
|
|
|
|
trackRect.y = t->GetY(true) - mViewInfo->vpos;
|
|
|
|
trackRect.height = t->GetHeight(true);
|
|
|
|
if (region.Contains(0, trackRect.y, GetLeftOffset(), trackRect.height)) {
|
2015-08-03 17:49:53 +00:00
|
|
|
wxRect rect = trackRect;
|
|
|
|
rect.x += GetVRulerOffset();
|
2015-08-21 15:04:30 +00:00
|
|
|
rect.y += kTopMargin;
|
2015-08-03 17:49:53 +00:00
|
|
|
rect.width = GetVRulerWidth();
|
2015-08-21 15:04:30 +00:00
|
|
|
rect.height -= (kTopMargin + kBottomMargin);
|
2015-08-03 17:49:53 +00:00
|
|
|
mTrackArtist->DrawVRuler(t, dc, rect);
|
2013-05-30 23:14:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2015-07-07 03:12:16 +00:00
|
|
|
if (mUIHandle)
|
|
|
|
mUIHandle->DrawExtras(UIHandle::Cells, dc, region, clip);
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
// Paint over the part below the tracks
|
|
|
|
trackRect.y += trackRect.height;
|
|
|
|
if (trackRect.y < clip.GetBottom()) {
|
|
|
|
AColor::TrackPanelBackground(dc, false);
|
|
|
|
dc->DrawRectangle(trackRect.x,
|
|
|
|
trackRect.y,
|
|
|
|
trackRect.width,
|
|
|
|
clip.height - trackRect.y);
|
|
|
|
}
|
|
|
|
|
2011-10-19 23:06:53 +00:00
|
|
|
// Sometimes highlight is not drawn on backing bitmap. I thought
|
2014-06-03 20:30:19 +00:00
|
|
|
// it was because FindFocus did not return "this" on Mac, but
|
2011-10-19 23:06:53 +00:00
|
|
|
// when I removed that test, yielding this condition:
|
|
|
|
// if (GetFocusedTrack() != NULL) {
|
|
|
|
// the highlight was reportedly drawn even when something else
|
|
|
|
// was the focus and no highlight should be drawn. -RBD
|
|
|
|
if (GetFocusedTrack() != NULL && wxWindow::FindFocus() == this) {
|
2010-01-23 19:44:49 +00:00
|
|
|
HighlightFocusedTrack(dc, focusRect);
|
|
|
|
}
|
|
|
|
|
2015-07-07 03:12:16 +00:00
|
|
|
if (mUIHandle)
|
|
|
|
mUIHandle->DrawExtras(UIHandle::Panel, dc, region, clip);
|
2015-08-13 23:49:09 +00:00
|
|
|
}
|
2015-04-17 18:09:35 +00:00
|
|
|
|
2015-07-14 17:17:08 +00:00
|
|
|
// Make this #include go away!
|
|
|
|
#include "tracks/ui/TrackControls.h"
|
|
|
|
|
2017-06-12 16:28:20 +00:00
|
|
|
void TrackInfo::DrawItems
|
|
|
|
( wxDC *dc, const wxRect &rect, const Track &track,
|
|
|
|
int mouseCapture, bool captured )
|
|
|
|
{
|
|
|
|
const auto topLines = getTCPLines( track );
|
|
|
|
const auto bottomLines = commonTrackTCPBottomLines;
|
|
|
|
DrawItems
|
|
|
|
( dc, rect, &track, topLines, bottomLines, mouseCapture, captured );
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackInfo::DrawItems
|
|
|
|
( wxDC *dc, const wxRect &rect, const Track *pTrack,
|
2017-06-13 17:34:12 +00:00
|
|
|
const std::vector<TCPLine> &topLines, const std::vector<TCPLine> &bottomLines,
|
2017-06-12 16:28:20 +00:00
|
|
|
int mouseCapture, bool captured )
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2017-06-12 16:28:20 +00:00
|
|
|
TrackInfo::SetTrackInfoFont(dc);
|
2010-01-23 19:44:49 +00:00
|
|
|
dc->SetTextForeground(theTheme.Colour(clrTrackPanelText));
|
2017-06-12 16:28:20 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
int yy = 0;
|
2017-06-13 17:34:12 +00:00
|
|
|
for ( const auto &line : topLines ) {
|
2017-06-12 16:28:20 +00:00
|
|
|
wxRect itemRect{
|
|
|
|
rect.x, rect.y + yy,
|
2017-06-13 17:34:12 +00:00
|
|
|
rect.width, line.height
|
2017-06-12 16:28:20 +00:00
|
|
|
};
|
|
|
|
if ( !TrackInfo::HideTopItem( rect, itemRect ) &&
|
2017-06-13 17:34:12 +00:00
|
|
|
line.drawFunction )
|
|
|
|
line.drawFunction( dc, itemRect, pTrack, mouseCapture, captured );
|
|
|
|
yy += line.height + line.extraSpace;
|
2017-06-12 16:28:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
int yy = rect.height;
|
2017-06-13 17:34:12 +00:00
|
|
|
for ( const auto &line : bottomLines ) {
|
|
|
|
yy -= line.height + line.extraSpace;
|
|
|
|
if ( line.drawFunction ) {
|
2017-06-12 16:28:20 +00:00
|
|
|
wxRect itemRect{
|
|
|
|
rect.x, rect.y + yy,
|
2017-06-13 17:34:12 +00:00
|
|
|
rect.width, line.height
|
2017-06-12 16:28:20 +00:00
|
|
|
};
|
2017-06-13 17:34:12 +00:00
|
|
|
line.drawFunction( dc, itemRect, pTrack, mouseCapture, captured );
|
2017-06-12 16:28:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-12 17:23:00 +00:00
|
|
|
void TrackInfo::CloseTitleDrawFunction
|
|
|
|
( wxDC *dc, const wxRect &rect, const Track *pTrack, int pressed, bool captured )
|
|
|
|
{
|
|
|
|
bool selected = pTrack ? pTrack->GetSelected() : true;
|
|
|
|
{
|
|
|
|
bool down = captured && (pressed == TrackPanel::IsClosing);
|
|
|
|
wxRect bev = rect;
|
|
|
|
GetCloseBoxHorizontalBounds( rect, bev );
|
|
|
|
AColor::Bevel2(*dc, !down, bev, selected );
|
|
|
|
|
|
|
|
#ifdef EXPERIMENTAL_THEMING
|
|
|
|
wxPen pen( theTheme.Colour( clrTrackPanelText ));
|
|
|
|
dc->SetPen( pen );
|
|
|
|
#else
|
|
|
|
dc->SetPen(*wxBLACK_PEN);
|
|
|
|
#endif
|
|
|
|
bev.Inflate( -1, -1 );
|
|
|
|
// Draw the "X"
|
|
|
|
const int s = 6;
|
|
|
|
|
|
|
|
int ls = bev.x + ((bev.width - s) / 2);
|
|
|
|
int ts = bev.y + ((bev.height - s) / 2);
|
|
|
|
int rs = ls + s;
|
|
|
|
int bs = ts + s;
|
|
|
|
|
|
|
|
AColor::Line(*dc, ls, ts, rs, bs);
|
|
|
|
AColor::Line(*dc, ls + 1, ts, rs + 1, bs);
|
|
|
|
AColor::Line(*dc, rs, ts, ls, bs);
|
|
|
|
AColor::Line(*dc, rs + 1, ts, ls + 1, bs);
|
|
|
|
|
|
|
|
// bev.Inflate(-1, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
wxString titleStr =
|
|
|
|
pTrack ? pTrack->GetName() : _("Name");
|
|
|
|
|
|
|
|
bool down = captured && (pressed == TrackPanel::IsPopping);
|
|
|
|
wxRect bev = rect;
|
|
|
|
GetTitleBarHorizontalBounds( rect, bev );
|
|
|
|
//bev.Inflate(-1, -1);
|
|
|
|
AColor::Bevel2(*dc, !down, bev, selected);
|
|
|
|
|
|
|
|
// Draw title text
|
|
|
|
SetTrackInfoFont(dc);
|
2017-06-18 11:42:12 +00:00
|
|
|
|
|
|
|
// Bug 1660 The 'k' of 'Audio Track' was being truncated.
|
|
|
|
// Constant of 32 found by counting pixels on a windows machine.
|
|
|
|
// I believe it's the size of the X close button + the size of the
|
|
|
|
// drop down arrow.
|
|
|
|
int allowableWidth = rect.width - 32;
|
2017-06-12 17:23:00 +00:00
|
|
|
|
|
|
|
wxCoord textWidth, textHeight;
|
|
|
|
dc->GetTextExtent(titleStr, &textWidth, &textHeight);
|
|
|
|
while (textWidth > allowableWidth) {
|
|
|
|
titleStr = titleStr.Left(titleStr.Length() - 1);
|
|
|
|
dc->GetTextExtent(titleStr, &textWidth, &textHeight);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pop-up triangle
|
|
|
|
#ifdef EXPERIMENTAL_THEMING
|
|
|
|
wxColour c = theTheme.Colour( clrTrackPanelText );
|
|
|
|
#else
|
|
|
|
wxColour c = *wxBLACK;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// wxGTK leaves little scraps (antialiasing?) of the
|
|
|
|
// characters if they are repeatedly drawn. This
|
|
|
|
// happens when holding down mouse button and moving
|
|
|
|
// in and out of the title bar. So clear it first.
|
|
|
|
// AColor::MediumTrackInfo(dc, t->GetSelected());
|
|
|
|
// dc->DrawRectangle(bev);
|
|
|
|
|
|
|
|
dc->SetTextForeground( c );
|
|
|
|
dc->SetTextBackground( wxTRANSPARENT );
|
|
|
|
dc->DrawText(titleStr, bev.x + 2, bev.y + (bev.height - textHeight) / 2);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dc->SetPen(c);
|
|
|
|
dc->SetBrush(c);
|
|
|
|
|
|
|
|
int s = 10; // Width of dropdown arrow...height is half of width
|
|
|
|
AColor::Arrow(*dc,
|
|
|
|
bev.GetRight() - s - 3, // 3 to offset from right border
|
|
|
|
bev.y + ((bev.height - (s / 2)) / 2),
|
|
|
|
s);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-12 19:17:26 +00:00
|
|
|
void TrackInfo::MinimizeSyncLockDrawFunction
|
|
|
|
( wxDC *dc, const wxRect &rect, const Track *pTrack, int pressed, bool captured )
|
|
|
|
{
|
|
|
|
bool selected = pTrack ? pTrack->GetSelected() : true;
|
|
|
|
bool syncLockSelected = pTrack ? pTrack->IsSyncLockSelected() : true;
|
|
|
|
bool minimized = pTrack ? pTrack->GetMinimized() : false;
|
|
|
|
{
|
|
|
|
bool down = captured && (pressed == TrackPanel::IsMinimizing);
|
|
|
|
wxRect bev = rect;
|
|
|
|
GetMinimizeHorizontalBounds(rect, bev);
|
|
|
|
|
|
|
|
// Clear background to get rid of previous arrow
|
|
|
|
//AColor::MediumTrackInfo(dc, t->GetSelected());
|
|
|
|
//dc->DrawRectangle(bev);
|
|
|
|
|
|
|
|
AColor::Bevel2(*dc, !down, bev, selected);
|
|
|
|
|
|
|
|
#ifdef EXPERIMENTAL_THEMING
|
|
|
|
wxColour c = theTheme.Colour(clrTrackPanelText);
|
|
|
|
dc->SetBrush(c);
|
|
|
|
dc->SetPen(c);
|
|
|
|
#else
|
|
|
|
AColor::Dark(dc, selected);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
AColor::Arrow(*dc,
|
|
|
|
bev.x - 5 + bev.width / 2,
|
|
|
|
bev.y - 2 + bev.height / 2,
|
|
|
|
10,
|
|
|
|
minimized);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw the sync-lock indicator if this track is in a sync-lock selected group.
|
|
|
|
if (syncLockSelected)
|
|
|
|
{
|
|
|
|
wxRect syncLockIconRect = rect;
|
|
|
|
|
|
|
|
GetSyncLockHorizontalBounds( rect, syncLockIconRect );
|
|
|
|
wxBitmap syncLockBitmap(theTheme.Image(bmpSyncLockIcon));
|
|
|
|
// Icon is 12x12 and syncLockIconRect is 16x16.
|
|
|
|
dc->DrawBitmap(syncLockBitmap,
|
|
|
|
syncLockIconRect.x + 3,
|
|
|
|
syncLockIconRect.y + 2,
|
|
|
|
true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-12 19:51:06 +00:00
|
|
|
void TrackInfo::MidiControlsDrawFunction
|
|
|
|
( wxDC *dc, const wxRect &rect, const Track *pTrack, int, bool )
|
|
|
|
{
|
|
|
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
|
|
|
wxRect midiRect = rect;
|
|
|
|
GetMidiControlsHorizontalBounds(rect, midiRect);
|
|
|
|
NoteTrack::DrawLabelControls
|
|
|
|
( static_cast<const NoteTrack *>(pTrack), *dc, midiRect );
|
|
|
|
#endif // EXPERIMENTAL_MIDI_OUT
|
|
|
|
}
|
|
|
|
|
2017-06-12 20:10:03 +00:00
|
|
|
template<typename TrackClass>
|
|
|
|
void TrackInfo::SliderDrawFunction
|
|
|
|
( LWSlider *(*Selector)
|
|
|
|
(const wxRect &sliderRect, const TrackClass *t, bool captured, wxWindow*),
|
|
|
|
wxDC *dc, const wxRect &rect, const Track *pTrack, bool captured )
|
|
|
|
{
|
|
|
|
wxRect sliderRect = rect;
|
|
|
|
TrackInfo::GetSliderHorizontalBounds( rect.GetTopLeft(), sliderRect );
|
|
|
|
auto wt = static_cast<const TrackClass*>( pTrack );
|
|
|
|
Selector( sliderRect, wt, captured, nullptr )->OnPaint(*dc);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackInfo::PanSliderDrawFunction
|
|
|
|
( wxDC *dc, const wxRect &rect, const Track *pTrack, int, bool captured )
|
|
|
|
{
|
|
|
|
SliderDrawFunction<WaveTrack>
|
|
|
|
( &TrackInfo::PanSlider, dc, rect, pTrack, captured);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackInfo::GainSliderDrawFunction
|
|
|
|
( wxDC *dc, const wxRect &rect, const Track *pTrack, int, bool captured )
|
|
|
|
{
|
|
|
|
SliderDrawFunction<WaveTrack>
|
|
|
|
( &TrackInfo::GainSlider, dc, rect, pTrack, captured);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
|
|
|
void TrackInfo::VelocitySliderDrawFunction
|
|
|
|
( wxDC *dc, const wxRect &rect, const Track *pTrack, int, bool captured )
|
|
|
|
{
|
|
|
|
SliderDrawFunction<NoteTrack>
|
|
|
|
( &TrackInfo::VelocitySlider, dc, rect, pTrack, captured);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-06-13 12:02:44 +00:00
|
|
|
void TrackInfo::MuteOrSoloDrawFunction
|
|
|
|
( wxDC *dc, const wxRect &bev, const Track *pTrack, int pressed, bool captured,
|
|
|
|
bool solo )
|
|
|
|
{
|
|
|
|
bool down = captured &&
|
|
|
|
(pressed == ( solo ? TrackPanel::IsSoloing : TrackPanel::IsMuting ));
|
|
|
|
//bev.Inflate(-1, -1);
|
|
|
|
bool selected = pTrack ? pTrack->GetSelected() : true;
|
|
|
|
auto pt = dynamic_cast<const PlayableTrack *>(pTrack);
|
|
|
|
bool value = pt ? (solo ? pt->GetSolo() : pt->GetMute()) : false;
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
AColor::MediumTrackInfo( dc, t->GetSelected());
|
|
|
|
if( solo )
|
|
|
|
{
|
|
|
|
if( pt && pt->GetSolo() )
|
|
|
|
{
|
|
|
|
AColor::Solo(dc, pt->GetSolo(), t->GetSelected());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( pt && pt->GetMute() )
|
|
|
|
{
|
|
|
|
AColor::Mute(dc, pt->GetMute(), t->GetSelected(), pt->GetSolo());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//(solo) ? AColor::Solo(dc, t->GetSolo(), t->GetSelected()) :
|
|
|
|
// AColor::Mute(dc, t->GetMute(), t->GetSelected(), t->GetSolo());
|
|
|
|
dc->SetPen( *wxTRANSPARENT_PEN );//No border!
|
|
|
|
dc->DrawRectangle(bev);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
wxCoord textWidth, textHeight;
|
|
|
|
wxString str = (solo) ?
|
|
|
|
/* i18n-hint: This is on a button that will silence all the other tracks.*/
|
|
|
|
_("Solo") :
|
|
|
|
/* i18n-hint: This is on a button that will silence this track.*/
|
|
|
|
_("Mute");
|
|
|
|
|
|
|
|
AColor::Bevel2(
|
|
|
|
*dc,
|
|
|
|
value == down,
|
|
|
|
bev,
|
|
|
|
selected
|
|
|
|
);
|
|
|
|
|
|
|
|
SetTrackInfoFont(dc);
|
|
|
|
dc->GetTextExtent(str, &textWidth, &textHeight);
|
|
|
|
dc->DrawText(str, bev.x + (bev.width - textWidth) / 2, bev.y + (bev.height - textHeight) / 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackInfo::WideMuteDrawFunction
|
|
|
|
( wxDC *dc, const wxRect &rect, const Track *pTrack, int pressed, bool captured )
|
|
|
|
{
|
|
|
|
wxRect bev = rect;
|
|
|
|
GetWideMuteSoloHorizontalBounds( rect, bev );
|
|
|
|
MuteOrSoloDrawFunction( dc, bev, pTrack, pressed, captured, false );
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackInfo::WideSoloDrawFunction
|
|
|
|
( wxDC *dc, const wxRect &rect, const Track *pTrack, int pressed, bool captured )
|
|
|
|
{
|
|
|
|
wxRect bev = rect;
|
|
|
|
GetWideMuteSoloHorizontalBounds( rect, bev );
|
|
|
|
MuteOrSoloDrawFunction( dc, bev, pTrack, pressed, captured, true );
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackInfo::MuteAndSoloDrawFunction
|
|
|
|
( wxDC *dc, const wxRect &rect, const Track *pTrack, int pressed, bool captured )
|
|
|
|
{
|
|
|
|
bool bHasSoloButton = TrackPanel::HasSoloButton();
|
|
|
|
|
|
|
|
wxRect bev = rect;
|
|
|
|
if ( bHasSoloButton )
|
|
|
|
GetNarrowMuteHorizontalBounds( rect, bev );
|
|
|
|
else
|
|
|
|
GetWideMuteSoloHorizontalBounds( rect, bev );
|
|
|
|
MuteOrSoloDrawFunction( dc, bev, pTrack, pressed, captured, false );
|
|
|
|
|
|
|
|
if( !bHasSoloButton )
|
|
|
|
return;
|
|
|
|
|
|
|
|
GetNarrowSoloHorizontalBounds( rect, bev );
|
|
|
|
MuteOrSoloDrawFunction( dc, bev, pTrack, pressed, captured, true );
|
|
|
|
}
|
|
|
|
|
2017-06-13 13:14:58 +00:00
|
|
|
void TrackInfo::StatusDrawFunction
|
|
|
|
( const wxString &string, wxDC *dc, const wxRect &rect )
|
|
|
|
{
|
|
|
|
static const int offset = 3;
|
|
|
|
dc->DrawText(string, rect.x + offset, rect.y);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackInfo::Status1DrawFunction
|
|
|
|
( wxDC *dc, const wxRect &rect, const Track *pTrack, int, bool )
|
|
|
|
{
|
|
|
|
auto wt = static_cast<const WaveTrack*>(pTrack);
|
|
|
|
|
|
|
|
/// Returns the string to be displayed in the track label
|
|
|
|
/// indicating whether the track is mono, left, right, or
|
|
|
|
/// stereo and what sample rate it's using.
|
|
|
|
auto rate = wt ? wt->GetRate() : 44100.0;
|
|
|
|
wxString s = wxString::Format(wxT("%dHz"), (int) (rate + 0.5));
|
|
|
|
if (!wt || (wt->GetLinked()
|
|
|
|
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
|
|
|
|
&& wt->GetChannel() != Track::MonoChannel
|
|
|
|
#endif
|
|
|
|
))
|
|
|
|
s = _("Stereo, ") + s;
|
|
|
|
else {
|
|
|
|
if (wt->GetChannel() == Track::MonoChannel)
|
|
|
|
s = _("Mono, ") + s;
|
|
|
|
else if (wt->GetChannel() == Track::LeftChannel)
|
|
|
|
s = _("Left, ") + s;
|
|
|
|
else if (wt->GetChannel() == Track::RightChannel)
|
|
|
|
s = _("Right, ") + s;
|
|
|
|
}
|
|
|
|
|
|
|
|
StatusDrawFunction( s, dc, rect );
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackInfo::Status2DrawFunction
|
|
|
|
( wxDC *dc, const wxRect &rect, const Track *pTrack, int, bool )
|
|
|
|
{
|
|
|
|
auto wt = static_cast<const WaveTrack*>(pTrack);
|
|
|
|
auto format = wt ? wt->GetSampleFormat() : floatSample;
|
|
|
|
auto s = GetSampleFormatStr(format);
|
|
|
|
StatusDrawFunction( s, dc, rect );
|
|
|
|
}
|
|
|
|
|
2017-06-12 16:28:20 +00:00
|
|
|
void TrackPanel::DrawOutside(Track * t, wxDC * dc, const wxRect & rec)
|
|
|
|
{
|
2010-01-23 19:44:49 +00:00
|
|
|
bool bIsWave = (t->GetKind() == Track::Wave);
|
|
|
|
|
2017-06-06 15:24:03 +00:00
|
|
|
// Draw things that extend right of track control panel
|
|
|
|
{
|
|
|
|
// Start with whole track rect
|
|
|
|
wxRect rect = rec;
|
|
|
|
DrawOutsideOfTrack(t, dc, rect);
|
|
|
|
|
|
|
|
// Now exclude left, right, and top insets
|
|
|
|
rect.x += kLeftInset;
|
|
|
|
rect.y += kTopInset;
|
|
|
|
rect.width -= kLeftInset * 2;
|
|
|
|
rect.height -= kTopInset;
|
|
|
|
|
|
|
|
int labelw = GetLabelWidth();
|
|
|
|
int vrul = GetVRulerOffset();
|
|
|
|
mTrackInfo.DrawBackground(dc, rect, t->GetSelected(), bIsWave, labelw, vrul);
|
|
|
|
|
|
|
|
// Vaughan, 2010-08-24: No longer doing this.
|
|
|
|
// Draw sync-lock tiles in ruler area.
|
|
|
|
//if (t->IsSyncLockSelected()) {
|
|
|
|
// wxRect tileFill = rect;
|
|
|
|
// tileFill.x = GetVRulerOffset();
|
|
|
|
// tileFill.width = GetVRulerWidth();
|
|
|
|
// TrackArtist::DrawSyncLockTiles(dc, tileFill);
|
|
|
|
//}
|
|
|
|
|
|
|
|
DrawBordersAroundTrack(t, dc, rect, labelw, vrul);
|
|
|
|
DrawShadow(t, dc, rect);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw things within the track control panel
|
|
|
|
wxRect rect = rec;
|
2017-06-06 15:43:58 +00:00
|
|
|
rect.x += kLeftMargin;
|
2017-06-06 18:18:27 +00:00
|
|
|
rect.width = kTrackInfoWidth - kLeftMargin;
|
2017-06-06 15:43:58 +00:00
|
|
|
rect.y += kTopMargin;
|
|
|
|
rect.height -= (kBottomMargin + kTopMargin);
|
2017-06-06 15:24:03 +00:00
|
|
|
|
2015-07-14 17:17:08 +00:00
|
|
|
// Need to know which button, if any, to draw as pressed.
|
|
|
|
const MouseCaptureEnum mouseCapture =
|
|
|
|
// This public global variable is a hack for now, which should go away
|
|
|
|
// when TrackPanelCell gets a virtual function into which we move this
|
|
|
|
// drawing code.
|
2015-09-09 01:15:35 +00:00
|
|
|
MouseCaptureEnum(TrackControls::gCaptureState);
|
|
|
|
const bool captured = (t == mpClickedTrack);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-07-14 17:17:08 +00:00
|
|
|
TrackInfo::DrawItems( dc, rect, *t, mouseCapture, captured );
|
2017-06-12 16:28:20 +00:00
|
|
|
|
2017-06-05 16:47:29 +00:00
|
|
|
//mTrackInfo.DrawBordersWithin( dc, rect, *t );
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2017-06-06 15:24:03 +00:00
|
|
|
// Given rectangle should be the whole track rectangle
|
|
|
|
// Paint the inset areas left, top, and right in a background color
|
|
|
|
// If linked to a following channel, also paint the separator area, which
|
|
|
|
// overlaps the next track rectangle's top
|
2015-08-03 17:49:53 +00:00
|
|
|
void TrackPanel::DrawOutsideOfTrack(Track * t, wxDC * dc, const wxRect & rect)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
|
|
|
// Fill in area outside of the track
|
|
|
|
AColor::TrackPanelBackground(dc, false);
|
|
|
|
wxRect side;
|
2013-10-29 21:49:45 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
// Area between panel border and left track border
|
2015-08-03 17:49:53 +00:00
|
|
|
side = rect;
|
2010-01-23 19:44:49 +00:00
|
|
|
side.width = kLeftInset;
|
|
|
|
dc->DrawRectangle(side);
|
|
|
|
|
|
|
|
// Area between panel border and top track border
|
2015-08-03 17:49:53 +00:00
|
|
|
side = rect;
|
2010-01-23 19:44:49 +00:00
|
|
|
side.height = kTopInset;
|
|
|
|
dc->DrawRectangle(side);
|
|
|
|
|
|
|
|
// Area between panel border and right track border
|
2015-08-03 17:49:53 +00:00
|
|
|
side = rect;
|
2010-01-23 19:44:49 +00:00
|
|
|
side.x += side.width - kTopInset;
|
|
|
|
side.width = kTopInset;
|
|
|
|
dc->DrawRectangle(side);
|
|
|
|
|
|
|
|
// Area between tracks of stereo group
|
2017-01-11 19:20:24 +00:00
|
|
|
if (t->GetLinked()
|
2013-05-30 23:14:25 +00:00
|
|
|
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
|
2017-01-11 19:20:24 +00:00
|
|
|
|| MONO_WAVE_PAN(t)
|
|
|
|
#endif
|
|
|
|
) {
|
2015-08-21 00:57:24 +00:00
|
|
|
// Paint the channel separator over (what would be) the shadow of the top
|
|
|
|
// channel, and the top inset of the bottom channel
|
2015-08-03 17:49:53 +00:00
|
|
|
side = rect;
|
2015-08-21 00:57:24 +00:00
|
|
|
side.y += t->GetHeight() - kShadowThickness;
|
|
|
|
side.height = kTopInset + kShadowThickness;
|
2010-01-23 19:44:49 +00:00
|
|
|
dc->DrawRectangle(side);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-07 03:12:16 +00:00
|
|
|
void TrackPanel::SetBackgroundCell
|
|
|
|
(const std::shared_ptr< TrackPanelCell > &pCell)
|
|
|
|
{
|
|
|
|
mpBackground = pCell;
|
|
|
|
}
|
|
|
|
|
2017-06-17 05:26:36 +00:00
|
|
|
std::shared_ptr< TrackPanelCell > TrackPanel::GetBackgroundCell()
|
|
|
|
{
|
|
|
|
return mpBackground;
|
|
|
|
}
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
/// Draw a three-level highlight gradient around the focused track.
|
2015-08-03 17:49:53 +00:00
|
|
|
void TrackPanel::HighlightFocusedTrack(wxDC * dc, const wxRect & rect)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2015-08-03 17:49:53 +00:00
|
|
|
wxRect theRect = rect;
|
|
|
|
theRect.x += kLeftInset;
|
|
|
|
theRect.y += kTopInset;
|
|
|
|
theRect.width -= kLeftInset * 2;
|
|
|
|
theRect.height -= kTopInset;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
dc->SetBrush(*wxTRANSPARENT_BRUSH);
|
|
|
|
|
|
|
|
AColor::TrackFocusPen(dc, 0);
|
2015-08-03 17:49:53 +00:00
|
|
|
dc->DrawRectangle(theRect.x - 1, theRect.y - 1, theRect.width + 2, theRect.height + 2);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
AColor::TrackFocusPen(dc, 1);
|
2015-08-03 17:49:53 +00:00
|
|
|
dc->DrawRectangle(theRect.x - 2, theRect.y - 2, theRect.width + 4, theRect.height + 4);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
AColor::TrackFocusPen(dc, 2);
|
2015-08-03 17:49:53 +00:00
|
|
|
dc->DrawRectangle(theRect.x - 3, theRect.y - 3, theRect.width + 6, theRect.height + 6);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TrackPanel::UpdateVRulers()
|
|
|
|
{
|
2016-08-20 17:58:56 +00:00
|
|
|
TrackListOfKindIterator iter(Track::Wave, GetTracks());
|
2010-01-23 19:44:49 +00:00
|
|
|
for (Track *t = iter.First(); t; t = iter.Next()) {
|
|
|
|
UpdateTrackVRuler(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateVRulerSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackPanel::UpdateVRuler(Track *t)
|
|
|
|
{
|
2017-06-25 05:33:31 +00:00
|
|
|
if (t)
|
|
|
|
UpdateTrackVRuler(t);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
UpdateVRulerSize();
|
|
|
|
}
|
|
|
|
|
2017-02-22 19:23:35 +00:00
|
|
|
void TrackPanel::UpdateTrackVRuler(const Track *t)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2011-11-10 20:17:44 +00:00
|
|
|
wxASSERT(t);
|
2011-11-20 07:09:46 +00:00
|
|
|
if (!t)
|
|
|
|
return;
|
|
|
|
|
2015-08-03 17:49:53 +00:00
|
|
|
wxRect rect(GetVRulerOffset(),
|
2015-08-21 15:04:30 +00:00
|
|
|
kTopMargin,
|
2010-01-23 19:44:49 +00:00
|
|
|
GetVRulerWidth(),
|
2015-08-21 15:04:30 +00:00
|
|
|
t->GetHeight() - (kTopMargin + kBottomMargin));
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-08-03 17:49:53 +00:00
|
|
|
mTrackArtist->UpdateVRuler(t, rect);
|
2017-02-22 19:23:35 +00:00
|
|
|
const Track *l = t->GetLink();
|
2014-06-03 20:30:19 +00:00
|
|
|
if (l)
|
2011-11-20 07:09:46 +00:00
|
|
|
{
|
2015-08-21 15:04:30 +00:00
|
|
|
rect.height = l->GetHeight() - (kTopMargin + kBottomMargin);
|
2015-08-03 17:49:53 +00:00
|
|
|
mTrackArtist->UpdateVRuler(l, rect);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
2013-05-30 23:14:25 +00:00
|
|
|
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
|
|
|
|
else if(MONO_WAVE_PAN(t)){
|
2015-08-21 15:04:30 +00:00
|
|
|
rect.height = t->GetHeight(true) - (kTopMargin + kBottomMargin);
|
2015-08-03 17:49:53 +00:00
|
|
|
mTrackArtist->UpdateVRuler(t, rect);
|
2013-05-30 23:14:25 +00:00
|
|
|
}
|
|
|
|
#endif
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TrackPanel::UpdateVRulerSize()
|
|
|
|
{
|
2016-08-20 17:58:56 +00:00
|
|
|
TrackListIterator iter(GetTracks());
|
2010-01-23 19:44:49 +00:00
|
|
|
Track *t = iter.First();
|
|
|
|
if (t) {
|
|
|
|
wxSize s = t->vrulerSize;
|
|
|
|
while (t) {
|
|
|
|
s.IncTo(t->vrulerSize);
|
|
|
|
t = iter.Next();
|
|
|
|
}
|
2016-09-19 18:41:49 +00:00
|
|
|
if (vrulerSize != s) {
|
|
|
|
vrulerSize = s;
|
2010-01-23 19:44:49 +00:00
|
|
|
mRuler->SetLeftOffset(GetLeftOffset()); // bevel on AdornedRuler
|
|
|
|
mRuler->Refresh();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Refresh(false);
|
|
|
|
}
|
|
|
|
|
2013-10-29 21:49:45 +00:00
|
|
|
// Make sure selection edge is in view
|
2010-01-23 19:44:49 +00:00
|
|
|
void TrackPanel::ScrollIntoView(double pos)
|
|
|
|
{
|
2015-08-21 16:02:48 +00:00
|
|
|
int w;
|
|
|
|
GetTracksUsableArea( &w, NULL );
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-04-19 23:26:36 +00:00
|
|
|
int pixel = mViewInfo->TimeToPosition(pos);
|
2015-08-21 17:22:44 +00:00
|
|
|
if (pixel < 0 || pixel >= w)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2015-04-19 21:12:06 +00:00
|
|
|
mListener->TP_ScrollWindow
|
|
|
|
(mViewInfo->OffsetTimeByPixels(pos, -(w / 2)));
|
2010-01-23 19:44:49 +00:00
|
|
|
Refresh(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackPanel::ScrollIntoView(int x)
|
|
|
|
{
|
2015-04-19 21:12:06 +00:00
|
|
|
ScrollIntoView(mViewInfo->PositionToTime(x, GetLeftOffset()));
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TrackPanel::OnTrackMenu(Track *t)
|
|
|
|
{
|
|
|
|
if(!t) {
|
|
|
|
t = GetFocusedTrack();
|
2015-07-30 18:57:13 +00:00
|
|
|
if(!t)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-06-10 21:02:01 +00:00
|
|
|
TrackPanelCell *const pCell = t->GetTrackControl();
|
|
|
|
const wxRect rect(FindTrackRect(t, true));
|
|
|
|
const UIHandle::Result refreshResult =
|
|
|
|
pCell->DoContextMenu(rect, this, NULL);
|
|
|
|
ProcessUIHandleResult(this, mRuler, t, t, refreshResult);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Track * TrackPanel::GetFirstSelectedTrack()
|
|
|
|
{
|
|
|
|
|
2016-08-20 17:58:56 +00:00
|
|
|
TrackListIterator iter(GetTracks());
|
2013-10-29 21:49:45 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
Track * t;
|
|
|
|
for ( t = iter.First();t!=NULL;t=iter.Next())
|
|
|
|
{
|
|
|
|
//Find the first selected track
|
|
|
|
if(t->GetSelected())
|
|
|
|
{
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
//if nothing is selected, return the first track
|
|
|
|
t = iter.First();
|
|
|
|
|
|
|
|
if(t)
|
|
|
|
return t;
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackPanel::EnsureVisible(Track * t)
|
|
|
|
{
|
2016-08-20 17:58:56 +00:00
|
|
|
TrackListIterator iter(GetTracks());
|
2010-01-23 19:44:49 +00:00
|
|
|
Track *it = NULL;
|
|
|
|
Track *nt = NULL;
|
|
|
|
|
|
|
|
SetFocusedTrack(t);
|
|
|
|
|
|
|
|
int trackTop = 0;
|
|
|
|
int trackHeight =0;
|
|
|
|
|
|
|
|
for (it = iter.First(); it; it = iter.Next()) {
|
|
|
|
trackTop += trackHeight;
|
|
|
|
trackHeight = it->GetHeight();
|
|
|
|
|
|
|
|
//find the second track if this is stereo
|
|
|
|
if (it->GetLinked()) {
|
|
|
|
nt = iter.Next();
|
|
|
|
trackHeight += nt->GetHeight();
|
|
|
|
}
|
2013-05-30 23:14:25 +00:00
|
|
|
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
|
|
|
|
else if(MONO_WAVE_PAN(it)){
|
|
|
|
trackHeight += it->GetHeight(true);
|
|
|
|
}
|
|
|
|
#endif
|
2010-01-23 19:44:49 +00:00
|
|
|
else {
|
|
|
|
nt = it;
|
|
|
|
}
|
|
|
|
|
|
|
|
//We have found the track we want to ensure is visible.
|
|
|
|
if ((it == t) || (nt == t)) {
|
2013-10-29 21:49:45 +00:00
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
//Get the size of the trackpanel.
|
|
|
|
int width, height;
|
|
|
|
GetSize(&width, &height);
|
|
|
|
|
|
|
|
if (trackTop < mViewInfo->vpos) {
|
|
|
|
height = mViewInfo->vpos - trackTop + mViewInfo->scrollStep;
|
|
|
|
height /= mViewInfo->scrollStep;
|
|
|
|
mListener->TP_ScrollUpDown(-height);
|
|
|
|
}
|
|
|
|
else if (trackTop + trackHeight > mViewInfo->vpos + height) {
|
|
|
|
height = (trackTop + trackHeight) - (mViewInfo->vpos + height);
|
|
|
|
height = (height + mViewInfo->scrollStep + 1) / mViewInfo->scrollStep;
|
|
|
|
mListener->TP_ScrollUpDown(height);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Refresh(false);
|
|
|
|
}
|
|
|
|
|
2017-06-06 15:24:03 +00:00
|
|
|
// Given rectangle excludes the insets left, right, and top
|
|
|
|
// Draw a rectangular border and also a vertical separator of track controls
|
|
|
|
// from the rest (ruler and proper track area)
|
2010-01-23 19:44:49 +00:00
|
|
|
void TrackPanel::DrawBordersAroundTrack(Track * t, wxDC * dc,
|
2017-06-06 15:38:11 +00:00
|
|
|
const wxRect & rect, const int labelw,
|
|
|
|
const int vrul)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
|
|
|
// Border around track and label area
|
2017-06-06 15:24:03 +00:00
|
|
|
// leaving room for the shadow
|
2010-01-23 19:44:49 +00:00
|
|
|
dc->SetBrush(*wxTRANSPARENT_BRUSH);
|
|
|
|
dc->SetPen(*wxBLACK_PEN);
|
2017-06-06 15:24:03 +00:00
|
|
|
dc->DrawRectangle(rect.x, rect.y,
|
|
|
|
rect.width - kShadowThickness,
|
|
|
|
rect.height - kShadowThickness);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2017-06-06 15:38:11 +00:00
|
|
|
// between vruler and TrackInfo
|
|
|
|
AColor::Line(*dc, vrul, rect.y, vrul, rect.y + rect.height - 1);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
// The lines at bottom of 1st track and top of second track of stereo group
|
|
|
|
// Possibly replace with DrawRectangle to add left border.
|
2017-01-11 19:20:24 +00:00
|
|
|
if (t->GetLinked()
|
2013-05-30 23:14:25 +00:00
|
|
|
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
|
2017-01-11 19:20:24 +00:00
|
|
|
|| MONO_WAVE_PAN(t)
|
|
|
|
#endif
|
|
|
|
) {
|
2015-08-21 00:57:24 +00:00
|
|
|
// The given rect has had the top inset subtracted
|
2015-08-03 17:49:53 +00:00
|
|
|
int h1 = rect.y + t->GetHeight() - kTopInset;
|
2015-08-21 00:57:24 +00:00
|
|
|
// h1 is the top coordinate of the second tracks' rectangle
|
|
|
|
// Draw (part of) the bottom border of the top channel and top border of the bottom
|
2017-06-06 15:24:03 +00:00
|
|
|
// At left it extends between the vertical rulers too
|
|
|
|
// These lines stroke over what is otherwise "border" of each channel
|
2017-06-06 15:38:11 +00:00
|
|
|
AColor::Line(*dc, labelw, h1 - kBottomMargin, rect.x + rect.width - 1, h1 - kBottomMargin);
|
|
|
|
AColor::Line(*dc, labelw, h1 + kTopInset, rect.x + rect.width - 1, h1 + kTopInset);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-06 15:24:03 +00:00
|
|
|
// Given rectangle has insets subtracted left, right, and top
|
|
|
|
// Stroke lines along bottom and right, which are slightly short at
|
|
|
|
// bottom-left and top-right
|
2015-08-03 17:49:53 +00:00
|
|
|
void TrackPanel::DrawShadow(Track * /* t */ , wxDC * dc, const wxRect & rect)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2015-08-03 17:49:53 +00:00
|
|
|
int right = rect.x + rect.width - 1;
|
|
|
|
int bottom = rect.y + rect.height - 1;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2017-06-06 15:24:03 +00:00
|
|
|
// shadow color for lines
|
2010-01-23 19:44:49 +00:00
|
|
|
dc->SetPen(*wxBLACK_PEN);
|
|
|
|
|
|
|
|
// bottom
|
2015-08-03 17:49:53 +00:00
|
|
|
AColor::Line(*dc, rect.x, bottom, right, bottom);
|
2010-01-23 19:44:49 +00:00
|
|
|
// right
|
2015-08-03 17:49:53 +00:00
|
|
|
AColor::Line(*dc, right, rect.y, right, bottom);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2017-06-06 15:24:03 +00:00
|
|
|
// background color erases small parts of those lines
|
2010-01-23 19:44:49 +00:00
|
|
|
AColor::Dark(dc, false);
|
|
|
|
|
2017-06-06 15:24:03 +00:00
|
|
|
// bottom-left
|
2015-08-03 17:49:53 +00:00
|
|
|
AColor::Line(*dc, rect.x, bottom, rect.x + 1, bottom);
|
2017-06-06 15:24:03 +00:00
|
|
|
// top-right
|
2015-08-03 17:49:53 +00:00
|
|
|
AColor::Line(*dc, right, rect.y, right, rect.y + 1);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2017-06-15 02:10:26 +00:00
|
|
|
/// Determines which cell is under the mouse
|
2010-01-23 19:44:49 +00:00
|
|
|
/// @param mouseX - mouse X position.
|
|
|
|
/// @param mouseY - mouse Y position.
|
2017-01-10 20:53:19 +00:00
|
|
|
TrackPanel::FoundCell TrackPanel::FindCell(int mouseX, int mouseY)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2017-06-17 05:26:36 +00:00
|
|
|
auto range = Cells();
|
|
|
|
auto &iter = range.first, &end = range.second;
|
|
|
|
auto prev = iter;
|
|
|
|
while
|
|
|
|
( iter != end &&
|
|
|
|
!(*iter).second.Contains( mouseX, mouseY ) )
|
|
|
|
prev = iter++;
|
|
|
|
if ( iter == end )
|
|
|
|
// Default to the background cell, which is always last in the sequence,
|
|
|
|
// even if it does not contain the point
|
|
|
|
iter = prev;
|
|
|
|
auto found = *iter;
|
|
|
|
return {
|
|
|
|
static_cast<CommonTrackPanelCell*>( found.first )->FindTrack(),
|
|
|
|
found.first,
|
|
|
|
found.second
|
2017-01-10 20:53:19 +00:00
|
|
|
};
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2017-06-17 02:57:03 +00:00
|
|
|
// This finds the rectangle of a given track (including all channels),
|
|
|
|
// either that of the label 'adornment' or the track itself
|
|
|
|
// The given track is assumed to be the first channel
|
2017-06-12 20:54:12 +00:00
|
|
|
wxRect TrackPanel::FindTrackRect( const Track * target, bool label )
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
|
|
|
if (!target) {
|
2017-06-04 14:55:46 +00:00
|
|
|
return { 0, 0, 0, 0 };
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2017-06-04 14:55:46 +00:00
|
|
|
wxRect rect{
|
|
|
|
0,
|
|
|
|
target->GetY() - mViewInfo->vpos,
|
|
|
|
GetSize().GetWidth(),
|
|
|
|
target->GetHeight()
|
|
|
|
};
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
// The check for a null linked track is necessary because there's
|
|
|
|
// a possible race condition between the time the 2 linked tracks
|
2010-09-12 05:11:43 +00:00
|
|
|
// are added and when wxAccessible methods are called. This is
|
2010-01-23 19:44:49 +00:00
|
|
|
// most evident when using Jaws.
|
|
|
|
if (target->GetLinked() && target->GetLink()) {
|
2015-08-03 17:49:53 +00:00
|
|
|
rect.height += target->GetLink()->GetHeight();
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2017-06-04 14:55:46 +00:00
|
|
|
rect.x += kLeftMargin;
|
|
|
|
if (label)
|
|
|
|
rect.width = GetVRulerOffset() - kLeftMargin;
|
|
|
|
else
|
|
|
|
rect.width -= (kLeftMargin + kRightMargin);
|
|
|
|
|
|
|
|
rect.y += kTopMargin;
|
|
|
|
rect.height -= (kTopMargin + kBottomMargin);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2015-08-03 17:49:53 +00:00
|
|
|
return rect;
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2014-06-03 20:30:19 +00:00
|
|
|
int TrackPanel::GetVRulerWidth() const
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
|
|
|
return vrulerSize.x;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Displays the bounds of the selection in the status bar.
|
|
|
|
void TrackPanel::DisplaySelection()
|
|
|
|
{
|
|
|
|
if (!mListener)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// DM: Note that the Selection Bar can actually MODIFY the selection
|
|
|
|
// if snap-to mode is on!!!
|
|
|
|
mListener->TP_DisplaySelection();
|
|
|
|
}
|
|
|
|
|
|
|
|
Track *TrackPanel::GetFocusedTrack()
|
|
|
|
{
|
|
|
|
return mAx->GetFocus();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackPanel::SetFocusedTrack( Track *t )
|
|
|
|
{
|
|
|
|
// Make sure we always have the first linked track of a stereo track
|
|
|
|
if (t && !t->GetLinked() && t->GetLink())
|
|
|
|
t = (WaveTrack*)t->GetLink();
|
|
|
|
|
2016-05-06 22:13:24 +00:00
|
|
|
if (t && AudacityProject::GetKeyboardCaptureHandler()) {
|
2015-07-28 19:35:09 +00:00
|
|
|
AudacityProject::ReleaseKeyboard(this);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2016-05-22 03:19:09 +00:00
|
|
|
if (t) {
|
2015-07-28 19:35:09 +00:00
|
|
|
AudacityProject::CaptureKeyboard(this);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mAx->SetFocus( t );
|
|
|
|
Refresh( false );
|
|
|
|
}
|
|
|
|
|
2013-08-25 21:51:26 +00:00
|
|
|
void TrackPanel::OnSetFocus(wxFocusEvent & WXUNUSED(event))
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
|
|
|
SetFocusedTrack( GetFocusedTrack() );
|
|
|
|
Refresh( false );
|
|
|
|
}
|
|
|
|
|
2013-08-25 21:51:26 +00:00
|
|
|
void TrackPanel::OnKillFocus(wxFocusEvent & WXUNUSED(event))
|
2015-08-18 15:03:56 +00:00
|
|
|
{
|
2015-08-19 07:46:57 +00:00
|
|
|
if (AudacityProject::HasKeyboardCapture(this))
|
|
|
|
{
|
|
|
|
AudacityProject::ReleaseKeyboard(this);
|
|
|
|
}
|
2015-08-18 15:03:56 +00:00
|
|
|
Refresh( false);
|
|
|
|
}
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
/**********************************************************************
|
|
|
|
|
|
|
|
TrackInfo code is destined to move out of this file.
|
2014-06-03 20:30:19 +00:00
|
|
|
Code should become a lot cleaner when we have sizers.
|
2010-01-23 19:44:49 +00:00
|
|
|
|
|
|
|
**********************************************************************/
|
|
|
|
|
Fix for bug #949
This removes the TrackInfo's slider "cache".
Originally, the cache would build to the maximum number of tracks you
had created in an Audacity session. So, if you created 128 tracks
and then reduced that to 1, you'd still have 256 sliders, 1 gain and
1 pan per track.
But, the only real thing the cache did was prevent continuous allocations
of sliders since the allocated sliders position and values wer still
being updated nearly with ever interaction since they were redrawn each
time.
In April 2010, the slider cache was changed to reduce its size by
creating a sort of ring buffer based on how many tracks were displayed
and how many tracks were in the project (I guess). Unfortunately, it
didn't really handle large number of tracks and this bug was born.
While trying to find the proper fix for this, I realized that the
cache really wasn't saving anything. Maybe a little when dragging
the thumb, but during normal track redraws and interaction, it really
didn't serve a purpose, other than use additional memory.
So, I've removed the cache and have allocated a single gain and a
single pan slider. As before, their position and value are changed
as needed when drawn and manipulated.
2015-05-25 03:54:10 +00:00
|
|
|
TrackInfo::TrackInfo(TrackPanel * pParentIn)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
|
|
|
pParent = pParentIn;
|
2010-04-21 05:03:24 +00:00
|
|
|
|
2017-04-05 19:16:12 +00:00
|
|
|
ReCreateSliders();
|
|
|
|
|
|
|
|
UpdatePrefs();
|
|
|
|
}
|
|
|
|
|
|
|
|
TrackInfo::~TrackInfo()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackInfo::ReCreateSliders(){
|
2017-06-06 15:24:03 +00:00
|
|
|
const wxPoint point{ 0, 0 };
|
Fix for bug #949
This removes the TrackInfo's slider "cache".
Originally, the cache would build to the maximum number of tracks you
had created in an Audacity session. So, if you created 128 tracks
and then reduced that to 1, you'd still have 256 sliders, 1 gain and
1 pan per track.
But, the only real thing the cache did was prevent continuous allocations
of sliders since the allocated sliders position and values wer still
being updated nearly with ever interaction since they were redrawn each
time.
In April 2010, the slider cache was changed to reduce its size by
creating a sort of ring buffer based on how many tracks were displayed
and how many tracks were in the project (I guess). Unfortunately, it
didn't really handle large number of tracks and this bug was born.
While trying to find the proper fix for this, I realized that the
cache really wasn't saving anything. Maybe a little when dragging
the thumb, but during normal track redraws and interaction, it really
didn't serve a purpose, other than use additional memory.
So, I've removed the cache and have allocated a single gain and a
single pan slider. As before, their position and value are changed
as needed when drawn and manipulated.
2015-05-25 03:54:10 +00:00
|
|
|
wxRect sliderRect;
|
2017-06-04 12:29:32 +00:00
|
|
|
GetGainRect(point, sliderRect);
|
Fix for bug #949
This removes the TrackInfo's slider "cache".
Originally, the cache would build to the maximum number of tracks you
had created in an Audacity session. So, if you created 128 tracks
and then reduced that to 1, you'd still have 256 sliders, 1 gain and
1 pan per track.
But, the only real thing the cache did was prevent continuous allocations
of sliders since the allocated sliders position and values wer still
being updated nearly with ever interaction since they were redrawn each
time.
In April 2010, the slider cache was changed to reduce its size by
creating a sort of ring buffer based on how many tracks were displayed
and how many tracks were in the project (I guess). Unfortunately, it
didn't really handle large number of tracks and this bug was born.
While trying to find the proper fix for this, I realized that the
cache really wasn't saving anything. Maybe a little when dragging
the thumb, but during normal track redraws and interaction, it really
didn't serve a purpose, other than use additional memory.
So, I've removed the cache and have allocated a single gain and a
single pan slider. As before, their position and value are changed
as needed when drawn and manipulated.
2015-05-25 03:54:10 +00:00
|
|
|
|
2017-04-05 19:16:12 +00:00
|
|
|
float defPos = 1.0;
|
Fix for bug #949
This removes the TrackInfo's slider "cache".
Originally, the cache would build to the maximum number of tracks you
had created in an Audacity session. So, if you created 128 tracks
and then reduced that to 1, you'd still have 256 sliders, 1 gain and
1 pan per track.
But, the only real thing the cache did was prevent continuous allocations
of sliders since the allocated sliders position and values wer still
being updated nearly with ever interaction since they were redrawn each
time.
In April 2010, the slider cache was changed to reduce its size by
creating a sort of ring buffer based on how many tracks were displayed
and how many tracks were in the project (I guess). Unfortunately, it
didn't really handle large number of tracks and this bug was born.
While trying to find the proper fix for this, I realized that the
cache really wasn't saving anything. Maybe a little when dragging
the thumb, but during normal track redraws and interaction, it really
didn't serve a purpose, other than use additional memory.
So, I've removed the cache and have allocated a single gain and a
single pan slider. As before, their position and value are changed
as needed when drawn and manipulated.
2015-05-25 03:54:10 +00:00
|
|
|
/* i18n-hint: Title of the Gain slider, used to adjust the volume */
|
2017-06-12 20:54:12 +00:00
|
|
|
gGain = std::make_unique<LWSlider>(pParent, _("Gain"),
|
Fix for bug #949
This removes the TrackInfo's slider "cache".
Originally, the cache would build to the maximum number of tracks you
had created in an Audacity session. So, if you created 128 tracks
and then reduced that to 1, you'd still have 256 sliders, 1 gain and
1 pan per track.
But, the only real thing the cache did was prevent continuous allocations
of sliders since the allocated sliders position and values wer still
being updated nearly with ever interaction since they were redrawn each
time.
In April 2010, the slider cache was changed to reduce its size by
creating a sort of ring buffer based on how many tracks were displayed
and how many tracks were in the project (I guess). Unfortunately, it
didn't really handle large number of tracks and this bug was born.
While trying to find the proper fix for this, I realized that the
cache really wasn't saving anything. Maybe a little when dragging
the thumb, but during normal track redraws and interaction, it really
didn't serve a purpose, other than use additional memory.
So, I've removed the cache and have allocated a single gain and a
single pan slider. As before, their position and value are changed
as needed when drawn and manipulated.
2015-05-25 03:54:10 +00:00
|
|
|
wxPoint(sliderRect.x, sliderRect.y),
|
|
|
|
wxSize(sliderRect.width, sliderRect.height),
|
|
|
|
DB_SLIDER);
|
2017-06-12 20:54:12 +00:00
|
|
|
gGain->SetDefaultValue(defPos);
|
2017-04-05 19:16:12 +00:00
|
|
|
|
2017-06-12 20:54:12 +00:00
|
|
|
gGainCaptured = std::make_unique<LWSlider>(pParent, _("Gain"),
|
2015-09-08 16:24:59 +00:00
|
|
|
wxPoint(sliderRect.x, sliderRect.y),
|
|
|
|
wxSize(sliderRect.width, sliderRect.height),
|
|
|
|
DB_SLIDER);
|
2017-06-12 20:54:12 +00:00
|
|
|
gGainCaptured->SetDefaultValue(defPos);
|
Fix for bug #949
This removes the TrackInfo's slider "cache".
Originally, the cache would build to the maximum number of tracks you
had created in an Audacity session. So, if you created 128 tracks
and then reduced that to 1, you'd still have 256 sliders, 1 gain and
1 pan per track.
But, the only real thing the cache did was prevent continuous allocations
of sliders since the allocated sliders position and values wer still
being updated nearly with ever interaction since they were redrawn each
time.
In April 2010, the slider cache was changed to reduce its size by
creating a sort of ring buffer based on how many tracks were displayed
and how many tracks were in the project (I guess). Unfortunately, it
didn't really handle large number of tracks and this bug was born.
While trying to find the proper fix for this, I realized that the
cache really wasn't saving anything. Maybe a little when dragging
the thumb, but during normal track redraws and interaction, it really
didn't serve a purpose, other than use additional memory.
So, I've removed the cache and have allocated a single gain and a
single pan slider. As before, their position and value are changed
as needed when drawn and manipulated.
2015-05-25 03:54:10 +00:00
|
|
|
|
2017-06-04 12:29:32 +00:00
|
|
|
GetPanRect(point, sliderRect);
|
Fix for bug #949
This removes the TrackInfo's slider "cache".
Originally, the cache would build to the maximum number of tracks you
had created in an Audacity session. So, if you created 128 tracks
and then reduced that to 1, you'd still have 256 sliders, 1 gain and
1 pan per track.
But, the only real thing the cache did was prevent continuous allocations
of sliders since the allocated sliders position and values wer still
being updated nearly with ever interaction since they were redrawn each
time.
In April 2010, the slider cache was changed to reduce its size by
creating a sort of ring buffer based on how many tracks were displayed
and how many tracks were in the project (I guess). Unfortunately, it
didn't really handle large number of tracks and this bug was born.
While trying to find the proper fix for this, I realized that the
cache really wasn't saving anything. Maybe a little when dragging
the thumb, but during normal track redraws and interaction, it really
didn't serve a purpose, other than use additional memory.
So, I've removed the cache and have allocated a single gain and a
single pan slider. As before, their position and value are changed
as needed when drawn and manipulated.
2015-05-25 03:54:10 +00:00
|
|
|
|
2017-04-05 19:16:12 +00:00
|
|
|
defPos = 0.0;
|
Fix for bug #949
This removes the TrackInfo's slider "cache".
Originally, the cache would build to the maximum number of tracks you
had created in an Audacity session. So, if you created 128 tracks
and then reduced that to 1, you'd still have 256 sliders, 1 gain and
1 pan per track.
But, the only real thing the cache did was prevent continuous allocations
of sliders since the allocated sliders position and values wer still
being updated nearly with ever interaction since they were redrawn each
time.
In April 2010, the slider cache was changed to reduce its size by
creating a sort of ring buffer based on how many tracks were displayed
and how many tracks were in the project (I guess). Unfortunately, it
didn't really handle large number of tracks and this bug was born.
While trying to find the proper fix for this, I realized that the
cache really wasn't saving anything. Maybe a little when dragging
the thumb, but during normal track redraws and interaction, it really
didn't serve a purpose, other than use additional memory.
So, I've removed the cache and have allocated a single gain and a
single pan slider. As before, their position and value are changed
as needed when drawn and manipulated.
2015-05-25 03:54:10 +00:00
|
|
|
/* i18n-hint: Title of the Pan slider, used to move the sound left or right */
|
2017-06-12 20:54:12 +00:00
|
|
|
gPan = std::make_unique<LWSlider>(pParent, _("Pan"),
|
Fix for bug #949
This removes the TrackInfo's slider "cache".
Originally, the cache would build to the maximum number of tracks you
had created in an Audacity session. So, if you created 128 tracks
and then reduced that to 1, you'd still have 256 sliders, 1 gain and
1 pan per track.
But, the only real thing the cache did was prevent continuous allocations
of sliders since the allocated sliders position and values wer still
being updated nearly with ever interaction since they were redrawn each
time.
In April 2010, the slider cache was changed to reduce its size by
creating a sort of ring buffer based on how many tracks were displayed
and how many tracks were in the project (I guess). Unfortunately, it
didn't really handle large number of tracks and this bug was born.
While trying to find the proper fix for this, I realized that the
cache really wasn't saving anything. Maybe a little when dragging
the thumb, but during normal track redraws and interaction, it really
didn't serve a purpose, other than use additional memory.
So, I've removed the cache and have allocated a single gain and a
single pan slider. As before, their position and value are changed
as needed when drawn and manipulated.
2015-05-25 03:54:10 +00:00
|
|
|
wxPoint(sliderRect.x, sliderRect.y),
|
|
|
|
wxSize(sliderRect.width, sliderRect.height),
|
|
|
|
PAN_SLIDER);
|
2017-06-12 20:54:12 +00:00
|
|
|
gPan->SetDefaultValue(defPos);
|
2017-04-05 19:16:12 +00:00
|
|
|
|
2017-06-12 20:54:12 +00:00
|
|
|
gPanCaptured = std::make_unique<LWSlider>(pParent, _("Pan"),
|
2015-09-08 16:24:59 +00:00
|
|
|
wxPoint(sliderRect.x, sliderRect.y),
|
|
|
|
wxSize(sliderRect.width, sliderRect.height),
|
|
|
|
PAN_SLIDER);
|
2017-06-12 20:54:12 +00:00
|
|
|
gPanCaptured->SetDefaultValue(defPos);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2017-02-22 02:05:35 +00:00
|
|
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
2017-06-04 12:29:32 +00:00
|
|
|
GetVelocityRect(point, sliderRect);
|
2017-02-22 02:05:35 +00:00
|
|
|
|
|
|
|
/* i18n-hint: Title of the Velocity slider, used to adjust the volume of note tracks */
|
2017-06-12 20:54:12 +00:00
|
|
|
gVelocity = std::make_unique<LWSlider>(pParent, _("Velocity"),
|
2017-02-22 02:05:35 +00:00
|
|
|
wxPoint(sliderRect.x, sliderRect.y),
|
|
|
|
wxSize(sliderRect.width, sliderRect.height),
|
|
|
|
VEL_SLIDER);
|
2017-06-12 20:54:12 +00:00
|
|
|
gVelocity->SetDefaultValue(0.0);
|
|
|
|
gVelocityCaptured = std::make_unique<LWSlider>(pParent, _("Velocity"),
|
2017-02-22 02:05:35 +00:00
|
|
|
wxPoint(sliderRect.x, sliderRect.y),
|
|
|
|
wxSize(sliderRect.width, sliderRect.height),
|
|
|
|
VEL_SLIDER);
|
2017-06-12 20:54:12 +00:00
|
|
|
gVelocityCaptured->SetDefaultValue(0.0);
|
2017-02-22 02:05:35 +00:00
|
|
|
#endif
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2014-06-03 20:30:19 +00:00
|
|
|
int TrackInfo::GetTrackInfoWidth() const
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2010-09-16 23:08:33 +00:00
|
|
|
return kTrackInfoWidth;
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2017-06-12 17:39:23 +00:00
|
|
|
void TrackInfo::GetCloseBoxHorizontalBounds( const wxRect & rect, wxRect &dest )
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2017-06-06 15:43:58 +00:00
|
|
|
dest.x = rect.x;
|
2017-06-12 17:39:23 +00:00
|
|
|
dest.width = kTrackInfoBtnSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackInfo::GetCloseBoxRect(const wxRect & rect, wxRect & dest)
|
|
|
|
{
|
|
|
|
GetCloseBoxHorizontalBounds( rect, dest );
|
2017-06-05 00:13:48 +00:00
|
|
|
auto results = CalcItemY( commonTrackTCPLines, kItemBarButtons );
|
|
|
|
dest.y = rect.y + results.first;
|
|
|
|
dest.height = results.second;
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2017-06-12 17:39:23 +00:00
|
|
|
static const int TitleSoloBorderOverlap = 1;
|
|
|
|
|
|
|
|
void TrackInfo::GetTitleBarHorizontalBounds( const wxRect & rect, wxRect &dest )
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2017-06-06 18:18:27 +00:00
|
|
|
// to right of CloseBoxRect, plus a little more
|
2017-06-12 17:39:23 +00:00
|
|
|
wxRect closeRect;
|
|
|
|
GetCloseBoxHorizontalBounds( rect, closeRect );
|
|
|
|
dest.x = rect.x + closeRect.width + 1;
|
|
|
|
dest.width = rect.x + rect.width - dest.x + TitleSoloBorderOverlap;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackInfo::GetTitleBarRect(const wxRect & rect, wxRect & dest)
|
|
|
|
{
|
|
|
|
GetTitleBarHorizontalBounds( rect, dest );
|
2017-06-05 00:13:48 +00:00
|
|
|
auto results = CalcItemY( commonTrackTCPLines, kItemBarButtons );
|
|
|
|
dest.y = rect.y + results.first;
|
|
|
|
dest.height = results.second;
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2017-06-12 17:39:23 +00:00
|
|
|
void TrackInfo::GetNarrowMuteHorizontalBounds( const wxRect & rect, wxRect &dest )
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2017-06-06 15:43:58 +00:00
|
|
|
dest.x = rect.x;
|
2017-06-12 17:39:23 +00:00
|
|
|
dest.width = rect.width / 2 + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackInfo::GetNarrowSoloHorizontalBounds( const wxRect & rect, wxRect &dest )
|
|
|
|
{
|
|
|
|
wxRect muteRect;
|
|
|
|
GetNarrowMuteHorizontalBounds( rect, muteRect );
|
|
|
|
dest.x = rect.x + muteRect.width;
|
|
|
|
dest.width = rect.width - muteRect.width + TitleSoloBorderOverlap;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackInfo::GetWideMuteSoloHorizontalBounds( const wxRect & rect, wxRect &dest )
|
|
|
|
{
|
|
|
|
// Larger button, symmetrically placed intended.
|
|
|
|
// On windows this gives 15 pixels each side.
|
|
|
|
dest.width = rect.width - 2 * kTrackInfoBtnSize + 6;
|
|
|
|
dest.x = rect.x + kTrackInfoBtnSize -3;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackInfo::GetMuteSoloRect
|
|
|
|
(const wxRect & rect, wxRect & dest, bool solo, bool bHasSoloButton,
|
|
|
|
const Track *pTrack)
|
|
|
|
{
|
2017-04-02 22:07:13 +00:00
|
|
|
|
2017-06-05 00:13:48 +00:00
|
|
|
auto resultsM = CalcItemY( getTCPLines( *pTrack ), kItemMute );
|
|
|
|
auto resultsS = CalcItemY( getTCPLines( *pTrack ), kItemSolo );
|
|
|
|
dest.height = resultsS.second;
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2017-06-05 00:13:48 +00:00
|
|
|
int yMute = resultsM.first;
|
|
|
|
int ySolo = resultsS.first;
|
2017-04-02 22:07:13 +00:00
|
|
|
|
|
|
|
bool bSameRow = ( yMute == ySolo );
|
|
|
|
bool bNarrow = bSameRow && bHasSoloButton;
|
|
|
|
|
|
|
|
if( bNarrow )
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2017-06-12 17:39:23 +00:00
|
|
|
if( solo )
|
|
|
|
GetNarrowSoloHorizontalBounds( rect, dest );
|
|
|
|
else
|
|
|
|
GetNarrowMuteHorizontalBounds( rect, dest );
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
2017-04-02 22:07:13 +00:00
|
|
|
else
|
2017-06-12 17:39:23 +00:00
|
|
|
GetWideMuteSoloHorizontalBounds( rect, dest );
|
2017-04-02 22:07:13 +00:00
|
|
|
|
|
|
|
if( bSameRow || !solo )
|
|
|
|
dest.y = rect.y + yMute;
|
|
|
|
else
|
|
|
|
dest.y = rect.y + ySolo;
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2017-06-12 17:39:23 +00:00
|
|
|
void TrackInfo::GetSliderHorizontalBounds( const wxPoint &topleft, wxRect &dest )
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2017-06-06 15:43:58 +00:00
|
|
|
dest.x = topleft.x + 6;
|
2017-06-12 17:39:23 +00:00
|
|
|
dest.width = kTrackInfoSliderWidth;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackInfo::GetGainRect(const wxPoint &topleft, wxRect & dest)
|
|
|
|
{
|
|
|
|
GetSliderHorizontalBounds( topleft, dest );
|
2017-06-05 00:13:48 +00:00
|
|
|
auto results = CalcItemY( waveTrackTCPLines, kItemGain );
|
|
|
|
dest.y = topleft.y + results.first;
|
|
|
|
dest.height = results.second;
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2017-06-12 17:33:07 +00:00
|
|
|
void TrackInfo::GetPanRect(const wxPoint &topleft, wxRect & dest)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2017-06-04 12:29:32 +00:00
|
|
|
GetGainRect( topleft, dest );
|
2017-06-05 00:13:48 +00:00
|
|
|
auto results = CalcItemY( waveTrackTCPLines, kItemPan );
|
|
|
|
dest.y = topleft.y + results.first;
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2017-02-22 02:05:35 +00:00
|
|
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
2017-06-12 17:33:07 +00:00
|
|
|
void TrackInfo::GetVelocityRect(const wxPoint &topleft, wxRect & dest)
|
2017-02-22 02:05:35 +00:00
|
|
|
{
|
2017-06-12 17:39:23 +00:00
|
|
|
GetSliderHorizontalBounds( topleft, dest );
|
2017-06-05 00:47:43 +00:00
|
|
|
auto results = CalcItemY( noteTrackTCPLines, kItemVelocity );
|
|
|
|
dest.y = topleft.y + results.first;
|
|
|
|
dest.height = results.second;
|
2017-02-22 02:05:35 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-06-12 17:39:23 +00:00
|
|
|
void TrackInfo::GetMinimizeHorizontalBounds( const wxRect &rect, wxRect &dest )
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2017-06-10 09:10:38 +00:00
|
|
|
const int space = 0;// was 3.
|
2017-06-06 18:18:27 +00:00
|
|
|
dest.x = rect.x + space;
|
2017-06-12 17:39:23 +00:00
|
|
|
|
|
|
|
wxRect syncLockRect;
|
|
|
|
GetSyncLockHorizontalBounds( rect, syncLockRect );
|
|
|
|
|
|
|
|
// Width is rect.width less space on left for track select
|
|
|
|
// and on right for sync-lock icon.
|
|
|
|
dest.width = rect.width - (space + syncLockRect.width);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackInfo::GetMinimizeRect(const wxRect & rect, wxRect &dest)
|
|
|
|
{
|
|
|
|
GetMinimizeHorizontalBounds( rect, dest );
|
2017-06-05 15:29:21 +00:00
|
|
|
auto results = CalcBottomItemY
|
|
|
|
( commonTrackTCPBottomLines, kItemMinimize, rect.height);
|
|
|
|
dest.y = rect.y + results.first;
|
|
|
|
dest.height = results.second;
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2017-06-12 17:39:23 +00:00
|
|
|
void TrackInfo::GetSyncLockHorizontalBounds( const wxRect &rect, wxRect &dest )
|
|
|
|
{
|
|
|
|
dest.width = kTrackInfoBtnSize;
|
|
|
|
dest.x = rect.x + rect.width - dest.width;
|
|
|
|
}
|
|
|
|
|
2017-06-12 17:33:07 +00:00
|
|
|
void TrackInfo::GetSyncLockIconRect(const wxRect & rect, wxRect &dest)
|
2010-09-16 23:08:33 +00:00
|
|
|
{
|
2017-06-12 17:39:23 +00:00
|
|
|
GetSyncLockHorizontalBounds( rect, dest );
|
2017-06-05 15:29:21 +00:00
|
|
|
auto results = CalcBottomItemY
|
|
|
|
( commonTrackTCPBottomLines, kItemSyncLock, rect.height);
|
|
|
|
dest.y = rect.y + results.first;
|
|
|
|
dest.height = results.second;
|
2010-09-16 23:08:33 +00:00
|
|
|
}
|
|
|
|
|
2017-03-28 18:29:40 +00:00
|
|
|
#ifdef USE_MIDI
|
2017-06-12 17:39:23 +00:00
|
|
|
void TrackInfo::GetMidiControlsHorizontalBounds
|
|
|
|
( const wxRect &rect, wxRect &dest )
|
2017-03-28 18:29:40 +00:00
|
|
|
{
|
2017-06-06 15:43:58 +00:00
|
|
|
dest.x = rect.x + 1; // To center slightly
|
2017-06-06 18:18:27 +00:00
|
|
|
// PRL: TODO: kMidiCellWidth is defined in terms of the other constant
|
|
|
|
// kTrackInfoWidth but I am trying to avoid use of that constant.
|
|
|
|
// Can cell width be computed from dest.width instead?
|
2017-03-28 18:29:40 +00:00
|
|
|
dest.width = kMidiCellWidth * 4;
|
2017-06-12 17:39:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TrackInfo::GetMidiControlsRect(const wxRect & rect, wxRect & dest)
|
|
|
|
{
|
|
|
|
GetMidiControlsHorizontalBounds( rect, dest );
|
2017-06-05 00:47:43 +00:00
|
|
|
auto results = CalcItemY( noteTrackTCPLines, kItemMidiControlsRect );
|
|
|
|
dest.y = rect.y + results.first;
|
|
|
|
dest.height = results.second;
|
2017-03-28 18:29:40 +00:00
|
|
|
}
|
|
|
|
#endif
|
2010-09-16 23:08:33 +00:00
|
|
|
|
2017-06-12 19:02:28 +00:00
|
|
|
wxFont TrackInfo::gFont;
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
/// \todo Probably should move to 'Utils.cpp'.
|
2017-06-12 19:02:28 +00:00
|
|
|
void TrackInfo::SetTrackInfoFont(wxDC * dc)
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2017-06-12 19:02:28 +00:00
|
|
|
dc->SetFont(gFont);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2017-06-05 16:47:29 +00:00
|
|
|
void TrackInfo::DrawBordersWithin
|
|
|
|
( wxDC* dc, const wxRect & rect, const Track &track ) const
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2010-09-16 23:08:33 +00:00
|
|
|
AColor::Dark(dc, false); // same color as border of toolbars (ToolBar::OnPaint())
|
2010-09-14 05:52:01 +00:00
|
|
|
|
2010-09-16 23:08:33 +00:00
|
|
|
// below close box and title bar
|
2017-06-05 16:47:29 +00:00
|
|
|
wxRect buttonRect;
|
|
|
|
GetTitleBarRect( rect, buttonRect );
|
|
|
|
AColor::Line
|
2017-06-06 19:43:42 +00:00
|
|
|
(*dc, rect.x, buttonRect.y + buttonRect.height,
|
|
|
|
rect.width - 1, buttonRect.y + buttonRect.height);
|
2010-09-16 23:08:33 +00:00
|
|
|
|
|
|
|
// between close box and title bar
|
2017-06-05 16:47:29 +00:00
|
|
|
AColor::Line
|
|
|
|
(*dc, buttonRect.x, buttonRect.y,
|
2017-06-06 19:43:42 +00:00
|
|
|
buttonRect.x, buttonRect.y + buttonRect.height - 1);
|
2017-06-05 16:47:29 +00:00
|
|
|
|
|
|
|
GetMuteSoloRect( rect, buttonRect, false, true, &track );
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2017-06-05 16:47:29 +00:00
|
|
|
bool bHasMuteSolo = dynamic_cast<const PlayableTrack*>( &track );
|
2017-06-07 14:39:47 +00:00
|
|
|
if( bHasMuteSolo && !TrackInfo::HideTopItem( rect, buttonRect ) )
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2017-06-05 16:47:29 +00:00
|
|
|
// above mute/solo
|
|
|
|
AColor::Line
|
|
|
|
(*dc, rect.x, buttonRect.y,
|
2017-06-06 19:43:42 +00:00
|
|
|
rect.width - 1, buttonRect.y);
|
2017-06-05 16:47:29 +00:00
|
|
|
|
|
|
|
// between mute/solo
|
|
|
|
// Draw this little line; if there is no solo, wide mute button will
|
|
|
|
// overpaint it later:
|
|
|
|
AColor::Line
|
|
|
|
(*dc, buttonRect.x + buttonRect.width, buttonRect.y,
|
2017-06-06 19:43:42 +00:00
|
|
|
buttonRect.x + buttonRect.width, buttonRect.y + buttonRect.height - 1);
|
2017-06-05 16:47:29 +00:00
|
|
|
|
|
|
|
// below mute/solo
|
|
|
|
AColor::Line
|
|
|
|
(*dc, rect.x, buttonRect.y + buttonRect.height,
|
2017-06-06 19:43:42 +00:00
|
|
|
rect.width - 1, buttonRect.y + buttonRect.height);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2010-09-16 23:08:33 +00:00
|
|
|
// left of and above minimize button
|
|
|
|
wxRect minimizeRect;
|
2015-08-03 17:49:53 +00:00
|
|
|
this->GetMinimizeRect(rect, minimizeRect);
|
2017-06-05 16:47:29 +00:00
|
|
|
AColor::Line
|
|
|
|
(*dc, minimizeRect.x - 1, minimizeRect.y,
|
2017-06-06 19:43:42 +00:00
|
|
|
minimizeRect.x - 1, minimizeRect.y + minimizeRect.height - 1);
|
2017-06-05 16:47:29 +00:00
|
|
|
AColor::Line
|
2017-06-06 19:43:42 +00:00
|
|
|
(*dc, minimizeRect.x, minimizeRect.y - 1,
|
|
|
|
minimizeRect.x + minimizeRect.width - 1, minimizeRect.y - 1);
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2016-08-21 17:17:02 +00:00
|
|
|
//#define USE_BEVELS
|
|
|
|
|
2017-06-06 15:24:03 +00:00
|
|
|
// Paint the whole given rectangle some fill color
|
2016-08-21 17:17:02 +00:00
|
|
|
void TrackInfo::DrawBackground(wxDC * dc, const wxRect & rect, bool bSelected,
|
|
|
|
bool bHasMuteSolo, const int labelw, const int vrul) const
|
2010-01-23 19:44:49 +00:00
|
|
|
{
|
2017-04-26 21:29:57 +00:00
|
|
|
//compiler food.
|
|
|
|
bHasMuteSolo;
|
|
|
|
vrul;
|
|
|
|
|
2010-01-23 19:44:49 +00:00
|
|
|
// fill in label
|
2015-08-03 17:49:53 +00:00
|
|
|
wxRect fill = rect;
|
2010-01-23 19:44:49 +00:00
|
|
|
fill.width = labelw-4;
|
2010-03-09 04:59:26 +00:00
|
|
|
AColor::MediumTrackInfo(dc, bSelected);
|
2014-06-03 20:30:19 +00:00
|
|
|
dc->DrawRectangle(fill);
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2016-08-21 17:17:02 +00:00
|
|
|
#ifdef USE_BEVELS
|
2017-06-06 15:24:03 +00:00
|
|
|
// This branch is not now used
|
|
|
|
// PRL: todo: banish magic numbers
|
2016-08-21 17:17:02 +00:00
|
|
|
if( bHasMuteSolo )
|
|
|
|
{
|
|
|
|
int ylast = rect.height-20;
|
|
|
|
int ybutton = wxMin(32,ylast-17);
|
|
|
|
int ybuttonEnd = 67;
|
|
|
|
|
|
|
|
fill=wxRect( rect.x+1, rect.y+17, vrul-6, ybutton);
|
|
|
|
AColor::BevelTrackInfo( *dc, true, fill );
|
|
|
|
|
|
|
|
if( ybuttonEnd < ylast ){
|
|
|
|
fill=wxRect( rect.x+1, rect.y+ybuttonEnd, fill.width, ylast - ybuttonEnd);
|
|
|
|
AColor::BevelTrackInfo( *dc, true, fill );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fill=wxRect( rect.x+1, rect.y+17, vrul-6, rect.height-37);
|
|
|
|
AColor::BevelTrackInfo( *dc, true, fill );
|
|
|
|
}
|
|
|
|
#endif
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2017-06-12 23:03:09 +00:00
|
|
|
namespace {
|
2017-06-13 17:34:12 +00:00
|
|
|
unsigned DefaultTrackHeight( const TCPLines &topLines )
|
2017-06-07 01:40:58 +00:00
|
|
|
{
|
|
|
|
int needed =
|
|
|
|
kTopMargin + kBottomMargin +
|
2017-06-12 23:03:09 +00:00
|
|
|
totalTCPLines( topLines, true ) +
|
|
|
|
totalTCPLines( commonTrackTCPBottomLines, false ) + 1;
|
2017-06-07 02:37:24 +00:00
|
|
|
return (unsigned) std::max( needed, (int) Track::DefaultHeight );
|
|
|
|
}
|
2017-06-12 23:03:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned TrackInfo::DefaultNoteTrackHeight()
|
|
|
|
{
|
|
|
|
return DefaultTrackHeight( noteTrackTCPLines );
|
|
|
|
}
|
2017-06-07 02:37:24 +00:00
|
|
|
|
|
|
|
unsigned TrackInfo::DefaultWaveTrackHeight()
|
|
|
|
{
|
2017-06-12 23:03:09 +00:00
|
|
|
return DefaultTrackHeight( waveTrackTCPLines );
|
2017-06-07 01:40:58 +00:00
|
|
|
}
|
|
|
|
|
2017-06-12 20:54:12 +00:00
|
|
|
std::unique_ptr<LWSlider>
|
|
|
|
TrackInfo::gGainCaptured
|
|
|
|
, TrackInfo::gPanCaptured
|
|
|
|
, TrackInfo::gGain
|
|
|
|
, TrackInfo::gPan
|
|
|
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
|
|
|
, TrackInfo::gVelocityCaptured
|
|
|
|
, TrackInfo::gVelocity
|
|
|
|
#endif
|
|
|
|
;
|
2010-04-21 05:03:24 +00:00
|
|
|
|
2017-06-12 20:54:12 +00:00
|
|
|
LWSlider * TrackInfo::GainSlider
|
|
|
|
(const wxRect &sliderRect, const WaveTrack *t, bool captured, wxWindow *pParent)
|
|
|
|
{
|
2015-09-08 16:24:59 +00:00
|
|
|
wxPoint pos = sliderRect.GetPosition();
|
2017-06-12 20:10:03 +00:00
|
|
|
float gain = t ? t->GetGain() : 1.0;
|
2010-04-21 05:03:24 +00:00
|
|
|
|
2017-06-12 20:54:12 +00:00
|
|
|
gGain->Move(pos);
|
|
|
|
gGain->Set(gain);
|
|
|
|
gGainCaptured->Move(pos);
|
|
|
|
gGainCaptured->Set(gain);
|
2015-09-08 16:24:59 +00:00
|
|
|
|
2017-06-12 20:54:12 +00:00
|
|
|
auto slider = (captured ? gGainCaptured : gGain).get();
|
|
|
|
slider->SetParent( pParent ? pParent : ::GetActiveProject() );
|
|
|
|
return slider;
|
2010-01-23 19:44:49 +00:00
|
|
|
}
|
|
|
|
|
2017-06-12 20:54:12 +00:00
|
|
|
LWSlider * TrackInfo::PanSlider
|
|
|
|
(const wxRect &sliderRect, const WaveTrack *t, bool captured, wxWindow *pParent)
|
2010-04-21 05:03:24 +00:00
|
|
|
{
|
2015-09-08 16:24:59 +00:00
|
|
|
wxPoint pos = sliderRect.GetPosition();
|
2017-06-12 20:10:03 +00:00
|
|
|
float pan = t ? t->GetPan() : 0.0;
|
2015-09-08 16:24:59 +00:00
|
|
|
|
2017-06-12 20:54:12 +00:00
|
|
|
gPan->Move(pos);
|
|
|
|
gPan->Set(pan);
|
|
|
|
gPanCaptured->Move(pos);
|
|
|
|
gPanCaptured->Set(pan);
|
Fix for bug #949
This removes the TrackInfo's slider "cache".
Originally, the cache would build to the maximum number of tracks you
had created in an Audacity session. So, if you created 128 tracks
and then reduced that to 1, you'd still have 256 sliders, 1 gain and
1 pan per track.
But, the only real thing the cache did was prevent continuous allocations
of sliders since the allocated sliders position and values wer still
being updated nearly with ever interaction since they were redrawn each
time.
In April 2010, the slider cache was changed to reduce its size by
creating a sort of ring buffer based on how many tracks were displayed
and how many tracks were in the project (I guess). Unfortunately, it
didn't really handle large number of tracks and this bug was born.
While trying to find the proper fix for this, I realized that the
cache really wasn't saving anything. Maybe a little when dragging
the thumb, but during normal track redraws and interaction, it really
didn't serve a purpose, other than use additional memory.
So, I've removed the cache and have allocated a single gain and a
single pan slider. As before, their position and value are changed
as needed when drawn and manipulated.
2015-05-25 03:54:10 +00:00
|
|
|
|
2017-06-12 20:54:12 +00:00
|
|
|
auto slider = (captured ? gPanCaptured : gPan).get();
|
|
|
|
slider->SetParent( pParent ? pParent : ::GetActiveProject() );
|
|
|
|
return slider;
|
2010-04-21 05:03:24 +00:00
|
|
|
}
|
2010-01-23 19:44:49 +00:00
|
|
|
|
2017-02-22 02:05:35 +00:00
|
|
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
2017-06-12 20:54:12 +00:00
|
|
|
LWSlider * TrackInfo::VelocitySlider
|
|
|
|
(const wxRect &sliderRect, const NoteTrack *t, bool captured, wxWindow *pParent)
|
2017-02-22 02:05:35 +00:00
|
|
|
{
|
|
|
|
wxPoint pos = sliderRect.GetPosition();
|
2017-06-12 20:10:03 +00:00
|
|
|
float velocity = t ? t->GetVelocity() : 0.0;
|
2017-02-22 02:05:35 +00:00
|
|
|
|
2017-06-12 20:54:12 +00:00
|
|
|
gVelocity->Move(pos);
|
|
|
|
gVelocity->Set(velocity);
|
|
|
|
gVelocityCaptured->Move(pos);
|
|
|
|
gVelocityCaptured->Set(velocity);
|
2017-02-22 02:05:35 +00:00
|
|
|
|
2017-06-12 20:54:12 +00:00
|
|
|
auto slider = (captured ? gVelocityCaptured : gVelocity).get();
|
|
|
|
slider->SetParent( pParent ? pParent : ::GetActiveProject() );
|
|
|
|
return slider;
|
2017-02-22 02:05:35 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-05-01 21:55:48 +00:00
|
|
|
void TrackInfo::UpdatePrefs()
|
|
|
|
{
|
|
|
|
// Calculation of best font size depends on language, so it should be redone in case
|
|
|
|
// the language preference changed.
|
|
|
|
|
|
|
|
int fontSize = 10;
|
2017-06-12 19:02:28 +00:00
|
|
|
gFont.Create(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
|
2016-05-01 21:55:48 +00:00
|
|
|
|
|
|
|
int allowableWidth = GetTrackInfoWidth() - 2; // 2 to allow for left/right borders
|
|
|
|
int textWidth, textHeight;
|
|
|
|
do {
|
2017-06-12 19:02:28 +00:00
|
|
|
gFont.SetPointSize(fontSize);
|
2016-05-01 21:55:48 +00:00
|
|
|
pParent->GetTextExtent(_("Stereo, 999999Hz"),
|
|
|
|
&textWidth,
|
|
|
|
&textHeight,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
2017-06-12 19:02:28 +00:00
|
|
|
&gFont);
|
2016-05-01 21:55:48 +00:00
|
|
|
fontSize--;
|
|
|
|
} while (textWidth >= allowableWidth);
|
|
|
|
}
|
|
|
|
|
2017-06-17 02:57:08 +00:00
|
|
|
IteratorRange< TrackPanelCellIterator > TrackPanel::Cells()
|
|
|
|
{
|
|
|
|
return {
|
|
|
|
TrackPanelCellIterator( this, true ),
|
|
|
|
TrackPanelCellIterator( this, false )
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2015-08-25 17:35:32 +00:00
|
|
|
TrackPanelCellIterator::TrackPanelCellIterator(TrackPanel *trackPanel, bool begin)
|
2017-06-17 05:26:36 +00:00
|
|
|
: mPanel{ trackPanel }
|
|
|
|
, mIter{ trackPanel->GetProject() }
|
|
|
|
, mpTrack{ begin ? mIter.First() : nullptr }
|
|
|
|
, mpCell{ begin
|
|
|
|
? ( mpTrack ? mpTrack : trackPanel->GetBackgroundCell().get() )
|
|
|
|
: nullptr
|
|
|
|
}
|
2015-08-25 17:35:32 +00:00
|
|
|
{
|
2017-06-17 05:26:36 +00:00
|
|
|
const auto size = mPanel->GetSize();
|
|
|
|
mRect = { 0, 0, size.x, size.y };
|
|
|
|
UpdateRect();
|
2015-08-25 17:35:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TrackPanelCellIterator &TrackPanelCellIterator::operator++ ()
|
|
|
|
{
|
2017-06-17 05:26:36 +00:00
|
|
|
if ( mpTrack ) {
|
|
|
|
if ( ++ mType == CellType::Background )
|
|
|
|
mType = CellType::Track, mpTrack = mIter.Next();
|
|
|
|
}
|
|
|
|
if ( mpTrack ) {
|
|
|
|
if ( mType == CellType::Label &&
|
|
|
|
mpTrack->GetLink() && !mpTrack->GetLinked() )
|
|
|
|
// Visit label of stereo track only once
|
|
|
|
++mType;
|
|
|
|
switch ( mType ) {
|
|
|
|
case CellType::Track:
|
|
|
|
mpCell = mpTrack;
|
|
|
|
break;
|
|
|
|
case CellType::Label:
|
|
|
|
mpCell = mpTrack->GetTrackControl();
|
|
|
|
break;
|
|
|
|
case CellType::VRuler:
|
|
|
|
mpCell = mpTrack->GetVRulerControl();
|
|
|
|
break;
|
|
|
|
case CellType::Resizer: {
|
|
|
|
auto instance = &TrackPanelResizerCell::Instance();
|
|
|
|
instance->mpTrack = mpTrack;
|
|
|
|
mpCell = instance;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
// should not happen
|
|
|
|
mpCell = nullptr;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ( !mDidBackground )
|
|
|
|
mpCell = mPanel->GetBackgroundCell().get(), mDidBackground = true;
|
|
|
|
else
|
|
|
|
mpCell = nullptr;
|
|
|
|
|
|
|
|
UpdateRect();
|
|
|
|
|
2015-08-25 17:35:32 +00:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
TrackPanelCellIterator TrackPanelCellIterator::operator++ (int)
|
|
|
|
{
|
|
|
|
TrackPanelCellIterator copy(*this);
|
|
|
|
++ *this;
|
|
|
|
return copy;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto TrackPanelCellIterator::operator* () const -> value_type
|
|
|
|
{
|
2017-06-17 05:26:36 +00:00
|
|
|
return { mpCell, mRect };
|
|
|
|
}
|
|
|
|
|
|
|
|
void TrackPanelCellIterator::UpdateRect()
|
|
|
|
{
|
|
|
|
// TODO: cooperate with EXPERIMENTAL_OUTPUT_DISPLAY
|
|
|
|
const auto size = mPanel->GetSize();
|
|
|
|
if ( mpTrack ) {
|
|
|
|
mRect = {
|
|
|
|
0,
|
|
|
|
mpTrack->GetY() - mPanel->GetViewInfo()->vpos,
|
|
|
|
size.x,
|
|
|
|
mpTrack->GetHeight()
|
|
|
|
};
|
|
|
|
switch ( mType ) {
|
|
|
|
case CellType::Track:
|
|
|
|
mRect.x = mPanel->GetLeftOffset();
|
|
|
|
mRect.width -= (mRect.x + kRightMargin);
|
|
|
|
mRect.y += kTopMargin;
|
|
|
|
mRect.height -= (kBottomMargin + kTopMargin);
|
|
|
|
break;
|
|
|
|
case CellType::Label: {
|
|
|
|
mRect.x = kLeftMargin;
|
|
|
|
mRect.width = kTrackInfoWidth - mRect.x;
|
|
|
|
mRect.y += kTopMargin;
|
|
|
|
mRect.height -= (kBottomMargin + kTopMargin);
|
|
|
|
auto partner = mpTrack->GetLink();
|
|
|
|
if ( partner && mpTrack->GetLinked() )
|
|
|
|
mRect.height += partner->GetHeight();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CellType::VRuler:
|
|
|
|
mRect.x = kTrackInfoWidth;
|
|
|
|
mRect.width = mPanel->GetLeftOffset() - mRect.x;
|
|
|
|
mRect.y += kTopMargin;
|
|
|
|
mRect.height -= (kBottomMargin + kTopMargin);
|
|
|
|
break;
|
|
|
|
case CellType::Resizer: {
|
|
|
|
// The resizer region encompasses the bottom margin proper to this
|
|
|
|
// track, plus the top margin of the next track (or, an equally
|
|
|
|
// tall zone below, in case there is no next track)
|
|
|
|
auto partner = mpTrack->GetLink();
|
|
|
|
if ( partner && mpTrack->GetLinked() )
|
2017-06-17 19:56:21 +00:00
|
|
|
mRect.x = kTrackInfoWidth;
|
2017-06-17 05:26:36 +00:00
|
|
|
else
|
|
|
|
mRect.x = kLeftMargin;
|
|
|
|
mRect.width -= (mRect.x + kRightMargin);
|
|
|
|
mRect.y += (mRect.height - kBottomMargin);
|
|
|
|
mRect.height = (kBottomMargin + kTopMargin);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
// should not happen
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ( mpCell ) {
|
|
|
|
// Find a disjoint, maybe empty, rectangle
|
|
|
|
// for the empty space appearing at bottom
|
2015-08-25 17:35:32 +00:00
|
|
|
|
2017-06-17 05:26:36 +00:00
|
|
|
mRect.x = kLeftMargin;
|
|
|
|
mRect.width = size.x - (mRect.x + kRightMargin);
|
|
|
|
|
|
|
|
// Use previous value of the bottom, either the whole area if
|
|
|
|
// there were no tracks, or else the resizer of the last track
|
|
|
|
mRect.y =
|
|
|
|
std::min( size.y,
|
|
|
|
std::max( 0,
|
|
|
|
mRect.y + mRect.height ) );
|
|
|
|
mRect.height = size.y - mRect.y;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
mRect = {};
|
2015-08-25 17:35:32 +00:00
|
|
|
}
|
|
|
|
|
2013-09-21 19:34:12 +00:00
|
|
|
static TrackPanel * TrackPanelFactory(wxWindow * parent,
|
2011-11-25 20:40:26 +00:00
|
|
|
wxWindowID id,
|
|
|
|
const wxPoint & pos,
|
|
|
|
const wxSize & size,
|
2016-08-20 17:58:56 +00:00
|
|
|
const std::shared_ptr<TrackList> &tracks,
|
2011-11-25 20:40:26 +00:00
|
|
|
ViewInfo * viewInfo,
|
|
|
|
TrackPanelListener * listener,
|
|
|
|
AdornedRulerPanel * ruler)
|
|
|
|
{
|
2016-02-14 07:54:25 +00:00
|
|
|
wxASSERT(parent); // to justify safenew
|
|
|
|
return safenew TrackPanel(
|
2011-11-25 20:40:26 +00:00
|
|
|
parent,
|
|
|
|
id,
|
|
|
|
pos,
|
|
|
|
size,
|
|
|
|
tracks,
|
|
|
|
viewInfo,
|
|
|
|
listener,
|
|
|
|
ruler);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Declare the static factory function.
|
|
|
|
// We defined it in the class.
|
|
|
|
TrackPanel *(*TrackPanel::FactoryFunction)(
|
|
|
|
wxWindow * parent,
|
|
|
|
wxWindowID id,
|
|
|
|
const wxPoint & pos,
|
|
|
|
const wxSize & size,
|
2016-08-20 17:58:56 +00:00
|
|
|
const std::shared_ptr<TrackList> &tracks,
|
2011-11-25 20:40:26 +00:00
|
|
|
ViewInfo * viewInfo,
|
|
|
|
TrackPanelListener * listener,
|
|
|
|
AdornedRulerPanel * ruler) = TrackPanelFactory;
|
|
|
|
|
2015-07-07 03:12:16 +00:00
|
|
|
TrackPanelCell::~TrackPanelCell()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned TrackPanelCell::HandleWheelRotation
|
|
|
|
(const TrackPanelMouseEvent &, AudacityProject *)
|
|
|
|
{
|
|
|
|
return RefreshCode::Cancelled;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned TrackPanelCell::DoContextMenu
|
|
|
|
(const wxRect &, wxWindow*, wxPoint *)
|
|
|
|
{
|
|
|
|
return RefreshCode::RefreshNone;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned TrackPanelCell::CaptureKey(wxKeyEvent &event, ViewInfo &, wxWindow *)
|
|
|
|
{
|
|
|
|
event.Skip();
|
|
|
|
return RefreshCode::RefreshNone;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned TrackPanelCell::KeyDown(wxKeyEvent &event, ViewInfo &, wxWindow *)
|
|
|
|
{
|
|
|
|
event.Skip();
|
|
|
|
return RefreshCode::RefreshNone;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned TrackPanelCell::KeyUp(wxKeyEvent &event, ViewInfo &, wxWindow *)
|
|
|
|
{
|
|
|
|
event.Skip();
|
|
|
|
return RefreshCode::RefreshNone;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned TrackPanelCell::Char(wxKeyEvent &event, ViewInfo &, wxWindow *)
|
|
|
|
{
|
|
|
|
event.Skip();
|
|
|
|
return RefreshCode::RefreshNone;
|
|
|
|
}
|