mirror of
				https://github.com/subsurface/subsurface.git
				synced 2025-02-19 22:16:15 +00:00 
			
		
		
		
	For this, the core functionality of the split_dive() and split_dive_at_time() functions were split out into new split_dive_dont_insert() and split_dive_at_time_dont_insert(), which do not add the new dives to the log. Thus, the undo-command can take ownership of these dives, without having to remove them first. The split-dive functionality is temporarily made desktop-only until mobile also supports "UndoObjects". Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
		
			
				
	
	
		
			262 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			262 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| #include "desktop-widgets/undocommands.h"
 | |
| #include "desktop-widgets/mainwindow.h"
 | |
| #include "desktop-widgets/divelistview.h"
 | |
| #include "core/divelist.h"
 | |
| #include "core/subsurface-string.h"
 | |
| #include "core/gettextfromc.h"
 | |
| 
 | |
| // This helper function removes a dive, takes ownership of the dive and adds it to a DiveToAdd structure.
 | |
| // It is crucial that dives are added in reverse order of deletion, so the the indices are correctly
 | |
| // set and that the trips are added before they are used!
 | |
| static DiveToAdd removeDive(struct dive *d)
 | |
| {
 | |
| 	DiveToAdd res;
 | |
| 	res.idx = get_divenr(d);
 | |
| 	if (res.idx < 0)
 | |
| 		qWarning() << "Deletion of unknown dive!";
 | |
| 
 | |
| 	// remove dive from trip - if this is the last dive in the trip
 | |
| 	// remove the whole trip.
 | |
| 	res.trip = unregister_dive_from_trip(d, false);
 | |
| 	if (res.trip && res.trip->nrdives == 0) {
 | |
| 		unregister_trip(res.trip);		// Remove trip from backend
 | |
| 		res.tripToAdd.reset(res.trip);		// Take ownership of trip
 | |
| 	}
 | |
| 
 | |
| 	res.dive.reset(unregister_dive(res.idx));	// Remove dive from backend
 | |
| 	return res;
 | |
| }
 | |
| 
 | |
| // This helper function adds a dive and returns ownership to the backend. It may also add a dive trip.
 | |
| // It is crucial that dives are added in reverse order of deletion (see comment above)!
 | |
| // Returns pointer to added dive (which is owned by the backend!)
 | |
| static dive *addDive(DiveToAdd &d)
 | |
| {
 | |
| 	if (d.tripToAdd) {
 | |
| 		dive_trip *t = d.tripToAdd.release();	// Give up ownership of trip
 | |
| 		insert_trip(&t);			// Return ownership to backend
 | |
| 	}
 | |
| 	if (d.trip)
 | |
| 		add_dive_to_trip(d.dive.get(), d.trip);
 | |
| 	dive *res = d.dive.release();			// Give up ownership of dive
 | |
| 	add_single_dive(d.idx, res);			// Return ownership to backend
 | |
| 	return res;
 | |
| }
 | |
| 
 | |
| UndoAddDive::UndoAddDive(dive *d)
 | |
| {
 | |
| 	setText(gettextFromC::tr("add dive"));
 | |
| 	// TODO: handle tags
 | |
| 	//saveTags();
 | |
| 	d->maxdepth.mm = 0;
 | |
| 	fixup_dive(d);
 | |
| 	diveToAdd.trip = d->divetrip;
 | |
| 	d->divetrip = nullptr;
 | |
| 	diveToAdd.idx = dive_get_insertion_index(d);
 | |
| 	d->number = get_dive_nr_at_idx(diveToAdd.idx);
 | |
| 	diveToAdd.dive.reset(clone_dive(d));
 | |
| }
 | |
| 
 | |
| void UndoAddDive::redo()
 | |
| {
 | |
| 	diveToRemove = addDive(diveToAdd);
 | |
| 	mark_divelist_changed(true);
 | |
| 
 | |
| 	// Finally, do the UI stuff:
 | |
| 	MainWindow::instance()->dive_list()->unselectDives();
 | |
| 	MainWindow::instance()->dive_list()->selectDive(diveToAdd.idx, true);
 | |
| 	MainWindow::instance()->refreshDisplay();
 | |
| }
 | |
| 
 | |
| void UndoAddDive::undo()
 | |
