diff options
Diffstat (limited to 'qt-ui/divelistview.cpp')
-rw-r--r-- | qt-ui/divelistview.cpp | 172 |
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 ¤t, 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; |