summaryrefslogtreecommitdiffstats
path: root/qt-ui/divelistview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'qt-ui/divelistview.cpp')
-rw-r--r--qt-ui/divelistview.cpp172
1 files changed, 154 insertions, 18 deletions
diff --git a/qt-ui/divelistview.cpp b/qt-ui/divelistview.cpp
index d4e744237..16033dca9 100644
--- a/qt-ui/divelistview.cpp
+++ b/qt-ui/divelistview.cpp
@@ -4,13 +4,22 @@
* classes for the divelist of Subsurface
*
*/
-#include "divelistview.h"
#include "filtermodels.h"
#include "modeldelegates.h"
#include "mainwindow.h"
+#include "divepicturewidget.h"
+#include "display.h"
+#include <unistd.h>
#include <QSettings>
+#include <QKeyEvent>
#include <QFileDialog>
+#include <QNetworkAccessManager>
+#include <QNetworkReply>
+#include <QStandardPaths>
+#include <QMessageBox>
#include "qthelper.h"
+#include "undocommands.h"
+#include "divelistview.h"
// # Date Rtg Dpth Dur Tmp Wght Suit Cyl Gas SAC OTU CNS Loc
static int defaultWidth[] = { 70, 140, 90, 50, 50, 50, 50, 70, 50, 50, 70, 50, 50, 500};
@@ -30,6 +39,7 @@ DiveListView::DiveListView(QWidget *parent) : QTreeView(parent), mouseClickSelec
setSortingEnabled(false);
setContextMenuPolicy(Qt::DefaultContextMenu);
+ setSelectionMode(ExtendedSelection);
header()->setContextMenuPolicy(Qt::ActionsContextMenu);
const QFontMetrics metrics(defaultModelFont());
@@ -39,7 +49,7 @@ DiveListView::DiveListView(QWidget *parent) : QTreeView(parent), mouseClickSelec
// Fixes for the layout needed for mac
#ifdef Q_OS_MAC
int ht = metrics.height();
- header()->setMinimumHeight(ht + 10);
+ header()->setMinimumHeight(ht + 4);
#endif
// TODO FIXME we need this to get the header names
@@ -84,6 +94,8 @@ DiveListView::DiveListView(QWidget *parent) : QTreeView(parent), mouseClickSelec
header()->setStretchLastSection(true);
+
+ installEventFilter(this);
}
DiveListView::~DiveListView()
@@ -175,8 +187,13 @@ void DiveListView::rememberSelection()
if (index.column() != 0) // We only care about the dives, so, let's stick to rows and discard columns.
continue;
struct dive *d = (struct dive *)index.data(DiveTripModel::DIVE_ROLE).value<void *>();
- if (d)
+ if (d) {
selectedDives.insert(d->divetrip, get_divenr(d));
+ } else {
+ struct dive_trip *t = (struct dive_trip *)index.data(DiveTripModel::TRIP_ROLE).value<void *>();
+ if (t)
+ selectedDives.insert(t, -1);
+ }
}
selectionSaved = true;
}
@@ -195,8 +212,10 @@ void DiveListView::restoreSelection()
QList<int> selectedDivesOnTrip = selectedDives.values(trip);
// Only select trip if all of its dives were selected
- if (trip != NULL && divesOnTrip.count() == selectedDivesOnTrip.count())
+ if(selectedDivesOnTrip.contains(-1)) {
selectTrip(trip);
+ selectedDivesOnTrip.removeAll(-1);
+ }
selectDives(selectedDivesOnTrip);
}
}
@@ -343,6 +362,10 @@ bool DiveListView::eventFilter(QObject *, QEvent *event)
if (event->type() != QEvent::KeyPress)
return false;
QKeyEvent *keyEv = static_cast<QKeyEvent *>(event);
+ if (keyEv->key() == Qt::Key_Delete) {
+ contextMenuIndex = currentIndex();
+ deleteDive();
+ }
if (keyEv->key() != Qt::Key_Escape)
return false;
return true;
@@ -492,6 +515,8 @@ void DiveListView::toggleColumnVisibilityByIndex()
void DiveListView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
{
+ if (!isVisible())
+ return;
if (!current.isValid())
return;
scrollTo(current);
@@ -542,15 +567,32 @@ void DiveListView::selectionChanged(const QItemSelection &selected, const QItemS
Q_EMIT currentDiveChanged(selected_dive);
}
-static bool can_merge(const struct dive *a, const struct dive *b)
+enum asked_user {NOTYET, MERGE, DONTMERGE};
+
+static bool can_merge(const struct dive *a, const struct dive *b, enum asked_user *have_asked)
{
if (!a || !b)
return false;
if (a->when > b->when)
return false;
/* Don't merge dives if there's more than half an hour between them */
- if (a->when + a->duration.seconds + 30 * 60 < b->when)
- return false;
+ if (a->when + a->duration.seconds + 30 * 60 < b->when) {
+ if (*have_asked == NOTYET) {
+ if (QMessageBox::warning(MainWindow::instance(),
+ MainWindow::instance()->tr("Warning"),
+ MainWindow::instance()->tr("Trying to merge dives with %1min interval in between").arg(
+ (b->when - a->when - a->duration.seconds) / 60),
+ QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) {
+ *have_asked = DONTMERGE;
+ return false;
+ } else {
+ *have_asked = MERGE;
+ return true;
+ }
+ } else {
+ return *have_asked == MERGE ? true : false;
+ }
+ }
return true;
}
@@ -558,10 +600,11 @@ void DiveListView::mergeDives()
{
int i;
struct dive *dive, *maindive = NULL;
+ enum asked_user have_asked = NOTYET;
for_each_dive (i, dive) {
if (dive->selected) {
- if (!can_merge(maindive, dive)) {
+ if (!can_merge(maindive, dive, &have_asked)) {
maindive = dive;
} else {
maindive = merge_two_dives(maindive, dive);
@@ -611,10 +654,14 @@ void DiveListView::removeFromTrip()
//TODO: move this to C-code.
int i;
struct dive *d;
+ QMap<struct dive*, dive_trip*> divesToRemove;
for_each_dive (i, d) {
if (d->selected)
- remove_dive_from_trip(d, false);
+ divesToRemove.insert(d, d->divetrip);
}
+ UndoRemoveDivesFromTrip *undoCommand = new UndoRemoveDivesFromTrip(divesToRemove);
+ MainWindow::instance()->undoStack->push(undoCommand);
+
rememberSelection();
reload(currentLayout, false);
fixMessyQtModelBehaviour();
@@ -721,13 +768,19 @@ void DiveListView::deleteDive()
// so instead of using the for_each_dive macro I'm using an explicit for loop
// to make this easier to understand
int lastDiveNr = -1;
+ QList<struct dive*> deletedDives; //a list of all deleted dives to be stored in the undo command
for_each_dive (i, d) {
if (!d->selected)
continue;
+ struct dive* undo_entry = alloc_dive();
+ copy_dive(get_dive(i), undo_entry);
+ deletedDives.append(undo_entry);
delete_single_dive(i);
i--; // so the next dive isn't skipped... it's now #i
lastDiveNr = i;
}
+ UndoDeleteDive *undoEntry = new UndoDeleteDive(deletedDives);
+ MainWindow::instance()->undoStack->push(undoEntry);
if (amount_selected == 0) {
MainWindow::instance()->cleanUpEmpty();
}
@@ -765,9 +818,32 @@ void DiveListView::contextMenuEvent(QContextMenuEvent *event)
dive_trip_t *trip = (dive_trip_t *)contextMenuIndex.data(DiveTripModel::TRIP_ROLE).value<void *>();
QMenu popup(this);
if (currentLayout == DiveTripModel::TREE) {
- popup.addAction(tr("Expand all"), this, SLOT(expandAll()));
- popup.addAction(tr("Collapse all"), this, SLOT(collapseAll()));
- collapseAction = popup.addAction(tr("Collapse others"), this, SLOT(collapseAll()));
+ // verify if there is a node that`s not expanded.
+ bool needs_expand = false;
+ bool needs_collapse = false;
+ uint expanded_nodes = 0;
+ for(int i = 0, end = model()->rowCount(); i < end; i++) {
+ QModelIndex idx = model()->index(i, 0);
+ if (idx.data(DiveTripModel::DIVE_ROLE).value<void *>())
+ continue;
+
+ if (!isExpanded(idx)) {
+ needs_expand = true;
+ } else {
+ needs_collapse = true;
+ expanded_nodes ++;
+ }
+ }
+ if (needs_expand)
+ popup.addAction(tr("Expand all"), this, SLOT(expandAll()));
+ if (needs_collapse)
+ popup.addAction(tr("Collapse all"), this, SLOT(collapseAll()));
+
+ // verify if there`s a need for collapse others
+ if (expanded_nodes > 1)
+ collapseAction = popup.addAction(tr("Collapse others"), this, SLOT(collapseAll()));
+
+
if (d) {
popup.addAction(tr("Remove dive(s) from trip"), this, SLOT(removeFromTrip()));
popup.addAction(tr("Create new trip above"), this, SLOT(newTripAbove()));
@@ -804,8 +880,9 @@ void DiveListView::contextMenuEvent(QContextMenuEvent *event)
popup.addAction(tr("Merge selected dives"), this, SLOT(mergeDives()));
if (amount_selected >= 1) {
popup.addAction(tr("Renumber dive(s)"), this, SLOT(renumberDives()));
- popup.addAction(tr("Shift times"), this, SLOT(shiftTimes()));
- popup.addAction(tr("Load images"), this, SLOT(loadImages()));
+ popup.addAction(tr("Shift dive times"), this, SLOT(shiftTimes()));
+ popup.addAction(tr("Load image(s) from file(s)"), this, SLOT(loadImages()));
+ popup.addAction(tr("Load image(s) from web"), this, SLOT(loadWebImages()));
}
// "collapse all" really closes all trips,
@@ -831,11 +908,16 @@ void DiveListView::loadImages()
QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Open image files"), lastUsedImageDir(), tr("Image files (*.jpg *.jpeg *.pnm *.tif *.tiff)"));
if (fileNames.isEmpty())
return;
-
updateLastUsedImageDir(QFileInfo(fileNames[0]).dir().path());
- ShiftImageTimesDialog shiftDialog(this);
+ matchImagesToDives(fileNames);
+}
+
+void DiveListView::matchImagesToDives(QStringList fileNames)
+{
+ ShiftImageTimesDialog shiftDialog(this, fileNames);
shiftDialog.setOffset(lastImageTimeOffset());
- shiftDialog.exec();
+ if (!shiftDialog.exec())
+ return;
updateLastImageTimeOffset(shiftDialog.amount());
Q_FOREACH (const QString &fileName, fileNames) {
@@ -844,7 +926,7 @@ void DiveListView::loadImages()
for_each_dive (j, dive) {
if (!dive->selected)
continue;
- dive_create_picture(dive, qstrdup(fileName.toUtf8().data()), shiftDialog.amount());
+ dive_create_picture(dive, copy_string(fileName.toUtf8().data()), shiftDialog.amount());
}
}
@@ -853,6 +935,60 @@ void DiveListView::loadImages()
DivePictureModel::instance()->updateDivePictures();
}
+void DiveListView::loadWebImages()
+{
+ URLDialog urlDialog(this);
+ if (!urlDialog.exec())
+ return;
+ loadImageFromURL(QUrl::fromUserInput(urlDialog.url()));
+
+}
+
+void DiveListView::loadImageFromURL(QUrl url)
+{
+ if (url.isValid()) {
+ QEventLoop loop;
+ QNetworkRequest request(url);
+ QNetworkReply *reply = manager.get(request);
+ while (reply->isRunning()) {
+ loop.processEvents();
+ sleep(1);
+ }
+ QByteArray imageData = reply->readAll();
+
+ QImage image = QImage();
+ image.loadFromData(imageData);
+ if (image.isNull())
+ // If this is not an image, maybe it's an html file and Miika can provide some xslr magic to extract images.
+ // In this case we would call the function recursively on the list of image source urls;
+ return;
+
+ // Since we already downloaded the image we can cache it as well.
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ hash.addData(imageData);
+ QString path = QStandardPaths::standardLocations(QStandardPaths::CacheLocation).first();
+ QDir dir(path);
+ if (!dir.exists())
+ dir.mkpath(path);
+ QFile imageFile(path.append("/").append(hash.result().toHex()));
+ if (imageFile.open(QIODevice::WriteOnly)) {
+ QDataStream stream(&imageFile);
+ stream.writeRawData(imageData.data(), imageData.length());
+ imageFile.waitForBytesWritten(-1);
+ imageFile.close();
+ add_hash(imageFile.fileName(), hash.result());
+ struct picture picture;
+ picture.hash = NULL;
+ picture.filename = strdup(url.toString().toUtf8().data());
+ learnHash(&picture, hash.result());
+ matchImagesToDives(QStringList(url.toString()));
+ }
+ }
+
+
+}
+
+
QString DiveListView::lastUsedImageDir()
{
QSettings settings;