| {
 | |
| 	// Simply remove the dive that was previously added
 | |
| 	diveToAdd = removeDive(diveToRemove);
 | |
| 
 | |
| 	// Finally, do the UI stuff:
 | |
| 	MainWindow::instance()->refreshDisplay();
 | |
| }
 | |
| 
 | |
| UndoDeleteDive::UndoDeleteDive(const QVector<struct dive*> &divesToDeleteIn) : divesToDelete(divesToDeleteIn)
 | |
| {
 | |
| 	setText(tr("delete %n dive(s)", "", divesToDelete.size()));
 | |
| }
 | |
| 
 | |
| void UndoDeleteDive::undo()
 | |
| {
 | |
| 	for (auto it = divesToAdd.rbegin(); it != divesToAdd.rend(); ++it)
 | |
| 		divesToDelete.append(addDive(*it));
 | |
| 
 | |
| 	mark_divelist_changed(true);
 | |
| 	divesToAdd.clear();
 | |
| 
 | |
| 	// Finally, do the UI stuff:
 | |
| 	MainWindow::instance()->refreshDisplay();
 | |
| }
 | |
| 
 | |
| void UndoDeleteDive::redo()
 | |
| {
 | |
| 	for (dive *d: divesToDelete)
 | |
| 		divesToAdd.push_back(removeDive(d));
 | |
| 
 | |
| 	divesToDelete.clear();
 | |
| 	mark_divelist_changed(true);
 | |
| 
 | |
| 	// Finally, do the UI stuff:
 | |
| 	MainWindow::instance()->refreshDisplay();
 | |
| }
 | |
| 
 | |
| 
 | |
| UndoShiftTime::UndoShiftTime(QVector<int> changedDives, int amount)
 | |
| 	: diveList(changedDives), timeChanged(amount)
 | |
| {
 | |
| 	setText(tr("delete %n dive(s)", "", changedDives.size()));
 | |
| }
 | |
| 
 | |
| void UndoShiftTime::undo()
 | |
| {
 | |
| 	for (int i = 0; i < diveList.count(); i++) {
 | |
| 		dive *d = get_dive_by_uniq_id(diveList.at(i));
 | |
| 		d->when -= timeChanged;
 | |
| 	}
 | |
| 	// Changing times may have unsorted the dive table
 | |
| 	sort_table(&dive_table);
 | |
| 	mark_divelist_changed(true);
 | |
| 
 | |
| 	// Negate the time-shift so that the next call does the reverse
 | |
| 	timeChanged = -timeChanged;
 | |
| 
 | |
| 	// Finally, do the UI stuff:
 | |
| 	MainWindow::instance()->refreshDisplay();
 | |
| }
 | |
| 
 | |
| void UndoShiftTime::redo()
 | |
| {
 | |
| 	// Same as undo(), since after undo() we reversed the timeOffset
 | |
| 	undo();
 | |
| }
 | |
| 
 | |
| 
 | |
| UndoRenumberDives::UndoRenumberDives(const QVector<QPair<int, int>> &divesToRenumberIn) : divesToRenumber(divesToRenumberIn)
 | |
| {
 | |
| 	setText(tr("renumber %n dive(s)", "", divesToRenumber.count()));
 | |
| }
 | |
| 
 | |
| void UndoRenumberDives::undo()
 | |
| {
 | |
| 	for (auto &pair: divesToRenumber) {
 | |
| 		dive *d = get_dive_by_uniq_id(pair.first);
 | |
| 		if (!d)
 | |
| 			continue;
 | |
| 		std::swap(d->number, pair.second);
 | |
| 	}
 | |
| 	mark_divelist_changed(true);
 | |
| 
 | |
| 	// Finally, do the UI stuff:
 | |
| 	MainWindow::instance()->refreshDisplay();
 | |
| }
 | |
| 
 | |
| void UndoRenumberDives::redo()
 | |
| {
 | |
| 	// Redo and undo do the same thing!
 | |
| 	undo();
 | |
| }
 | |
| 
 | |
| UndoRemoveDivesFromTrip::UndoRemoveDivesFromTrip(const QVector<dive *> &divesToRemoveIn) : divesToRemove(divesToRemoveIn)
 | |
| {
 | |
| 	setText(tr("remove %n dive(s) from trip", "", divesToRemove.size()));
 | |
| }
 | |
| 
 | |
