1 /**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 LabelTextHandle.cpp
6
7 Paul Licameli split from TrackPanel.cpp
8
9 **********************************************************************/
10
11
12 #include "LabelTextHandle.h"
13
14 #include "LabelTrackView.h"
15
16 #include "../../../HitTestResult.h"
17 #include "../../../LabelTrack.h"
18 #include "../../../ProjectAudioIO.h"
19 #include "../../../ProjectHistory.h"
20 #include "../../../RefreshCode.h"
21 #include "../../../SelectionState.h"
22 #include "../../../TrackPanelMouseEvent.h"
23 #include "ViewInfo.h"
24 #include "../../../../images/Cursors.h"
25
26 #include <wx/clipbrd.h>
27
LabelTextHandle(const std::shared_ptr<LabelTrack> & pLT,int labelNum)28 LabelTextHandle::LabelTextHandle
29 ( const std::shared_ptr<LabelTrack> &pLT, int labelNum )
30 : mpLT{ pLT }
31 , mLabelNum{ labelNum }
32 {
33 }
34
Enter(bool,AudacityProject *)35 void LabelTextHandle::Enter(bool, AudacityProject *)
36 {
37 #ifdef EXPERIMENTAL_TRACK_PANEL_HIGHLIGHTING
38 mChangeHighlight = RefreshCode::RefreshCell;
39 #endif
40 }
41
HitPreview()42 HitTestPreview LabelTextHandle::HitPreview()
43 {
44 static auto ibeamCursor =
45 ::MakeCursor(wxCURSOR_IBEAM, IBeamCursorXpm, 17, 16);
46 return {
47 XO("Click to edit label text"),
48 ibeamCursor.get()
49 };
50 }
51
HitTest(std::weak_ptr<LabelTextHandle> & holder,const wxMouseState & state,const std::shared_ptr<LabelTrack> & pLT)52 UIHandlePtr LabelTextHandle::HitTest
53 (std::weak_ptr<LabelTextHandle> &holder,
54 const wxMouseState &state, const std::shared_ptr<LabelTrack> &pLT)
55 {
56 // If Control is down, let the select handle be hit instead
57 int labelNum;
58 if (!state.ControlDown() &&
59 (labelNum =
60 LabelTrackView::OverATextBox(*pLT, state.m_x, state.m_y) ) >= 0) {
61 auto result = std::make_shared<LabelTextHandle>( pLT, labelNum );
62 result = AssignUIHandlePtr(holder, result);
63 return result;
64 }
65
66 return {};
67 }
68
~LabelTextHandle()69 LabelTextHandle::~LabelTextHandle()
70 {
71 }
72
HandleTextClick(AudacityProject & project,const wxMouseEvent & evt)73 void LabelTextHandle::HandleTextClick(AudacityProject &project, const wxMouseEvent & evt)
74 {
75 auto pTrack = mpLT.lock();
76 if (!pTrack)
77 return;
78
79 auto &view = LabelTrackView::Get( *pTrack );
80 if (evt.ButtonDown())
81 {
82 const auto selIndex = LabelTrackView::OverATextBox( *pTrack, evt.m_x, evt.m_y );
83 if ( selIndex != -1 ) {
84 if (evt.LeftDown()) {
85 mRightDragging = false;
86 // Find the NEW drag end
87 auto position = view.FindCursorPosition(selIndex, evt.m_x );
88
89 // Anchor shift-drag at the farther end of the previous highlight
90 // that is farther from the click, on Mac, for consistency with
91 // its text editors, but on the others, re-use the previous
92 // anchor.
93 auto initial = view.GetInitialCursorPosition();
94 if (evt.ShiftDown()) {
95 #ifdef __WXMAC__
96 // Set the drag anchor at the end of the previous selection
97 // that is farther from the NEW drag end
98 const auto current = view.GetCurrentCursorPosition();
99 if ( abs( position - current ) > abs( position - initial ) )
100 initial = current;
101 #else
102 // initial position remains as before
103 #endif
104 }
105 else
106 initial = position;
107
108 view.SetTextSelection(selIndex, initial, position );
109 }
110 else
111 {
112 if (!view.IsTextSelected(project))
113 {
114 auto position = view.FindCursorPosition(selIndex, evt.m_x);
115 view.SetTextSelection(selIndex, position, position);
116 }
117 // Actually this might be right or middle down
118 mRightDragging = true;
119 }
120 // Middle click on GTK: paste from primary selection
121 #if defined(__WXGTK__) && (HAVE_GTK)
122 if (evt.MiddleDown()) {
123 // Check for a click outside of the selected label's text box; in this
124 // case PasteSelectedText() will start a NEW label at the click
125 // location
126 if (!LabelTrackView::OverTextBox(&labelStruct, evt.m_x, evt.m_y))
127 view.ResetTextSelection();
128 double t = zoomInfo.PositionToTime(evt.m_x, r.x);
129 newSel = SelectedRegion(t, t);
130 }
131 #endif
132 }
133 #if defined(__WXGTK__) && (HAVE_GTK)
134 if (evt.MiddleDown()) {
135 // Paste text, making a NEW label if none is selected.
136 wxTheClipboard->UsePrimarySelection(true);
137 view.PasteSelectedText(project, newSel.t0(), newSel.t1());
138 wxTheClipboard->UsePrimarySelection(false);
139 }
140 #endif
141 }
142 }
143
HandlesRightClick()144 bool LabelTextHandle::HandlesRightClick()
145 {
146 return true;
147 }
148
Click(const TrackPanelMouseEvent & evt,AudacityProject * pProject)149 UIHandle::Result LabelTextHandle::Click
150 (const TrackPanelMouseEvent &evt, AudacityProject *pProject)
151 {
152 auto pLT = mpLT.lock();
153 if (!pLT)
154 return RefreshCode::Cancelled;
155
156 auto result = LabelDefaultClickHandle::Click( evt, pProject );
157
158 const wxMouseEvent &event = evt.event;
159 auto &viewInfo = ViewInfo::Get( *pProject );
160
161 HandleTextClick(*pProject, event);
162
163 return result | RefreshCode::RefreshCell;
164 }
165
HandleTextDragRelease(AudacityProject & project,const wxMouseEvent & evt)166 void LabelTextHandle::HandleTextDragRelease(
167 AudacityProject &project, const wxMouseEvent & evt)
168 {
169 auto pTrack = mpLT.lock();
170 if (!pTrack)
171 return;
172 auto &view = LabelTrackView::Get( *pTrack );
173
174 if(evt.LeftUp())
175 {
176 #if 0
177 // AWD: Due to wxWidgets bug #7491 (fix not ported to 2.8 branch) we
178 // should never write the primary selection. We can enable this block
179 // when we move to the 3.0 branch (or if a fixed 2.8 version is released
180 // and we can do a runtime version check)
181 #if defined (__WXGTK__) && defined (HAVE_GTK)
182 // On GTK, if we just dragged out a text selection, set the primary
183 // selection
184 if (mInitialCursorPos != mCurrentCursorPos) {
185 wxTheClipboard->UsePrimarySelection(true);
186 CopySelectedText();
187 wxTheClipboard->UsePrimarySelection(false);
188 }
189 #endif
190 #endif
191
192 return;
193 }
194
195 if(evt.Dragging())
196 {
197 auto index = view.GetTextEditIndex(project);
198 if (!mRightDragging && index != -1)
199 // Update drag end
200 view.SetCurrentCursorPosition(view.FindCursorPosition(index, evt.m_x ));
201 return;
202 }
203
204 if (evt.RightUp())
205 {
206 auto index = view.GetTextEditIndex(project);
207 if(index != -1 &&
208 LabelTrackView::OverTextBox(pTrack->GetLabel(index), evt.m_x, evt.m_y))
209 {
210 // popup menu for editing
211 // TODO: handle context menus via CellularPanel?
212 view.ShowContextMenu( project );
213 }
214 }
215
216 return;
217 }
218
Drag(const TrackPanelMouseEvent & evt,AudacityProject * pProject)219 UIHandle::Result LabelTextHandle::Drag
220 (const TrackPanelMouseEvent &evt, AudacityProject *pProject)
221 {
222 auto &project = *pProject;
223 using namespace RefreshCode;
224 auto result = LabelDefaultClickHandle::Drag( evt, pProject );
225
226 const wxMouseEvent &event = evt.event;
227 auto pLT = TrackList::Get( *pProject ).Lock(mpLT);
228 if(pLT)
229 HandleTextDragRelease( project, event );
230
231 // locate the initial mouse position
232 if (event.LeftIsDown()) {
233 if (mLabelTrackStartXPos == -1) {
234 mLabelTrackStartXPos = event.m_x;
235 mLabelTrackStartYPos = event.m_y;
236
237 auto pView = pLT ? &LabelTrackView::Get( *pLT ) : nullptr;
238 if (pLT && (pView->GetTextEditIndex( project ) != -1) &&
239 LabelTrackView::OverTextBox(
240 pLT->GetLabel(pView->GetTextEditIndex( project )),
241 mLabelTrackStartXPos,
242 mLabelTrackStartYPos))
243 mLabelTrackStartYPos = -1;
244 }
245 // if initial mouse position in the text box
246 // then only drag text
247 if (mLabelTrackStartYPos == -1)
248 result |= RefreshCell;
249 }
250
251 return result;
252 }
253
Preview(const TrackPanelMouseState &,AudacityProject *)254 HitTestPreview LabelTextHandle::Preview
255 (const TrackPanelMouseState &, AudacityProject *)
256 {
257 return HitPreview();
258 }
259
Release(const TrackPanelMouseEvent & evt,AudacityProject * pProject,wxWindow * pParent)260 UIHandle::Result LabelTextHandle::Release
261 (const TrackPanelMouseEvent &evt, AudacityProject *pProject,
262 wxWindow *pParent)
263 {
264 auto result = LabelDefaultClickHandle::Release( evt, pProject, pParent );
265
266 // Only selected a part of a text string and changed track selectedness.
267 // No undoable effects.
268
269 const wxMouseEvent &event = evt.event;
270 auto pLT = TrackList::Get( *pProject ).Lock(mpLT);
271 if (pLT)
272 HandleTextDragRelease( *pProject, event );
273
274 // handle mouse left button up
275 if (event.LeftUp())
276 mLabelTrackStartXPos = -1;
277
278 return result | RefreshCode::RefreshNone;
279 }
280
Cancel(AudacityProject * pProject)281 UIHandle::Result LabelTextHandle::Cancel( AudacityProject *pProject )
282 {
283 auto result = LabelDefaultClickHandle::Cancel( pProject );
284 return result | RefreshCode::RefreshAll;
285 }
286