subsurface/desktop-widgets/undocommands.cpp
Berthold Stoeger b51e616b6a Undo: clear list of trips to be recreated in UndoDeleteDive::undo()
UndoDeleteDive::tripList kept track of the trips to be recreated on
undo. But the list wasn't cleared on undo, thus on subsequent redo
the same trip was readded to the list, leading to double-free.

This could trivially be reproduced by repeated CTRL-Z, CTRL-SHIFT-Z
pairs.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2018-07-19 02:43:08 +03:00

162 lines
4.3 KiB
C++

// SPDX-License-Identifier: GPL-2.0
#include "desktop-widgets/undocommands.h"
#include "desktop-widgets/mainwindow.h"
#include "core/divelist.h"
#include "core/subsurface-string.h"
UndoDeleteDive::UndoDeleteDive(QList<dive *> deletedDives) : diveList(deletedDives)
{
setText("delete dive");
if (diveList.count() > 1)
setText(QString("delete %1 dives").arg(QString::number(diveList.count())));
}
void UndoDeleteDive::undo()
{
// first bring back the trip(s)
Q_FOREACH(struct dive_trip *trip, tripList)
insert_trip(&trip);
// now walk the list of deleted dives
for (int i = 0; i < diveList.count(); i++) {
struct dive *d = diveList.at(i);
// we adjusted the divetrip to point to the "new" divetrip
if (d->divetrip) {
struct dive_trip *trip = d->divetrip;
tripflag_t tripflag = d->tripflag; // this gets overwritten in add_dive_to_trip()
d->divetrip = NULL;
d->next = NULL;
d->pprev = NULL;
add_dive_to_trip(d, trip);
d->tripflag = tripflag;
}
record_dive(diveList.at(i));
}
mark_divelist_changed(true);
tripList.clear();
MainWindow::instance()->refreshDisplay();
}
void UndoDeleteDive::redo()
{
QList<struct dive*> newList;
for (int i = 0; i < diveList.count(); i++) {
// make a copy of the dive before deleting it
struct dive* d = alloc_dive();
copy_dive(diveList.at(i), d);
newList.append(d);
// check for trip - if this is the last dive in the trip
// the trip will get deleted, so we need to remember it as well
if (d->divetrip && d->divetrip->nrdives == 1) {
struct dive_trip *undo_trip = (struct dive_trip *)calloc(1, sizeof(struct dive_trip));
*undo_trip = *d->divetrip;
undo_trip->location = copy_string(d->divetrip->location);
undo_trip->notes = copy_string(d->divetrip->notes);
undo_trip->nrdives = 0;
undo_trip->next = NULL;
undo_trip->dives = NULL;
// update all the dives who were in this trip to point to the copy of the
// trip that we are about to delete implicitly when deleting its last dive below
Q_FOREACH(struct dive *inner_dive, newList) {
if (inner_dive->divetrip == d->divetrip)
inner_dive->divetrip = undo_trip;
}
d->divetrip = undo_trip;
tripList.append(undo_trip);
}
//delete the dive
int nr;
if ((nr = get_divenr(diveList.at(i))) >= 0)
delete_single_dive(nr);
}
mark_divelist_changed(true);
MainWindow::instance()->refreshDisplay();
diveList.clear();
diveList = newList;
}
UndoShiftTime::UndoShiftTime(QList<int> changedDives, int amount)
: diveList(changedDives), timeChanged(amount)
{
setText("shift time");
}
void UndoShiftTime::undo()
{
for (int i = 0; i < diveList.count(); i++) {
struct dive* d = get_dive_by_uniq_id(diveList.at(i));
d->when -= timeChanged;
}
mark_divelist_changed(true);
MainWindow::instance()->refreshDisplay();
}
void UndoShiftTime::redo()
{
for (int i = 0; i < diveList.count(); i++) {
struct dive* d = get_dive_by_uniq_id(diveList.at(i));
d->when += timeChanged;
}
mark_divelist_changed(true);
MainWindow::instance()->refreshDisplay();
}
UndoRenumberDives::UndoRenumberDives(QMap<int, QPair<int, int> > originalNumbers)
{
oldNumbers = originalNumbers;
if (oldNumbers.count() > 1)
setText(QString("renumber %1 dives").arg(QString::number(oldNumbers.count())));
else
setText("renumber dive");
}
void UndoRenumberDives::undo()
{
foreach (int key, oldNumbers.keys()) {
struct dive* d = get_dive_by_uniq_id(key);
d->number = oldNumbers.value(key).first;
}
mark_divelist_changed(true);
MainWindow::instance()->refreshDisplay();
}
void UndoRenumberDives::redo()
{
foreach (int key, oldNumbers.keys()) {
struct dive* d = get_dive_by_uniq_id(key);
d->number = oldNumbers.value(key).second;
}
mark_divelist_changed(true);
MainWindow::instance()->refreshDisplay();
}
UndoRemoveDivesFromTrip::UndoRemoveDivesFromTrip(QMap<dive *, dive_trip *> removedDives)
{
divesToUndo = removedDives;
setText("remove dive(s) from trip");
}
void UndoRemoveDivesFromTrip::undo()
{
QMapIterator<dive*, dive_trip*> i(divesToUndo);
while (i.hasNext()) {
i.next();
add_dive_to_trip(i.key (), i.value());
}
mark_divelist_changed(true);
MainWindow::instance()->refreshDisplay();
}
void UndoRemoveDivesFromTrip::redo()
{
QMapIterator<dive*, dive_trip*> i(divesToUndo);
while (i.hasNext()) {
i.next();
remove_dive_from_trip(i.key(), false);
}
mark_divelist_changed(true);
MainWindow::instance()->refreshDisplay();
}