diff --git a/src/AudacityHeaders.h b/src/AudacityHeaders.h index 2219e93cc..14754edfc 100644 --- a/src/AudacityHeaders.h +++ b/src/AudacityHeaders.h @@ -74,6 +74,11 @@ #include "widgets/ProgressDialog.h" #include "widgets/Ruler.h" +// PRL: These lines allow you to remove Project.h above. +// They must be included before the definition of macro new below. +#include +#include + //#ifdef __WXMSW__ // Enable this to diagnose memory leaks too! // #include // redefines the new() operator diff --git a/src/FreqWindow.cpp b/src/FreqWindow.cpp index e6c3dd401..6e130f6b3 100644 --- a/src/FreqWindow.cpp +++ b/src/FreqWindow.cpp @@ -400,8 +400,8 @@ void FreqWindow::GetAudio() if (selcount==0) { mRate = track->GetRate(); sampleCount start, end; - start = track->TimeToLongSamples(p->mViewInfo.sel0); - end = track->TimeToLongSamples(p->mViewInfo.sel1); + start = track->TimeToLongSamples(p->mViewInfo.selectedRegion.t0()); + end = track->TimeToLongSamples(p->mViewInfo.selectedRegion.t1()); mDataLen = (sampleCount)(end - start); if (mDataLen > 10485760) { warning = true; @@ -421,7 +421,7 @@ void FreqWindow::GetAudio() return; } sampleCount start; - start = track->TimeToLongSamples(p->mViewInfo.sel0); + start = track->TimeToLongSamples(p->mViewInfo.selectedRegion.t0()); float *buffer2 = new float[mDataLen]; track->Get((samplePtr)buffer2, floatSample, start, mDataLen); for(i=0; iSetCellValue(i, Col_Track, TrackName(rd->index)); mGrid->SetCellValue(i, Col_Label, rd->title); - mGrid->SetCellValue(i, Col_Stime, wxString::Format(wxT("%g"), rd->stime)); - mGrid->SetCellValue(i, Col_Etime, wxString::Format(wxT("%g"), rd->etime)); + mGrid->SetCellValue(i, Col_Stime, + wxString::Format(wxT("%g"), rd->selectedRegion.t0())); + mGrid->SetCellValue(i, Col_Etime, + wxString::Format(wxT("%g"), rd->selectedRegion.t1())); + + // PRL: to do: -- populate future additional selection fields + // and write event code to update them from controls } // Autosize all the rows @@ -336,7 +340,7 @@ bool LabelDialog::TransferDataFromWindow() return false; // Add the label to it - ((LabelTrack *) t)->AddLabel(rd->stime, rd->etime, rd->title); + ((LabelTrack *) t)->AddLabel(rd->selectedRegion, rd->title); ((LabelTrack *) t)->Unselect(); } @@ -400,8 +404,7 @@ void LabelDialog::AddLabels(LabelTrack *t) RowData *rd = new RowData(); rd->index = tndx; - rd->stime = ls->t; - rd->etime = ls->t1; + rd->selectedRegion = ls->selectedRegion; rd->title = ls->title; mData.Add(rd); @@ -446,8 +449,7 @@ void LabelDialog::OnInsert(wxCommandEvent &event) // Initialize the new label rd->index = index; - rd->stime = 0.0; - rd->etime = 0.0; + rd->selectedRegion = SelectedRegion(); rd->title = wxT(""); // Insert it before or after the current row @@ -612,7 +614,7 @@ void LabelDialog::OnExport(wxCommandEvent & WXUNUSED(event)) for (i = 0; i < cnt; i++) { RowData *rd = mData[i]; - lt->AddLabel(rd->stime, rd->etime, rd->title); + lt->AddLabel(rd->selectedRegion, rd->title); } // Export them and clean @@ -641,8 +643,7 @@ void LabelDialog::OnSelectCell(wxGridEvent &event) { RowData *rd; rd = mData[event.GetRow()]; - mViewInfo->sel0 = rd->stime; - mViewInfo->sel1 = rd->etime; + mViewInfo->selectedRegion = rd->selectedRegion; GetActiveProject()->RedrawProject(); } @@ -739,11 +740,11 @@ void LabelDialog::OnChangeLabel(wxGridEvent & WXUNUSED(event), int row, RowData void LabelDialog::OnChangeStime(wxGridEvent & WXUNUSED(event), int row, RowData *rd) { // Remember the value...no need to repopulate - mGrid->GetCellValue(row, Col_Stime).ToDouble(&rd->stime); - if (rd->etime < rd->stime) { - rd->etime = rd->stime; - mGrid->SetCellValue(row, Col_Etime, wxString::Format(wxT("%g"), rd->etime)); - } + double t; + mGrid->GetCellValue(row, Col_Stime).ToDouble(&t); + rd->selectedRegion.setT0(t, false); + mGrid->SetCellValue(row, Col_Etime, wxString::Format(wxT("%g"), + rd->selectedRegion.t1())); return; } @@ -751,11 +752,11 @@ void LabelDialog::OnChangeStime(wxGridEvent & WXUNUSED(event), int row, RowData void LabelDialog::OnChangeEtime(wxGridEvent & WXUNUSED(event), int row, RowData *rd) { // Remember the value...no need to repopulate - mGrid->GetCellValue(row, Col_Etime).ToDouble(&rd->etime); - if (rd->etime < rd->stime) { - rd->stime = rd->etime; - mGrid->SetCellValue(row, Col_Stime, wxString::Format(wxT("%g"), rd->stime)); - } + double t; + mGrid->GetCellValue(row, Col_Etime).ToDouble(&t); + rd->selectedRegion.setT1(t, false); + mGrid->SetCellValue(row, Col_Stime, wxString::Format(wxT("%g"), + rd->selectedRegion.t0())); return; } diff --git a/src/LabelTrack.cpp b/src/LabelTrack.cpp index f5eb1867e..ac8fe87e1 100644 --- a/src/LabelTrack.cpp +++ b/src/LabelTrack.cpp @@ -121,10 +121,9 @@ LabelTrack::LabelTrack(const LabelTrack &orig) : int len = orig.mLabels.Count(); for (int i = 0; i < len; i++) { - LabelStruct *l = new LabelStruct(); - l->t = orig.mLabels[i]->t; - l->t1 = orig.mLabels[i]->t1; - l->title = orig.mLabels[i]->title; + const LabelStruct& original = *orig.mLabels[i]; + LabelStruct *l = + new LabelStruct(original.selectedRegion, original.title); mLabels.Add(l); } mSelIndex = orig.mSelIndex; @@ -146,8 +145,7 @@ void LabelTrack::SetOffset(double dOffset) int len = mLabels.Count(); for (int i = 0; i < len; i++) { - mLabels[i]->t += dOffset; - mLabels[i]->t1 += dOffset; + mLabels[i]->selectedRegion.move(dOffset); } } @@ -157,18 +155,18 @@ bool LabelTrack::Clear(double b, double e) LabelStruct::TimeRelations relation = mLabels[i]->RegionRelation(b, e, this); if (relation == LabelStruct::BEFORE_LABEL) { - mLabels[i]->t = mLabels[i]->t - (e-b); - mLabels[i]->t1 = mLabels[i]->t1 - (e-b); + mLabels[i]->selectedRegion.move(- (e-b)); } else if (relation == LabelStruct::SURROUNDS_LABEL) { DeleteLabel( i ); i--; } else if (relation == LabelStruct::ENDS_IN_LABEL) { - mLabels[i]->t = b; - mLabels[i]->t1 = mLabels[i]->t1 - (e - mLabels[i]->t); + mLabels[i]->selectedRegion.setTimes( + b, + mLabels[i]->getT1() - (e - mLabels[i]->getT0())); } else if (relation == LabelStruct::BEGINS_IN_LABEL) { - mLabels[i]->t1 = b; + mLabels[i]->selectedRegion.setT1(b); } else if (relation == LabelStruct::WITHIN_LABEL) { - mLabels[i]->t1 = mLabels[i]->t1 - (e-b); + mLabels[i]->selectedRegion.moveT1( - (e-b)); } } @@ -185,11 +183,11 @@ bool LabelTrack::SplitDelete(double b, double e) DeleteLabel(i); i--; } else if (relation == LabelStruct::WITHIN_LABEL) { - mLabels[i]->t1 = mLabels[i]->t1 - (e-b); + mLabels[i]->selectedRegion.moveT1( - (e-b)); } else if (relation == LabelStruct::ENDS_IN_LABEL) { - mLabels[i]->t = e; + mLabels[i]->selectedRegion.setT0(e); } else if (relation == LabelStruct::BEGINS_IN_LABEL) { - mLabels[i]->t1 = b; + mLabels[i]->selectedRegion.setT1(b); } } @@ -202,11 +200,10 @@ void LabelTrack::ShiftLabelsOnInsert(double length, double pt) mLabels[i]->RegionRelation(pt, pt, this); if (relation == LabelStruct::BEFORE_LABEL) { - mLabels[i]->t = mLabels[i]->t + length; - mLabels[i]->t1 = mLabels[i]->t1 + length; + mLabels[i]->selectedRegion.move(length); } else if (relation == LabelStruct::WITHIN_LABEL) { - mLabels[i]->t1 = mLabels[i]->t1 + length; + mLabels[i]->selectedRegion.moveT1(length); } } } @@ -217,9 +214,10 @@ void LabelTrack::ChangeLabelsOnReverse(double b, double e) if (mLabels[i]->RegionRelation(b, e, this) == LabelStruct::SURROUNDS_LABEL) { - double aux = b + (e - mLabels[i]->t1); - mLabels[i]->t1 = e - (mLabels[i]->t - b); - mLabels[i]->t = aux; + double aux = b + (e - mLabels[i]->getT1()); + mLabels[i]->selectedRegion.setTimes( + aux, + e - (mLabels[i]->getT0() - b)); } } SortLabels(); @@ -228,8 +226,9 @@ void LabelTrack::ChangeLabelsOnReverse(double b, double e) void LabelTrack::ScaleLabels(double b, double e, double change) { for (unsigned int i=0;it = AdjustTimeStampOnScale(mLabels[i]->t, b, e, change); - mLabels[i]->t1 = AdjustTimeStampOnScale(mLabels[i]->t1, b, e, change); + mLabels[i]->selectedRegion.setTimes( + AdjustTimeStampOnScale(mLabels[i]->getT0(), b, e, change), + AdjustTimeStampOnScale(mLabels[i]->getT1(), b, e, change)); } } @@ -254,8 +253,9 @@ double LabelTrack::AdjustTimeStampOnScale(double t, double b, double e, double c // specified time, as in most cases they don't need to move.) void LabelTrack::WarpLabels(const TimeWarper &warper) { for (int i = 0; i < (int)mLabels.GetCount(); ++i) { - double &labelT0 = mLabels[i]->t; labelT0 = warper.Warp(labelT0); - double &labelT1 = mLabels[i]->t1; labelT1 = warper.Warp(labelT1); + mLabels[i]->selectedRegion.setTimes( + warper.Warp(mLabels[i]->getT0()), + warper.Warp(mLabels[i]->getT1())); } } @@ -467,8 +467,8 @@ void LabelTrack::ComputeLayout(const wxRect & r, double h, double pps) for (i = 0; i < (int)mLabels.Count(); i++) { - int x = r.x + (int) ((mLabels[i]->t - h) * pps); - int x1 = r.x + (int) ((mLabels[i]->t1 - h) * pps); + int x = r.x + (int) ((mLabels[i]->getT0() - h) * pps); + int x1 = r.x + (int) ((mLabels[i]->getT1() - h) * pps); int y = r.y; mLabels[i]->x=x; @@ -506,13 +506,33 @@ void LabelTrack::ComputeLayout(const wxRect & r, double h, double pps) } } -LabelStruct::LabelStruct() +LabelStruct::LabelStruct(const SelectedRegion ®ion, + const wxString& aTitle) +: selectedRegion(region) +, title(aTitle) { changeInitialMouseXPos = true; highlighted = false; updated = false; - t = 0.0; - t1 = 0.0; + width = 0; + x = 0; + x1 = 0; + xText = 0; + y = 0; +} + +LabelStruct::LabelStruct(const SelectedRegion ®ion, + double t0, double t1, + const wxString& aTitle) +: selectedRegion(region) +, title(aTitle) +{ + // Overwrite the times + selectedRegion.setTimes(t0, t1); + + changeInitialMouseXPos = true; + highlighted = false; + updated = false; width = 0; x = 0; x1 = 0; @@ -1002,12 +1022,13 @@ bool LabelTrack::CopySelectedText() return true; } +// PRL: should this set other fields of the label selection? /// Paste the text on the clipboard to text box /// @return true if mouse is clicked in text box, false otherwise bool LabelTrack::PasteSelectedText(double sel0, double sel1) { if (mSelIndex == -1) { - AddLabel(sel0, sel1, wxT("")); + AddLabel(SelectedRegion(sel0, sel1), wxT("")); } wxString text; @@ -1102,7 +1123,7 @@ double LabelTrack::GetStartTime() if (len == 0) return 0.0; else - return mLabels[0]->t; + return mLabels[0]->getT0(); } double LabelTrack::GetEndTime() @@ -1116,8 +1137,9 @@ double LabelTrack::GetEndTime() double end = 0.0; for(int i = 0; i < len; i++) { - if(mLabels[i]->t1 > end) - end = mLabels[i]->t1; + const double t1 = mLabels[i]->getT1(); + if(t1 > end) + end = t1; } return end; } @@ -1208,13 +1230,14 @@ bool LabelTrack::OverTextBox(const LabelStruct *pLabel, int x, int y) } // Adjust label's left or right boundary, depending which is requested. -void LabelStruct::AdjustEdge( int iEdge, double fNewTime) +// Return true iff the label flipped. +bool LabelStruct::AdjustEdge( int iEdge, double fNewTime) { - if( iEdge < 0 ) - t = fNewTime; - else - t1 = fNewTime; updated = true; + if( iEdge < 0 ) + return selectedRegion.setT0(fNewTime); + else + return selectedRegion.setT1(fNewTime); } // We're moving the label. Adjust both left and right edge. @@ -1224,13 +1247,11 @@ void LabelStruct::MoveLabel( int iEdge, double fNewTime) if( iEdge < 0 ) { - t = fNewTime; - t1 = fNewTime+fTimeSpan; + selectedRegion.setTimes(fNewTime, fNewTime+fTimeSpan); } else { - t = fNewTime-fTimeSpan; - t1 = fNewTime; + selectedRegion.setTimes(fNewTime-fTimeSpan, fNewTime); } updated = true; } @@ -1249,17 +1270,18 @@ LabelStruct::TimeRelations LabelStruct::RegionRelation( // than the length of the label if the selection is within the label or // matching exactly a (region) label. - if (reg_t0 < t && reg_t1 > t1) + if (reg_t0 < getT0() && reg_t1 > getT1()) return SURROUNDS_LABEL; - else if (reg_t1 < t) + else if (reg_t1 < getT0()) return BEFORE_LABEL; - else if (reg_t0 > t1) + else if (reg_t0 > getT1()) return AFTER_LABEL; - else if (reg_t0 >= t && reg_t0 <= t1 && reg_t1 >= t && reg_t1 <= t1) + else if (reg_t0 >= getT0() && reg_t0 <= getT1() && + reg_t1 >= getT0() && reg_t1 <= getT1()) return WITHIN_LABEL; - else if (reg_t0 >= t && reg_t0 <= t1) + else if (reg_t0 >= getT0() && reg_t0 <= getT1()) return BEGINS_IN_LABEL; else return ENDS_IN_LABEL; @@ -1276,21 +1298,22 @@ LabelStruct::TimeRelations LabelStruct::RegionRelation( // The first test catches bordered point-labels and selected-through // region-labels; move it to third and selection edges become inclusive // WRT point-labels. - if (reg_t0 <= t && reg_t1 >= t1) + if (reg_t0 <= getT0() && reg_t1 >= getT1()) return SURROUNDS_LABEL; - else if (reg_t1 <= t) + else if (reg_t1 <= getT0()) return BEFORE_LABEL; - else if (reg_t0 >= t1) + else if (reg_t0 >= getT1()) return AFTER_LABEL; // At this point, all point labels should have returned. - else if (reg_t0 > t && reg_t0 < t1 && reg_t1 > t && reg_t1 < t1) + else if (reg_t0 > getT0() && reg_t0 < getT1() && + reg_t1 > getT0() && reg_t1 < getT1()) return WITHIN_LABEL; // Knowing that none of the other relations match simplifies remaining // tests - else if (reg_t0 > t && reg_t0 < t1) + else if (reg_t0 > getT0() && reg_t0 < getT1()) return BEGINS_IN_LABEL; else return ENDS_IN_LABEL; @@ -1310,9 +1333,9 @@ void LabelTrack::MayAdjustLabel( int iLabel, int iEdge, bool bAllowSwapping, dou LabelStruct * pLabel = mLabels[ iLabel ]; // Adjust the requested edge. - pLabel->AdjustEdge( iEdge, fNewTime ); - // If the label is not inverted, then we are done. - if( pLabel->t <= pLabel->t1 ) + bool flipped = pLabel->AdjustEdge( iEdge, fNewTime ); + // If the edges did not swap, then we are done. + if( ! flipped ) return; // If swapping's not allowed we must also move the edge @@ -1323,12 +1346,6 @@ void LabelTrack::MayAdjustLabel( int iLabel, int iEdge, bool bAllowSwapping, dou return; } - // Swapping's allowed and we moved the 'wrong' edge. - // Swap the edges. - double fTemp = pLabel->t; - pLabel->t = pLabel->t1; - pLabel->t1 = fTemp; - // Swap our record of what we are dragging. int Temp = mMouseOverLabelLeft; mMouseOverLabelLeft = mMouseOverLabelRight; @@ -1360,7 +1377,7 @@ static int Constrain( int value, int min, int max ) /// bool LabelTrack::HandleMouse(const wxMouseEvent & evt, wxRect & r, double h, double pps, - double *newSel0, double *newSel1) + SelectedRegion *newSel) { if(evt.LeftUp()) { @@ -1447,8 +1464,7 @@ bool LabelTrack::HandleMouse(const wxMouseEvent & evt, { //Set the selection region to be equal to //the new size of the label. - *newSel0 = mLabels[mSelIndex]->t; - *newSel1 = mLabels[mSelIndex]->t1; + *newSel = mLabels[mSelIndex]->selectedRegion; } SortLabels(); } @@ -1515,7 +1531,8 @@ bool LabelTrack::HandleMouse(const wxMouseEvent & evt, (mMouseOverLabelLeft >=0) ) { - t = (mLabels[mMouseOverLabelRight]->t1+mLabels[mMouseOverLabelLeft]->t)/2.0f; + t = (mLabels[mMouseOverLabelRight]->getT1() + + mLabels[mMouseOverLabelLeft]->getT0()) / 2.0f; // If we're moving two edges, then it's a move (label size preserved) // if both edges are the same label, and it's an adjust (label sizes change) // if we're on a boundary between two different labels. @@ -1523,11 +1540,11 @@ bool LabelTrack::HandleMouse(const wxMouseEvent & evt, } else if(mMouseOverLabelRight >=0) { - t = mLabels[mMouseOverLabelRight]->t1; + t = mLabels[mMouseOverLabelRight]->getT1(); } else if(mMouseOverLabelLeft >=0) { - t = mLabels[mMouseOverLabelLeft]->t; + t = mLabels[mMouseOverLabelLeft]->getT0(); } mxMouseDisplacement = (int)((((t-h) * pps) + r.x )-evt.m_x); return false; @@ -1581,8 +1598,7 @@ bool LabelTrack::HandleMouse(const wxMouseEvent & evt, if(OverTextBox(pLabel, evt.m_x, evt.m_y)) { mSelIndex = i; - *newSel0 = mLabels[i]->t; - *newSel1 = mLabels[i]->t1; + *newSel = mLabels[i]->selectedRegion; // set mouseXPos to set current cursor position if (changeCursor) mMouseXPos = evt.m_x; @@ -1624,12 +1640,14 @@ bool LabelTrack::CaptureKey(wxKeyEvent & event) { double t0, t1; pProj->GetPlayRegion(&t0, &t1); - if (pProj->mViewInfo.sel0 == t0 && pProj->mViewInfo.sel1 == t1) + if (pProj->mViewInfo.selectedRegion.t0() == t0 && + pProj->mViewInfo.selectedRegion.t1() == t1) return false; } // If there's a label there already don't capture - if( GetLabelIndex( pProj->mViewInfo.sel0, pProj->mViewInfo.sel1) != wxNOT_FOUND ) + if( GetLabelIndex(pProj->mViewInfo.selectedRegion.t0(), + pProj->mViewInfo.selectedRegion.t1()) != wxNOT_FOUND ) return false; return true; @@ -1640,7 +1658,7 @@ bool LabelTrack::CaptureKey(wxKeyEvent & event) } /// KeyEvent is called for every keypress when over the label track. -bool LabelTrack::OnKeyDown(double & newSel0, double & newSel1, wxKeyEvent & event) +bool LabelTrack::OnKeyDown(SelectedRegion &newSel, wxKeyEvent & event) { // Only track true changes to the label bool updated = false; @@ -1814,8 +1832,7 @@ bool LabelTrack::OnKeyDown(double & newSel0, double & newSel1, wxKeyEvent & even if (mSelIndex >= 0 && mSelIndex < (int)mLabels.Count()) { mCurrentCursorPos = mLabels[mSelIndex]->title.Length(); //Set the selection region to be equal to the selection bounds of the tabbed-to label. - newSel0 = mLabels[mSelIndex]->t; - newSel1 = mLabels[mSelIndex]->t1; + newSel = mLabels[mSelIndex]->selectedRegion; } else { mSelIndex = -1; @@ -1837,15 +1854,17 @@ bool LabelTrack::OnKeyDown(double & newSel0, double & newSel1, wxKeyEvent & even int len = (int) mLabels.Count(); if (event.ShiftDown()) { mSelIndex = len - 1; - if (newSel0 > mLabels[0]->t) { - while (mSelIndex >= 0 && mLabels[mSelIndex]->t >= newSel0) { + if (newSel.t0() > mLabels[0]->getT0()) { + while (mSelIndex >= 0 && + mLabels[mSelIndex]->getT0() >= newSel.t0()) { mSelIndex--; } } } else { mSelIndex = 0; - if (newSel0 < mLabels[len - 1]->t) { - while (mSelIndex < len && mLabels[mSelIndex]->t <= newSel0) { + if (newSel.t0() < mLabels[len - 1]->getT0()) { + while (mSelIndex < len && + mLabels[mSelIndex]->getT0() <= newSel.t0()) { mSelIndex++; } } @@ -1854,8 +1873,7 @@ bool LabelTrack::OnKeyDown(double & newSel0, double & newSel1, wxKeyEvent & even if (mSelIndex >= 0 && mSelIndex < len) { mCurrentCursorPos = mLabels[mSelIndex]->title.Length(); //Set the selection region to be equal to the selection bounds of the tabbed-to label. - newSel0 = mLabels[mSelIndex]->t; - newSel1 = mLabels[mSelIndex]->t1; + newSel = mLabels[mSelIndex]->selectedRegion; } else { mSelIndex = -1; @@ -1877,7 +1895,7 @@ bool LabelTrack::OnKeyDown(double & newSel0, double & newSel1, wxKeyEvent & even /// OnChar is called for incoming characters -- that's any keypress not handled /// by OnKeyDown. -bool LabelTrack::OnChar(double & WXUNUSED(newSel0), double & WXUNUSED(newSel1), wxKeyEvent & event) +bool LabelTrack::OnChar(SelectedRegion &WXUNUSED(newSel), wxKeyEvent & event) { // Only track true changes to the label bool updated = false; @@ -1933,7 +1951,7 @@ bool LabelTrack::OnChar(double & WXUNUSED(newSel0), double & WXUNUSED(newSel1), } SetSelected(true); AudacityProject *p = GetActiveProject(); - AddLabel(p->mViewInfo.sel0, p->mViewInfo.sel1); + AddLabel(p->mViewInfo.selectedRegion); p->PushState(_("Added label"), _("Label")); } @@ -2009,10 +2027,11 @@ bool LabelTrack::IsSelected() const /// Export labels including label start and end-times. void LabelTrack::Export(wxTextFile & f) { + // PRL: to do: export other selection fields for (int i = 0; i < (int)mLabels.Count(); i++) { f.AddLine(wxString::Format(wxT("%f\t%f\t%s"), - (double)mLabels[i]->t, - (double)mLabels[i]->t1, + (double)mLabels[i]->getT0(), + (double)mLabels[i]->getT1(), mLabels[i]->title.c_str())); } } @@ -2025,7 +2044,7 @@ void LabelTrack::Import(wxTextFile & in) int index, lines; wxString s,s1; wxString title; - double t,t1; + double t0,t1; lines = in.GetLineCount(); @@ -2051,7 +2070,7 @@ void LabelTrack::Import(wxTextFile & in) } s = currentLine.Left(i); - if (!Internat::CompatibleToDouble(s, &t)) + if (!Internat::CompatibleToDouble(s, &t0)) return; //Increment one letter. @@ -2079,7 +2098,7 @@ void LabelTrack::Import(wxTextFile & in) if (!Internat::CompatibleToDouble(s1, &t1)) { //s1 is not a number. - t1 = t; //This is a one-sided label; t1 == t. + t1 = t0; //This is a one-sided label; t1 == t0. //Because s1 is not a number, the label should be //The rest of the line, starting at i2; @@ -2095,10 +2114,8 @@ void LabelTrack::Import(wxTextFile & in) title = currentLine.Right(len - i).Strip(wxString::stripType(0x3)); //0x3 indicates both. } - LabelStruct *l = new LabelStruct(); - l->t = t; - l->t1 = t1; - l->title = title; + // PRL: to do: import other selection fields + LabelStruct *l = new LabelStruct(SelectedRegion(t0, t1), title); mLabels.Add(l); } SortLabels(); @@ -2108,7 +2125,8 @@ bool LabelTrack::HandleXMLTag(const wxChar *tag, const wxChar **attrs) { if (!wxStrcmp(tag, wxT("label"))) { - LabelStruct *l = new LabelStruct(); + SelectedRegion selectedRegion; + wxString title; // loop through attrs, which is a null-terminated list of // attribute-value pairs @@ -2124,27 +2142,28 @@ bool LabelTrack::HandleXMLTag(const wxChar *tag, const wxChar **attrs) const wxString strValue = value; if (!XMLValueChecker::IsGoodString(strValue)) { - delete l; return false; } if (!wxStrcmp(attr, wxT("t")) && Internat::CompatibleToDouble(strValue, &dblValue)) - l->t = dblValue; + selectedRegion.setT0(dblValue, false); else if (!wxStrcmp(attr, wxT("t1")) && Internat::CompatibleToDouble(strValue, &dblValue)) { has_t1 = true; - l->t1 = dblValue; + selectedRegion.setT1(dblValue, false); } + // PRL: to do: read other selection fields else if (!wxStrcmp(attr, wxT("title"))) - l->title = strValue; + title = strValue; } // while // Handle files created by Audacity 1.1. Labels in Audacity 1.1 // did not have separate start- and end-times. if (!has_t1) - l->t1 = l->t; + selectedRegion.collapseToT0(); + LabelStruct *l = new LabelStruct(selectedRegion, title); mLabels.Add(l); return true; @@ -2211,8 +2230,10 @@ void LabelTrack::WriteXML(XMLWriter &xmlFile) for (i = 0; i < len; i++) { xmlFile.StartTag(wxT("label")); - xmlFile.WriteAttr(wxT("t"), mLabels[i]->t, 8); - xmlFile.WriteAttr(wxT("t1"), mLabels[i]->t1, 8); + // PRL: mismatch of attribute name and structure field name, historical + xmlFile.WriteAttr(wxT("t"), mLabels[i]->getT0(), 8); + xmlFile.WriteAttr(wxT("t1"), mLabels[i]->getT1(), 8); + // PRL: to do: write other selection fields xmlFile.WriteAttr(wxT("title"), mLabels[i]->title); xmlFile.EndTag(wxT("label")); } @@ -2238,10 +2259,13 @@ bool LabelTrack::Load(wxTextFile * in, DirManager * dirManager) for (i = 0; i < len; i++) { LabelStruct *l = new LabelStruct(); - if (!Internat::CompatibleToDouble(in->GetNextLine(), &l->t)) + double t0; + if (!Internat::CompatibleToDouble(in->GetNextLine(), &t0)) return false; + l->selectedRegion.setT0(t0, false); // Legacy file format does not include label end-times. - l->t1 = l->t; + l->selectedRegion.collapseToT0(); + // PRL: nothing new to do, legacy file support l->title = in->GetNextLine(); mLabels.Add(l); } @@ -2259,7 +2283,7 @@ bool LabelTrack::Save(wxTextFile * out, bool overwrite) out->AddLine(wxString::Format(wxT("%d"), len)); for (int i = 0; i < len; i++) { - out->AddLine(wxString::Format(wxT("%lf"), mLabels[i]->t)); + out->AddLine(wxString::Format(wxT("%lf"), mLabels[i]->selectedRegion.mT0)); out->AddLine(mLabels[i]->title); } out->AddLine(wxT("MLabelsEnd")); @@ -2299,31 +2323,37 @@ bool LabelTrack::Copy(double t0, double t1, Track ** dest) LabelStruct::TimeRelations relation = mLabels[i]->RegionRelation(t0, t1, this); if (relation == LabelStruct::SURROUNDS_LABEL) { - LabelStruct *l = new LabelStruct(); - l->t = mLabels[i]->t - t0; - l->t1 = mLabels[i]->t1 - t0; - l->title = mLabels[i]->title; + const LabelStruct &label = *mLabels[i]; + LabelStruct *l = + new LabelStruct(label.selectedRegion, + label.getT0() - t0, + label.getT1() - t0, + label.title); ((LabelTrack *) (*dest))->mLabels.Add(l); } else if (relation == LabelStruct::WITHIN_LABEL) { - LabelStruct *l = new LabelStruct(); - l->t = 0; - l->t1 = t1 - t0; - l->title = mLabels[i]->title; + const LabelStruct &label = *mLabels[i]; + LabelStruct *l = + new LabelStruct(label.selectedRegion, 0, t1-t0, + label.title); ((LabelTrack *) (*dest))->mLabels.Add(l); } else if (relation == LabelStruct::BEGINS_IN_LABEL) { - LabelStruct *l = new LabelStruct(); - l->t = 0; - l->t1 = mLabels[i]->t1 - t0; - l->title = mLabels[i]->title; + const LabelStruct &label = *mLabels[i]; + LabelStruct *l = + new LabelStruct(label.selectedRegion, + 0, + label.getT1() - t0, + label.title); ((LabelTrack *) (*dest))->mLabels.Add(l); } else if (relation == LabelStruct::ENDS_IN_LABEL) { - LabelStruct *l = new LabelStruct(); - l->t = mLabels[i]->t - t0; - l->t1 = t1 - t0; - l->title = mLabels[i]->title; + const LabelStruct &label = *mLabels[i]; + LabelStruct *l = + new LabelStruct(label.selectedRegion, + label.getT0() - t0, + t1 - t0, + label.title); ((LabelTrack *) (*dest))->mLabels.Add(l); } } @@ -2341,15 +2371,17 @@ bool LabelTrack::PasteOver(double t, Track * src) int len = mLabels.Count(); int pos = 0; - while (pos < len && mLabels[pos]->t < t) + while (pos < len && mLabels[pos]->getT0() < t) pos++; LabelTrack *sl = (LabelTrack *) src; for (unsigned int j = 0; j < sl->mLabels.Count(); j++) { - LabelStruct *l = new LabelStruct(); - l->t = sl->mLabels[j]->t + t; - l->t1 = sl->mLabels[j]->t1 + t; - l->title = sl->mLabels[j]->title; + const LabelStruct &label = *sl->mLabels[j]; + LabelStruct *l = + new LabelStruct(label.selectedRegion, + label.getT0() + t, + label.getT1() + t, + label.title); mLabels.Insert(l, pos++); len++; } @@ -2393,13 +2425,16 @@ bool LabelTrack::Repeat(double t0, double t1, int n) for (int j = 1; j <= n; j++) { - LabelStruct *l = new LabelStruct(); - l->t = mLabels[i]->t + j * tLen; - l->t1 = mLabels[i]->t1 + j * tLen; - l->title = mLabels[i]->title; + const LabelStruct &label = *mLabels[i]; + LabelStruct *l = + new LabelStruct(label.selectedRegion, + label.getT0() + j * tLen, + label.getT1() + j * tLen, + label.title); // Figure out where to insert - while (pos < mLabels.Count() && mLabels[pos]->t < l->t) + while (pos < mLabels.Count() && + mLabels[pos]->getT0() < l->getT0()) pos++; mLabels.Insert(l, pos); } @@ -2408,7 +2443,7 @@ bool LabelTrack::Repeat(double t0, double t1, int n) { // Label ends inside the selection; ShiftLabelsOnInsert() hasn't touched // it, and we need to extend it through to the last repeat interval - mLabels[i]->t1 += n * tLen; + mLabels[i]->selectedRegion.moveT1(n * tLen); } // Other cases have already been handled by ShiftLabelsOnInsert() @@ -2427,12 +2462,12 @@ bool LabelTrack::Silence(double t0, double t1) if (relation == LabelStruct::WITHIN_LABEL) { // Split label around the selection - LabelStruct *l = new LabelStruct(); - l->t = t1; - l->t1 = mLabels[i]->t1; - l->title = mLabels[i]->title; + const LabelStruct &label = *mLabels[i]; + LabelStruct *l = + new LabelStruct(label.selectedRegion, t1, label.getT1(), + label.title); - mLabels[i]->t1 = t0; + mLabels[i]->selectedRegion.setT1(t0); // This might not be the right place to insert, but we sort at the end ++i; @@ -2441,12 +2476,12 @@ bool LabelTrack::Silence(double t0, double t1) else if (relation == LabelStruct::ENDS_IN_LABEL) { // Beginning of label to selection end - mLabels[i]->t = t1; + mLabels[i]->selectedRegion.setT0(t1); } else if (relation == LabelStruct::BEGINS_IN_LABEL) { // End of label to selection beginning - mLabels[i]->t1 = t0; + mLabels[i]->selectedRegion.setT1(t0); } else if (relation == LabelStruct::SURROUNDS_LABEL) { @@ -2466,11 +2501,14 @@ bool LabelTrack::InsertSilence(double t, double len) int numLabels = mLabels.Count(); for (int i = 0; i < numLabels; i++) { - if (mLabels[i]->t >= t) - mLabels[i]->t += len; + double t0 = mLabels[i]->getT0(); + double t1 = mLabels[i]->getT1(); + if (t0 >= t) + t0 += len; - if (mLabels[i]->t1 >= t) - mLabels[i]->t1 += len; + if (t1 >= t) + t1 += len; + mLabels[i]->selectedRegion.setTimes(t0, t1); } return true; @@ -2501,9 +2539,9 @@ int LabelTrack::GetLabelIndex(double t, double t1) for( i=0;it - t ) > delta ) + if( fabs( l->getT0() - t ) > delta ) continue; - if( fabs( l->t1 - t1 ) > delta ) + if( fabs( l->getT1() - t1 ) > delta ) continue; return i; } @@ -2511,19 +2549,17 @@ int LabelTrack::GetLabelIndex(double t, double t1) return wxNOT_FOUND; } -int LabelTrack::AddLabel(double t, double t1, const wxString &title) +int LabelTrack::AddLabel(const SelectedRegion &selectedRegion, + const wxString &title) { - LabelStruct *l = new LabelStruct(); - l->t = t; - l->t1 = t1; - l->title = title; + LabelStruct *l = new LabelStruct(selectedRegion, title); mCurrentCursorPos = title.length(); mInitialCursorPos = mCurrentCursorPos; int len = mLabels.Count(); int pos = 0; - while (pos < len && mLabels[pos]->t < t) + while (pos < len && mLabels[pos]->getT0() < selectedRegion.t0()) pos++; mLabels.Insert(l, pos); @@ -2710,7 +2746,8 @@ void LabelTrack::SortLabels() for (i = 1; i < (int)mLabels.Count(); i++) { j=i-1; - while( (j>=0) && (mLabels[j]->t > mLabels[i]->t) ) + while( (j>=0) && (mLabels[j]->getT0() > + mLabels[i]->getT0()) ) { j--; } @@ -2756,7 +2793,8 @@ wxString LabelTrack::GetTextOfLabels(double t0, double t1) for (unsigned int i=0; i < mLabels.GetCount(); ++i) { - if (mLabels[i]->t >= t0 && mLabels[i]->t1 <= t1) + if (mLabels[i]->getT0() >= t0 && + mLabels[i]->getT1() <= t1) { if (!firstLabel) retVal += '\t'; diff --git a/src/LabelTrack.h b/src/LabelTrack.h index bf34e9d5b..acfe33fbf 100644 --- a/src/LabelTrack.h +++ b/src/LabelTrack.h @@ -13,6 +13,7 @@ #ifndef _LABELTRACK_ #define _LABELTRACK_ +#include "SelectedRegion.h" #include "Track.h" #include @@ -39,16 +40,27 @@ class TimeWarper; class LabelStruct { + // disallow copy +private: + LabelStruct(const LabelStruct&); + LabelStruct& operator= (const LabelStruct&); public: - LabelStruct(); + // Copies region + LabelStruct(const SelectedRegion& region, const wxString &aTitle); + // Copies region but then overwrites other times + LabelStruct(const SelectedRegion& region, double t0, double t1, + const wxString &aTitle); void DrawLines( wxDC & dc, const wxRect & r); void DrawGlyphs( wxDC & dc, const wxRect & r, int GlyphLeft, int GlyphRight); void DrawText( wxDC & dc, const wxRect & r); void DrawTextBox( wxDC & dc, const wxRect & r); void DrawHighlight( wxDC & dc, int xPos1, int xPos2, int charHeight); void getXPos( wxDC & dc, int * xPos1, int cursorPos); - double getDuration(){return t1-t;}; - void AdjustEdge( int iEdge, double fNewTime); + double getDuration() const { return selectedRegion.duration(); } + double getT0() const { return selectedRegion.t0(); } + double getT1() const { return selectedRegion.t1(); } + // Returns true iff the label got inverted: + bool AdjustEdge( int iEdge, double fNewTime); void MoveLabel( int iEdge, double fNewTime); /// Relationships between selection region and labels @@ -70,8 +82,7 @@ public: LabelTrack *parent = NULL); public: - double t; /// Time for left hand of label. - double t1; /// Time for right hand of label. + SelectedRegion selectedRegion; wxString title; /// Text of the label. int width; /// width of the text in pixels. @@ -165,11 +176,11 @@ class AUDACITY_DLL_API LabelTrack : public Track void SetDrawCursor(bool drawCursorFlag) { mDrawCursor = drawCursorFlag; }; bool HandleMouse(const wxMouseEvent & evt, wxRect & r, double h, double pps, - double *newSel0, double *newSel1); + SelectedRegion *newSel); bool CaptureKey(wxKeyEvent & event); - bool OnKeyDown(double & sel0, double & sel1, wxKeyEvent & event); - bool OnChar(double & sel0, double & sel1, wxKeyEvent & event); + bool OnKeyDown(SelectedRegion &sel, wxKeyEvent & event); + bool OnChar(SelectedRegion &sel, wxKeyEvent & event); void Import(wxTextFile & f); void Export(wxTextFile & f); @@ -182,7 +193,7 @@ class AUDACITY_DLL_API LabelTrack : public Track const LabelStruct *GetLabel(int index) const; //This returns the index of the label we just added. - int AddLabel(double t, double t1, const wxString &title = wxT("")); + int AddLabel(const SelectedRegion ®ion, const wxString &title = wxT("")); //And this tells us the index, if there is a label already there. int GetLabelIndex(double t, double t1); diff --git a/src/Lyrics.cpp b/src/Lyrics.cpp index 5370f4107..11f2fe00b 100644 --- a/src/Lyrics.cpp +++ b/src/Lyrics.cpp @@ -58,7 +58,7 @@ void HighlightTextCtrl::OnMouseEvent(wxMouseEvent& event) //v Should probably select to end as in AudacityProject::OnSelectCursorEnd, // but better to generalize that in AudacityProject methods. - pProj->mViewInfo.sel1 = pCurSyl->t; + pProj->mViewInfo.selectedRegion.setT1(pCurSyl->t); } } diff --git a/src/Menus.cpp b/src/Menus.cpp index 8df240196..eb9e5a8a8 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -1416,7 +1416,7 @@ wxUint32 AudacityProject::GetUpdateFlags() else flags |= AudioIOBusyFlag; - if (mViewInfo.sel1 > mViewInfo.sel0) + if (!mViewInfo.selectedRegion.isPoint()) flags |= TimeSelectedFlag; TrackListIterator iter(mTracks); @@ -1432,7 +1432,8 @@ wxUint32 AudacityProject::GetUpdateFlags() flags |= TracksSelectedFlag; for (int i = 0; i < lt->GetNumLabels(); i++) { const LabelStruct *ls = lt->GetLabel(i); - if (ls->t >= mViewInfo.sel0 && ls->t1 <= mViewInfo.sel1) { + if (ls->getT0() >= mViewInfo.selectedRegion.t0() && + ls->getT1() <= mViewInfo.selectedRegion.t1()) { flags |= LabelsSelectedFlag; break; } @@ -1523,7 +1524,8 @@ wxUint32 AudacityProject::GetUpdateFlags() void AudacityProject::SelectAllIfNone() { wxUint32 flags = GetUpdateFlags(); - if(((flags & TracksSelectedFlag) ==0) || (mViewInfo.sel0 >= mViewInfo.sel1)) + if(((flags & TracksSelectedFlag) ==0) || + (mViewInfo.selectedRegion.isPoint())) OnSelectAll(); } @@ -1783,10 +1785,11 @@ void AudacityProject::OnPlayToSelection() double t0,t1; // check region between pointer and the nearest selection edge - if (fabs(pos - mViewInfo.sel0) < fabs(pos - mViewInfo.sel1)) { - t0 = t1 = mViewInfo.sel0; + if (fabs(pos - mViewInfo.selectedRegion.t0()) < + fabs(pos - mViewInfo.selectedRegion.t1())) { + t0 = t1 = mViewInfo.selectedRegion.t0(); } else { - t0 = t1 = mViewInfo.sel1; + t0 = t1 = mViewInfo.selectedRegion.t1(); } if( pos < t1) t0=pos; @@ -1920,10 +1923,7 @@ void AudacityProject::OnPlayStopSelect() if (gAudioIO->IsStreamActive(GetAudioIOToken())) { toolbar->SetPlay(false); //Pops toolbar->SetStop(true); //Pushes stop down - mViewInfo.sel0 = gAudioIO->GetStreamTime(); - if( mViewInfo.sel1 < mViewInfo.sel0 ) { - mViewInfo.sel1 = mViewInfo.sel0; - } + mViewInfo.selectedRegion.setT0(gAudioIO->GetStreamTime(), false); ModifyState(false); // without bWantsAutoSave toolbar->OnStop(evt); } @@ -1942,10 +1942,7 @@ void AudacityProject::OnStopSelect() wxCommandEvent evt; if (gAudioIO->IsStreamActive()) { - mViewInfo.sel0 = gAudioIO->GetStreamTime(); - if( mViewInfo.sel1 < mViewInfo.sel0 ) { - mViewInfo.sel1 = mViewInfo.sel0; - } + mViewInfo.selectedRegion.setT0(gAudioIO->GetStreamTime(), false); GetControlToolBar()->OnStop(evt); ModifyState(false); // without bWantsAutoSave } @@ -2253,34 +2250,24 @@ void AudacityProject::OnSetLeftSelection() if ((GetAudioIOToken() > 0) && gAudioIO->IsStreamActive(GetAudioIOToken())) { double indicator = gAudioIO->GetStreamTime(); - mViewInfo.sel0 = indicator; + mViewInfo.selectedRegion.setT0(indicator, false); bSelChanged = true; } else { wxString fmt = GetSelectionFormat(); TimeDialog dlg(this, _("Set Left Selection Boundary"), - fmt, mRate, mViewInfo.sel0, _("Position")); + fmt, mRate, mViewInfo.selectedRegion.t0(), _("Position")); if (wxID_OK == dlg.ShowModal()) { //Get the value from the dialog - mViewInfo.sel0 = dlg.GetTimeValue(); - - //Make sure it is 'legal' - if (mViewInfo.sel0 < 0.0) - mViewInfo.sel0 = 0.0; - + mViewInfo.selectedRegion.setT0( + std::max(0.0, dlg.GetTimeValue()), false); bSelChanged = true; } } - if (mViewInfo.sel1 < mViewInfo.sel0) - { - mViewInfo.sel1 = mViewInfo.sel0; - bSelChanged = true; - } - if (bSelChanged) { ModifyState(false); @@ -2295,34 +2282,24 @@ void AudacityProject::OnSetRightSelection() if ((GetAudioIOToken() > 0) && gAudioIO->IsStreamActive(GetAudioIOToken())) { double indicator = gAudioIO->GetStreamTime(); - mViewInfo.sel1 = indicator; + mViewInfo.selectedRegion.setT1(indicator, false); bSelChanged = true; } else { wxString fmt = GetSelectionFormat(); TimeDialog dlg(this, _("Set Right Selection Boundary"), - fmt, mRate, mViewInfo.sel1, _("Position")); + fmt, mRate, mViewInfo.selectedRegion.t1(), _("Position")); if (wxID_OK == dlg.ShowModal()) { //Get the value from the dialog - mViewInfo.sel1 = dlg.GetTimeValue(); - - //Make sure it is 'legal' - if(mViewInfo.sel1 < 0) - mViewInfo.sel1 = 0; - + mViewInfo.selectedRegion.setT1( + std::max(0.0, dlg.GetTimeValue()), false); bSelChanged = true; } } - if (mViewInfo.sel0 > mViewInfo.sel1) - { - mViewInfo.sel0 = mViewInfo.sel1; - bSelChanged = true; - } - if (bSelChanged) { ModifyState(false); @@ -2633,15 +2610,12 @@ double AudacityProject::NearestZeroCrossing(double t0) void AudacityProject::OnZeroCrossing() { - if (mViewInfo.sel0 == mViewInfo.sel1) - mViewInfo.sel0 = mViewInfo.sel1 = - NearestZeroCrossing(mViewInfo.sel0); + const double t0 = NearestZeroCrossing(mViewInfo.selectedRegion.t0()); + if (mViewInfo.selectedRegion.isPoint()) + mViewInfo.selectedRegion.setTimes(t0, t0); else { - mViewInfo.sel0 = NearestZeroCrossing(mViewInfo.sel0); - mViewInfo.sel1 = NearestZeroCrossing(mViewInfo.sel1); - - if (mViewInfo.sel1 < mViewInfo.sel0) - mViewInfo.sel1 = mViewInfo.sel0; + const double t1 = NearestZeroCrossing(mViewInfo.selectedRegion.t1()); + mViewInfo.selectedRegion.setTimes(t0, t1); } ModifyState(false); @@ -2725,7 +2699,7 @@ bool AudacityProject::OnEffect(int type, } if (f->DoEffect(this, type, mRate, mTracks, mTrackFactory, - &mViewInfo.sel0, &mViewInfo.sel1, params)) { + &mViewInfo.selectedRegion, params)) { if (saveState) { wxString longDesc = f->GetEffectDescription(); @@ -2754,7 +2728,8 @@ bool AudacityProject::OnEffect(int type, //mchinen:12/14/08 reapplying for generate effects if ( f->GetEffectFlags() & INSERT_EFFECT) { - if (count == 0 || (clean && mViewInfo.sel0 == 0.0)) OnZoomFit(); + if (count == 0 || (clean && mViewInfo.selectedRegion.t0() == 0.0)) + OnZoomFit(); // mTrackPanel->Refresh(false); } RedrawProject(); @@ -3043,7 +3018,8 @@ void AudacityProject::OnExportSelection() wxGetApp().SetMissingAliasedFileWarningShouldShow(true); e.SetFileDialogTitle( _("Export Selected Audio") ); - e.Process(this, true, mViewInfo.sel0, mViewInfo.sel1); + e.Process(this, true, mViewInfo.selectedRegion.t0(), + mViewInfo.selectedRegion.t1()); } void AudacityProject::OnExportMultiple() @@ -3110,7 +3086,7 @@ void AudacityProject::OnUndo() return; } - TrackList *l = mUndoManager.Undo(&mViewInfo.sel0, &mViewInfo.sel1); + TrackList *l = mUndoManager.Undo(&mViewInfo.selectedRegion); PopState(l); mTrackPanel->SetFocusedTrack(NULL); @@ -3131,7 +3107,7 @@ void AudacityProject::OnRedo() return; } - TrackList *l = mUndoManager.Redo(&mViewInfo.sel0, &mViewInfo.sel1); + TrackList *l = mUndoManager.Redo(&mViewInfo.selectedRegion); PopState(l); mTrackPanel->SetFocusedTrack(NULL); @@ -3175,10 +3151,12 @@ void AudacityProject::OnCut() #if defined(USE_MIDI) if (n->GetKind() == Track::Note) // Since portsmf has a built-in cut operator, we use that instead - n->Cut(mViewInfo.sel0, mViewInfo.sel1, &dest); + n->Cut(mViewInfo.selectedRegion.t0(), + mViewInfo.selectedRegion.t1(), &dest); else #endif - n->Copy(mViewInfo.sel0, mViewInfo.sel1, &dest); + n->Copy(mViewInfo.selectedRegion.t0(), + mViewInfo.selectedRegion.t1(), &dest); if (dest) { dest->SetChannel(n->GetChannel()); @@ -3203,30 +3181,32 @@ void AudacityProject::OnCut() #endif case Track::Wave: if (gPrefs->Read(wxT("/GUI/EnableCutLines"), (long)0)) { - ((WaveTrack*)n)->ClearAndAddCutLine(mViewInfo.sel0, - mViewInfo.sel1); + ((WaveTrack*)n)->ClearAndAddCutLine( + mViewInfo.selectedRegion.t0(), + mViewInfo.selectedRegion.t1()); break; } // Fall through default: - n->Clear(mViewInfo.sel0, mViewInfo.sel1); + n->Clear(mViewInfo.selectedRegion.t0(), + mViewInfo.selectedRegion.t1()); break; } } n = iter.Next(); } - msClipT0 = mViewInfo.sel0; - msClipT1 = mViewInfo.sel1; + msClipT0 = mViewInfo.selectedRegion.t0(); + msClipT1 = mViewInfo.selectedRegion.t1(); msClipProject = this; PushState(_("Cut to the clipboard"), _("Cut")); RedrawProject(); - mViewInfo.sel1 = mViewInfo.sel0; + mViewInfo.selectedRegion.collapseToT0(); } @@ -3243,11 +3223,15 @@ void AudacityProject::OnSplitCut() dest = NULL; if (n->GetKind() == Track::Wave) { - ((WaveTrack*)n)->SplitCut(mViewInfo.sel0, mViewInfo.sel1, &dest); + ((WaveTrack*)n)->SplitCut( + mViewInfo.selectedRegion.t0(), + mViewInfo.selectedRegion.t1(), &dest); } else { - n->Copy(mViewInfo.sel0, mViewInfo.sel1, &dest); - n->Silence(mViewInfo.sel0, mViewInfo.sel1); + n->Copy(mViewInfo.selectedRegion.t0(), + mViewInfo.selectedRegion.t1(), &dest); + n->Silence(mViewInfo.selectedRegion.t0(), + mViewInfo.selectedRegion.t1()); } if (dest) { dest->SetChannel(n->GetChannel()); @@ -3259,8 +3243,8 @@ void AudacityProject::OnSplitCut() n = iter.Next(); } - msClipT0 = mViewInfo.sel0; - msClipT1 = mViewInfo.sel1; + msClipT0 = mViewInfo.selectedRegion.t0(); + msClipT1 = mViewInfo.selectedRegion.t1(); msClipProject = this; PushState(_("Split-cut to the clipboard"), _("Split Cut")); @@ -3294,7 +3278,8 @@ void AudacityProject::OnCopy() while (n) { if (n->GetSelected()) { dest = NULL; - n->Copy(mViewInfo.sel0, mViewInfo.sel1, &dest); + n->Copy(mViewInfo.selectedRegion.t0(), + mViewInfo.selectedRegion.t1(), &dest); if (dest) { dest->SetChannel(n->GetChannel()); dest->SetLinked(n->GetLinked()); @@ -3305,8 +3290,8 @@ void AudacityProject::OnCopy() n = iter.Next(); } - msClipT0 = mViewInfo.sel0; - msClipT1 = mViewInfo.sel1; + msClipT0 = mViewInfo.selectedRegion.t0(); + msClipT1 = mViewInfo.selectedRegion.t1(); msClipProject = this; //Make sure the menus/toolbar states get updated @@ -3324,8 +3309,8 @@ void AudacityProject::OnPaste() return; // Otherwise, paste into the selected tracks. - double t0 = mViewInfo.sel0; - double t1 = mViewInfo.sel1; + double t0 = mViewInfo.selectedRegion.t0(); + double t1 = mViewInfo.selectedRegion.t1(); TrackListIterator iter(mTracks); TrackListIterator clipIter(msClipboard); @@ -3512,7 +3497,7 @@ void AudacityProject::OnPaste() if (bPastedSomething) { - mViewInfo.sel1 = t0 + msClipT1 - msClipT0; + mViewInfo.selectedRegion.setT1(t0 + msClipT1 - msClipT0); PushState(_("Pasted from the clipboard"), _("Paste")); @@ -3535,7 +3520,8 @@ bool AudacityProject::HandlePasteText() if (pLabelTrack->IsSelected()) { // Yes, so try pasting into it - if (pLabelTrack->PasteSelectedText(mViewInfo.sel0, mViewInfo.sel1)) + if (pLabelTrack->PasteSelectedText(mViewInfo.selectedRegion.t0(), + mViewInfo.selectedRegion.t1())) { PushState(_("Pasted text from the clipboard"), _("Paste")); @@ -3637,8 +3623,10 @@ bool AudacityProject::HandlePasteNothingSelected() double projRate = p->GetRate(); double quantT0 = QUANTIZED_TIME(msClipT0, projRate); double quantT1 = QUANTIZED_TIME(msClipT1, projRate); - mViewInfo.sel0 = 0.0; // anywhere else and this should be half a sample earlier - mViewInfo.sel1 = quantT1 - quantT0; + mViewInfo.selectedRegion.setTimes( + 0.0, // anywhere else and this should be + // half a sample earlier + quantT1 - quantT0); PushState(_("Pasted from the clipboard"), _("Paste")); @@ -3699,8 +3687,10 @@ void AudacityProject::OnPasteNewLabel() plt->Unselect(); // Add a new label, paste into it - lt->AddLabel(mViewInfo.sel0, mViewInfo.sel1); - if (lt->PasteSelectedText(mViewInfo.sel0, mViewInfo.sel1)) + // Paul L: copy whatever defines the selected region, not just times + lt->AddLabel(mViewInfo.selectedRegion); + if (lt->PasteSelectedText(mViewInfo.selectedRegion.t0(), + mViewInfo.selectedRegion.t1())) bPastedSomething = true; // Set previous track @@ -3726,7 +3716,9 @@ void AudacityProject::OnPasteOver() // not currently in use it appears { if((msClipT1 - msClipT0) > 0.0) { - mViewInfo.sel1 = mViewInfo.sel0 + (msClipT1 - msClipT0); // MJS: pointless, given what we do in OnPaste? + mViewInfo.selectedRegion.setT1( + mViewInfo.selectedRegion.t0() + (msClipT1 - msClipT0)); + // MJS: pointless, given what we do in OnPaste? } OnPaste(); @@ -3735,7 +3727,7 @@ void AudacityProject::OnPasteOver() // not currently in use it appears void AudacityProject::OnTrim() { - if (mViewInfo.sel0 >= mViewInfo.sel1) + if (mViewInfo.selectedRegion.isPoint()) return; TrackListIterator iter(mTracks); @@ -3747,13 +3739,15 @@ void AudacityProject::OnTrim() { #if defined(USE_MIDI) case Track::Note: - ((NoteTrack*)n)->Trim(mViewInfo.sel0, mViewInfo.sel1); + ((NoteTrack*)n)->Trim(mViewInfo.selectedRegion.t0(), + mViewInfo.selectedRegion.t1()); break; #endif case Track::Wave: //Delete the section before the left selector - ((WaveTrack*)n)->Trim(mViewInfo.sel0, mViewInfo.sel1); + ((WaveTrack*)n)->Trim(mViewInfo.selectedRegion.t0(), + mViewInfo.selectedRegion.t1()); break; default: @@ -3764,7 +3758,7 @@ void AudacityProject::OnTrim() } PushState(wxString::Format(_("Trim selected audio tracks from %.2f seconds to %.2f seconds"), - mViewInfo.sel0, mViewInfo.sel1), + mViewInfo.selectedRegion.t0(), mViewInfo.selectedRegion.t1()), _("Trim Audio")); RedrawProject(); @@ -3785,18 +3779,20 @@ void AudacityProject::OnSplitDelete() if (n->GetSelected()) { if (n->GetKind() == Track::Wave) { - ((WaveTrack*)n)->SplitDelete(mViewInfo.sel0, mViewInfo.sel1); + ((WaveTrack*)n)->SplitDelete(mViewInfo.selectedRegion.t0(), + mViewInfo.selectedRegion.t1()); } else { - n->Silence(mViewInfo.sel0, mViewInfo.sel1); + n->Silence(mViewInfo.selectedRegion.t0(), + mViewInfo.selectedRegion.t1()); } } n = iter.Next(); } PushState(wxString::Format(_("Split-deleted %.2f seconds at t=%.2f"), - mViewInfo.sel1 - mViewInfo.sel0, - mViewInfo.sel0), + mViewInfo.selectedRegion.duration(), + mViewInfo.selectedRegion.t0()), _("Split Delete")); RedrawProject(); @@ -3812,15 +3808,16 @@ void AudacityProject::OnDisjoin() if (n->GetSelected()) { if (n->GetKind() == Track::Wave) { - ((WaveTrack*)n)->Disjoin(mViewInfo.sel0, mViewInfo.sel1); + ((WaveTrack*)n)->Disjoin(mViewInfo.selectedRegion.t0(), + mViewInfo.selectedRegion.t1()); } } n = iter.Next(); } PushState(wxString::Format(_("Detached %.2f seconds at t=%.2f"), - mViewInfo.sel1 - mViewInfo.sel0, - mViewInfo.sel0), + mViewInfo.selectedRegion.duration(), + mViewInfo.selectedRegion.t0()), _("Detach")); RedrawProject(); @@ -3836,15 +3833,16 @@ void AudacityProject::OnJoin() if (n->GetSelected()) { if (n->GetKind() == Track::Wave) { - ((WaveTrack*)n)->Join(mViewInfo.sel0, mViewInfo.sel1); + ((WaveTrack*)n)->Join(mViewInfo.selectedRegion.t0(), + mViewInfo.selectedRegion.t1()); } } n = iter.Next(); } PushState(wxString::Format(_("Joined %.2f seconds at t=%.2f"), - mViewInfo.sel1 - mViewInfo.sel0, - mViewInfo.sel0), + mViewInfo.selectedRegion.duration(), + mViewInfo.selectedRegion.t0()), _("Join")); RedrawProject(); @@ -3855,11 +3853,12 @@ void AudacityProject::OnSilence() SelectedTrackListOfKindIterator iter(Track::Wave, mTracks); for (Track *n = iter.First(); n; n = iter.Next()) - n->Silence(mViewInfo.sel0, mViewInfo.sel1); + n->Silence(mViewInfo.selectedRegion.t0(), mViewInfo.selectedRegion.t1()); PushState(wxString:: Format(_("Silenced selected tracks for %.2f seconds at %.2f"), - mViewInfo.sel1 - mViewInfo.sel0, mViewInfo.sel0), + mViewInfo.selectedRegion.duration(), + mViewInfo.selectedRegion.t0()), _("Silence")); mTrackPanel->Refresh(false); @@ -3875,10 +3874,11 @@ void AudacityProject::OnDuplicate() while (n) { if (n->GetSelected()) { Track *dest = NULL; - n->Copy(mViewInfo.sel0, mViewInfo.sel1, &dest); + n->Copy(mViewInfo.selectedRegion.t0(), + mViewInfo.selectedRegion.t1(), &dest); if (dest) { dest->Init(*n); - dest->SetOffset(wxMax(mViewInfo.sel0, n->GetOffset())); + dest->SetOffset(wxMax(mViewInfo.selectedRegion.t0(), n->GetOffset())); mTracks->Add(dest); } } @@ -3897,7 +3897,7 @@ void AudacityProject::OnDuplicate() void AudacityProject::OnCutLabels() { - if( mViewInfo.sel0 >= mViewInfo.sel1 ) + if( mViewInfo.selectedRegion.isPoint() ) return; // Because of grouping the copy may need to operate on different tracks than @@ -3911,7 +3911,7 @@ void AudacityProject::OnCutLabels() msClipProject = this; - mViewInfo.sel1 = mViewInfo.sel0; + mViewInfo.selectedRegion.collapseToT0(); PushState( /* i18n-hint: (verb) past tense. Audacity has just cut the labeled audio regions.*/ @@ -3924,7 +3924,7 @@ void AudacityProject::OnCutLabels() void AudacityProject::OnSplitCutLabels() { - if( mViewInfo.sel0 >= mViewInfo.sel1 ) + if( mViewInfo.selectedRegion.isPoint() ) return; EditClipboardByLabel( &WaveTrack::SplitCut ); @@ -3942,7 +3942,7 @@ void AudacityProject::OnSplitCutLabels() void AudacityProject::OnCopyLabels() { - if( mViewInfo.sel0 >= mViewInfo.sel1 ) + if( mViewInfo.selectedRegion.isPoint() ) return; EditClipboardByLabel( &WaveTrack::Copy ); @@ -3958,12 +3958,12 @@ void AudacityProject::OnCopyLabels() void AudacityProject::OnDeleteLabels() { - if( mViewInfo.sel0 >= mViewInfo.sel1 ) + if( mViewInfo.selectedRegion.isPoint() ) return; EditByLabel( &WaveTrack::Clear, true ); - mViewInfo.sel1 = mViewInfo.sel0; + mViewInfo.selectedRegion.collapseToT0(); PushState( /* i18n-hint: (verb) Audacity has just deleted the labeled audio regions*/ @@ -3976,7 +3976,7 @@ void AudacityProject::OnDeleteLabels() void AudacityProject::OnSplitDeleteLabels() { - if( mViewInfo.sel0 >= mViewInfo.sel1 ) + if( mViewInfo.selectedRegion.isPoint() ) return; EditByLabel( &WaveTrack::SplitDelete, false ); @@ -3992,7 +3992,7 @@ void AudacityProject::OnSplitDeleteLabels() void AudacityProject::OnSilenceLabels() { - if( mViewInfo.sel0 >= mViewInfo.sel1 ) + if( mViewInfo.selectedRegion.isPoint() ) return; EditByLabel( &WaveTrack::Silence, false ); @@ -4021,7 +4021,7 @@ void AudacityProject::OnSplitLabels() void AudacityProject::OnJoinLabels() { - if( mViewInfo.sel0 >= mViewInfo.sel1 ) + if( mViewInfo.selectedRegion.isPoint() ) return; EditByLabel( &WaveTrack::Join, false ); @@ -4037,7 +4037,7 @@ void AudacityProject::OnJoinLabels() void AudacityProject::OnDisjoinLabels() { - if( mViewInfo.sel0 >= mViewInfo.sel1 ) + if( mViewInfo.selectedRegion.isPoint() ) return; EditByLabel( &WaveTrack::Disjoin, false ); @@ -4057,8 +4057,8 @@ void AudacityProject::OnSplit() { TrackListIterator iter(mTracks); - double sel0 = mViewInfo.sel0; - double sel1 = mViewInfo.sel1; + double sel0 = mViewInfo.selectedRegion.t0(); + double sel1 = mViewInfo.selectedRegion.t1(); for (Track* n=iter.First(); n; n = iter.Next()) { @@ -4088,8 +4088,8 @@ void AudacityProject::OnSplit() while (n) { if (n->GetSelected()) { - double sel0 = mViewInfo.sel0; - double sel1 = mViewInfo.sel1; + double sel0 = mViewInfo.selectedRegion.t0(); + double sel1 = mViewInfo.selectedRegion.t1(); dest = NULL; n->Copy(sel0, sel1, &dest); @@ -4136,20 +4136,22 @@ void AudacityProject::OnSplitNew() Track *dest = NULL; double offset = n->GetOffset(); if (n->GetKind() == Track::Wave) { - ((WaveTrack*)n)->SplitCut(mViewInfo.sel0, mViewInfo.sel1, &dest); + ((WaveTrack*)n)->SplitCut(mViewInfo.selectedRegion.t0(), + mViewInfo.selectedRegion.t1(), &dest); } #if 0 // LL: For now, just skip all non-wave tracks since the other do not // yet support proper splitting. else { - n->Cut(mViewInfo.sel0, mViewInfo.sel1, &dest); + n->Cut(mViewInfo.selectedRegion.t0(), + mViewInfo.selectedRegion.t1(), &dest); } #endif if (dest) { dest->SetChannel(n->GetChannel()); dest->SetLinked(n->GetLinked()); dest->SetName(n->GetName()); - dest->SetOffset(wxMax(mViewInfo.sel0, offset)); + dest->SetOffset(wxMax(mViewInfo.selectedRegion.t0(), offset)); mTracks->Add(dest); } } @@ -4173,8 +4175,8 @@ void AudacityProject::OnSelectAll() t->SetSelected(true); t = iter.Next(); } - mViewInfo.sel0 = mTracks->GetMinOffset(); - mViewInfo.sel1 = mTracks->GetEndTime(); + mViewInfo.selectedRegion.setTimes( + mTracks->GetMinOffset(), mTracks->GetEndTime()); ModifyState(false); @@ -4186,7 +4188,7 @@ void AudacityProject::OnSelectAll() void AudacityProject::OnSelectNone() { this->SelectNone(); - mViewInfo.sel1 = mViewInfo.sel0; + mViewInfo.selectedRegion.collapseToT0(); ModifyState(false); } @@ -4206,7 +4208,7 @@ void AudacityProject::OnSelectCursorEnd() t = iter.Next(); } - mViewInfo.sel1 = maxEndOffset; + mViewInfo.selectedRegion.setT1(maxEndOffset); ModifyState(false); @@ -4229,7 +4231,7 @@ void AudacityProject::OnSelectStartCursor() t = iter.Next(); } - mViewInfo.sel0 = minOffset; + mViewInfo.selectedRegion.setT0(minOffset); ModifyState(false); @@ -4294,24 +4296,26 @@ void AudacityProject::ZoomInByFactor( double ZoomFactor ) // partially on-screen bool selectionIsOnscreen = - (mViewInfo.sel0 < mViewInfo.h + mViewInfo.screen) && - (mViewInfo.sel1 >= mViewInfo.h); + (mViewInfo.selectedRegion.t0() < mViewInfo.h + mViewInfo.screen) && + (mViewInfo.selectedRegion.t1() >= mViewInfo.h); bool selectionFillsScreen = - (mViewInfo.sel0 < mViewInfo.h) && - (mViewInfo.sel1 > mViewInfo.h + mViewInfo.screen); + (mViewInfo.selectedRegion.t0() < mViewInfo.h) && + (mViewInfo.selectedRegion.t1() > mViewInfo.h + mViewInfo.screen); if (selectionIsOnscreen && !selectionFillsScreen) { // Start with the center of the selection - double selCenter = (mViewInfo.sel0 + mViewInfo.sel1) / 2; + double selCenter = (mViewInfo.selectedRegion.t0() + + mViewInfo.selectedRegion.t1()) / 2; // If the selection center is off-screen, pick the // center of the part that is on-screen. if (selCenter < mViewInfo.h) - selCenter = mViewInfo.h + (mViewInfo.sel1 - mViewInfo.h) / 2; + selCenter = mViewInfo.h + + (mViewInfo.selectedRegion.t1() - mViewInfo.h) / 2; if (selCenter > mViewInfo.h + mViewInfo.screen) selCenter = mViewInfo.h + mViewInfo.screen - - (mViewInfo.h + mViewInfo.screen - mViewInfo.sel0) / 2; + (mViewInfo.h + mViewInfo.screen - mViewInfo.selectedRegion.t0()) / 2; // Zoom in Zoom(mViewInfo.zoom *= ZoomFactor); @@ -4332,13 +4336,13 @@ void AudacityProject::ZoomInByFactor( double ZoomFactor ) /* // make sure that the *right-hand* end of the selection is // no further *left* than 1/3 of the way across the screen - if (mViewInfo.sel1 < newh + mViewInfo.screen / 3) - newh = mViewInfo.sel1 - mViewInfo.screen / 3; + if (mViewInfo.selectedRegion.t1() < newh + mViewInfo.screen / 3) + newh = mViewInfo.selectedRegion.t1() - mViewInfo.screen / 3; // make sure that the *left-hand* end of the selection is // no further *right* than 2/3 of the way across the screen - if (mViewInfo.sel0 > newh + mViewInfo.screen * 2 / 3) - newh = mViewInfo.sel0 - mViewInfo.screen * 2 / 3; + if (mViewInfo.selectedRegion.t0() > newh + mViewInfo.screen * 2 / 3) + newh = mViewInfo.selectedRegion.t0() - mViewInfo.screen * 2 / 3; */ TP_ScrollWindow(newh); @@ -4454,7 +4458,7 @@ void AudacityProject::OnZoomFitV() void AudacityProject::OnZoomSel() { - if (mViewInfo.sel1 <= mViewInfo.sel0) + if (mViewInfo.selectedRegion.isPoint()) return; // LL: The "-1" is just a hack to get around an issue where zooming to @@ -4463,24 +4467,25 @@ void AudacityProject::OnZoomSel() // where the selected region may be scrolled off the left of the screen. // I know this isn't right, but until the real rounding or 1-off issue is // found, this will have to work. - Zoom(((mViewInfo.zoom * mViewInfo.screen) - 1) / (mViewInfo.sel1 - mViewInfo.sel0)); - TP_ScrollWindow(mViewInfo.sel0); + Zoom(((mViewInfo.zoom * mViewInfo.screen) - 1) / + (mViewInfo.selectedRegion.t1() - mViewInfo.selectedRegion.t0())); + TP_ScrollWindow(mViewInfo.selectedRegion.t0()); } void AudacityProject::OnGoSelStart() { - if (mViewInfo.sel1 <= mViewInfo.sel0) + if (mViewInfo.selectedRegion.isPoint()) return; - TP_ScrollWindow(mViewInfo.sel0 - (mViewInfo.screen / 2)); + TP_ScrollWindow(mViewInfo.selectedRegion.t0() - (mViewInfo.screen / 2)); } void AudacityProject::OnGoSelEnd() { - if (mViewInfo.sel1 <= mViewInfo.sel0) + if (mViewInfo.selectedRegion.isPoint()) return; - TP_ScrollWindow(mViewInfo.sel1 - (mViewInfo.screen / 2)); + TP_ScrollWindow(mViewInfo.selectedRegion.t1() - (mViewInfo.screen / 2)); } void AudacityProject::OnShowClipping() @@ -4857,17 +4862,16 @@ void AudacityProject::OnMixAndRenderToNewTrack() void AudacityProject::OnSelectionSave() { - mSel0save = mViewInfo.sel0; - mSel1save = mViewInfo.sel1; + mRegionSave = mViewInfo.selectedRegion; } void AudacityProject::OnSelectionRestore() { - if ((mSel0save == 0.0) && (mSel1save == 0.0)) + if ((mRegionSave.t0() == 0.0) && + (mRegionSave.t1() == 0.0)) return; - mViewInfo.sel0 = mSel0save; - mViewInfo.sel1 = mSel1save; + mViewInfo.selectedRegion = mRegionSave; ModifyState(false); @@ -4891,10 +4895,9 @@ void AudacityProject::OnCursorTrackStart() } if (minOffset < 0.0) minOffset = 0.0; - mViewInfo.sel0 = minOffset; - mViewInfo.sel1 = minOffset; + mViewInfo.selectedRegion.setTimes(minOffset, minOffset); ModifyState(false); - mTrackPanel->ScrollIntoView(mViewInfo.sel0); + mTrackPanel->ScrollIntoView(mViewInfo.selectedRegion.t0()); mTrackPanel->Refresh(false); } @@ -4916,26 +4919,25 @@ void AudacityProject::OnCursorTrackEnd() t = iter.Next(); } - mViewInfo.sel0 = maxEndOffset; - mViewInfo.sel1 = maxEndOffset; + mViewInfo.selectedRegion.setTimes(maxEndOffset, maxEndOffset); ModifyState(false); - mTrackPanel->ScrollIntoView(mViewInfo.sel1); + mTrackPanel->ScrollIntoView(mViewInfo.selectedRegion.t1()); mTrackPanel->Refresh(false); } void AudacityProject::OnCursorSelStart() { - mViewInfo.sel1 = mViewInfo.sel0; + mViewInfo.selectedRegion.collapseToT0(); ModifyState(false); - mTrackPanel->ScrollIntoView(mViewInfo.sel0); + mTrackPanel->ScrollIntoView(mViewInfo.selectedRegion.t0()); mTrackPanel->Refresh(false); } void AudacityProject::OnCursorSelEnd() { - mViewInfo.sel0 = mViewInfo.sel1; + mViewInfo.selectedRegion.collapseToT1(); ModifyState(false); - mTrackPanel->ScrollIntoView(mViewInfo.sel1); + mTrackPanel->ScrollIntoView(mViewInfo.selectedRegion.t1()); mTrackPanel->Refresh(false); } @@ -5003,22 +5005,22 @@ void AudacityProject::HandleAlign(int index, bool moveSel) shortAction = _("Start"); break; case kAlignStartSelStart: - delta = mViewInfo.sel0 - minOffset; + delta = mViewInfo.selectedRegion.t0() - minOffset; action = _("start to cursor/selection start"); shortAction = _("Start"); break; case kAlignStartSelEnd: - delta = mViewInfo.sel1 - minOffset; + delta = mViewInfo.selectedRegion.t1() - minOffset; action = _("start to selection end"); shortAction = _("Start"); break; case kAlignEndSelStart: - delta = mViewInfo.sel0 - maxEndOffset; + delta = mViewInfo.selectedRegion.t0() - maxEndOffset; action = _("end to cursor/selection start"); shortAction = _("End"); break; case kAlignEndSelEnd: - delta = mViewInfo.sel1 - maxEndOffset; + delta = mViewInfo.selectedRegion.t1() - maxEndOffset; action = _("end to selection end"); shortAction = _("End"); break; @@ -5105,8 +5107,7 @@ void AudacityProject::HandleAlign(int index, bool moveSel) } if (moveSel) { - mViewInfo.sel0 += delta; - mViewInfo.sel1 += delta; + mViewInfo.selectedRegion.move(delta); action = wxString::Format(_("Aligned/Moved %s"), action.c_str()); shortAction = wxString::Format(_("Align %s/Move"),shortAction.c_str()); PushState(action, shortAction); @@ -5518,7 +5519,7 @@ void AudacityProject::OnRescanDevices() DeviceManager::Instance()->Rescan(); } -int AudacityProject::DoAddLabel(double left, double right) +int AudacityProject::DoAddLabel(const SelectedRegion ®ion) { LabelTrack *lt = NULL; @@ -5558,7 +5559,7 @@ int AudacityProject::DoAddLabel(double left, double right) // SelectNone(); lt->SetSelected(true); - int index = lt->AddLabel(left, right); + int index = lt->AddLabel(region); PushState(_("Added label"), _("Label")); @@ -5584,7 +5585,7 @@ void AudacityProject::OnSyncLock() void AudacityProject::OnAddLabel() { - DoAddLabel(mViewInfo.sel0, mViewInfo.sel1); + DoAddLabel(mViewInfo.selectedRegion); } void AudacityProject::OnAddLabelPlaying() @@ -5592,7 +5593,7 @@ void AudacityProject::OnAddLabelPlaying() if (GetAudioIOToken()>0 && gAudioIO->IsStreamActive(GetAudioIOToken())) { double indicator = gAudioIO->GetStreamTime(); - DoAddLabel(indicator, indicator); + DoAddLabel(SelectedRegion(indicator, indicator)); } } diff --git a/src/Menus.h b/src/Menus.h index 0f43514df..d29354631 100644 --- a/src/Menus.h +++ b/src/Menus.h @@ -51,7 +51,7 @@ double NearestZeroCrossing(double t0); public: //Adds label and returns index of label in labeltrack. -int DoAddLabel(double left, double right); +int DoAddLabel(const SelectedRegion& region); private: @@ -307,8 +307,7 @@ void OnMixAndRenderToNewTrack(); void HandleMixAndRender(bool toNewTrack); private: -double mSel0save; -double mSel1save; +SelectedRegion mRegionSave; public: void OnSelectionSave(); void OnSelectionRestore(); diff --git a/src/MixerBoard.cpp b/src/MixerBoard.cpp index d62835257..afe3da14f 100644 --- a/src/MixerBoard.cpp +++ b/src/MixerBoard.cpp @@ -730,11 +730,11 @@ void MixerTrackCluster::HandleSelect(const bool bShiftDown) { // No range previously selected, so use the range of this track. #ifdef EXPERIMENTAL_MIDI_OUT - mProject->mViewInfo.sel0 = mTrack->GetOffset(); - mProject->mViewInfo.sel1 = mTrack->GetEndTime(); + mProject->mViewInfo.selectedRegion.setTimes( + mTrack->GetOffset(), mTrack->GetEndTime()); #else - mProject->mViewInfo.sel0 = mLeftTrack->GetOffset(); - mProject->mViewInfo.sel1 = mLeftTrack->GetEndTime(); + mProject->mViewInfo.selectedRegion.setTimes( + mLeftTrack->GetOffset(), mLeftTrack->GetEndTime()); #endif } @@ -1213,7 +1213,7 @@ void MixerBoard::RemoveTrackCluster(const WaveTrack* pTrack) #ifdef EXPERIMENTAL_MIDI_OUT -wxBitmap* MixerBoard::GetMusicalInstrumentBitmap(const wxString name) +wxBitmap* MixerBoard::GetMusicalInstrumentBitmap(wxString name) #else wxBitmap* MixerBoard::GetMusicalInstrumentBitmap(const WaveTrack* pLeftTrack) #endif diff --git a/src/Printing.cpp b/src/Printing.cpp index a4191b8ef..bce31eef9 100644 --- a/src/Printing.cpp +++ b/src/Printing.cpp @@ -81,7 +81,7 @@ bool AudacityPrintout::OnPrintPage(int WXUNUSED(page)) artist.SetBackgroundBrushes(*wxWHITE_BRUSH, *wxWHITE_BRUSH, *wxWHITE_PEN, *wxWHITE_PEN); ViewInfo viewInfo; - viewInfo.sel0 = viewInfo.sel1 = 0; + viewInfo.selectedRegion = SelectedRegion(); viewInfo.vpos = 0; viewInfo.h = 0.0; viewInfo.screen = mTracks->GetEndTime() - viewInfo.h; diff --git a/src/Project.cpp b/src/Project.cpp index eab022685..e8e521d7d 100644 --- a/src/Project.cpp +++ b/src/Project.cpp @@ -727,8 +727,7 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id, const wxPoint & pos, const wxSize & size) : wxFrame(parent, id, wxT("Audacity"), pos, size), - mSel0save(0.0), - mSel1save(0.0), + mRegionSave(), mLastPlayMode(normalPlay), mRate((double) gPrefs->Read(wxT("/SamplingRate/DefaultProjectSampleRate"), AudioIO::GetOptimalSupportedSampleRate())), mDefaultFormat((sampleFormat) gPrefs-> @@ -790,8 +789,7 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id, // // Selection - mViewInfo.sel0 = 0.0; - mViewInfo.sel1 = 0.0; + mViewInfo.selectedRegion = SelectedRegion(); // Horizontal scrollbar mViewInfo.total = 1.0; @@ -1105,14 +1103,14 @@ void AudacityProject::SetSel0(double newSel0) { //Bound checking should go on here - mViewInfo.sel0 = newSel0; + mViewInfo.selectedRegion.setT0(newSel0); } void AudacityProject::SetSel1(double newSel1) { //Bound checking should go on here - mViewInfo.sel1 = newSel1; + mViewInfo.selectedRegion.setT1(newSel1); } @@ -1234,8 +1232,7 @@ const wxString & AudacityProject::GetSelectionFormat() void AudacityProject::AS_ModifySelection(double &start, double &end, bool done) { - mViewInfo.sel0 = start; - mViewInfo.sel1 = end; + mViewInfo.selectedRegion.setTimes(start, end); mTrackPanel->Refresh(false); if (done) { ModifyState(false); @@ -1395,7 +1392,8 @@ void AudacityProject::FixScrollbars() // Add 1/4 of a screen of blank space to the end of the longest track mViewInfo.screen = ((double) panelWidth) / mViewInfo.zoom; - double LastTime = wxMax( mTracks->GetEndTime(), mViewInfo.sel1 ); + double LastTime = + wxMax( mTracks->GetEndTime(), mViewInfo.selectedRegion.t1() ); mViewInfo.total = LastTime + mViewInfo.screen / 4; // Don't remove time from total that's still on the screen @@ -2827,11 +2825,19 @@ bool AudacityProject::HandleXMLTag(const wxChar *tag, const wxChar **attrs) requiredTags++; } - if (!wxStrcmp(attr, wxT("sel0"))) - Internat::CompatibleToDouble(value, &mViewInfo.sel0); + if (!wxStrcmp(attr, wxT("sel0"))) { + double t0; + Internat::CompatibleToDouble(value, &t0); + mViewInfo.selectedRegion.setT0(t0, false); + } - if (!wxStrcmp(attr, wxT("sel1"))) - Internat::CompatibleToDouble(value, &mViewInfo.sel1); + if (!wxStrcmp(attr, wxT("sel1"))) { + double t1; + Internat::CompatibleToDouble(value, &t1); + mViewInfo.selectedRegion.setT1(t1, false); + } + + // PRL: to do: persistence of other fields of the selection long longVpos = 0; if (!wxStrcmp(attr, wxT("vpos"))) @@ -3025,8 +3031,9 @@ void AudacityProject::WriteXML(XMLWriter &xmlFile) xmlFile.WriteAttr(wxT("projname"), projName); xmlFile.WriteAttr(wxT("version"), wxT(AUDACITY_FILE_FORMAT_VERSION)); xmlFile.WriteAttr(wxT("audacityversion"), AUDACITY_VERSION_STRING); - xmlFile.WriteAttr(wxT("sel0"), mViewInfo.sel0, 10); - xmlFile.WriteAttr(wxT("sel1"), mViewInfo.sel1, 10); + xmlFile.WriteAttr(wxT("sel0"), mViewInfo.selectedRegion.t0(), 10); + xmlFile.WriteAttr(wxT("sel1"), mViewInfo.selectedRegion.t1(), 10); + // PRL: to do: persistence of other fields of the selection xmlFile.WriteAttr(wxT("vpos"), mViewInfo.vpos); xmlFile.WriteAttr(wxT("h"), mViewInfo.h, 10); xmlFile.WriteAttr(wxT("zoom"), mViewInfo.zoom, 10); @@ -3696,7 +3703,7 @@ void AudacityProject::InitialState() mUndoManager.ClearStates(); - mUndoManager.PushState(mTracks, mViewInfo.sel0, mViewInfo.sel1, + mUndoManager.PushState(mTracks, mViewInfo.selectedRegion, _("Created new project"), wxT("")); mUndoManager.StateSaved(); @@ -3715,7 +3722,7 @@ void AudacityProject::PushState(wxString desc, wxString shortDesc, int flags ) { - mUndoManager.PushState(mTracks, mViewInfo.sel0, mViewInfo.sel1, + mUndoManager.PushState(mTracks, mViewInfo.selectedRegion, desc, shortDesc, flags); mDirty = true; @@ -3746,7 +3753,7 @@ void AudacityProject::PushState(wxString desc, void AudacityProject::ModifyState(bool bWantsAutoSave) { - mUndoManager.ModifyState(mTracks, mViewInfo.sel0, mViewInfo.sel1); + mUndoManager.ModifyState(mTracks, mViewInfo.selectedRegion); if (bWantsAutoSave) AutoSave(); } @@ -3807,7 +3814,7 @@ void AudacityProject::PopState(TrackList * l) void AudacityProject::SetStateTo(unsigned int n) { TrackList *l = - mUndoManager.SetStateTo(n, &mViewInfo.sel0, &mViewInfo.sel1); + mUndoManager.SetStateTo(n, &mViewInfo.selectedRegion); PopState(l); HandleResize(); @@ -3840,7 +3847,7 @@ void AudacityProject::UpdateLyrics() Lyrics* pLyricsPanel = mLyricsWindow->GetLyricsPanel(); pLyricsPanel->Clear(); for (int i = 0; i < pLabelTrack->GetNumLabels(); i++) - pLyricsPanel->Add(pLabelTrack->GetLabel(i)->t, + pLyricsPanel->Add(pLabelTrack->GetLabel(i)->getT0(), pLabelTrack->GetLabel(i)->title); pLyricsPanel->Finish(pLabelTrack->GetEndTime()); pLyricsPanel->Update(this->GetSel0()); @@ -3905,18 +3912,18 @@ void AudacityProject::Clear() while (n) { if (n->GetSelected() || n->IsSyncLockSelected()) { - n->Clear(mViewInfo.sel0, mViewInfo.sel1); + n->Clear(mViewInfo.selectedRegion.t0(), mViewInfo.selectedRegion.t1()); } n = iter.Next(); } - double seconds = mViewInfo.sel1 - mViewInfo.sel0; + double seconds = mViewInfo.selectedRegion.duration(); - mViewInfo.sel1 = mViewInfo.sel0; + mViewInfo.selectedRegion.collapseToT0(); PushState(wxString::Format(_("Deleted %.2f seconds at t=%.2f"), seconds, - mViewInfo.sel0), + mViewInfo.selectedRegion.t0()), _("Delete")); RedrawProject(); @@ -3957,9 +3964,9 @@ void AudacityProject::Zoom(double level) /////////////////////////////////////////////////////////////////// void AudacityProject::Rewind(bool shift) { - mViewInfo.sel0 = 0; - if (!shift || mViewInfo.sel1 < mViewInfo.sel0) - mViewInfo.sel1 = 0; + mViewInfo.selectedRegion.setT0(0, false); + if (!shift) + mViewInfo.selectedRegion.setT1(0); TP_ScrollWindow(0); } @@ -3977,9 +3984,9 @@ void AudacityProject::SkipEnd(bool shift) { double len = mTracks->GetEndTime(); - mViewInfo.sel1 = len; - if (!shift || mViewInfo.sel0 > mViewInfo.sel1) - mViewInfo.sel0 = len; + mViewInfo.selectedRegion.setT1(len, false); + if (!shift) + mViewInfo.selectedRegion.setT0(len); // Make sure the end of the track is visible mTrackPanel->ScrollIntoView(len); @@ -4168,11 +4175,12 @@ void AudacityProject::GetRegionsByLabel( Regions ®ions ) for( int i = 0; i < lt->GetNumLabels(); i++ ) { const LabelStruct *ls = lt->GetLabel( i ); - if( ls->t >= mViewInfo.sel0 && ls->t1 <= mViewInfo.sel1 ) + if( ls->selectedRegion.t0() >= mViewInfo.selectedRegion.t0() && + ls->selectedRegion.t1() <= mViewInfo.selectedRegion.t1() ) { Region *region = new Region; - region->start = ls->t; - region->end = ls->t1; + region->start = ls->getT0(); + region->end = ls->getT1(); regions.Add( region ); } } @@ -4368,10 +4376,12 @@ void AudacityProject::TP_DisplaySelection() audioTime = 0; } - GetSelectionBar()->SetTimes(mViewInfo.sel0, mViewInfo.sel1, audioTime); + GetSelectionBar()->SetTimes(mViewInfo.selectedRegion.t0(), + mViewInfo.selectedRegion.t1(), audioTime); if (!gAudioIO->IsBusy() && !mLockPlayRegion) - mRuler->SetPlayRegion(mViewInfo.sel0, mViewInfo.sel1); + mRuler->SetPlayRegion(mViewInfo.selectedRegion.t0(), + mViewInfo.selectedRegion.t1()); } diff --git a/src/Project.h b/src/Project.h index c859c97b7..717c03cad 100644 --- a/src/Project.h +++ b/src/Project.h @@ -134,8 +134,8 @@ class AUDACITY_DLL_API AudacityProject: public wxFrame, double GetRate() { return mRate; } double GetZoom() { return mViewInfo.zoom; } - double GetSel0() { return mViewInfo.sel0; } - double GetSel1() { return mViewInfo.sel1; } + double GetSel0() { return mViewInfo.selectedRegion.t0(); } + double GetSel1() { return mViewInfo.selectedRegion.t1(); } Track *GetFirstVisible(); void UpdateFirstVisible(); diff --git a/src/SelectedRegion.h b/src/SelectedRegion.h new file mode 100644 index 000000000..44f02cbd4 --- /dev/null +++ b/src/SelectedRegion.h @@ -0,0 +1,121 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + SelectedRegion.h + + Dominic Mazzoni + +**********************************************************************/ + +#ifndef __AUDACITY_SELECTEDREGION__ +#define __AUDACITY_SELECTEDREGION__ + +#include "Audacity.h" + +class AUDACITY_DLL_API SelectedRegion { + + // Maintains the invariant: t1() >= t0() + +public: + SelectedRegion() + : mT0(0.0) + , mT1(0.0) + {} + + SelectedRegion(double t0, double t1) + : mT0(t0) + , mT1(t1) + { ensureOrdering(); } + + explicit + SelectedRegion(const SelectedRegion &x) + : mT0(x.mT0) + , mT1(x.mT1) + {} + + SelectedRegion& operator=(const SelectedRegion& x) + { + if (this != &x) { + mT0 = x.mT0; + mT1 = x.mT1; + } + return *this; + } + + double t0() const { return mT0; } + double t1() const { return mT1; } + double duration() const { return mT1 - mT0; } + bool isPoint() const { return mT1 <= mT0; } + + // PRL: to do: more integrity checks + + // Returns true iff the bounds got swapped + bool setT0(double t, bool maySwap = true) { + mT0 = t; + if (maySwap) + return ensureOrdering(); + else { + if (mT1 < mT0) + mT1 = mT0; + return false; + } + } + + // Returns true iff the bounds got swapped + bool setT1(double t, bool maySwap = true) { + mT1 = t; + if (maySwap) + return ensureOrdering(); + else { + if (mT1 < mT0) + mT0 = mT1; + return false; + } + } + + // Returns true iff the bounds got swapped + bool setTimes(double t0, double t1) { + mT0 = t0; + mT1 = t1; + return ensureOrdering(); + } + + // Returns true iff the bounds got swapped + bool moveT0(double delta, bool maySwap = true) { + return setT0(mT0 + delta, maySwap); + } + + // Returns true iff the bounds got swapped + bool moveT1(double delta, bool maySwap = true) { + return setT1(mT1 + delta, maySwap); + } + + void move(double delta) { + mT0 += delta; + mT1 += delta; + } + + void collapseToT0() { mT1 = mT0; } + + void collapseToT1() { mT0 = mT1; } + +private: + bool ensureOrdering() + { + if (mT1 < mT0) { + const double t = mT1; + mT1 = mT0; + mT0 = t; + return true; + } + else + return false; + } + + double mT0; + double mT1; + +}; + +#endif diff --git a/src/Snap.cpp b/src/Snap.cpp index 30c7b44e5..1cfd26df9 100644 --- a/src/Snap.cpp +++ b/src/Snap.cpp @@ -69,9 +69,11 @@ SnapManager::SnapManager(TrackList *tracks, TrackClipArray *exclusions, LabelTrack *labelTrack = (LabelTrack *)track; for(i = 0; i < labelTrack->GetNumLabels(); i++) { const LabelStruct *label = labelTrack->GetLabel(i); - CondListAdd(label->t, labelTrack); - if (label->t1 != label->t) { - CondListAdd(label->t1, labelTrack); + const double t0 = label->getT0(); + const double t1 = label->getT1(); + CondListAdd(t0, labelTrack); + if (t1 != t0) { + CondListAdd(t1, labelTrack); } } } diff --git a/src/TrackArtist.cpp b/src/TrackArtist.cpp index beffd0cee..3054a036f 100644 --- a/src/TrackArtist.cpp +++ b/src/TrackArtist.cpp @@ -99,7 +99,7 @@ TrackPanel::DoDrawIndicator(); AdornedRulerPanel::DrawIndicator(); [not part of TrackPanel graphics] draw indicator on each track TrackPanel::DoDrawCursor(); - draw cursor on each track [at mViewInfo->sel0] + draw cursor on each track [at mviewInfo->selectedRegion.t0()] AdornedRulerPanel::DrawCursor(); [not part of TrackPanel graphics] TrackPanel::DisplaySelection(); \endcode @@ -1390,7 +1390,8 @@ void TrackArtist::DrawWaveform(WaveTrack *track, bool muted) { DrawBackgroundWithSelection(&dc, r, track, blankSelectedBrush, blankBrush, - viewInfo->sel0, viewInfo->sel1, viewInfo->h, viewInfo->zoom); + viewInfo->selectedRegion.t0(), viewInfo->selectedRegion.t1(), + viewInfo->h, viewInfo->zoom); for (WaveClipList::compatibility_iterator it = track->GetClipIterator(); it; it = it->GetNext()) DrawClipWaveform(track, it->GetData(), dc, r, viewInfo, @@ -1441,8 +1442,8 @@ void TrackArtist::DrawClipWaveform(WaveTrack *track, #endif double h = viewInfo->h; //The horizontal position in seconds double pps = viewInfo->zoom; //points-per-second--the zoom level - double sel0 = viewInfo->sel0; //left selection bound - double sel1 = viewInfo->sel1; //right selection bound + double sel0 = viewInfo->selectedRegion.t0(); //left selection bound + double sel1 = viewInfo->selectedRegion.t1(); //right selection bound double trackLen = clip->GetEndTime() - clip->GetStartTime(); double tOffset = clip->GetOffset(); double rate = clip->GetRate(); @@ -1731,7 +1732,8 @@ void TrackArtist::DrawSpectrum(WaveTrack *track, bool logF) { DrawBackgroundWithSelection(&dc, r, track, blankSelectedBrush, blankBrush, - viewInfo->sel0, viewInfo->sel1, viewInfo->h, viewInfo->zoom); + viewInfo->selectedRegion.t0(), viewInfo->selectedRegion.t1(), + viewInfo->h, viewInfo->zoom); if(!viewInfo->bUpdateTrackIndicator && viewInfo->bIsPlaying) { // BG: Draw (undecorated) waveform instead of spectrum @@ -1789,8 +1791,8 @@ void TrackArtist::DrawClipSpectrum(WaveTrack *track, #endif double h = viewInfo->h; double pps = viewInfo->zoom; - double sel0 = viewInfo->sel0; - double sel1 = viewInfo->sel1; + double sel0 = viewInfo->selectedRegion.t0(); + double sel1 = viewInfo->selectedRegion.t1(); double tOffset = clip->GetOffset(); double rate = clip->GetRate(); @@ -2554,8 +2556,8 @@ void TrackArtist::DrawNoteTrack(NoteTrack *track, SonifyBeginNoteBackground(); double h = viewInfo->h; double pps = viewInfo->zoom; - double sel0 = viewInfo->sel0; - double sel1 = viewInfo->sel1; + double sel0 = viewInfo->selectedRegion.t0(); + double sel1 = viewInfo->selectedRegion.t1(); double h1 = X_TO_TIME(r.x + r.width); @@ -2672,7 +2674,7 @@ void TrackArtist::DrawNoteTrack(NoteTrack *track, iterator.begin(); //for every event Alg_event_ptr evt; - while ((evt = iterator.next())) { + while (0 != (evt = iterator.next())) { if (evt->get_type() == 'n') { // 'n' means a note Alg_note_ptr note = (Alg_note_ptr) evt; // if the note's channel is visible @@ -2681,7 +2683,7 @@ void TrackArtist::DrawNoteTrack(NoteTrack *track, double x1 = x + note->dur; if (x < h1 && x1 > h) { // omit if outside box const char *shape = NULL; - if (note->loud > 0.0 || !(shape = IsShape(note))) { + if (note->loud > 0.0 || 0 == (shape = IsShape(note))) { wxRect nr; // "note rectangle" nr.y = track->PitchToY(note->pitch); nr.height = track->GetPitchHeight(); @@ -2924,8 +2926,8 @@ void TrackArtist::DrawLabelTrack(LabelTrack *track, const wxRect & r, const ViewInfo *viewInfo) { - double sel0 = viewInfo->sel0; - double sel1 = viewInfo->sel1; + double sel0 = viewInfo->selectedRegion.t0(); + double sel1 = viewInfo->selectedRegion.t1(); if (!track->GetSelected() && !track->IsSyncLockSelected()) sel0 = sel1 = 0.0; diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp index d7cb24630..fbaeadae9 100644 --- a/src/TrackPanel.cpp +++ b/src/TrackPanel.cpp @@ -892,8 +892,9 @@ void TrackPanel::SelectTrackLength(Track *t) } } - mViewInfo->sel0 = minOffset; - mViewInfo->sel1 = maxEnd; + // PRL: double click or click on track control. + // should this select all frequencies too? I think not. + mViewInfo->selectedRegion.setTimes(minOffset, maxEnd); } void TrackPanel::GetTracksUsableArea(int *width, int *height) const @@ -1256,7 +1257,7 @@ void TrackPanel::DoDrawCursor(wxDC & dc) mLastCursor = -1; } - mLastCursor = mViewInfo->sel0; + mLastCursor = mViewInfo->selectedRegion.t0(); onScreen = between_inclusive( mViewInfo->h, mLastCursor, @@ -1393,7 +1394,7 @@ void TrackPanel::OnPaint(wxPaintEvent & /* event */) } // Draw the cursor - if( mViewInfo->sel0 == mViewInfo->sel1) + if( mViewInfo->selectedRegion.isPoint()) DoDrawCursor( cdc ); #if DEBUG_DRAW_TIMING @@ -1632,8 +1633,8 @@ void TrackPanel::SetCursorAndTipWhenSelectTool( Track * t, } return; } - wxInt64 leftSel = TimeToPosition(mViewInfo->sel0, r.x); - wxInt64 rightSel = TimeToPosition(mViewInfo->sel1, r.x); + wxInt64 leftSel = TimeToPosition(mViewInfo->selectedRegion.t0(), r.x); + wxInt64 rightSel = TimeToPosition(mViewInfo->selectedRegion.t1(), r.x); // Something is wrong if right edge comes before left edge wxASSERT(!(rightSel < leftSel)); @@ -1878,8 +1879,8 @@ void TrackPanel::HandleSelect(wxMouseEvent & event) WaveTrack *w = (WaveTrack *)mCapturedTrack; WaveClip *selectedClip = w->GetClipAtX(event.m_x); if (selectedClip) { - mViewInfo->sel0 = selectedClip->GetOffset(); - mViewInfo->sel1 = selectedClip->GetEndTime(); + mViewInfo->selectedRegion.setTimes( + selectedClip->GetOffset(), selectedClip->GetEndTime()); } //Also, capture this track for dragging until we up-click. mCapturedClipArray.Add(TrackClip(w, selectedClip)); @@ -1971,10 +1972,11 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event, } // Edit the selection boundary nearest the mouse click. - if (fabs(selend - mViewInfo->sel0) < fabs(selend - mViewInfo->sel1)) - mSelStart = mViewInfo->sel1; + if (fabs(selend - mViewInfo->selectedRegion.t0()) < + fabs(selend - mViewInfo->selectedRegion.t1())) + mSelStart = mViewInfo->selectedRegion.t1(); else - mSelStart = mViewInfo->sel0; + mSelStart = mViewInfo->selectedRegion.t0(); // If the shift button is down, extend the current selection. ExtendSelection(event.m_x, r.x, pTrack); @@ -1994,7 +1996,8 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event, if (p) { double clicktime = PositionToTime(event.m_x, GetLeftOffset()); - double endtime = clicktime < mViewInfo->sel1? mViewInfo->sel1: mViewInfo->total ; + const double t1 = mViewInfo->selectedRegion.t1(); + double endtime = clicktime < t1 ? t1 : mViewInfo->total; //Behavior should differ depending upon whether we are //currently in playback mode or not. @@ -2027,8 +2030,8 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event, } //Make sure you are within the selected track if (pTrack && pTrack->GetSelected()) { - wxInt64 leftSel = TimeToPosition(mViewInfo->sel0, r.x); - wxInt64 rightSel = TimeToPosition(mViewInfo->sel1, r.x); + wxInt64 leftSel = TimeToPosition(mViewInfo->selectedRegion.t0(), r.x); + wxInt64 rightSel = TimeToPosition(mViewInfo->selectedRegion.t1(), r.x); wxASSERT(leftSel <= rightSel); // Adjusting selection edges can be turned off in the // preferences now @@ -2037,13 +2040,13 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event, // Is the cursor over the left selection boundary? else if (within(event.m_x, leftSel, SELECTION_RESIZE_REGION)) { // Pin the right selection boundary - mSelStart = mViewInfo->sel1; + mSelStart = mViewInfo->selectedRegion.t1(); startNewSelection = false; } // Is the cursor over the right selection boundary? else if (within(event.m_x, rightSel, SELECTION_RESIZE_REGION)) { // Pin the left selection boundary - mSelStart = mViewInfo->sel0; + mSelStart = mViewInfo->selectedRegion.t0(); startNewSelection = false; } } @@ -2054,7 +2057,7 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event, LabelTrack *lt = (LabelTrack *) pTrack; if (lt->HandleMouse(event, r,//mCapturedRect, mViewInfo->h, mViewInfo->zoom, - &mViewInfo->sel0, &mViewInfo->sel1)) { + &mViewInfo->selectedRegion)) { MakeParentPushState(_("Modified Label"), _("Label Edit"), PUSH_CONSOLIDATE); @@ -2076,8 +2079,8 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event, double minPeriod = 0.05; // minimum beat period double qBeat0, qBeat1; double centerBeat = 0.0f; - mStretchSel0 = nt->NearestBeatTime(mViewInfo->sel0, &qBeat0); - mStretchSel1 = nt->NearestBeatTime(mViewInfo->sel1, &qBeat1); + mStretchSel0 = nt->NearestBeatTime(mViewInfo->selectedRegion.t0(), &qBeat0); + mStretchSel1 = nt->NearestBeatTime(mViewInfo->selectedRegion.t1(), &qBeat1); // If there is not (almost) a beat to stretch that is slower // than 20 beats per second, don't stretch @@ -2093,14 +2096,16 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event, _("Click and drag to stretch selected region.")); SetCursor(*mStretchLeftCursor); // mStretchMode = stretchLeft; - mSelStart = mViewInfo->sel1; // condition that implies stretchLeft + mSelStart = mViewInfo->selectedRegion.t1(); + // condition that implies stretchLeft startNewSelection = false; } else if (within(qBeat1, centerBeat, 0.1)) { mListener->TP_DisplayStatusMessage( _("Click and drag to stretch selected region.")); SetCursor(*mStretchRightCursor); // mStretchMode = stretchRight; - mSelStart = mViewInfo->sel0; // condition that implies stretchRight + mSelStart = mViewInfo->selectedRegion.t0(); + // condition that implies stretchRight startNewSelection = false; } } @@ -2109,7 +2114,7 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event, mStretchMode = stretchCenter; mStretchLeftBeats = qBeat1 - centerBeat; mStretchRightBeats = centerBeat - qBeat0; - } else if (mViewInfo->sel1 == mSelStart) { + } else if (mViewInfo->selectedRegion.t1() == mSelStart) { // note that at this point, mSelStart is at the opposite // end of the selection from the cursor. If the cursor is // over sel0, then mSelStart is at sel1. @@ -2125,8 +2130,7 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event, mStretchLeftBeats = qBeat1 - qBeat0; mStretchRightBeats = 0; } - mViewInfo->sel0 = mStretchSel0; - mViewInfo->sel1 = mStretchSel1; + mViewInfo->selectedRegion.setTimes(mStretchSel0, mStretchSel1); mStretching = true; mStretched = false; @@ -2188,8 +2192,7 @@ void TrackPanel::StartSelection(int mouseXCoordinate, int trackLeftEdge) } } - mViewInfo->sel0 = s; - mViewInfo->sel1 = s; + mViewInfo->selectedRegion.setTimes(s, s); SonifyBeginModifyState(); MakeParentModifyState(false); @@ -2247,8 +2250,7 @@ void TrackPanel::ExtendSelection(int mouseXCoordinate, int trackLeftEdge, } } - mViewInfo->sel0 = sel0; - mViewInfo->sel1 = sel1; + mViewInfo->selectedRegion.setTimes(sel0, sel1); //On-Demand: check to see if there is an OD thing associated with this track. If so we want to update the focal point for the task. if (pTrack && (pTrack->GetKind() == Track::Wave) && ODManager::IsInstanceCreated()) @@ -2284,8 +2286,7 @@ void TrackPanel::Stretch(int mouseXCoordinate, int trackLeftEdge, // Undo brings us back to the pre-click state, but we want to // quantize selected region to integer beat boundaries. These // were saved in mStretchSel[12] variables: - mViewInfo->sel0 = mStretchSel0; - mViewInfo->sel1 = mStretchSel1; + mViewInfo->selectedRegion.setTimes(mStretchSel0, mStretchSel1); mStretched = false; int index = 0; @@ -2310,40 +2311,40 @@ void TrackPanel::Stretch(int mouseXCoordinate, int trackLeftEdge, // (In principle, tempo can be higher, but not infinity.) double minPeriod = 0.05; // minimum beat period double qBeat0, qBeat1; - pNt->NearestBeatTime(mViewInfo->sel0, &qBeat0); // get beat - pNt->NearestBeatTime(mViewInfo->sel1, &qBeat1); + pNt->NearestBeatTime(mViewInfo->selectedRegion.t0(), &qBeat0); // get beat + pNt->NearestBeatTime(mViewInfo->selectedRegion.t1(), &qBeat1); // We could be moving 3 things: left edge, right edge, a point between switch (mStretchMode) { case stretchLeft: { // make sure target duration is not too short - double dur = mViewInfo->sel1 - moveto; + double dur = mViewInfo->selectedRegion.t1() - moveto; if (dur < mStretchRightBeats * minPeriod) { dur = mStretchRightBeats * minPeriod; - moveto = mViewInfo->sel1 - dur; + moveto = mViewInfo->selectedRegion.t1() - dur; } if (pNt->StretchRegion(mStretchSel0, mStretchSel1, dur)) { pNt->SetOffset(pNt->GetOffset() + moveto - mStretchSel0); - mViewInfo->sel0 = moveto; + mViewInfo->selectedRegion.setT0(moveto); } break; } case stretchRight: { // make sure target duration is not too short - double dur = moveto - mViewInfo->sel0; + double dur = moveto - mViewInfo->selectedRegion.t0(); if (dur < mStretchLeftBeats * minPeriod) { dur = mStretchLeftBeats * minPeriod; moveto = mStretchSel0 + dur; } if (pNt->StretchRegion(mStretchSel0, mStretchSel1, dur)) { - mViewInfo->sel1 = moveto; + mViewInfo->selectedRegion.setT1(moveto); } break; } case stretchCenter: { // make sure both left and right target durations are not too short - double left_dur = moveto - mViewInfo->sel0; - double right_dur = mViewInfo->sel1 - moveto; + double left_dur = moveto - mViewInfo->selectedRegion.t0(); + double right_dur = mViewInfo->selectedRegion.t1() - moveto; double centerBeat; pNt->NearestBeatTime(mSelStart, ¢erBeat); if (left_dur < mStretchLeftBeats * minPeriod) { @@ -2735,8 +2736,8 @@ void TrackPanel::StartSlide(wxMouseEvent & event) PositionToTime(event.m_x, GetLeftOffset()); bool clickedInSelection = (vt->GetSelected() && - clickTime > mViewInfo->sel0 && - clickTime < mViewInfo->sel1); + clickTime > mViewInfo->selectedRegion.t0() && + clickTime < mViewInfo->selectedRegion.t1()); // First, if click was in selection, capture selected clips; otherwise // just the clicked-on clip @@ -2849,7 +2850,8 @@ void TrackPanel::StartSlide(wxMouseEvent & event) void TrackPanel::AddClipsToCaptured(Track *t, bool withinSelection) { if (withinSelection) - AddClipsToCaptured(t, mViewInfo->sel0, mViewInfo->sel1); + AddClipsToCaptured(t, mViewInfo->selectedRegion.t0(), + mViewInfo->selectedRegion.t1()); else AddClipsToCaptured(t, t->GetStartTime(), t->GetEndTime()); } @@ -2956,8 +2958,7 @@ void TrackPanel::DoSlide(wxMouseEvent & event) if (mCapturedClipIsSelection) { // Slide the selection, too - mViewInfo->sel0 -= mHSlideAmount; - mViewInfo->sel1 -= mHSlideAmount; + mViewInfo->selectedRegion.move(-mHSlideAmount); } mHSlideAmount = 0.0; @@ -3054,8 +3055,7 @@ void TrackPanel::DoSlide(wxMouseEvent & event) if (mCapturedClipIsSelection) { // Slide the selection, too - mViewInfo->sel0 += desiredSlideAmount; - mViewInfo->sel1 += desiredSlideAmount; + mViewInfo->selectedRegion.move(desiredSlideAmount); } // Make the offset permanent; start from a "clean slate" @@ -3150,8 +3150,7 @@ void TrackPanel::DoSlide(wxMouseEvent & event) } if (mCapturedClipIsSelection) { // Slide the selection, too - mViewInfo->sel0 += mHSlideAmount; - mViewInfo->sel1 += mHSlideAmount; + mViewInfo->selectedRegion.move(mHSlideAmount); } Refresh(false); @@ -4015,8 +4014,8 @@ void TrackPanel::UpdateViewIfNoTracks() mViewInfo->zoom = 44100.0 / 512.0; //STM: Set selection to 0,0 - mViewInfo->sel0 = 0.0; - mViewInfo->sel1 = 0.0; + //PRL: and default the rest of the selection information + mViewInfo->selectedRegion = SelectedRegion(); mListener->TP_RedrawScrollbars(); mListener->TP_DisplayStatusMessage(wxT("")); //STM: Clear message if all tracks are removed @@ -5034,11 +5033,12 @@ void TrackPanel::OnKeyDown(wxKeyEvent & event) } LabelTrack *lt = (LabelTrack *)t; - double bkpSel0 = mViewInfo->sel0, bkpSel1 = mViewInfo->sel1; + double bkpSel0 = mViewInfo->selectedRegion.t0(), + bkpSel1 = mViewInfo->selectedRegion.t1(); // Pass keystroke to labeltrack's handler and add to history if any // updates were done - if (lt->OnKeyDown(mViewInfo->sel0, mViewInfo->sel1, event)) + if (lt->OnKeyDown(mViewInfo->selectedRegion, event)) MakeParentPushState(_("Modified Label"), _("Label Edit"), PUSH_CONSOLIDATE); @@ -5051,7 +5051,8 @@ void TrackPanel::OnKeyDown(wxKeyEvent & event) // If selection modified, refresh // Otherwise, refresh track display if the keystroke was handled - if( bkpSel0 != mViewInfo->sel0 || bkpSel1 != mViewInfo->sel1 ) + if( bkpSel0 != mViewInfo->selectedRegion.t0() || + bkpSel1 != mViewInfo->selectedRegion.t1() ) Refresh( false ); else if (!event.GetSkipped()) RefreshTrack(t); @@ -5073,17 +5074,19 @@ void TrackPanel::OnChar(wxKeyEvent & event) return; } - double bkpSel0 = mViewInfo->sel0, bkpSel1 = mViewInfo->sel1; + double bkpSel0 = mViewInfo->selectedRegion.t0(), + bkpSel1 = mViewInfo->selectedRegion.t1(); // Pass keystroke to labeltrack's handler and add to history if any // updates were done - if (((LabelTrack *)t)->OnChar(mViewInfo->sel0, mViewInfo->sel1, event)) + if (((LabelTrack *)t)->OnChar(mViewInfo->selectedRegion, event)) MakeParentPushState(_("Modified Label"), _("Label Edit"), PUSH_CONSOLIDATE); // If selection modified, refresh // Otherwise, refresh track display if the keystroke was handled - if( bkpSel0 != mViewInfo->sel0 || bkpSel1 != mViewInfo->sel1 ) + if( bkpSel0 != mViewInfo->selectedRegion.t0() || + bkpSel1 != mViewInfo->selectedRegion.t1() ) Refresh( false ); else if (!event.GetSkipped()) RefreshTrack(t); @@ -5244,8 +5247,7 @@ bool TrackPanel::HandleTrackLocationMouseEvent(WaveTrack * track, wxRect &r, wxM !linked->ExpandCutLine(mCapturedTrackLocation.pos)) return false; - mViewInfo->sel0 = cutlineStart; - mViewInfo->sel1 = cutlineEnd; + mViewInfo->selectedRegion.setTimes(cutlineStart, cutlineEnd); DisplaySelection(); MakeParentPushState(_("Expanded Cut Line"), _("Expand")); handled = true; @@ -5348,7 +5350,7 @@ bool TrackPanel::HandleLabelTrackMouseEvent(LabelTrack * lTrack, wxRect &r, wxMo } if (lTrack->HandleMouse(event, mCapturedRect, - mViewInfo->h, mViewInfo->zoom, &mViewInfo->sel0, &mViewInfo->sel1)) { + mViewInfo->h, mViewInfo->zoom, &mViewInfo->selectedRegion)) { MakeParentPushState(_("Modified Label"), _("Label Edit"), @@ -5607,8 +5609,8 @@ bool TrackPanel::HitTestStretch(Track *track, wxRect &r, wxMouseEvent & event) int center = r.y + r.height / 2; int distance = abs(event.m_y - center); const int yTolerance = 10; - wxInt64 leftSel = TimeToPosition(mViewInfo->sel0, r.x); - wxInt64 rightSel = TimeToPosition(mViewInfo->sel1, r.x); + wxInt64 leftSel = TimeToPosition(mViewInfo->selectedRegion.t0(), r.x); + wxInt64 rightSel = TimeToPosition(mViewInfo->selectedRegion.t1(), r.x); // Something is wrong if right edge comes before left edge wxASSERT(!(rightSel < leftSel)); return (leftSel <= event.m_x && event.m_x <= rightSel && @@ -6552,19 +6554,15 @@ void TrackPanel::OnCursorLeft( bool shift, bool ctrl, bool keyup ) if( shift && ctrl ) { // Reduce and constrain (counter-intuitive) - if (snapToTime) { - mViewInfo->sel1 = GridMove(mViewInfo->sel1, -multiplier); - } - else { - mViewInfo->sel1 -= multiplier / mViewInfo->zoom; - } - if( mViewInfo->sel1 < mViewInfo->sel0 ) - { - mViewInfo->sel1 = mViewInfo->sel0; - } + mViewInfo->selectedRegion.setT1( + std::max(mViewInfo->selectedRegion.t0(), + snapToTime + ? GridMove(mViewInfo->selectedRegion.t1(), -multiplier) + : mViewInfo->selectedRegion.t1() - + multiplier / mViewInfo->zoom)); // Make sure it's visible. - ScrollIntoView( mViewInfo->sel1 ); + ScrollIntoView( mViewInfo->selectedRegion.t1() ); Refresh( false ); } // Extend selection toward the left @@ -6579,19 +6577,15 @@ void TrackPanel::OnCursorLeft( bool shift, bool ctrl, bool keyup ) } // Expand and constrain - if (snapToTime) { - mViewInfo->sel0 = GridMove(mViewInfo->sel0, -multiplier); - } - else { - mViewInfo->sel0 -= multiplier / mViewInfo->zoom; - } - if( mViewInfo->sel0 < 0.0 ) - { - mViewInfo->sel0 = 0.0; - } + mViewInfo->selectedRegion.setT0( + std::max(0.0, + snapToTime + ? GridMove(mViewInfo->selectedRegion.t0(), -multiplier) + : mViewInfo->selectedRegion.t0() - + multiplier / mViewInfo->zoom)); // Make sure it's visible. - ScrollIntoView( mViewInfo->sel0 ); + ScrollIntoView( mViewInfo->selectedRegion.t0() ); Refresh( false ); } // Move the cursor toward the left @@ -6606,20 +6600,17 @@ void TrackPanel::OnCursorLeft( bool shift, bool ctrl, bool keyup ) } // Already in cursor mode? - if( mViewInfo->sel0 == mViewInfo->sel1 ) + if( mViewInfo->selectedRegion.isPoint() ) { // Move and constrain - if (snapToTime) { - mViewInfo->sel0 = GridMove(mViewInfo->sel0, -multiplier); - } - else { - mViewInfo->sel0 -= multiplier / mViewInfo->zoom; - } - if( mViewInfo->sel0 < 0.0 ) - { - mViewInfo->sel0 = 0.0; - } - mViewInfo->sel1 = mViewInfo->sel0; + mViewInfo->selectedRegion.setT0( + std::max(0.0, + snapToTime + ? GridMove(mViewInfo->selectedRegion.t0(), -multiplier) + : mViewInfo->selectedRegion.t0() - + multiplier / mViewInfo->zoom), + false); + mViewInfo->selectedRegion.collapseToT0(); // Move the visual cursor DrawCursor(); @@ -6627,12 +6618,12 @@ void TrackPanel::OnCursorLeft( bool shift, bool ctrl, bool keyup ) else { // Transition to cursor mode. - mViewInfo->sel1 = mViewInfo->sel0; + mViewInfo->selectedRegion.collapseToT0(); Refresh( false ); } // Make sure it's visible - ScrollIntoView( mViewInfo->sel0 ); + ScrollIntoView( mViewInfo->selectedRegion.t0() ); } } @@ -6666,19 +6657,15 @@ void TrackPanel::OnCursorRight( bool shift, bool ctrl, bool keyup ) if( shift && ctrl ) { // Reduce and constrain (counter-intuitive) - if (snapToTime) { - mViewInfo->sel0 = GridMove(mViewInfo->sel0, multiplier); - } - else { - mViewInfo->sel0 += multiplier / mViewInfo->zoom; - } - if( mViewInfo->sel0 > mViewInfo->sel1 ) - { - mViewInfo->sel0 = mViewInfo->sel1; - } + mViewInfo->selectedRegion.setT0( + std::min(mViewInfo->selectedRegion.t1(), + snapToTime + ? GridMove(mViewInfo->selectedRegion.t0(), multiplier) + : mViewInfo->selectedRegion.t0() + + multiplier / mViewInfo->zoom)); // Make sure new position is in view. - ScrollIntoView( mViewInfo->sel0 ); + ScrollIntoView( mViewInfo->selectedRegion.t0() ); Refresh( false ); } // Extend selection toward the right @@ -6693,20 +6680,15 @@ void TrackPanel::OnCursorRight( bool shift, bool ctrl, bool keyup ) } // Expand and constrain - if (snapToTime) { - mViewInfo->sel1 = GridMove(mViewInfo->sel1, multiplier); - } - else { - mViewInfo->sel1 += multiplier/mViewInfo->zoom; - } double end = mTracks->GetEndTime(); - if( mViewInfo->sel1 > end ) - { - mViewInfo->sel1 = end; - } + mViewInfo->selectedRegion.setT1( + std::min(end, + snapToTime + ? GridMove(mViewInfo->selectedRegion.t1(), multiplier) + : mViewInfo->selectedRegion.t1() + multiplier/mViewInfo->zoom)); // Make sure new position is in view. - ScrollIntoView( mViewInfo->sel1 ); + ScrollIntoView( mViewInfo->selectedRegion.t1() ); Refresh( false ); } // Move the cursor toward the right @@ -6721,21 +6703,18 @@ void TrackPanel::OnCursorRight( bool shift, bool ctrl, bool keyup ) } // Already in cursor mode? - if (mViewInfo->sel0 == mViewInfo->sel1) + if (mViewInfo->selectedRegion.isPoint()) { // Move and constrain - if (snapToTime) { - mViewInfo->sel1 = GridMove(mViewInfo->sel1, multiplier); - } - else { - mViewInfo->sel1 += multiplier / mViewInfo->zoom; - } double end = mTracks->GetEndTime(); - if( mViewInfo->sel1 > end ) - { - mViewInfo->sel1 = end; - } - mViewInfo->sel0 = mViewInfo->sel1; + mViewInfo->selectedRegion.setT1( + std::min(end, + snapToTime + ? GridMove(mViewInfo->selectedRegion.t1(), multiplier) + : mViewInfo->selectedRegion.t1() + + multiplier / mViewInfo->zoom), + false); + mViewInfo->selectedRegion.collapseToT1(); // Move the visual cursor DrawCursor(); @@ -6743,12 +6722,12 @@ void TrackPanel::OnCursorRight( bool shift, bool ctrl, bool keyup ) else { // Transition to cursor mode. - mViewInfo->sel0 = mViewInfo->sel1; + mViewInfo->selectedRegion.collapseToT1(); Refresh( false ); } // Make sure new position is in view - ScrollIntoView( mViewInfo->sel1 ); + ScrollIntoView( mViewInfo->selectedRegion.t1() ); } } @@ -6799,13 +6778,11 @@ void TrackPanel::OnBoundaryMove(bool left, bool boundaryContract) { double indicator = gAudioIO->GetStreamTime(); if (left) { - mViewInfo->sel0 = indicator; - if(mViewInfo->sel1 < mViewInfo->sel0) - mViewInfo->sel1 = mViewInfo->sel0; + mViewInfo->selectedRegion.setT0(indicator, false); } else { - mViewInfo->sel1 = indicator; + mViewInfo->selectedRegion.setT1(indicator); } MakeParentModifyState(false); @@ -6819,24 +6796,23 @@ void TrackPanel::OnBoundaryMove(bool left, bool boundaryContract) { if (left) { // Reduce and constrain left boundary (counter-intuitive) - mViewInfo->sel0 += multiplier / mViewInfo->zoom; - if( mViewInfo->sel0 > mViewInfo->sel1 ) - { - mViewInfo->sel0 = mViewInfo->sel1; - } + mViewInfo->selectedRegion.setT0( + std::min(mViewInfo->selectedRegion.t1(), + mViewInfo->selectedRegion.t0() + + multiplier / mViewInfo->zoom)); + // Make sure it's visible - ScrollIntoView( mViewInfo->sel0 ); + ScrollIntoView( mViewInfo->selectedRegion.t0() ); } else { // Reduce and constrain right boundary (counter-intuitive) - mViewInfo->sel1 -= multiplier / mViewInfo->zoom; - if( mViewInfo->sel1 < mViewInfo->sel0 ) - { - mViewInfo->sel1 = mViewInfo->sel0; - } + mViewInfo->selectedRegion.setT1( + std::max(mViewInfo->selectedRegion.t0(), + mViewInfo->selectedRegion.t1() - + multiplier / mViewInfo->zoom)); // Make sure it's visible - ScrollIntoView( mViewInfo->sel1 ); + ScrollIntoView( mViewInfo->selectedRegion.t1() ); } } // BOUNDARY MOVEMENT @@ -6845,23 +6821,21 @@ void TrackPanel::OnBoundaryMove(bool left, bool boundaryContract) { if (left) { // Expand and constrain left boundary - mViewInfo->sel0 -= multiplier / mViewInfo->zoom; - if( mViewInfo->sel0 < 0.0 ) - { - mViewInfo->sel0 = 0.0; - } + mViewInfo->selectedRegion.setT0( + std::max(0.0, + mViewInfo->selectedRegion.t0() - + multiplier / mViewInfo->zoom)); // Make sure it's visible - ScrollIntoView( mViewInfo->sel0 ); + ScrollIntoView( mViewInfo->selectedRegion.t0() ); } else { // Expand and constrain right boundary - mViewInfo->sel1 += multiplier/mViewInfo->zoom; double end = mTracks->GetEndTime(); - if( mViewInfo->sel1 > end ) - { - mViewInfo->sel1 = end; - } + mViewInfo->selectedRegion.setT1( + std::min(end, + mViewInfo->selectedRegion.t1() + + multiplier/mViewInfo->zoom)); } } Refresh( false ); @@ -6911,20 +6885,22 @@ void TrackPanel::OnCursorMove(bool forward, bool jump, bool longjump ) else { // Already in cursor mode? - if( mViewInfo->sel0 == mViewInfo->sel1 ) + if( mViewInfo->selectedRegion.isPoint() ) { // Move and constrain - mViewInfo->sel0 += mSeek; - if( !forward && mViewInfo->sel0 < 0.0 ) + + double t0 = mViewInfo->selectedRegion.t0() + mSeek; + if( !forward && t0 < 0.0 ) { - mViewInfo->sel0 = 0.0; + t0 = 0.0; } double end = mTracks->GetEndTime(); - if( forward && mViewInfo->sel0 > end) + if( forward && t0 > end) { - mViewInfo->sel0 = end; + t0 = end; } - mViewInfo->sel1 = mViewInfo->sel0; + mViewInfo->selectedRegion.setT0(t0, false); + mViewInfo->selectedRegion.collapseToT0(); // Move the visual cursor DrawCursor(); @@ -6932,12 +6908,12 @@ void TrackPanel::OnCursorMove(bool forward, bool jump, bool longjump ) else { // Transition to cursor mode. - mViewInfo->sel1 = mViewInfo->sel0; + mViewInfo->selectedRegion.collapseToT0(); Refresh( false ); } // Make sure it's visible - ScrollIntoView( mViewInfo->sel0 ); + ScrollIntoView( mViewInfo->selectedRegion.t0() ); MakeParentModifyState(false); } @@ -7210,8 +7186,8 @@ void TrackPanel::OnTrackClose() mViewInfo->zoom = 44100.0 / 512.0; //STM: Set selection to 0,0 - mViewInfo->sel0 = 0.0; - mViewInfo->sel1 = 0.0; + //PRL: and default the rest of the selection information + mViewInfo->selectedRegion = SelectedRegion(); mListener->TP_RedrawScrollbars(); mListener->TP_DisplayStatusMessage( wxT( "" ) ); //STM: Clear message if all tracks are removed @@ -7983,11 +7959,12 @@ void TrackPanel::OnCopySelectedText(wxCommandEvent & WXUNUSED(event)) RefreshTrack(lt); } -/// paste selected text if p`aste menu item is selected +/// paste selected text if paste menu item is selected void TrackPanel::OnPasteSelectedText(wxCommandEvent & WXUNUSED(event)) { LabelTrack *lt = (LabelTrack *)mPopupMenuTarget; - if (lt->PasteSelectedText(mViewInfo->sel0, mViewInfo->sel1)) { + if (lt->PasteSelectedText(mViewInfo->selectedRegion.t0(), + mViewInfo->selectedRegion.t1())) { MakeParentPushState(_("Modified Label"), _("Label Edit"), true /* consolidate */); @@ -7999,7 +7976,8 @@ void TrackPanel::OnPasteSelectedText(wxCommandEvent & WXUNUSED(event)) void TrackPanel::OnDeleteSelectedLabel(wxCommandEvent & WXUNUSED(event)) { LabelTrack *lt = (LabelTrack *)mPopupMenuTarget; - int ndx = lt->GetLabelIndex(mViewInfo->sel0, mViewInfo->sel1); + int ndx = lt->GetLabelIndex(mViewInfo->selectedRegion.t0(), + mViewInfo->selectedRegion.t1()); if (ndx != -1) { lt->DeleteLabel(ndx); MakeParentPushState(_("Deleted Label"), diff --git a/src/TrackPanel.h b/src/TrackPanel.h index b920a6914..d288e1489 100644 --- a/src/TrackPanel.h +++ b/src/TrackPanel.h @@ -17,7 +17,6 @@ #include #include -#include "LabelTrack.h" #include "Sequence.h" //Stm: included for the sampleCount declaration #include "WaveClip.h" #include "WaveTrack.h" @@ -27,6 +26,7 @@ class wxMenu; class wxRect; +class LabelTrack; class TrackPanel; class TrackArtist; class Ruler; diff --git a/src/UndoManager.cpp b/src/UndoManager.cpp index bbd826679..50b15d478 100644 --- a/src/UndoManager.cpp +++ b/src/UndoManager.cpp @@ -184,7 +184,8 @@ bool UndoManager::RedoAvailable() return (current < (int)stack.Count() - 1); } -void UndoManager::ModifyState(TrackList * l, double sel0, double sel1) +void UndoManager::ModifyState(TrackList * l, + const SelectedRegion &selectedRegion) { if (current == wxNOT_FOUND) { return; @@ -206,12 +207,12 @@ void UndoManager::ModifyState(TrackList * l, double sel0, double sel1) // Replace stack[current]->tracks = tracksCopy; - stack[current]->sel0 = sel0; - stack[current]->sel1 = sel1; + stack[current]->selectedRegion = selectedRegion; SonifyEndModifyState(); } -void UndoManager::PushState(TrackList * l, double sel0, double sel1, +void UndoManager::PushState(TrackList * l, + const SelectedRegion &selectedRegion, wxString longDescription, wxString shortDescription, int flags) @@ -222,7 +223,7 @@ void UndoManager::PushState(TrackList * l, double sel0, double sel1, if (((flags&PUSH_CONSOLIDATE)!=0) && lastAction == longDescription && consolidationCount < 2) { consolidationCount++; - ModifyState(l, sel0, sel1); + ModifyState(l, selectedRegion); // MB: If the "saved" state was modified by ModifyState, reset // it so that UnsavedChanges returns true. if (current == saved) { @@ -248,8 +249,7 @@ void UndoManager::PushState(TrackList * l, double sel0, double sel1, UndoStackElem *push = new UndoStackElem(); push->tracks = tracksCopy; - push->sel0 = sel0; - push->sel1 = sel1; + push->selectedRegion = selectedRegion; push->description = longDescription; push->shortDescription = shortDescription; push->spaceUsage = 0; // Calculate actual value after it's on the stack. @@ -266,7 +266,8 @@ void UndoManager::PushState(TrackList * l, double sel0, double sel1, lastAction = longDescription; } -TrackList *UndoManager::SetStateTo(unsigned int n, double *sel0, double *sel1) +TrackList *UndoManager::SetStateTo(unsigned int n, + SelectedRegion *selectedRegion) { n -= 1; @@ -275,14 +276,10 @@ TrackList *UndoManager::SetStateTo(unsigned int n, double *sel0, double *sel1) current = n; if (current == int(stack.Count()-1)) { - *sel0 = stack[current]->sel0; - *sel1 = stack[current]->sel1; + *selectedRegion = stack[current]->selectedRegion; } else { - current++; - *sel0 = stack[current]->sel0; - *sel1 = stack[current]->sel1; - current--; + *selectedRegion = stack[current + 1]->selectedRegion; } lastAction = wxT(""); @@ -291,14 +288,13 @@ TrackList *UndoManager::SetStateTo(unsigned int n, double *sel0, double *sel1) return stack[current]->tracks; } -TrackList *UndoManager::Undo(double *sel0, double *sel1) +TrackList *UndoManager::Undo(SelectedRegion *selectedRegion) { wxASSERT(UndoAvailable()); current--; - *sel0 = stack[current]->sel0; - *sel1 = stack[current]->sel1; + *selectedRegion = stack[current]->selectedRegion; lastAction = wxT(""); consolidationCount = 0; @@ -306,14 +302,13 @@ TrackList *UndoManager::Undo(double *sel0, double *sel1) return stack[current]->tracks; } -TrackList *UndoManager::Redo(double *sel0, double *sel1) +TrackList *UndoManager::Redo(SelectedRegion *selectedRegion) { wxASSERT(RedoAvailable()); current++; - *sel0 = stack[current]->sel0; - *sel1 = stack[current]->sel1; + *selectedRegion = stack[current]->selectedRegion; /* if (!RedoAvailable()) { diff --git a/src/UndoManager.h b/src/UndoManager.h index 67f213fb1..d30b438f5 100644 --- a/src/UndoManager.h +++ b/src/UndoManager.h @@ -51,6 +51,7 @@ #include #include #include "ondemand/ODTaskThread.h" +#include "SelectedRegion.h" class Track; class TrackList; @@ -59,8 +60,7 @@ struct UndoStackElem { TrackList *tracks; wxString description; wxString shortDescription; - double sel0; - double sel1; + SelectedRegion selectedRegion; wxLongLong spaceUsage; }; @@ -79,10 +79,12 @@ class AUDACITY_DLL_API UndoManager { UndoManager(); ~UndoManager(); - void PushState(TrackList * l, double sel0, double sel1, + void PushState(TrackList * l, + const SelectedRegion &selectedRegion, wxString longDescription, wxString shortDescription, int flags = PUSH_CALC_SPACE|PUSH_AUTOSAVE ); - void ModifyState(TrackList * l, double sel0, double sel1); + void ModifyState(TrackList * l, + const SelectedRegion &selectedRegion); void ClearStates(); void RemoveStates(int num); // removes the 'num' oldest states void RemoveStateAt(int n); // removes the n'th state (1 is oldest) @@ -93,9 +95,9 @@ class AUDACITY_DLL_API UndoManager { void GetLongDescription(unsigned int n, wxString *desc, wxString *size); void SetLongDescription(unsigned int n, wxString desc); - TrackList *SetStateTo(unsigned int n, double *sel0, double *sel1); - TrackList *Undo(double *sel0, double *sel1); - TrackList *Redo(double *sel0, double *sel1); + TrackList *SetStateTo(unsigned int n, SelectedRegion *selectedRegion); + TrackList *Undo(SelectedRegion *selectedRegion); + TrackList *Redo(SelectedRegion *selectedRegion); bool UndoAvailable(); bool RedoAvailable(); diff --git a/src/ViewInfo.h b/src/ViewInfo.h index 074618906..0f2316f39 100644 --- a/src/ViewInfo.h +++ b/src/ViewInfo.h @@ -11,6 +11,8 @@ #ifndef __AUDACITY_VIEWINFO__ #define __AUDACITY_VIEWINFO__ +#include "SelectedRegion.h" + const double gMaxZoom = 6000000, gMinZoom = 0.001; @@ -18,10 +20,9 @@ class Track; struct ViewInfo { - // Current selection (in seconds) + // Current selection - double sel0; - double sel1; + SelectedRegion selectedRegion; // Scroll info diff --git a/src/commands/CompareAudioCommand.cpp b/src/commands/CompareAudioCommand.cpp index afb209fcf..a7c07c30d 100644 --- a/src/commands/CompareAudioCommand.cpp +++ b/src/commands/CompareAudioCommand.cpp @@ -41,8 +41,8 @@ Command *CompareAudioCommandType::Create(CommandOutputTarget *target) bool CompareAudioCommand::GetSelection(AudacityProject &proj) { // Get the selected time interval - mT0 = proj.mViewInfo.sel0; - mT1 = proj.mViewInfo.sel1; + mT0 = proj.mViewInfo.selectedRegion.t0(); + mT1 = proj.mViewInfo.selectedRegion.t1(); if (mT0 >= mT1) { Error(wxT("There is no selection!")); diff --git a/src/commands/ImportExportCommands.cpp b/src/commands/ImportExportCommands.cpp index fe07620dc..6e6ea7be3 100644 --- a/src/commands/ImportExportCommands.cpp +++ b/src/commands/ImportExportCommands.cpp @@ -81,8 +81,8 @@ bool ExportCommand::Apply(CommandExecutionContext context) double t0, t1; if (selection) { - t0 = context.proj->mViewInfo.sel0; - t1 = context.proj->mViewInfo.sel1; + t0 = context.proj->mViewInfo.selectedRegion.t0(); + t1 = context.proj->mViewInfo.selectedRegion.t1(); } else { diff --git a/src/commands/SelectCommand.cpp b/src/commands/SelectCommand.cpp index 67302b673..75b0d41bb 100644 --- a/src/commands/SelectCommand.cpp +++ b/src/commands/SelectCommand.cpp @@ -84,8 +84,16 @@ bool SelectCommand::Apply(CommandExecutionContext context) Error(wxT("End time is after end of track!")); return false; } - context.proj->mViewInfo.sel0 = t0; - context.proj->mViewInfo.sel1 = t1; + + // PRL: to do: only setting time boundaries of current selection. + // Should other fields be left alone, or rather + // defaulted, as in the second branch? + // Or should this command take more parameters? +#if 1 + context.proj->mViewInfo.selectedRegion.setTimes(t0, t1); +#else + context.proj->mViewInfo.selectedRegion = SelectedRegion(t0, t1); +#endif // select specified tracks long firstTrack = GetLong(wxT("FirstTrack")); diff --git a/src/effects/ChangeSpeed.cpp b/src/effects/ChangeSpeed.cpp index b1fb85f49..455c74ede 100644 --- a/src/effects/ChangeSpeed.cpp +++ b/src/effects/ChangeSpeed.cpp @@ -27,6 +27,7 @@ #include "../Audacity.h" #include "../Envelope.h" +#include "../LabelTrack.h" #include "../Prefs.h" #include "../Project.h" #include "../ShuttleGui.h" diff --git a/src/effects/Contrast.cpp b/src/effects/Contrast.cpp index c896556bf..d0f40a3df 100644 --- a/src/effects/Contrast.cpp +++ b/src/effects/Contrast.cpp @@ -410,8 +410,8 @@ void ContrastDialog::OnUseSelectionF(wxCommandEvent & event) Track *t = iter.First(); while (t) { if (t->GetSelected() && t->GetKind() == Track::Wave) { - mForegroundStartT->SetTimeValue(p->mViewInfo.sel0); - mForegroundEndT->SetTimeValue(p->mViewInfo.sel1); + mForegroundStartT->SetTimeValue(p->mViewInfo.selectedRegion.t0()); + mForegroundEndT->SetTimeValue(p->mViewInfo.selectedRegion.t1()); break; } t = iter.Next(); @@ -427,8 +427,8 @@ void ContrastDialog::OnUseSelectionB(wxCommandEvent & event) Track *t = iter.First(); while (t) { if (t->GetSelected() && t->GetKind() == Track::Wave) { - mBackgroundStartT->SetTimeValue(p->mViewInfo.sel0); - mBackgroundEndT->SetTimeValue(p->mViewInfo.sel1); + mBackgroundStartT->SetTimeValue(p->mViewInfo.selectedRegion.t0()); + mBackgroundEndT->SetTimeValue(p->mViewInfo.selectedRegion.t1()); break; } t = iter.Next(); diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index a25aa5d55..5c6bf0895 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -87,9 +87,11 @@ bool Effect::DoEffect(wxWindow *parent, int flags, double projectRate, TrackList *list, TrackFactory *factory, - double *t0, double *t1, wxString params) + SelectedRegion *selectedRegion, wxString params) { - wxASSERT(*t0 <= *t1); + double t0 = selectedRegion->t0(); + double t1 = selectedRegion->t1(); + wxASSERT(t0 <= t1); if (mOutputTracks) { delete mOutputTracks; @@ -100,8 +102,8 @@ bool Effect::DoEffect(wxWindow *parent, int flags, mProjectRate = projectRate; mParent = parent; mTracks = list; - mT0 = *t0; - mT1 = *t1; + mT0 = t0; + mT1 = t1; CountWaveTracks(); // Note: Init may read parameters from preferences @@ -155,8 +157,7 @@ bool Effect::DoEffect(wxWindow *parent, int flags, } if (returnVal) { - *t0 = mT0; - *t1 = mT1; + selectedRegion->setTimes(mT0, mT1); } return returnVal; diff --git a/src/effects/Effect.h b/src/effects/Effect.h index e489f279e..48d749d59 100644 --- a/src/effects/Effect.h +++ b/src/effects/Effect.h @@ -27,6 +27,7 @@ class wxWindow; #include "../Internat.h" #include "../widgets/ProgressDialog.h" +class SelectedRegion; class TimeWarper; #define PLUGIN_EFFECT 0x0001 @@ -142,7 +143,8 @@ class AUDACITY_DLL_API Effect { // have the "selected" flag set to true, which is consistent with // Audacity's standard UI. bool DoEffect(wxWindow *parent, int flags, double projectRate, TrackList *list, - TrackFactory *factory, double *t0, double *t1, wxString params); + TrackFactory *factory, SelectedRegion *selectedRegion, + wxString params); wxString GetPreviewName(); diff --git a/src/effects/FindClipping.cpp b/src/effects/FindClipping.cpp index 8bad09c37..925c2bcbb 100644 --- a/src/effects/FindClipping.cpp +++ b/src/effects/FindClipping.cpp @@ -181,8 +181,8 @@ bool EffectFindClipping::ProcessOne(LabelTrack * l, samps++; if (stoprun >= mStop) { - l->AddLabel(startTime, - t->LongSamplesToTime(start + s - mStop), + l->AddLabel(SelectedRegion(startTime, + t->LongSamplesToTime(start + s - mStop)), wxString::Format(wxT("%lld of %lld"), startrun, samps - mStop)); startrun = 0; stoprun = 0; diff --git a/src/effects/SBSMSEffect.cpp b/src/effects/SBSMSEffect.cpp index 8767a16e5..3cbed773b 100644 --- a/src/effects/SBSMSEffect.cpp +++ b/src/effects/SBSMSEffect.cpp @@ -18,6 +18,7 @@ effect that uses SBSMS to do its processing (TimeScale) #include #include "SBSMSEffect.h" +#include "../LabelTrack.h" #include "../WaveTrack.h" #include "../Project.h" #include "TimeWarper.h" diff --git a/src/effects/SoundTouchEffect.cpp b/src/effects/SoundTouchEffect.cpp index 5c6489836..a1acaca87 100644 --- a/src/effects/SoundTouchEffect.cpp +++ b/src/effects/SoundTouchEffect.cpp @@ -18,6 +18,7 @@ effect that uses SoundTouch to do its processing (ChangeTempo #include +#include "../LabelTrack.h" #include "../WaveTrack.h" #include "../Project.h" #include "SoundTouchEffect.h" diff --git a/src/effects/nyquist/Nyquist.cpp b/src/effects/nyquist/Nyquist.cpp index 902c12a29..53624bde7 100644 --- a/src/effects/nyquist/Nyquist.cpp +++ b/src/effects/nyquist/Nyquist.cpp @@ -900,9 +900,11 @@ bool EffectNyquist::ProcessOne() double t0, t1; const char *str; + // PRL: to do: + // let Nyquist analyzers define more complicated selections nyx_get_label(l, &t0, &t1, &str); - ltrack->AddLabel(t0 + mT0, t1 + mT0, UTF8CTOWX(str)); + ltrack->AddLabel(SelectedRegion(t0 + mT0, t1 + mT0), UTF8CTOWX(str)); } return true; } diff --git a/src/effects/vamp/VampEffect.cpp b/src/effects/vamp/VampEffect.cpp index 284be4ff1..ee7362838 100644 --- a/src/effects/vamp/VampEffect.cpp +++ b/src/effects/vamp/VampEffect.cpp @@ -298,7 +298,7 @@ void VampEffect::AddFeatures(LabelTrack *ltrack, } } - ltrack->AddLabel(ltime0, ltime1, label); + ltrack->AddLabel(SelectedRegion(ltime0, ltime1), label); } } diff --git a/src/export/ExportMultiple.cpp b/src/export/ExportMultiple.cpp index 45057f42c..d21c3d274 100644 --- a/src/export/ExportMultiple.cpp +++ b/src/export/ExportMultiple.cpp @@ -631,16 +631,17 @@ int ExportMultiple::ExportMultipleByLabel(bool byName, else { info = mLabels->GetLabel(l); name = (info->title); - setting.t0 = info->t; + setting.t0 = info->selectedRegion.t0(); } // Figure out the ending time - if (info && info->t < info->t1) { - setting.t1 = info->t1; + if (info && !info->selectedRegion.isPoint()) { + setting.t1 = info->selectedRegion.t1(); } else if (l < mNumLabels-1) { + // Use start of next label as end const LabelStruct *info1 = mLabels->GetLabel(l+1); - setting.t1 = info1->t; + setting.t1 = info1->selectedRegion.t0(); } else { setting.t1 = mTracks->GetEndTime(); diff --git a/src/toolbars/TranscriptionToolBar.cpp b/src/toolbars/TranscriptionToolBar.cpp index 29451d25d..c8ebeb92e 100644 --- a/src/toolbars/TranscriptionToolBar.cpp +++ b/src/toolbars/TranscriptionToolBar.cpp @@ -795,7 +795,7 @@ void TranscriptionToolBar::OnAutomateSelection(wxCommandEvent & WXUNUSED(event)) //Increment start = newEnd; - p->DoAddLabel(newStartPos, newEndPos); + p->DoAddLabel(SelectedRegion(newStartPos, newEndPos)); p->RedrawProject(); } SetButton(false, mButtons[TTB_AutomateSelection]); @@ -806,7 +806,7 @@ void TranscriptionToolBar::OnMakeLabel(wxCommandEvent & WXUNUSED(event)) { AudacityProject *p = GetActiveProject(); SetButton(false, mButtons[TTB_MakeLabel]); - p->DoAddLabel(p->GetSel0(), p->GetSel1()); + p->DoAddLabel(SelectedRegion(p->GetSel0(), p->GetSel1())); } //This returns a double z-score between 0 and 10. diff --git a/src/widgets/Ruler.cpp b/src/widgets/Ruler.cpp index 8b2314a58..e78f4d6be 100644 --- a/src/widgets/Ruler.cpp +++ b/src/widgets/Ruler.cpp @@ -1652,7 +1652,7 @@ void AdornedRulerPanel::OnPaint(wxPaintEvent & WXUNUSED(evt)) DoDrawBorder(&dc); - if (mViewInfo->sel0 < mViewInfo->sel1) + if (!mViewInfo->selectedRegion.isPoint()) { DoDrawSelection(&dc); } @@ -1664,7 +1664,7 @@ void AdornedRulerPanel::OnPaint(wxPaintEvent & WXUNUSED(evt)) DoDrawMarks(&dc, true); - if (mViewInfo->sel0 == mViewInfo->sel1) + if (mViewInfo->selectedRegion.isPoint()) { DoDrawCursor(&dc); } @@ -1927,8 +1927,10 @@ void AdornedRulerPanel::DoDrawSelection(wxDC * dc) { // Draw selection double zoom = mViewInfo->zoom; - double sel0 = mViewInfo->sel0 - mViewInfo->h + mLeftOffset / zoom; - double sel1 = mViewInfo->sel1 - mViewInfo->h + mLeftOffset / zoom; + double sel0 = + mViewInfo->selectedRegion.t0() - mViewInfo->h + mLeftOffset / zoom; + double sel1 = + mViewInfo->selectedRegion.t1() - mViewInfo->h + mLeftOffset / zoom; if( sel0 < 0.0 ) sel0 = 0.0; diff --git a/win/Projects/Audacity/Audacity.vcproj b/win/Projects/Audacity/Audacity.vcproj index 742df2ad4..81e081d55 100644 --- a/win/Projects/Audacity/Audacity.vcproj +++ b/win/Projects/Audacity/Audacity.vcproj @@ -674,6 +674,10 @@ RelativePath="..\..\..\src\Screenshot.h" > + +