1 /*
2 Copyright (C) 2005-2008 Remon Sijrier
3 
4 This file is part of Traverso
5 
6 Traverso is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
19 
20 */
21 
22 #include "MoveClip.h"
23 
24 #include "AudioClip.h"
25 #include "AudioClipManager.h"
26 #include "ContextPointer.h"
27 #include "InputEngine.h"
28 #include "SnapList.h"
29 #include "Sheet.h"
30 #include "Track.h"
31 #include "TimeLine.h"
32 
33 #include "ClipsViewPort.h"
34 #include "SheetView.h"
35 #include "TrackView.h"
36 #include "AudioClipView.h"
37 
38 #include "Zoom.h"
39 
40 
41 // Always put me below _all_ includes, this is needed
42 // in case we run with memory leak detection enabled!
43 #include "Debugger.h"
44 
45 /**
46   *	\class MoveClip
47 	\brief A Command class for Dragging or Copy-dragging an AudioClip
48 
49 	\sa TraversoCommands
50  */
51 
52 
53 /**
54  *	Creates  a Move Clip or Copy Clip Command object.
55  */
MoveClip(ViewItem * view,QVariantList args)56 MoveClip::MoveClip(ViewItem* view, QVariantList args)
57 	: Command(view->get_context(), "")
58 	, d(new Data)
59 {
60 	QString action = "move"; // default action!
61 
62 	if (args.size() > 0) {
63 		action = args.at(0).toString();
64 	}
65 	if (args.size() > 1) {
66 		d->verticalOnly = args.at(1).toBool();
67 	} else {
68 		d->verticalOnly = false;
69 	}
70 
71 	QString des;
72 	if (action == "copy") {
73 		des = tr("Copy Clip");
74 		m_actionType = COPY;
75 	} else if (action == "move") {
76 		des = tr("Move Clip");
77 		m_actionType = MOVE;
78 	} else if (action == "move_to_start") {
79 		des = tr("Move Clip To Start");
80 		m_actionType = MOVE_TO_START;
81 	} else if (action == "move_to_end") {
82 		des = tr("Move Clip To End");
83 		m_actionType = MOVE_TO_END;
84 	} else if (action == "fold_sheet") {
85 		des = tr("Fold Sheet");
86 		m_actionType = FOLD_SHEET;
87 	} else if (action == "fold_track") {
88 		des = tr("Fold Track");
89 		m_actionType = FOLD_TRACK;
90 	} else if (action == "fold_markers") {
91 		des = tr("Fold Markers");
92 		m_actionType = FOLD_MARKERS;
93 	} else {
94 		PERROR("MoveClip: Unknown action type: %s", QS_C(action));
95 	}
96 
97 	setText(des);
98 
99 	if (m_actionType == FOLD_SHEET || m_actionType == FOLD_TRACK || m_actionType == FOLD_MARKERS) {
100 
101 		QList<AudioClip*> movingClips;
102 		QList<Track*> tracks;
103 
104 		if (m_actionType == FOLD_TRACK) {
105 			TrackView* tv = qobject_cast<TrackView*>(view);
106 			Q_ASSERT(tv);
107 			d->sv= tv->get_sheetview();
108 			tracks.append(tv->get_track());
109 		} else if (m_actionType == FOLD_SHEET) {
110 			d->sv = qobject_cast<SheetView*>(view);
111 			Q_ASSERT(d->sv);
112 			tracks = d->sv->get_sheet()->get_tracks();
113 		} else {
114 			d->sv = qobject_cast<SheetView*>(view->get_sheetview());
115 			Q_ASSERT(d->sv);
116 		}
117 
118 		TimeRef currentLocation = TimeRef(cpointer().on_first_input_event_scene_x() * d->sv->timeref_scalefactor);
119 
120 		if (d->sv->get_trackview_under(cpointer().scene_pos())) {
121 			d->pointedTrackIndex = d->sv->get_trackview_under(cpointer().scene_pos())->get_track()->get_sort_index();
122 		} else {
123 			d->pointedTrackIndex = 0;
124 		}
125 
126 		if (m_actionType == FOLD_SHEET || m_actionType == FOLD_MARKERS) {
127 			QList<Marker*> movingMarkers = d->sv->get_sheet()->get_timeline()->get_markers();
128 			foreach(Marker* marker, movingMarkers) {
129 				if (marker->get_when() > currentLocation) {
130 					MarkerAndOrigin markerAndOrigin;
131 					markerAndOrigin.marker = marker;
132 					markerAndOrigin.origin = marker->get_when();
133 					m_markers.append(markerAndOrigin);
134 				}
135 			}
136 		}
137 
138 		if (m_actionType == FOLD_SHEET || m_actionType == FOLD_TRACK) {
139 			foreach(Track* track, tracks) {
140 				QList<AudioClip*> clips = track->get_cliplist();
141 				foreach(AudioClip* clip, clips) {
142 					if (clip->get_track_end_location() > currentLocation) {
143 						movingClips.append(clip);
144 					}
145 				}
146 			}
147 		}
148 
149 		m_group.set_clips(movingClips);
150 
151 	} else {
152 		AudioClipView* cv = qobject_cast<AudioClipView*>(view);
153 		Q_ASSERT(cv);
154 		d->sv = cv->get_sheetview();
155 		AudioClip* clip  = cv->get_clip();
156 		if (clip->is_selected()) {
157 			QList<AudioClip*> selected;
158 			clip->get_sheet()->get_audioclip_manager()->get_selected_clips(selected);
159 			m_group.set_clips(selected);
160 		} else {
161 			m_group.add_clip(clip);
162 		}
163 		d->pointedTrackIndex = clip->get_track()->get_sort_index();
164 	}
165 
166 	m_origTrackIndex = m_newTrackIndex = m_group.get_track_index();
167 	if (m_group.get_size() == 0 && m_markers.count() > 0) {
168 		m_trackStartLocation = m_markers[0].origin;
169 	} else {
170 		m_trackStartLocation = m_group.get_track_start_location();
171 	}
172 	m_sheet = d->sv->get_sheet();
173 	d->zoom = 0;
174 }
175 
~MoveClip()176 MoveClip::~MoveClip()
177 {
178 	if (d) {
179 		if (d->zoom) {
180 			delete d->zoom;
181 		}
182 		delete d;
183 	}
184 }
185 
begin_hold()186 int MoveClip::begin_hold()
187 {
188 	if ((!m_group.get_size() || m_group.is_locked()) && !m_markers.count()) {
189 		return -1;
190 	}
191 
192 	if (m_actionType == COPY) {
193 		// FIXME Memory leak here!
194 		QList<AudioClip*> newclips = m_group.copy_clips();
195 		m_group.set_clips(newclips);
196 		m_group.add_all_clips_to_tracks();
197 		m_group.move_to(m_origTrackIndex, m_trackStartLocation + TimeRef(d->sv->timeref_scalefactor * 3));
198 	}
199 
200 	m_group.set_as_moving(true);
201 
202 	d->sv->stop_follow_play_head();
203 	d->sv->start_shuttle(true, true);
204 	d->sceneXStartPos = cpointer().on_first_input_event_scene_x();
205 
206 	return 1;
207 }
208 
209 
finish_hold()210 int MoveClip::finish_hold()
211 {
212 	m_group.set_as_moving(false);
213 
214 	d->sv->start_shuttle(false);
215 
216 	return 1;
217 }
218 
219 
prepare_actions()220 int MoveClip::prepare_actions()
221 {
222 	if (d->zoom) {
223 		delete d->zoom;
224 	}
225 	delete d;
226 	d = 0;
227 
228 	if (m_actionType == COPY) {
229 		m_group.remove_all_clips_from_tracks();
230 	}
231 
232 	if (m_origTrackIndex == m_newTrackIndex &&  m_posDiff == TimeRef() &&
233 	    ! (m_actionType == COPY || m_actionType == MOVE_TO_START || m_actionType == MOVE_TO_END) ) {
234 		return -1;
235 	}
236 
237 	return 1;
238 }
239 
240 
do_action()241 int MoveClip::do_action()
242 {
243 	PENTER;
244 	if (m_actionType == MOVE || m_actionType == FOLD_SHEET || m_actionType == FOLD_TRACK) {
245 		m_group.move_to(m_newTrackIndex, m_trackStartLocation + m_posDiff);
246 	}
247 	else if (m_actionType == COPY) {
248 		m_group.add_all_clips_to_tracks();
249 		m_group.move_to(m_newTrackIndex, m_trackStartLocation + m_posDiff);
250 	}
251 	else if (m_actionType == MOVE_TO_START) {
252 		move_to_start(false);
253 	}
254 	else if (m_actionType == MOVE_TO_END) {
255 		move_to_end(false);
256 	}
257 
258 	foreach(MarkerAndOrigin markerAndOrigin, m_markers) {
259 		markerAndOrigin.marker->set_when(markerAndOrigin.origin + m_posDiff);
260 	}
261 
262 	return 1;
263 }
264 
265 
undo_action()266 int MoveClip::undo_action()
267 {
268 	PENTER;
269 
270 	if (m_actionType == COPY) {
271 		m_group.remove_all_clips_from_tracks();
272 	} else {
273 		m_group.move_to(m_origTrackIndex, m_trackStartLocation);
274 	}
275 
276 	foreach(MarkerAndOrigin markerAndOrigin, m_markers) {
277 		markerAndOrigin.marker->set_when(markerAndOrigin.origin);
278 	}
279 
280 	return 1;
281 }
282 
cancel_action()283 void MoveClip::cancel_action()
284 {
285 	finish_hold();
286 	undo_action();
287 }
288 
jog()289 int MoveClip::jog()
290 {
291 	if (d->zoom) {
292 		d->zoom->jog();
293 		return 0;
294 	}
295 
296 	TrackView* trackView = d->sv->get_trackview_under(cpointer().scene_pos());
297 	int deltaTrackIndex = 0;
298 	if (trackView/* && !(m_actionType == FOLD_SHEET)*/) {
299 		deltaTrackIndex = trackView->get_track()->get_sort_index() - d->pointedTrackIndex;
300 		m_group.check_valid_track_index_delta(deltaTrackIndex);
301 		m_newTrackIndex = m_newTrackIndex + deltaTrackIndex;
302 		d->pointedTrackIndex = trackView->get_track()->get_sort_index();
303 	}
304 
305 	// Calculate the distance moved based on the current scene x pos and the initial one.
306 	// Only assign if we the movements is allowed in horizontal direction
307 	TimeRef diff_f;
308 	if (!d->verticalOnly) {
309 		diff_f = (cpointer().scene_x() - d->sceneXStartPos) * d->sv->timeref_scalefactor;
310 	}
311 
312 	// If the moved distance (diff_f) makes as go beyond the left most position (== 0, or TimeRef())
313 	// set the newTrackStartLocation to 0. Else calculate it based on the original track start location
314 	// and the distance moved.
315 	TimeRef newTrackStartLocation;
316 	if (diff_f < TimeRef() && m_trackStartLocation < (-1 * diff_f)) {
317 		newTrackStartLocation = qint64(0);
318 	} else {
319 		newTrackStartLocation = m_trackStartLocation + diff_f;
320 	}
321 
322 	// substract the snap distance, if snap is turned on.
323 	if (m_sheet->is_snap_on() && !d->verticalOnly) {
324 		newTrackStartLocation -= m_sheet->get_snap_list()->calculate_snap_diff(newTrackStartLocation, newTrackStartLocation + m_group.get_length());
325 	}
326 
327 	// Now that the new track start location is known, the position diff can be calculated
328 	m_posDiff = newTrackStartLocation - m_trackStartLocation;
329 
330 	// and used to move the group to it's new location.
331 	m_group.move_to(m_newTrackIndex, m_trackStartLocation + m_posDiff);
332 
333 	// and used to move the markers
334 	foreach(MarkerAndOrigin markerAndOrigin, m_markers) {
335 		markerAndOrigin.marker->set_when(markerAndOrigin.origin + m_posDiff);
336 	}
337 
338 	d->sv->update_shuttle_factor();
339 
340 	cpointer().get_viewport()->set_holdcursor_pos(d->sv->get_clips_viewport()->mapToScene(cpointer().pos()).toPoint());
341 	cpointer().get_viewport()->set_holdcursor_text(timeref_to_text(newTrackStartLocation, d->sv->timeref_scalefactor));
342 
343 
344 	return 1;
345 }
346 
347 
next_snap_pos(bool autorepeat)348 void MoveClip::next_snap_pos(bool autorepeat)
349 {
350 	Q_UNUSED(autorepeat);
351 	do_prev_next_snap(m_sheet->get_snap_list()->next_snap_pos(m_group.get_track_start_location()),
352 			  m_sheet->get_snap_list()->next_snap_pos(m_group.get_track_end_location()));
353 }
354 
prev_snap_pos(bool autorepeat)355 void MoveClip::prev_snap_pos(bool autorepeat)
356 {
357 	Q_UNUSED(autorepeat);
358 	do_prev_next_snap(m_sheet->get_snap_list()->prev_snap_pos(m_group.get_track_start_location()),
359 			m_sheet->get_snap_list()->prev_snap_pos(m_group.get_track_end_location()));
360 }
361 
do_prev_next_snap(TimeRef trackStartLocation,TimeRef trackEndLocation)362 void MoveClip::do_prev_next_snap(TimeRef trackStartLocation, TimeRef trackEndLocation)
363 {
364 	if (d->verticalOnly) return;
365 	ie().bypass_jog_until_mouse_movements_exceeded_manhattenlength();
366 	trackStartLocation -= m_sheet->get_snap_list()->calculate_snap_diff(trackStartLocation, trackEndLocation);
367 	m_posDiff = trackStartLocation - m_trackStartLocation;
368 	do_move();
369 }
370 
move_to_start(bool autorepeat)371 void MoveClip::move_to_start(bool autorepeat)
372 {
373 	Q_UNUSED(autorepeat);
374 	m_group.move_to(m_group.get_track_index(), TimeRef());
375 }
376 
move_to_end(bool autorepeat)377 void MoveClip::move_to_end(bool autorepeat)
378 {
379 	Q_UNUSED(autorepeat);
380 	m_group.move_to(m_group.get_track_index(), m_sheet->get_last_location());
381 }
382 
move_up(bool autorepeat)383 void MoveClip::move_up(bool autorepeat)
384 {
385 	Q_UNUSED(autorepeat);
386 	ie().bypass_jog_until_mouse_movements_exceeded_manhattenlength();
387 	int deltaTrackIndex = -1;
388 	m_group.check_valid_track_index_delta(deltaTrackIndex);
389 	m_newTrackIndex = m_newTrackIndex + deltaTrackIndex;
390 	do_move();
391 }
392 
move_down(bool autorepeat)393 void MoveClip::move_down(bool autorepeat)
394 {
395 	Q_UNUSED(autorepeat);
396 	ie().bypass_jog_until_mouse_movements_exceeded_manhattenlength();
397 	int deltaTrackIndex = 1;
398 	m_group.check_valid_track_index_delta(deltaTrackIndex);
399 	m_newTrackIndex = m_newTrackIndex + deltaTrackIndex;
400 	do_move();
401 }
402 
move_left(bool autorepeat)403 void MoveClip::move_left(bool autorepeat)
404 {
405 	Q_UNUSED(autorepeat);
406 	if (d->verticalOnly) return;
407 	ie().bypass_jog_until_mouse_movements_exceeded_manhattenlength();
408 	m_posDiff -= d->sv->timeref_scalefactor;
409 	do_move();
410 }
411 
move_right(bool autorepeat)412 void MoveClip::move_right(bool autorepeat)
413 {
414 	Q_UNUSED(autorepeat);
415 	if (d->verticalOnly) return;
416 	ie().bypass_jog_until_mouse_movements_exceeded_manhattenlength();
417 	m_posDiff += d->sv->timeref_scalefactor;
418 	do_move();
419 }
420 
start_zoom(bool autorepeat)421 void MoveClip::start_zoom(bool autorepeat)
422 {
423 	if (autorepeat) return;
424 
425 	if (!d->zoom) {
426 		d->zoom = new Zoom(d->sv, QList<QVariant>() << "HJogZoom" << "1.2" << "0.2");
427 		d->zoom->begin_hold();
428 		cpointer().get_viewport()->set_holdcursor(":/cursorZoomHorizontal");
429 		d->sv->start_shuttle(false);
430 	} else {
431 		cpointer().get_viewport()->set_holdcursor(":/cursorHoldLrud");
432 		d->sv->start_shuttle(true, true);
433 	}
434 }
435 
set_cursor_shape(int useX,int useY)436 void MoveClip::set_cursor_shape(int useX, int useY)
437 {
438 	if (useX && useY) {
439 		cpointer().get_viewport()->set_holdcursor(":/cursorHoldLrud");
440 	} else if (useX) {
441 		cpointer().get_viewport()->set_holdcursor(":/cursorHoldLr");
442 	} else {
443 		cpointer().get_viewport()->set_holdcursor(":/cursorHoldUd");
444 	}
445 }
446 
toggle_vertical_only(bool autorepeat)447 void MoveClip::toggle_vertical_only(bool autorepeat)
448 {
449 	if (autorepeat) return;
450 
451 	d->verticalOnly = !d->verticalOnly;
452 	if (d->verticalOnly) {
453 		set_cursor_shape(0, 1);
454 	} else {
455 		set_cursor_shape(1, 1);
456 	}
457 }
458 
do_move()459 void MoveClip::do_move()
460 {
461 	m_group.move_to(m_newTrackIndex, m_trackStartLocation + m_posDiff);
462 	if (d) {
463 		cpointer().get_viewport()->set_holdcursor_text(timeref_to_text(m_trackStartLocation + m_posDiff, d->sv->timeref_scalefactor));
464 	}
465 }
466