1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-25 08:38:39 +02:00

Splitting mSelIndex

Using mTextEditIndex for tracking active text box index, and mNavigationIndex used to track selection for keyboard navigation purposes
See #1195
This commit is contained in:
Vitaly Sverchinsky 2021-07-21 19:48:32 +03:00
parent 068ef4c36f
commit b90b5cfd47
11 changed files with 307 additions and 296 deletions

View File

@ -426,7 +426,7 @@ bool LabelDialog::TransferDataFromWindow()
// Add the label to it
lt->AddLabel(rd.selectedRegion, rd.title);
LabelTrackView::Get( *lt ).SetSelectedIndex( -1 );
LabelTrackView::Get( *lt ).ResetTextSelection();
}
return true;

View File

@ -114,13 +114,13 @@ bool SetLabelCommand::Apply(const CommandContext & context)
auto &view = LabelTrackView::Get( *labelTrack );
if( mbSelected )
{
view.SetSelectedIndex( ii );
view.SetNavigationIndex( ii );
double t0 = pLabel->selectedRegion.t0();
double t1 = pLabel->selectedRegion.t1();
selectedRegion.setTimes( t0, t1);
}
else if( view.GetSelectedIndex( context.project ) == ii )
view.SetSelectedIndex( -1 );
else if( view.GetNavigationIndex( context.project ) == ii )
view.SetNavigationIndex( -1 );
}
labelTrack->SortLabels();

View File