| void UndoRemoveDivesFromTrip::undo()
 | |
| {
 | |
| 	// first bring back the trip(s)
 | |
| 	for (auto &trip: tripsToAdd) {
 | |
| 		dive_trip *t = trip.release();	// Give up ownership
 | |
| 		insert_trip(&t);		// Return ownership to backend
 | |
| 	}
 | |
| 	tripsToAdd.clear();
 | |
| 
 | |
| 	for (auto &pair: divesToAdd)
 | |
| 		add_dive_to_trip(pair.first, pair.second);
 | |
| 	divesToAdd.clear();
 | |
| 	mark_divelist_changed(true);
 | |
| 
 | |
| 	// Finally, do the UI stuff:
 | |
| 	MainWindow::instance()->refreshDisplay();
 | |
| }
 | |
| 
 | |
| void UndoRemoveDivesFromTrip::redo()
 | |
| {
 | |
| 	for (dive *d: divesToRemove) {
 | |
| 		// remove dive from trip - if this is the last dive in the trip
 | |
| 		// remove the whole trip.
 | |
| 		dive_trip *trip = unregister_dive_from_trip(d, false);
 | |
| 		if (!trip)
 | |
| 			continue;				// This was not part of a trip
 | |
| 		if (trip->nrdives == 0) {
 | |
| 			unregister_trip(trip);			// Remove trip from backend
 | |
| 			tripsToAdd.emplace_back(trip);		// Take ownership of trip
 | |
| 		}
 | |
| 		divesToAdd.emplace_back(d, trip);
 | |
| 	}
 | |
| 	mark_divelist_changed(true);
 | |
| 
 | |
| 	// Finally, do the UI stuff:
 | |
| 	MainWindow::instance()->refreshDisplay();
 | |
| }
 | |
| 
 | |
| UndoSplitDives::UndoSplitDives(dive *d, duration_t time)
 | |
| {
 | |
| 	setText(gettextFromC::tr("split dive"));
 | |
| 
 | |
| 	// Split the dive
 | |
| 	dive *new1, *new2;
 | |
| 	int idx = time.seconds < 0 ?
 | |
| 		split_dive_dont_insert(d, &new1, &new2) :
 | |
| 		split_dive_at_time_dont_insert(d, time, &new1, &new2);
 | |
| 
 | |
| 	// If this didn't work, reset pointers so that redo() and undo() do nothing
 | |
| 	if (idx < 0) {
 | |
| 		diveToSplit = nullptr;
 | |
| 		divesToUnsplit[0] = divesToUnsplit[1];
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	diveToSplit = d;
 | |
| 	splitDives[0].dive.reset(new1);
 | |
| 	splitDives[0].trip = d->divetrip;
 | |
| 	splitDives[0].idx = idx;
 | |
| 	splitDives[1].dive.reset(new2);
 | |
| 	splitDives[1].trip = d->divetrip;
 | |
| 	splitDives[1].idx = idx + 1;
 | |
| }
 | |
| 
 | |
| void UndoSplitDives::redo()
 | |
| {
 | |
| 	if (!diveToSplit)
 | |
| 		return;
 | |
| 	divesToUnsplit[0] = addDive(splitDives[0]);
 | |
| 	divesToUnsplit[1] = addDive(splitDives[1]);
 | |
| 	unsplitDive = removeDive(diveToSplit);
 | |
| 	mark_divelist_changed(true);
 | |
| 
 | |
| 	// Finally, do the UI stuff:
 | |
| 	MainWindow::instance()->refreshDisplay();
 | |
| 	MainWindow::instance()->refreshProfile();
 | |
| }
 | |
| 
 | |
| void UndoSplitDives::undo()
 | |
| {
 | |
| 	if (!unsplitDive.dive)
 | |
| 		return;
 | |
| 	// Note: reverse order with respect to redo()
 | |
| 	diveToSplit = addDive(unsplitDive);
 | |
| 	splitDives[1] = removeDive(divesToUnsplit[1]);
 | |
| 	splitDives[0] = removeDive(divesToUnsplit[0]);
 | |
| 	mark_divelist_changed(true);
 | |
| 
 | |
| 	// Finally, do the UI stuff:
 | |
| 	MainWindow::instance()->refreshDisplay();
 | |
| 	MainWindow::instance()->refreshProfile();
 | |
| }
 |