@ -46,7 +46,7 @@ bool DoPasteText(AudacityProject &project)
for (auto pLabelTrack : tracks.Any<LabelTrack>())
{
// Does this track have an active label?
if (LabelTrackView::Get( *pLabelTrack ).HasSelection( project )) {
if (LabelTrackView::Get( *pLabelTrack ).GetTextEditIndex(project) != -1) {
// Yes, so try pasting into it
auto &view = LabelTrackView::Get( *pLabelTrack );

View File

@ -346,7 +346,7 @@ void OnPasteNewLabel(const CommandContext &context)
// Unselect the last label, so we'll have just one active label when
// we're done
if (plt)
LabelTrackView::Get( *plt ).SetSelectedIndex( -1 );
LabelTrackView::Get( *plt ).ResetTextSelection();
// Add a NEW label, paste into it
// Paul L: copy whatever defines the selected region, not just times

View File

@ -72,7 +72,6 @@ UIHandle::Result LabelDefaultClickHandle::Click
if (pLT != &TrackView::Get( *lt )) {
auto &view = LabelTrackView::Get( *lt );
view.ResetFlags();
view.SetSelectedIndex( -1 );
}
}
}

View File

@ -19,6 +19,9 @@ Paul Licameli split from TrackPanel.cpp
#include "../../../TrackPanelMouseEvent.h"
#include "../../../UndoManager.h"
#include "../../../ViewInfo.h"
#include "../../../SelectionState.h"
#include "../../../ProjectAudioIO.h"
#include "../../../tracks/ui/TimeShiftHandle.h"
#include <wx/cursor.h>
#include <wx/translation.h>
@ -125,18 +128,21 @@ LabelGlyphHandle::~LabelGlyphHandle()
void LabelGlyphHandle::HandleGlyphClick
(LabelTrackHit &hit, const wxMouseEvent & evt,
const wxRect & r, const ZoomInfo &zoomInfo,
NotifyingSelectedRegion &WXUNUSED(newSel))
NotifyingSelectedRegion &newSel)
{
if (evt.ButtonDown())
{
//OverGlyph sets mMouseOverLabel to be the chosen label.
const auto pTrack = mpLT;
LabelTrackView::OverGlyph(*pTrack, hit, evt.m_x, evt.m_y);
hit.mIsAdjustingLabel = evt.Button(wxMOUSE_BTN_LEFT) &&
( hit.mEdge & 3 ) != 0;
if (hit.mIsAdjustingLabel)
{
auto& view = LabelTrackView::Get(*pTrack);
view.ResetTextSelection();
double t = 0.0;
@ -204,6 +210,8 @@ UIHandle::Result LabelGlyphHandle::Click
auto result = LabelDefaultClickHandle::Click( evt, pProject );
const wxMouseEvent &event = evt.event;
auto& selectionState = SelectionState::Get(*pProject);
auto& tracks = TrackList::Get(*pProject);
auto &viewInfo = ViewInfo::Get( *pProject );
HandleGlyphClick(
@ -310,6 +318,47 @@ bool LabelGlyphHandle::HandleGlyphDragRelease
pTrack->SetLabel( hit.mMouseOverLabelRight, labelStruct );
}
if (hit.mMouseOverLabel >= 0)
{
auto labelStruct = mLabels[hit.mMouseOverLabel];
if (!labelStruct.updated)
{
//happens on click over bar between handles (without moving a cursor)
newSel = labelStruct.selectedRegion;
// IF the user clicked a label, THEN select all other tracks by Label
// do nothing if at least one other track is selected
auto& selectionState = SelectionState::Get(project);
auto& tracks = TrackList::Get(project);
bool done = tracks.Selected().any_of(
[&](const Track* track) { return track != static_cast<Track*>(pTrack.get()); }
);
if (!done) {
//otherwise, select all tracks
for (auto t : tracks.Any())
selectionState.SelectTrack(*t, true, true);
}
// Do this after, for its effect on TrackPanel's memory of last selected
// track (which affects shift-click actions)
selectionState.SelectTrack(*pTrack.get(), true, true);
// PRL: bug1659 -- make selection change undo correctly
updated = !ProjectAudioIO::Get(project).IsAudioActive();
auto& view = LabelTrackView::Get(*pTrack);
view.SetNavigationIndex(hit.mMouseOverLabel);
}
else
{
labelStruct.updated = false;
pTrack->SetLabel(hit.mMouseOverLabel, labelStruct);
updated = true;
}
}
hit.mIsAdjustingLabel = false;
hit.mMouseOverLabelLeft = -1;
hit.mMouseOverLabelRight = -1;
@ -350,12 +399,15 @@ bool LabelGlyphHandle::HandleGlyphDragRelease
}
const auto &view = LabelTrackView::Get( *pTrack );
if( view.HasSelection( project ) )
auto navigationIndex = view.GetNavigationIndex(project);
if(navigationIndex != -1 &&
(navigationIndex == hit.mMouseOverLabel ||
navigationIndex == hit.mMouseOverLabelLeft ||
navigationIndex == hit.mMouseOverLabelRight))
{
auto selIndex = view.GetSelectedIndex( project );
//Set the selection region to be equal to
//the NEW size of the label.
newSel = mLabels[ selIndex ].selectedRegion;
newSel = mLabels[navigationIndex].selectedRegion;
}
pTrack->SortLabels();
}

View File

@ -70,34 +70,21 @@ LabelTextHandle::~LabelTextHandle()
{
}
void LabelTextHandle::HandleTextClick(AudacityProject &
#if defined(__WXGTK__) && (HAVE_GTK)
project
#endif
,
const wxMouseEvent & evt,
const wxRect & r, const ZoomInfo &zoomInfo,
NotifyingSelectedRegion &newSel)
void LabelTextHandle::HandleTextClick(AudacityProject &project, const wxMouseEvent & evt)
{
auto pTrack = mpLT.lock();
if (!pTrack)
return;
auto &view = LabelTrackView::Get( *pTrack );
static_cast<void>(r);//compiler food.
static_cast<void>(zoomInfo);//compiler food.
if (evt.ButtonDown())
{
const auto selIndex = LabelTrackView::OverATextBox( *pTrack, evt.m_x, evt.m_y );
view.SetSelectedIndex( selIndex );
if ( selIndex != -1 ) {
const auto &mLabels = pTrack->GetLabels();
const auto &labelStruct = mLabels[ selIndex ];
newSel = labelStruct.selectedRegion;
if (evt.LeftDown()) {
mRightDragging = false;
// Find the NEW drag end
auto position = view.FindCursorPosition( evt.m_x );
auto position = view.FindCursorPosition(selIndex, evt.m_x );
// Anchor shift-drag at the farther end of the previous highlight
// that is farther from the click, on Mac, for consistency with
@ -118,13 +105,18 @@ void LabelTextHandle::HandleTextClick(AudacityProject &
else
initial = position;
view.SetTextHighlight( initial, position );
mRightDragging = false;
view.SetTextSelection(selIndex, initial, position );
}
else
{
if (!view.IsTextSelected(project))
{
auto position = view.FindCursorPosition(selIndex, evt.m_x);
view.SetTextSelection(selIndex, position, position);
}
// Actually this might be right or middle down
mRightDragging = true;
}
// Middle click on GTK: paste from primary selection
#if defined(__WXGTK__) && (HAVE_GTK)
if (evt.MiddleDown()) {
@ -132,7 +124,7 @@ void LabelTextHandle::HandleTextClick(AudacityProject &
// case PasteSelectedText() will start a NEW label at the click
// location
if (!LabelTrackView::OverTextBox(&labelStruct, evt.m_x, evt.m_y))
view.SetSelectedIndex( -1 );
view.ResetTextSelection();
double t = zoomInfo.PositionToTime(evt.m_x, r.x);
newSel = SelectedRegion(t, t);
}
@ -158,41 +150,10 @@ UIHandle::Result LabelTextHandle::Click
auto result = LabelDefaultClickHandle::Click( evt, pProject );
auto &selectionState = SelectionState::Get( *pProject );
auto &tracks = TrackList::Get( *pProject );
mChanger =
std::make_shared< SelectionStateChanger >( selectionState, tracks );
const wxMouseEvent &event = evt.event;
auto &viewInfo = ViewInfo::Get( *pProject );
mSelectedRegion = viewInfo.selectedRegion;
HandleTextClick( *pProject,
event, evt.rect, viewInfo, viewInfo.selectedRegion );
{
// IF the user clicked a label, THEN select all other tracks by Label
//do nothing if at least one other track is selected
bool done = tracks.Selected().any_of(
[&](const Track *pTrack){ return pTrack != pLT.get(); }
);
if (!done) {
//otherwise, select all tracks
for (auto t : tracks.Any())
selectionState.SelectTrack( *t, true, true );
}
// Do this after, for its effect on TrackPanel's memory of last selected
// track (which affects shift-click actions)
selectionState.SelectTrack( *pLT, true, true );
}
// PRL: bug1659 -- make selection change undo correctly
const bool unsafe = ProjectAudioIO::Get( *pProject ).IsAudioActive();
if (!unsafe)
ProjectHistory::Get( *pProject ).ModifyState(false);
HandleTextClick(*pProject, event);
return result | RefreshCode::RefreshCell;
}
@ -228,19 +189,19 @@ void LabelTextHandle::HandleTextDragRelease(
if(evt.Dragging())
{
if (!mRightDragging)
auto index = view.GetTextEditIndex(project);
if (!mRightDragging && index != -1)
// Update drag end
view.SetCurrentCursorPosition(
view.FindCursorPosition( evt.m_x ) );
view.SetCurrentCursorPosition(view.FindCursorPosition(index, evt.m_x ));
return;
}
if (evt.RightUp()) {
const auto selIndex = view.GetSelectedIndex( project );
if ( selIndex != -1 &&
LabelTrackView::OverTextBox(
pTrack->GetLabel( selIndex ), evt.m_x, evt.m_y ) ) {
if (evt.RightUp())
{
auto index = view.GetTextEditIndex(project);
if(index != -1 &&
LabelTrackView::OverTextBox(pTrack->GetLabel(index), evt.m_x, evt.m_y))
{
// popup menu for editing
// TODO: handle context menus via CellularPanel?
view.ShowContextMenu( project );
@ -269,10 +230,9 @@ UIHandle::Result LabelTextHandle::Drag
mLabelTrackStartYPos = event.m_y;
auto pView = pLT ? &LabelTrackView::Get( *pLT ) : nullptr;
if (pLT &&
(pView->GetSelectedIndex( project ) != -1) &&
if (pLT && (pView->GetTextEditIndex( project ) != -1) &&
LabelTrackView::OverTextBox(
pLT->GetLabel(pView->GetSelectedIndex( project )),
pLT->GetLabel(pView->GetTextEditIndex( project )),
mLabelTrackStartXPos,
mLabelTrackStartYPos))
mLabelTrackStartYPos = -1;
@ -301,11 +261,6 @@ UIHandle::Result LabelTextHandle::Release
// Only selected a part of a text string and changed track selectedness.
// No undoable effects.
if (mChanger) {
mChanger->Commit();
mChanger.reset();
}
const wxMouseEvent &event = evt.event;
auto pLT = TrackList::Get( *pProject ).Lock(mpLT);
if (pLT)
@ -320,11 +275,6 @@ UIHandle::Result LabelTextHandle::Release
UIHandle::Result LabelTextHandle::Cancel( AudacityProject *pProject )
{
// Restore the selection states of tracks
// Note that we are also relying on LabelDefaultClickHandle::Cancel
// to restore the selection state of the labels in the tracks.
auto &viewInfo = ViewInfo::Get( *pProject );
viewInfo.selectedRegion = mSelectedRegion;
auto result = LabelDefaultClickHandle::Cancel( pProject );
return result | RefreshCode::RefreshAll;
}

View File

@ -58,9 +58,7 @@ public:
private:
void HandleTextClick
(AudacityProject &project,
const wxMouseEvent & evt, const wxRect & r, const ZoomInfo &zoomInfo,
NotifyingSelectedRegion &newSel);
(AudacityProject &project, const wxMouseEvent & evt);
void HandleTextDragRelease(
AudacityProject &project, const wxMouseEvent & evt);
@ -68,8 +66,6 @@ private:
int mLabelNum{ -1 };
int mLabelTrackStartXPos { -1 };
int mLabelTrackStartYPos { -1 };
SelectedRegion mSelectedRegion{};
std::shared_ptr<SelectionStateChanger> mChanger;
/// flag to tell if it's a valid dragging
bool mRightDragging{ false };

View File

@ -60,7 +60,7 @@ public:
iLabel =
LabelTrackView::OverATextBox(*mpTrack, pParams->xx, pParams->yy);
if (iLabel == -1)
iLabel = LabelTrackView::Get(*mpTrack).GetSelectedIndex(mProject);
iLabel = LabelTrackView::Get(*mpTrack).GetNavigationIndex(mProject);
if (iLabel != -1) {
UnfixIntervals([&](const auto &myInterval){
return GetIndex( myInterval ) == iLabel;

View File

@ -147,10 +147,10 @@ void LabelTrackView::CopyTo( Track &track ) const
auto &other = TrackView::Get( track );
if ( const auto pOther = dynamic_cast< const LabelTrackView* >( &other ) ) {
pOther->mSelIndex = mSelIndex;
pOther->mNavigationIndex = mNavigationIndex;
pOther->mInitialCursorPos = mInitialCursorPos;
pOther->mCurrentCursorPos = mCurrentCursorPos;
pOther->mDrawCursor = mDrawCursor;
pOther->mTextEditIndex = mTextEditIndex;
pOther->mUndoLabel = mUndoLabel;
}
}
@ -218,15 +218,24 @@ void LabelTrackView::ResetFlags()
{
mInitialCursorPos = 1;
mCurrentCursorPos = 1;
mDrawCursor = false;
mTextEditIndex = -1;
mNavigationIndex = -1;
}
LabelTrackView::Flags LabelTrackView::SaveFlags() const
{
return {
mInitialCursorPos, mCurrentCursorPos, mNavigationIndex,
mTextEditIndex, mUndoLabel
};
}
void LabelTrackView::RestoreFlags( const Flags& flags )
{
mInitialCursorPos = flags.mInitialCursorPos;
mCurrentCursorPos = flags.mCurrentCursorPos;
mSelIndex = flags.mSelIndex;
mDrawCursor = flags.mDrawCursor;
mNavigationIndex = flags.mNavigationIndex;
mTextEditIndex = flags.mTextEditIndex;
}
wxFont LabelTrackView::GetFont(const wxString &faceName, int size)
@ -705,7 +714,7 @@ void getXPos( const LabelStruct &ls, wxDC & dc, int * xPos1, int cursorPos)
bool LabelTrackView::CalcCursorX( AudacityProject &project, int * x) const
{
if ( HasSelection( project ) ) {
if (IsValidIndex(mTextEditIndex, project)) {
wxMemoryDC dc;
if (msFont.Ok()) {
@ -715,7 +724,7 @@ bool LabelTrackView::CalcCursorX( AudacityProject &project, int * x) const
const auto pTrack = FindLabelTrack();
const auto &mLabels = pTrack->GetLabels();
getXPos(mLabels[mSelIndex], dc, x, mCurrentCursorPos);
getXPos(mLabels[mTextEditIndex], dc, x, mCurrentCursorPos);
*x += mIconWidth / 2;
return true;
}
@ -737,7 +746,7 @@ void LabelTrackView::CalcHighlightXs(int *x1, int *x2) const
const auto pTrack = FindLabelTrack();
const auto &mLabels = pTrack->GetLabels();
const auto &labelStruct = mLabels[mSelIndex];
const auto &labelStruct = mLabels[mTextEditIndex];
// find the left X pos of highlighted area
getXPos(labelStruct, dc, x1, pos1);
@ -850,44 +859,47 @@ void LabelTrackView::Draw
#ifdef EXPERIMENTAL_TRACK_PANEL_HIGHLIGHTING
highlight = highlightTrack && target->GetLabelNum() == i;
#endif
bool selected = GetSelectedIndex( project ) == i;
dc.SetBrush(pHit && pHit->mMouseOverLabel == i
dc.SetBrush(mNavigationIndex == i || (pHit && pHit->mMouseOverLabel == i)
? AColor::labelTextEditBrush : AColor::labelTextNormalBrush);
DrawBar(dc, labelStruct, r);
bool selected = mTextEditIndex == i;
if (selected)
dc.SetBrush(AColor::labelTextEditBrush);
else if (highlight)
dc.SetBrush(AColor::uglyBrush);
else
dc.SetBrush(AColor::labelTextNormalBrush);
DrawTextBox(dc, labelStruct, r);
if (highlight || selected)
dc.SetBrush(AColor::labelTextNormalBrush);
}
}
// Draw highlights
if ( (mInitialCursorPos != mCurrentCursorPos) && HasSelection( project ) )
if ( (mInitialCursorPos != mCurrentCursorPos) && IsValidIndex(mTextEditIndex, project))
{
int xpos1, xpos2;
CalcHighlightXs(&xpos1, &xpos2);
DrawHighlight(dc, mLabels[mSelIndex],
DrawHighlight(dc, mLabels[mTextEditIndex],
xpos1, xpos2, dc.GetFontMetrics().ascent + dc.GetFontMetrics().descent);
}
// Draw the text and the label boxes.
{ int i = -1; for (const auto &labelStruct : mLabels) { ++i;
if( GetSelectedIndex( project ) == i )
if(mTextEditIndex == i )
dc.SetBrush(AColor::labelTextEditBrush);
DrawText( dc, labelStruct, r );
if( GetSelectedIndex( project ) == i )
if(mTextEditIndex == i )
dc.SetBrush(AColor::labelTextNormalBrush);
}}
// Draw the cursor, if there is one.
if( mDrawCursor && HasSelection( project ) )
if(mInitialCursorPos == mCurrentCursorPos && IsValidIndex(mTextEditIndex, project))
{
const auto &labelStruct = mLabels[mSelIndex];
const auto &labelStruct = mLabels[mTextEditIndex];
int xPos = labelStruct.xText;
if( mCurrentCursorPos > 0)
@ -918,17 +930,9 @@ void LabelTrackView::Draw(
CommonTrackView::Draw( context, rect, iPass );
}
void LabelTrackView::SetSelectedIndex( int index )
{
if ( index >= 0 && index < (int)FindLabelTrack()->GetLabels().size() )
mSelIndex = index;
else
mSelIndex = -1;
}
/// uses GetTextExtent to find the character position
/// corresponding to the x pixel position.
int LabelTrackView::FindCursorPosition(wxCoord xPos)
int LabelTrackView::FindCursorPosition(int labelIndex, wxCoord xPos)
{
int result = -1;
wxMemoryDC dc;
@ -945,7 +949,7 @@ int LabelTrackView::FindCursorPosition(wxCoord xPos)
const auto pTrack = FindLabelTrack();
const auto &mLabels = pTrack->GetLabels();
const auto &labelStruct = mLabels[mSelIndex];
const auto &labelStruct = mLabels[labelIndex];
const auto &title = labelStruct.title;
const int length = title.length();
while (!finished && (charIndex < length + 1))
@ -986,13 +990,33 @@ void LabelTrackView::SetCurrentCursorPosition(int pos)
{
mCurrentCursorPos = pos;
}
void LabelTrackView::SetTextHighlight(
int initialPosition, int currentPosition )
void LabelTrackView::SetTextSelection(int labelIndex, int start, int end)
{
mInitialCursorPos = initialPosition;
mCurrentCursorPos = currentPosition;
mDrawCursor = true;
mTextEditIndex = labelIndex;
mInitialCursorPos = start;
mCurrentCursorPos = end;
}
int LabelTrackView::GetTextEditIndex(AudacityProject& project) const
{
if (IsValidIndex(mTextEditIndex, project))
return mTextEditIndex;
return -1;
}
void LabelTrackView::ResetTextSelection()
{
mTextEditIndex = -1;
mCurrentCursorPos = 1;
mInitialCursorPos = 1;
}
void LabelTrackView::SetNavigationIndex(int index)
{
mNavigationIndex = index;
}
int LabelTrackView::GetNavigationIndex(AudacityProject& project) const
{
if (IsValidIndex(mNavigationIndex, project))
return mNavigationIndex;
return -1;
}
void LabelTrackView::calculateFontHeight(wxDC & dc)
@ -1013,13 +1037,20 @@ void LabelTrackView::calculateFontHeight(wxDC & dc)
mFontHeight += CursorExtraHeight - (charLeading+charDescent);
}
bool LabelTrackView::IsValidIndex(const Index& index, AudacityProject& project) const
{
if (index == -1)
return false;
// may make delayed update of mutable mSelIndex after track selection change
auto track = FindLabelTrack();
if (track->GetSelected() || (TrackFocus::Get(project).Get() == track.get()))
return index >= 0 && index < static_cast<int>(track->GetLabels().size());
return false;
}
bool LabelTrackView::IsTextSelected( AudacityProject &project ) const
{
if ( !HasSelection( project ) )
return false;
if (mCurrentCursorPos == mInitialCursorPos)
return false;
return true;
return mCurrentCursorPos != mInitialCursorPos && IsValidIndex(mTextEditIndex, project);
}
/// Cut the selected text in the text box
@ -1033,10 +1064,10 @@ bool LabelTrackView::CutSelectedText( AudacityProject &project )
const auto &mLabels = pTrack->GetLabels();
wxString left, right;
auto labelStruct = mLabels[mSelIndex];
auto labelStruct = mLabels[mTextEditIndex];
auto &text = labelStruct.title;
if (!mSelIndex.IsModified()) {
if (!mTextEditIndex.IsModified()) {
mUndoLabel = text;
}
@ -1059,7 +1090,7 @@ bool LabelTrackView::CutSelectedText( AudacityProject &project )
// set title to the combination of the two remainders
text = left + right;
pTrack->SetLabel( mSelIndex, labelStruct );
pTrack->SetLabel( mTextEditIndex, labelStruct );
// copy data onto clipboard
if (wxTheClipboard->Open()) {
@ -1071,7 +1102,7 @@ bool LabelTrackView::CutSelectedText( AudacityProject &project )
// set cursor positions
mInitialCursorPos = mCurrentCursorPos = left.length();
mSelIndex.SetModified(true);
mTextEditIndex.SetModified(true);
return true;
}
@ -1079,13 +1110,13 @@ bool LabelTrackView::CutSelectedText( AudacityProject &project )
/// @return true if text is selected in text box, false otherwise
bool LabelTrackView::CopySelectedText( AudacityProject &project )
{
if ( !HasSelection( project ) )
if (!IsTextSelected(project))
return false;
const auto pTrack = FindLabelTrack();
const auto &mLabels = pTrack->GetLabels();
const auto &labelStruct = mLabels[mSelIndex];
const auto &labelStruct = mLabels[mTextEditIndex];
int init = mInitialCursorPos;
int cur = mCurrentCursorPos;
@ -1116,8 +1147,8 @@ bool LabelTrackView::PasteSelectedText(
{
const auto pTrack = FindLabelTrack();
if ( !HasSelection( project ) )
AddLabel(SelectedRegion(sel0, sel1));
if (!IsValidIndex(mTextEditIndex, project))
SetTextSelection(AddLabel(SelectedRegion(sel0, sel1)));
wxString text, left, right;
@ -1130,7 +1161,7 @@ bool LabelTrackView::PasteSelectedText(
text = data.GetText();
}
if (!mSelIndex.IsModified()) {
if (!mTextEditIndex.IsModified()) {
mUndoLabel = text;
}
@ -1143,7 +1174,7 @@ bool LabelTrackView::PasteSelectedText(
}
const auto &mLabels = pTrack->GetLabels();
auto labelStruct = mLabels[mSelIndex];
auto labelStruct = mLabels[mTextEditIndex];
auto &title = labelStruct.title;
int cur = mCurrentCursorPos, init = mInitialCursorPos;
if (init > cur)
@ -1154,11 +1185,11 @@ bool LabelTrackView::PasteSelectedText(
title = left + text + right;
pTrack->SetLabel( mSelIndex, labelStruct );
pTrack->SetLabel(mTextEditIndex, labelStruct );
mInitialCursorPos = mCurrentCursorPos = left.length() + text.length();
mSelIndex.SetModified(true);
mTextEditIndex.SetModified(true);
return true;
}
@ -1168,20 +1199,6 @@ bool LabelTrackView::IsTextClipSupported()
return wxTheClipboard->IsSupported(wxDF_UNICODETEXT);
}
int LabelTrackView::GetSelectedIndex( AudacityProject &project ) const
{
// may make delayed update of mutable mSelIndex after track selection change
auto track = FindLabelTrack();
if ( track->GetSelected() ||
TrackFocus::Get( project ).Get() == track.get()
)
return mSelIndex = std::max( -1,
std::min<int>( track->GetLabels().size() - 1, mSelIndex ) );
else
return mSelIndex = -1;
}
/// TODO: Investigate what happens with large
/// numbers of labels, might need a binary search
/// rather than a linear one.
@ -1331,7 +1348,7 @@ bool LabelTrackView::DoCaptureKey(
!mLabels.empty())
return true;
if ( HasSelection( project ) ) {
if (IsValidIndex(mTextEditIndex, project) || IsValidIndex(mNavigationIndex, project)) {
if (IsGoodLabelEditKey(event)) {
return true;
}
@ -1390,10 +1407,10 @@ unsigned LabelTrackView::KeyDown(
double bkpSel0 = viewInfo.selectedRegion.t0(),
bkpSel1 = viewInfo.selectedRegion.t1();
if (!mSelIndex.IsModified() && HasSelection( *project )) {
if (IsValidIndex(mTextEditIndex, *project) && !mTextEditIndex.IsModified()) {
const auto pTrack = FindLabelTrack();
const auto &mLabels = pTrack->GetLabels();
auto labelStruct = mLabels[mSelIndex];
auto labelStruct = mLabels[mTextEditIndex];
auto &title = labelStruct.title;
mUndoLabel = title;
}
@ -1403,12 +1420,12 @@ unsigned LabelTrackView::KeyDown(
if (DoKeyDown( *project, viewInfo.selectedRegion, event )) {
ProjectHistory::Get( *project ).PushState(XO("Modified Label"),
XO("Label Edit"),
mSelIndex.IsModified() ? UndoPush::CONSOLIDATE : UndoPush::NONE);
mTextEditIndex.IsModified() ? UndoPush::CONSOLIDATE : UndoPush::NONE);
mSelIndex.SetModified(true);
mTextEditIndex.SetModified(true);
}
if (!mSelIndex.IsModified()) {
if (!mTextEditIndex.IsModified()) {
mUndoLabel.clear();
}
@ -1436,10 +1453,10 @@ unsigned LabelTrackView::Char(
// Pass keystroke to labeltrack's handler and add to history if any
// updates were done
if (!mSelIndex.IsModified() && HasSelection( *project )) {
if (IsValidIndex(mTextEditIndex, *project) && !mTextEditIndex.IsModified()) {
const auto pTrack = FindLabelTrack();
const auto &mLabels = pTrack->GetLabels();
auto labelStruct = mLabels[mSelIndex];
auto labelStruct = mLabels[mTextEditIndex];
auto &title = labelStruct.title;
mUndoLabel = title;
}
@ -1447,12 +1464,12 @@ unsigned LabelTrackView::Char(
if (DoChar( *project, viewInfo.selectedRegion, event )) {
ProjectHistory::Get( *project ).PushState(XO("Modified Label"),
XO("Label Edit"),
mSelIndex.IsModified() ? UndoPush::CONSOLIDATE : UndoPush::NONE);
mTextEditIndex.IsModified() ? UndoPush::CONSOLIDATE : UndoPush::NONE);
mSelIndex.SetModified(true);
mTextEditIndex.SetModified(true);
}
if (!mSelIndex.IsModified()) {
if (!mTextEditIndex.IsModified()) {
mUndoLabel.clear();
}
@ -1487,8 +1504,8 @@ bool LabelTrackView::DoKeyDown(
// All editing keys are only active if we're currently editing a label
const auto pTrack = FindLabelTrack();
const auto &mLabels = pTrack->GetLabels();
if ( HasSelection( project ) ) {
auto labelStruct = mLabels[mSelIndex];
if (IsValidIndex(mTextEditIndex, project)) {
auto labelStruct = mLabels[mTextEditIndex];
auto &title = labelStruct.title;
wxUniChar wchar;
bool more=true;
@ -1513,7 +1530,7 @@ bool LabelTrackView::DoKeyDown(
title.erase(mCurrentCursorPos-1, 1);
mCurrentCursorPos--;
if( ((int)wchar > 0xDFFF) || ((int)wchar <0xDC00)){
pTrack->SetLabel(mSelIndex, labelStruct);
pTrack->SetLabel(mTextEditIndex, labelStruct);
more = false;
}
}
@ -1522,7 +1539,8 @@ bool LabelTrackView::DoKeyDown(
else
{
// ELSE no text in text box, so DELETE whole label.
pTrack->DeleteLabel( mSelIndex );
pTrack->DeleteLabel(mTextEditIndex);
ResetTextSelection();
}
mInitialCursorPos = mCurrentCursorPos;
updated = true;
@ -1547,7 +1565,7 @@ bool LabelTrackView::DoKeyDown(
wchar = title.at( mCurrentCursorPos );
title.erase(mCurrentCursorPos, 1);
if( ((int)wchar > 0xDBFF) || ((int)wchar <0xD800)){
pTrack->SetLabel(mSelIndex, labelStruct);
pTrack->SetLabel(mTextEditIndex, labelStruct);
more = false;
}
}
@ -1556,7 +1574,8 @@ bool LabelTrackView::DoKeyDown(
else
{
// DELETE whole label if no text in text box
pTrack->DeleteLabel( mSelIndex );
pTrack->DeleteLabel(mTextEditIndex);
ResetTextSelection();
}
mInitialCursorPos = mCurrentCursorPos;
updated = true;
@ -1616,17 +1635,18 @@ bool LabelTrackView::DoKeyDown(
break;
case WXK_ESCAPE:
if (mSelIndex.IsModified()) {
if (mTextEditIndex.IsModified()) {
title = mUndoLabel;
pTrack->SetLabel(mSelIndex, labelStruct);
pTrack->SetLabel(mTextEditIndex, labelStruct);
ProjectHistory::Get( project ).PushState(XO("Modified Label"),
XO("Label Edit"),
mSelIndex.IsModified() ? UndoPush::CONSOLIDATE : UndoPush::NONE);
mTextEditIndex.IsModified() ? UndoPush::CONSOLIDATE : UndoPush::NONE);
}
case WXK_RETURN:
case WXK_NUMPAD_ENTER:
case WXK_TAB:
if (mRestoreFocus >= 0) {
auto track = *TrackList::Get( project ).Any()
.begin().advance(mRestoreFocus);
@ -1634,27 +1654,9 @@ bool LabelTrackView::DoKeyDown(
TrackFocus::Get( project ).Set(track);
mRestoreFocus = -2;
}
mSelIndex = -1;
SetNavigationIndex(mTextEditIndex);
ResetTextSelection();
break;
case WXK_TAB:
case WXK_NUMPAD_TAB:
if (event.ShiftDown()) {
--mSelIndex;
} else {
++mSelIndex;
}
mSelIndex = (mSelIndex + (int)mLabels.size()) % (int)mLabels.size(); // wrap round if necessary
{
const auto &newLabel = mLabels[mSelIndex];
mCurrentCursorPos = newLabel.title.length();
mInitialCursorPos = mCurrentCursorPos;
//Set the selection region to be equal to the selection bounds of the tabbed-to label.
newSel = newLabel.selectedRegion;
}
break;
case '\x10': // OSX
case WXK_MENU:
case WXK_WINDOWS_MENU:
@ -1676,37 +1678,61 @@ bool LabelTrackView::DoKeyDown(
case WXK_NUMPAD_TAB:
if (!mLabels.empty()) {
int len = (int) mLabels.size();
if (IsValidIndex(mNavigationIndex, project))
{
if (event.ShiftDown()) {
mSelIndex = len - 1;
--mNavigationIndex;
}
else {
++mNavigationIndex;
}
mNavigationIndex = (mNavigationIndex + (int)mLabels.size()) % (int)mLabels.size(); // wrap round if necessary
}
else
{
// no valid navigation index, then
if (event.ShiftDown()) {
//search for the first label starting from the end (and before selection)
mNavigationIndex = len - 1;
if (newSel.t0() > mLabels[0].getT0()) {
while (mSelIndex >= 0 &&
mLabels[mSelIndex].getT0() > newSel.t0()) {
--mSelIndex;
while (mNavigationIndex >= 0 &&
mLabels[mNavigationIndex].getT0() > newSel.t0()) {
--mNavigationIndex;
}
}
} else {
mSelIndex = 0;
}
else {
//search for the first label starting from the beginning (and after selection)
mNavigationIndex = 0;
if (newSel.t0() < mLabels[len - 1].getT0()) {
while (mSelIndex < len &&
mLabels[mSelIndex].getT0() < newSel.t0()) {
++mSelIndex;
while (mNavigationIndex < len &&
mLabels[mNavigationIndex].getT0() < newSel.t0()) {
++mNavigationIndex;
}
}
}
}
if (mSelIndex >= 0 && mSelIndex < len) {
const auto &labelStruct = mLabels[mSelIndex];
if (mNavigationIndex >= 0 && mNavigationIndex < len) {
const auto &labelStruct = mLabels[mNavigationIndex];
mCurrentCursorPos = labelStruct.title.length();
mInitialCursorPos = mCurrentCursorPos;
//Set the selection region to be equal to the selection bounds of the tabbed-to label.
newSel = labelStruct.selectedRegion;
}
else {
mSelIndex = -1;
mNavigationIndex = -1;
}
}
break;
case WXK_RETURN:
case WXK_NUMPAD_ENTER:
//pressing Enter key activates editing of the label
//pointed to by mNavigationIndex (if valid)
if (IsValidIndex(mNavigationIndex, project)) {
SetTextSelection(mNavigationIndex);
}
break;
default:
if (!IsGoodLabelFirstKey(event)) {
event.Skip();
@ -1715,9 +1741,6 @@ bool LabelTrackView::DoKeyDown(
}
}
// Make sure the caret is visible
mDrawCursor = true;
return updated;
}
@ -1738,7 +1761,7 @@ bool LabelTrackView::DoChar(
}
// Only track true changes to the label
bool updated = false;
//bool updated = false;
// Cache the character
wxChar charCode = event.GetUnicodeKey();
@ -1751,7 +1774,7 @@ bool LabelTrackView::DoChar(
// If we've reached this point and aren't currently editing, add NEW label
const auto pTrack = FindLabelTrack();
if ( !HasSelection( project ) ) {
if (!IsValidIndex(mTextEditIndex, project)) {
// Don't create a NEW label for a space
if (wxIsspace(charCode)) {
event.Skip();
@ -1781,6 +1804,9 @@ bool LabelTrackView::DoChar(
}
}
if (!IsValidIndex(mTextEditIndex, project))
return false;
//
// Now we are definitely in a label; append the incoming character
//
@ -1790,7 +1816,7 @@ bool LabelTrackView::DoChar(
RemoveSelectedText();
const auto& mLabels = pTrack->GetLabels();
auto labelStruct = mLabels[mSelIndex];
auto labelStruct = mLabels[mTextEditIndex];
auto& title = labelStruct.title;
if (mCurrentCursorPos < (int)title.length()) {
@ -1807,16 +1833,12 @@ bool LabelTrackView::DoChar(
//append charCode
title += charCode;
pTrack->SetLabel( mSelIndex, labelStruct );
pTrack->SetLabel(mTextEditIndex, labelStruct );
//moving cursor position forward
mInitialCursorPos = ++mCurrentCursorPos;
updated = true;
// Make sure the caret is visible
mDrawCursor = true;
return updated;
return true;
}
enum
@ -1856,13 +1878,12 @@ void LabelTrackView::ShowContextMenu( AudacityProject &project )
menu.Enable(OnDeleteSelectedLabelID, true);
menu.Enable(OnEditSelectedLabelID, true);
if( !HasSelection( project ) ) {
wxASSERT( false );
if(!IsValidIndex(mTextEditIndex, project)) {
return;
}
const auto pTrack = FindLabelTrack();
const LabelStruct *ls = pTrack->GetLabel(mSelIndex);
const LabelStruct *ls = pTrack->GetLabel(mTextEditIndex);
wxClientDC dc(parent);
@ -1902,7 +1923,7 @@ void LabelTrackView::OnContextMenu(
{
ProjectHistory::Get( project ).PushState(XO("Modified Label"),
XO("Label Edit"),
mSelIndex.IsModified() ? UndoPush::CONSOLIDATE : UndoPush::NONE);
mTextEditIndex.IsModified() ? UndoPush::CONSOLIDATE : UndoPush::NONE);
}
break;
@ -1918,17 +1939,16 @@ void LabelTrackView::OnContextMenu(
{
ProjectHistory::Get( project ).PushState(XO("Modified Label"),
XO("Label Edit"),
mSelIndex.IsModified() ? UndoPush::CONSOLIDATE : UndoPush::NONE);
mTextEditIndex.IsModified() ? UndoPush::CONSOLIDATE : UndoPush::NONE);
}
break;
/// DELETE selected label
case OnDeleteSelectedLabelID: {
int ndx = GetLabelIndex(selectedRegion.t0(), selectedRegion.t1());
if (ndx != -1)
if (IsValidIndex(mTextEditIndex, project))
{
const auto pTrack = FindLabelTrack();
pTrack->DeleteLabel(ndx);
pTrack->DeleteLabel(mTextEditIndex);
ProjectHistory::Get( project ).PushState(XO("Deleted Label"),
XO("Label Edit"),
UndoPush::CONSOLIDATE);
@ -1938,7 +1958,8 @@ void LabelTrackView::OnContextMenu(
case OnEditSelectedLabelID: {
// Bug #2571: See above
mEditIndex = GetLabelIndex(selectedRegion.t0(), selectedRegion.t1());
if (IsValidIndex(mTextEditIndex, project))
mEditIndex = mTextEditIndex;
}
break;
}
@ -1955,7 +1976,7 @@ void LabelTrackView::RemoveSelectedText()
const auto pTrack = FindLabelTrack();
const auto &mLabels = pTrack->GetLabels();
auto labelStruct = mLabels[mSelIndex];
auto labelStruct = mLabels[mTextEditIndex];
auto &title = labelStruct.title;
if (init > 0)
@ -1965,16 +1986,16 @@ void LabelTrackView::RemoveSelectedText()
right = title.Mid(cur);
title = left + right;
pTrack->SetLabel( mSelIndex, labelStruct );
pTrack->SetLabel( mTextEditIndex, labelStruct );
mInitialCursorPos = mCurrentCursorPos = left.length();
}
bool LabelTrackView::HasSelection( AudacityProject &project ) const
/*
bool LabelTrackView::HasSelectedLabel( AudacityProject &project ) const
{
const auto selIndex = GetSelectedIndex( project );
const auto selIndex = GetSelectionIndex( project );
return (selIndex >= 0 &&
selIndex < (int)FindLabelTrack()->GetLabels().size());
}
}*/
int LabelTrackView::GetLabelIndex(double t, double t1)
{
@ -2026,21 +2047,10 @@ void LabelTrackView::OnLabelAdded( LabelTrackEvent &e )
// -1 means we don't need to restore it to anywhere.
// 0 or above is the track to restore to after editing the label is complete.
if( mRestoreFocus >= -1 )
mSelIndex = pos;
mTextEditIndex = pos;
if( mRestoreFocus < 0 )
mRestoreFocus = -2;
// Make sure the caret is visible
//
// LLL: The cursor will not be drawn when the first label
// is added since mDrawCursor will be false. Presumably,
// if the user adds a label, then a cursor should be drawn
// to indicate that typing is expected.
//
// If the label is added during actions like import, then the
// mDrawCursor flag will be reset once the action is complete.
mDrawCursor = true;
}
void LabelTrackView::OnLabelDeleted( LabelTrackEvent &e )
@ -2053,17 +2063,13 @@ void LabelTrackView::OnLabelDeleted( LabelTrackEvent &e )
// IF we've deleted the selected label
// THEN set no label selected.
if( mSelIndex== index )
{
mSelIndex = -1;
mCurrentCursorPos = 1;
}
if (mTextEditIndex == index)
ResetTextSelection();
// IF we removed a label before the selected label
// THEN the NEW selected label number is one less.
else if( index < mSelIndex )
{
--mSelIndex;
}
else if( index < mTextEditIndex)
--mTextEditIndex;//NB: Keep cursor selection region
}
void LabelTrackView::OnLabelPermuted( LabelTrackEvent &e )
@ -2075,12 +2081,16 @@ void LabelTrackView::OnLabelPermuted( LabelTrackEvent &e )
auto former = e.mFormerPosition;
auto present = e.mPresentPosition;
if ( mSelIndex == former )
mSelIndex = present;
else if ( former < mSelIndex && mSelIndex <= present )
-- mSelIndex;
else if ( former > mSelIndex && mSelIndex >= present )
++ mSelIndex;
auto fix = [&](Index& index) {
if (index == former)
index = present;
else if (former < index && index <= present)
--index;
else if (former > index && index >= present)
++index;
};
fix(mNavigationIndex);
fix(mTextEditIndex);
}
void LabelTrackView::OnSelectionChange( LabelTrackEvent &e )
@ -2090,7 +2100,10 @@ void LabelTrackView::OnSelectionChange( LabelTrackEvent &e )
return;
if (!FindTrack()->GetSelected())
mSelIndex = -1;
{
SetNavigationIndex(-1);
ResetTextSelection();
}
}
wxBitmap & LabelTrackView::GetGlyph( int i)

View File

@ -112,9 +112,6 @@ public:
void Draw( TrackPanelDrawingContext &context, const wxRect & r ) const;
int GetSelectedIndex( AudacityProject &project ) const;
void SetSelectedIndex( int index );
bool CutSelectedText( AudacityProject &project );
bool CopySelectedText( AudacityProject &project );
bool PasteSelectedText(
@ -146,19 +143,13 @@ private:
public:
struct Flags {
int mInitialCursorPos, mCurrentCursorPos;
Index mSelIndex{-1};
bool mDrawCursor;
Index mNavigationIndex;
Index mTextEditIndex;
wxString mUndoLabel;
};
void ResetFlags();
Flags SaveFlags() const
{
return {
mInitialCursorPos, mCurrentCursorPos, mSelIndex,
mDrawCursor, mUndoLabel
};
}
Flags SaveFlags() const;
void RestoreFlags( const Flags& flags );
static int OverATextBox( const LabelTrack &track, int xx, int yy );
@ -190,7 +181,11 @@ public:
private:
void OnContextMenu( AudacityProject &project, wxCommandEvent & evt);
mutable Index mSelIndex{-1}; /// Keeps track of the currently selected label
/// Keeps track of the currently selected label (not same as selection region)
/// used for navigation between labels
mutable Index mNavigationIndex{ -1 };
/// Index of the current label text beeing edited
mutable Index mTextEditIndex{ -1 };
mutable wxString mUndoLabel;
@ -205,8 +200,7 @@ private:
mutable int mCurrentCursorPos; /// current cursor position
mutable int mInitialCursorPos; /// initial cursor position
mutable bool mDrawCursor; /// flag to tell if drawing the
/// cursor or not
int mRestoreFocus{-2}; /// Restore focus to this track
/// when done editing
@ -224,11 +218,19 @@ private:
public:
/// convert pixel coordinate to character position in text box
int FindCursorPosition(wxCoord xPos);
int FindCursorPosition(int labelIndex, wxCoord xPos);
int GetCurrentCursorPosition() const { return mCurrentCursorPos; }
void SetCurrentCursorPosition(int pos);
int GetInitialCursorPosition() const { return mInitialCursorPos; }
void SetTextHighlight( int initialPosition, int currentPosition );
/// Sets the label with specified index for editing,
/// optionaly selection may be specified with [start, end]
void SetTextSelection(int labelIndex, int start = 1, int end = 1);
int GetTextEditIndex(AudacityProject& project) const;
void ResetTextSelection();
void SetNavigationIndex(int index);
int GetNavigationIndex(AudacityProject& project) const;
private:
@ -239,8 +241,7 @@ private:
static void calculateFontHeight(wxDC & dc);
public:
bool HasSelection( AudacityProject &project ) const;
bool IsValidIndex(const Index& index, AudacityProject& project) const;
private:
void RemoveSelectedText();