2017-04-27 18:26:05 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2013-04-14 03:44:02 +00:00
|
|
|
/*
|
|
|
|
* divelistview.cpp
|
|
|
|
*
|
|
|
|
* classes for the divelist of Subsurface
|
|
|
|
*
|
|
|
|
*/
|
2016-04-05 05:02:03 +00:00
|
|
|
#include "qt-models/filtermodels.h"
|
|
|
|
#include "desktop-widgets/modeldelegates.h"
|
|
|
|
#include "desktop-widgets/mainwindow.h"
|
|
|
|
#include "desktop-widgets/divepicturewidget.h"
|
|
|
|
#include "core/display.h"
|
2015-04-24 15:10:55 +00:00
|
|
|
#include <unistd.h>
|
2013-05-21 19:51:49 +00:00
|
|
|
#include <QSettings>
|
2015-02-09 20:58:40 +00:00
|
|
|
#include <QKeyEvent>
|
2013-10-18 00:50:08 +00:00
|
|
|
#include <QFileDialog>
|
2015-04-24 15:10:55 +00:00
|
|
|
#include <QNetworkAccessManager>
|
|
|
|
#include <QNetworkReply>
|
|
|
|
#include <QStandardPaths>
|
2015-05-18 08:52:03 +00:00
|
|
|
#include <QMessageBox>
|
2018-05-23 07:55:49 +00:00
|
|
|
#include <QHeaderView>
|
2016-04-05 05:02:03 +00:00
|
|
|
#include "core/qthelper.h"
|
|
|
|
#include "desktop-widgets/undocommands.h"
|
|
|
|
#include "desktop-widgets/divelistview.h"
|
|
|
|
#include "qt-models/divepicturemodel.h"
|
|
|
|
#include "core/metrics.h"
|
2013-04-12 07:24:07 +00:00
|
|
|
|
2014-05-28 17:16:53 +00:00
|
|
|
DiveListView::DiveListView(QWidget *parent) : QTreeView(parent), mouseClickSelection(false), sortColumn(0),
|
2018-07-25 18:40:47 +00:00
|
|
|
currentOrder(Qt::DescendingOrder), dontEmitDiveChangedSignal(false), selectionSaved(false),
|
|
|
|
initialColumnWidths(DiveTripModel::COLUMNS, 50) // Set up with default length 50
|
2013-04-12 07:24:07 +00:00
|
|
|
{
|
2014-01-15 17:52:42 +00:00
|
|
|
setItemDelegate(new DiveListDelegate(this));
|
2013-04-24 15:57:30 +00:00
|
|
|
setUniformRowHeights(true);
|
2014-01-15 17:52:42 +00:00
|
|
|
setItemDelegateForColumn(DiveTripModel::RATING, new StarWidgetsDelegate(this));
|
2014-10-31 17:27:33 +00:00
|
|
|
MultiFilterSortModel *model = MultiFilterSortModel::instance();
|
2013-06-17 21:59:50 +00:00
|
|
|
model->setSortRole(DiveTripModel::SORT_ROLE);
|
2013-05-30 04:04:42 +00:00
|
|
|
model->setFilterKeyColumn(-1); // filter all columns
|
2014-01-09 18:50:47 +00:00
|
|
|
model->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
2018-07-25 19:23:19 +00:00
|
|
|
model->setSourceModel(DiveTripModel::instance());
|
2013-05-16 19:00:33 +00:00
|
|
|
setModel(model);
|
2013-06-12 17:14:25 +00:00
|
|
|
|
2013-05-28 19:56:58 +00:00
|
|
|
setSortingEnabled(false);
|
2013-06-06 00:06:42 +00:00
|
|
|
setContextMenuPolicy(Qt::DefaultContextMenu);
|
2015-02-10 20:08:01 +00:00
|
|
|
setSelectionMode(ExtendedSelection);
|
2013-05-21 19:51:49 +00:00
|
|
|
header()->setContextMenuPolicy(Qt::ActionsContextMenu);
|
2014-10-15 13:30:49 +00:00
|
|
|
|
2013-11-13 18:59:56 +00:00
|
|
|
header()->setStretchLastSection(true);
|
2015-02-06 11:19:04 +00:00
|
|
|
|
|
|
|
installEventFilter(this);
|
2018-07-25 18:40:47 +00:00
|
|
|
|
|
|
|
for (int i = DiveTripModel::NR; i < DiveTripModel::COLUMNS; i++)
|
2018-07-25 19:23:19 +00:00
|
|
|
calculateInitialColumnWidth(i);
|
2018-07-26 16:03:07 +00:00
|
|
|
setColumnWidths();
|
2013-11-08 22:30:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DiveListView::~DiveListView()
|
|
|
|
{
|
|
|
|
QSettings settings;
|
|
|
|
settings.beginGroup("ListWidget");
|
2014-08-27 00:23:41 +00:00
|
|
|
// don't set a width for the last column - location is supposed to be "the rest"
|
|
|
|
for (int i = DiveTripModel::NR; i < DiveTripModel::COLUMNS - 1; i++) {
|
2013-11-08 22:30:17 +00:00
|
|
|
if (isColumnHidden(i))
|
|
|
|
continue;
|
2014-08-27 00:23:41 +00:00
|
|
|
// we used to hardcode them all to 100 - so that might still be in the settings
|
2018-07-25 18:40:47 +00:00
|
|
|
if (columnWidth(i) == 100 || columnWidth(i) == initialColumnWidths[i])
|
2014-08-27 00:23:41 +00:00
|
|
|
settings.remove(QString("colwidth%1").arg(i));
|
|
|
|
else
|
|
|
|
settings.setValue(QString("colwidth%1").arg(i), columnWidth(i));
|
2013-11-08 22:30:17 +00:00
|
|
|
}
|
2014-08-27 00:23:41 +00:00
|
|
|
settings.remove(QString("colwidth%1").arg(DiveTripModel::COLUMNS - 1));
|
2013-11-08 22:30:17 +00:00
|
|
|
settings.endGroup();
|
|
|
|
}
|
|
|
|
|
2018-07-25 19:23:19 +00:00
|
|
|
void DiveListView::calculateInitialColumnWidth(int col)
|
2018-07-25 18:40:47 +00:00
|
|
|
{
|
|
|
|
const QFontMetrics metrics(defaultModelFont());
|
|
|
|
int em = metrics.width('m');
|
|
|
|
int zw = metrics.width('0');
|
|
|
|
|
2018-07-25 19:23:19 +00:00
|
|
|
QString header_txt = DiveTripModel::instance()->headerData(col, Qt::Horizontal, Qt::DisplayRole).toString();
|
2018-07-25 18:40:47 +00:00
|
|
|
int width = metrics.width(header_txt);
|
|
|
|
int sw = 0;
|
|
|
|
switch (col) {
|
|
|
|
case DiveTripModel::NR:
|
|
|
|
case DiveTripModel::DURATION:
|
|
|
|
sw = 8*zw;
|
|
|
|
break;
|
|
|
|
case DiveTripModel::DATE:
|
|
|
|
sw = 14*em;
|
|
|
|
break;
|
|
|
|
case DiveTripModel::RATING:
|
|
|
|
sw = static_cast<StarWidgetsDelegate*>(itemDelegateForColumn(col))->starSize().width();
|
|
|
|
break;
|
|
|
|
case DiveTripModel::SUIT:
|
|
|
|
case DiveTripModel::SAC:
|
|
|
|
sw = 7*em;
|
|
|
|
break;
|
|
|
|
case DiveTripModel::PHOTOS:
|
|
|
|
sw = 5*em;
|
|
|
|
break;
|
2018-09-04 15:10:11 +00:00
|
|
|
case DiveTripModel::BUDDIES:
|
|
|
|
sw = 50*em;
|
|
|
|
break;
|
2018-07-25 18:40:47 +00:00
|
|
|
case DiveTripModel::LOCATION:
|
|
|
|
sw = 50*em;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
sw = 5*em;
|
|
|
|
}
|
|
|
|
if (sw > width)
|
|
|
|
width = sw;
|
|
|
|
width += zw; // small padding
|
|
|
|
initialColumnWidths[col] = std::max(initialColumnWidths[col], width);
|
|
|
|
}
|
|
|
|
|
2018-07-26 16:03:07 +00:00
|
|
|
void DiveListView::setColumnWidths()
|
2014-01-16 04:50:56 +00:00
|
|
|
{
|
2013-11-08 22:30:17 +00:00
|
|
|
QSettings settings;
|
2018-07-26 16:03:07 +00:00
|
|
|
backupExpandedRows();
|
2013-11-08 22:30:17 +00:00
|
|
|
settings.beginGroup("ListWidget");
|
|
|
|
/* if no width are set, use the calculated width for each column;
|
|
|
|
* for that to work we need to temporarily expand all rows */
|
|
|
|
expandAll();
|
|
|
|
for (int i = DiveTripModel::NR; i < DiveTripModel::COLUMNS; i++) {
|
2014-01-16 04:50:56 +00:00
|
|
|
if (isColumnHidden(i))
|
2013-11-08 22:30:17 +00:00
|
|
|
continue;
|
|
|
|
QVariant width = settings.value(QString("colwidth%1").arg(i));
|
|
|
|
if (width.isValid())
|
|
|
|
setColumnWidth(i, width.toInt());
|
|
|
|
else
|
2018-07-25 18:40:47 +00:00
|
|
|
setColumnWidth(i, initialColumnWidths[i]);
|
2013-11-08 22:30:17 +00:00
|
|
|
}
|
|
|
|
settings.endGroup();
|
2018-07-26 16:03:07 +00:00
|
|
|
restoreExpandedRows();
|
2013-11-13 18:59:56 +00:00
|
|
|
setColumnWidth(lastVisibleColumn(), 10);
|
|
|
|
}
|
|
|
|
|
|
|
|
int DiveListView::lastVisibleColumn()
|
|
|
|
{
|
|
|
|
int lastColumn = -1;
|
|
|
|
for (int i = DiveTripModel::NR; i < DiveTripModel::COLUMNS; i++) {
|
2014-01-16 04:50:56 +00:00
|
|
|
if (isColumnHidden(i))
|
2013-11-13 18:59:56 +00:00
|
|
|
continue;
|
|
|
|
lastColumn = i;
|
|
|
|
}
|
|
|
|
return lastColumn;
|
2013-11-08 23:08:14 +00:00
|
|
|
}
|
2013-11-08 22:46:25 +00:00
|
|
|
|
2014-01-16 04:50:56 +00:00
|
|
|
void DiveListView::backupExpandedRows()
|
|
|
|
{
|
2013-11-08 23:08:14 +00:00
|
|
|
expandedRows.clear();
|
2014-02-28 04:09:57 +00:00
|
|
|
for (int i = 0; i < model()->rowCount(); i++)
|
|
|
|
if (isExpanded(model()->index(i, 0)))
|
2013-11-08 23:08:14 +00:00
|
|
|
expandedRows.push_back(i);
|
2013-05-30 04:04:42 +00:00
|
|
|
}
|
|
|
|
|
2014-01-16 04:50:56 +00:00
|
|
|
void DiveListView::restoreExpandedRows()
|
|
|
|
{
|
2013-11-16 20:41:47 +00:00
|
|
|
setAnimated(false);
|
2014-05-22 18:40:22 +00:00
|
|
|
Q_FOREACH (const int &i, expandedRows)
|
2014-03-03 21:25:55 +00:00
|
|
|
setExpanded(model()->index(i, 0), true);
|
2013-11-16 20:41:47 +00:00
|
|
|
setAnimated(true);
|
2013-11-08 23:08:14 +00:00
|
|
|
}
|
2018-07-29 15:40:53 +00:00
|
|
|
|
|
|
|
// If the model is reset, check which items are trip-items and expand the first column
|
|
|
|
void DiveListView::reset()
|
2013-06-12 17:14:25 +00:00
|
|
|
{
|
2018-07-29 15:40:53 +00:00
|
|
|
// First, let the QTreeView do its thing.
|
|
|
|
QTreeView::reset();
|
|
|
|
|
2013-06-12 17:14:25 +00:00
|
|
|
QAbstractItemModel *m = model();
|
2018-07-29 15:40:53 +00:00
|
|
|
for (int i = 0; i < m->rowCount(); ++i) {
|
2014-02-28 04:09:57 +00:00
|
|
|
if (m->rowCount(m->index(i, 0)) != 0)
|
2013-06-12 17:14:25 +00:00
|
|
|
setFirstColumnSpanned(i, QModelIndex(), true);
|
2018-07-29 15:40:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If rows are added, check which of these rows is a trip and expand the first column
|
|
|
|
void DiveListView::rowsInserted(const QModelIndex &parent, int start, int end)
|
|
|
|
{
|
|
|
|
// First, let the QTreeView do its thing.
|
|
|
|
QTreeView::rowsInserted(parent, start, end);
|
|
|
|
|
|
|
|
// Now check for each inserted row whether this is a trip and expand the first column
|
|
|
|
if (parent.isValid()) // Trips don't have a parent
|
|
|
|
return;
|
|
|
|
QAbstractItemModel *m = model();
|
|
|
|
for (int i = start; i <= end; ++i) {
|
|
|
|
if (m->rowCount(m->index(i, 0)) != 0)
|
|
|
|
setFirstColumnSpanned(i, QModelIndex(), true);
|
|
|
|
}
|
2013-06-12 17:14:25 +00:00
|
|
|
}
|
|
|
|
|
2013-11-14 08:52:03 +00:00
|
|
|
// this only remembers dives that were selected, not trips
|
|
|
|
void DiveListView::rememberSelection()
|
|
|
|
{
|
|
|
|
selectedDives.clear();
|
|
|
|
QItemSelection selection = selectionModel()->selection();
|
2014-05-22 18:40:22 +00:00
|
|
|
Q_FOREACH (const QModelIndex &index, selection.indexes()) {
|
2013-11-14 08:52:03 +00:00
|
|
|
if (index.column() != 0) // We only care about the dives, so, let's stick to rows and discard columns.
|
|
|
|
continue;
|
2014-02-28 04:09:57 +00:00
|
|
|
struct dive *d = (struct dive *)index.data(DiveTripModel::DIVE_ROLE).value<void *>();
|
2015-04-22 15:38:44 +00:00
|
|
|
if (d) {
|
2013-11-26 17:44:18 +00:00
|
|
|
selectedDives.insert(d->divetrip, get_divenr(d));
|
2015-04-22 15:38:44 +00:00
|
|
|
} else {
|
|
|
|
struct dive_trip *t = (struct dive_trip *)index.data(DiveTripModel::TRIP_ROLE).value<void *>();
|
|
|
|
if (t)
|
|
|
|
selectedDives.insert(t, -1);
|
|
|
|
}
|
2013-11-14 08:52:03 +00:00
|
|
|
}
|
2014-05-28 22:03:10 +00:00
|
|
|
selectionSaved = true;
|
2013-11-14 08:52:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DiveListView::restoreSelection()
|
|
|
|
{
|
2014-05-28 22:03:10 +00:00
|
|
|
if (!selectionSaved)
|
|
|
|
return;
|
|
|
|
|
|
|
|
selectionSaved = false;
|
2014-07-21 23:44:46 +00:00
|
|
|
dontEmitDiveChangedSignal = true;
|
2013-11-14 08:52:03 +00:00
|
|
|
unselectDives();
|
2014-07-21 23:44:46 +00:00
|
|
|
dontEmitDiveChangedSignal = false;
|
2014-05-22 18:40:22 +00:00
|
|
|
Q_FOREACH (dive_trip_t *trip, selectedDives.keys()) {
|
2013-11-26 17:44:18 +00:00
|
|
|
QList<int> divesOnTrip = getDivesInTrip(trip);
|
|
|
|
QList<int> selectedDivesOnTrip = selectedDives.values(trip);
|
|
|
|
|
2014-05-28 20:10:33 +00:00
|
|
|
// Only select trip if all of its dives were selected
|
2015-04-22 15:38:44 +00:00
|
|
|
if(selectedDivesOnTrip.contains(-1)) {
|
2013-11-26 17:44:18 +00:00
|
|
|
selectTrip(trip);
|
2015-04-22 15:38:44 +00:00
|
|
|
selectedDivesOnTrip.removeAll(-1);
|
|
|
|
}
|
2014-05-28 20:10:33 +00:00
|
|
|
selectDives(selectedDivesOnTrip);
|
2013-11-14 08:52:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-28 04:09:57 +00:00
|
|
|
void DiveListView::selectTrip(dive_trip_t *trip)
|
2013-11-26 17:44:18 +00:00
|
|
|
{
|
|
|
|
if (!trip)
|
|
|
|
return;
|
|
|
|
|
2014-02-28 04:09:57 +00:00
|
|
|
QSortFilterProxyModel *m = qobject_cast<QSortFilterProxyModel *>(model());
|
|
|
|
QModelIndexList match = m->match(m->index(0, 0), DiveTripModel::TRIP_ROLE, QVariant::fromValue<void *>(trip), 2, Qt::MatchRecursive);
|
2013-11-26 17:44:18 +00:00
|
|
|
QItemSelectionModel::SelectionFlags flags;
|
|
|
|
if (!match.count())
|
|
|
|
return;
|
|
|
|
QModelIndex idx = match.first();
|
2014-02-28 04:09:57 +00:00
|
|
|
flags = QItemSelectionModel::Select;
|
2013-11-26 17:44:18 +00:00
|
|
|
flags |= QItemSelectionModel::Rows;
|
|
|
|
selectionModel()->select(idx, flags);
|
|
|
|
expand(idx);
|
|
|
|
}
|
|
|
|
|
2014-11-04 01:52:04 +00:00
|
|
|
// this is an odd one - when filtering the dive list the selection status of the trips
|
|
|
|
// is kept - but all other selections are lost. That's gets us into rather inconsistent state
|
|
|
|
// we call this function which clears the selection state of the trips as well, but does so
|
|
|
|
// without updating our internal "->selected" state. So once we called this function we can
|
|
|
|
// go back and select those dives that are still visible under the filter and everything
|
|
|
|
// works as expected
|
|
|
|
void DiveListView::clearTripSelection()
|
|
|
|
{
|
|
|
|
// we want to make sure no trips are selected
|
|
|
|
disconnect(selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(selectionChanged(QItemSelection, QItemSelection)));
|
|
|
|
disconnect(selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), this, SLOT(currentChanged(QModelIndex, QModelIndex)));
|
|
|
|
|
|
|
|
Q_FOREACH (const QModelIndex &index, selectionModel()->selectedRows()) {
|
|
|
|
dive_trip_t *trip = static_cast<dive_trip_t *>(index.data(DiveTripModel::TRIP_ROLE).value<void *>());
|
|
|
|
if (!trip)
|
|
|
|
continue;
|
|
|
|
selectionModel()->select(index, QItemSelectionModel::Deselect);
|
|
|
|
}
|
|
|
|
|
|
|
|
connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(selectionChanged(QItemSelection, QItemSelection)));
|
|
|
|
connect(selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), this, SLOT(currentChanged(QModelIndex, QModelIndex)));
|
|
|
|
}
|
|
|
|
|
2013-06-05 06:41:52 +00:00
|
|
|
void DiveListView::unselectDives()
|
|
|
|
{
|
2014-08-22 00:58:15 +00:00
|
|
|
// make sure we don't try to redraw the dives during the selection change
|
|
|
|
selected_dive = -1;
|
|
|
|
amount_selected = 0;
|
|
|
|
// clear the Qt selection
|
2013-06-05 06:41:52 +00:00
|
|
|
selectionModel()->clearSelection();
|
2014-05-24 20:55:18 +00:00
|
|
|
// clearSelection should emit selectionChanged() but sometimes that
|
|
|
|
// appears not to happen
|
2014-08-22 00:58:15 +00:00
|
|
|
// since we are unselecting all dives there is no need to use deselect_dive() - that
|
|
|
|
// would only cause pointless churn
|
2014-05-24 20:55:18 +00:00
|
|
|
int i;
|
|
|
|
struct dive *dive;
|
2014-09-17 22:39:49 +00:00
|
|
|
for_each_dive (i, dive) {
|
2014-08-22 00:58:15 +00:00
|
|
|
dive->selected = false;
|
2014-05-24 20:55:18 +00:00
|
|
|
}
|
2013-06-05 06:41:52 +00:00
|
|
|
}
|
|
|
|
|
2014-02-28 04:09:57 +00:00
|
|
|
QList<dive_trip_t *> DiveListView::selectedTrips()
|
2013-11-26 17:44:18 +00:00
|
|
|
{
|
2014-02-28 04:09:57 +00:00
|
|
|
QList<dive_trip_t *> ret;
|
2014-07-15 17:43:20 +00:00
|
|
|
Q_FOREACH (const QModelIndex &index, selectionModel()->selectedRows()) {
|
2014-02-28 04:09:57 +00:00
|
|
|
dive_trip_t *trip = static_cast<dive_trip_t *>(index.data(DiveTripModel::TRIP_ROLE).value<void *>());
|
2014-01-16 04:50:56 +00:00
|
|
|
if (!trip)
|
2013-11-26 17:44:18 +00:00
|
|
|
continue;
|
|
|
|
ret.push_back(trip);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-11-16 20:41:47 +00:00
|
|
|
void DiveListView::selectDive(int i, bool scrollto, bool toggle)
|
2013-06-05 06:41:52 +00:00
|
|
|
{
|
2014-02-28 04:09:57 +00:00
|
|
|
if (i == -1)
|
2013-11-17 00:56:48 +00:00
|
|
|
return;
|
2014-02-28 04:09:57 +00:00
|
|
|
QSortFilterProxyModel *m = qobject_cast<QSortFilterProxyModel *>(model());
|
|
|
|
QModelIndexList match = m->match(m->index(0, 0), DiveTripModel::DIVE_IDX, i, 2, Qt::MatchRecursive);
|
2013-06-08 05:57:45 +00:00
|
|
|
QItemSelectionModel::SelectionFlags flags;
|
2014-05-18 10:08:58 +00:00
|
|
|
if (match.isEmpty())
|
|
|
|
return;
|
2013-06-05 06:41:52 +00:00
|
|
|
QModelIndex idx = match.first();
|
2013-06-08 01:25:30 +00:00
|
|
|
flags = toggle ? QItemSelectionModel::Toggle : QItemSelectionModel::Select;
|
|
|
|
flags |= QItemSelectionModel::Rows;
|
2014-01-06 22:13:21 +00:00
|
|
|
selectionModel()->setCurrentIndex(idx, flags);
|
2014-01-16 04:50:56 +00:00
|
|
|
if (idx.parent().isValid()) {
|
2013-11-16 20:54:14 +00:00
|
|
|
setAnimated(false);
|
|
|
|
expand(idx.parent());
|
When scrolling to dive, scroll to trip first
The behavior at startup is actually very annoying: we select the latest
dive, and expand the trip it is in, but since we use "scrollTo()" on
just the dive, and it's not initially visible, the startup will make the
first dive be at the top of the list view.
Which means that the actual _trip_ detail is not visible at all, since
it will have been scrolled off the list view entirely.
Fix this by first scrolling to the trip, and only then scrolling to the
actual dive (using the default "EnsureVisible" policy). Obviously, if
it's a trip with lots of dives, scrolling to the dive may end up
scrolling away from the trip header again, but at least that never
happens at startup, and at that point you have to scroll away from the
trip just to show the dive.
Do this same dance when changing the dive selection (mainly noticeable
when picking dives on the globe view).
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2014-04-28 17:39:06 +00:00
|
|
|
if (scrollto)
|
|
|
|
scrollTo(idx.parent());
|
2013-11-16 20:54:14 +00:00
|
|
|
setAnimated(true);
|
|
|
|
}
|
2013-06-05 06:41:52 +00:00
|
|
|
if (scrollto)
|
|
|
|
scrollTo(idx, PositionAtCenter);
|
|
|
|
}
|
2013-12-02 19:15:40 +00:00
|
|
|
|
2014-02-28 04:09:57 +00:00
|
|
|
void DiveListView::selectDives(const QList<int> &newDiveSelection)
|
2013-12-02 19:15:40 +00:00
|
|
|
{
|
2014-11-10 05:55:17 +00:00
|
|
|
int firstInList, newSelection;
|
2014-11-12 06:59:04 +00:00
|
|
|
struct dive *d;
|
|
|
|
|
2014-01-16 04:50:56 +00:00
|
|
|
if (!newDiveSelection.count())
|
2013-12-02 19:15:40 +00:00
|
|
|
return;
|
|
|
|
|
2014-05-25 04:03:18 +00:00
|
|
|
dontEmitDiveChangedSignal = true;
|
2014-05-25 03:55:28 +00:00
|
|
|
// select the dives, highest index first - this way the oldest of the dives
|
|
|
|
// becomes the selected_dive that we scroll to
|
|
|
|
QList<int> sortedSelection = newDiveSelection;
|
|
|
|
qSort(sortedSelection.begin(), sortedSelection.end());
|
2014-11-10 05:55:17 +00:00
|
|
|
newSelection = firstInList = sortedSelection.first();
|
|
|
|
|
2014-05-25 03:55:28 +00:00
|
|
|
while (!sortedSelection.isEmpty())
|
|
|
|
selectDive(sortedSelection.takeLast());
|
2013-12-02 19:15:40 +00:00
|
|
|
|
2014-11-10 05:55:17 +00:00
|
|
|
while (selected_dive == -1) {
|
|
|
|
// that can happen if we restored a selection after edit
|
|
|
|
// and the only selected dive is no longer visible because of a filter
|
|
|
|
newSelection--;
|
|
|
|
if (newSelection < 0)
|
|
|
|
newSelection = dive_table.nr - 1;
|
|
|
|
if (newSelection == firstInList)
|
|
|
|
break;
|
2014-11-12 06:59:04 +00:00
|
|
|
if ((d = get_dive(newSelection)) != NULL && !d->hidden_by_filter)
|
|
|
|
selectDive(newSelection);
|
2014-11-10 05:55:17 +00:00
|
|
|
}
|
2014-02-28 04:09:57 +00:00
|
|
|
QSortFilterProxyModel *m = qobject_cast<QSortFilterProxyModel *>(model());
|
2014-11-10 05:32:38 +00:00
|
|
|
QModelIndexList idxList = m->match(m->index(0, 0), DiveTripModel::DIVE_IDX, selected_dive, 2, Qt::MatchRecursive);
|
|
|
|
if (!idxList.isEmpty()) {
|
|
|
|
QModelIndex idx = idxList.first();
|
|
|
|
if (idx.parent().isValid())
|
|
|
|
scrollTo(idx.parent());
|
|
|
|
scrollTo(idx);
|
|
|
|
}
|
2014-05-25 04:03:18 +00:00
|
|
|
// now that everything is up to date, update the widgets
|
2018-07-30 18:41:33 +00:00
|
|
|
emit currentDiveChanged();
|
2014-05-25 04:03:18 +00:00
|
|
|
dontEmitDiveChangedSignal = false;
|
2014-05-25 03:55:28 +00:00
|
|
|
return;
|
2013-12-02 19:15:40 +00:00
|
|
|
}
|
|
|
|
|
2014-02-28 04:09:57 +00:00
|
|
|
bool DiveListView::eventFilter(QObject *, QEvent *event)
|
2013-05-30 04:04:42 +00:00
|
|
|
{
|
2014-01-16 04:50:56 +00:00
|
|
|
if (event->type() != QEvent::KeyPress)
|
2013-05-30 04:04:42 +00:00
|
|
|
return false;
|
2014-02-28 04:09:57 +00:00
|
|
|
QKeyEvent *keyEv = static_cast<QKeyEvent *>(event);
|
2015-02-06 11:19:04 +00:00
|
|
|
if (keyEv->key() == Qt::Key_Delete) {
|
|
|
|
contextMenuIndex = currentIndex();
|
|
|
|
deleteDive();
|
|
|
|
}
|
2013-10-11 13:21:04 +00:00
|
|
|
if (keyEv->key() != Qt::Key_Escape)
|
2013-05-30 04:04:42 +00:00
|
|
|
return false;
|
|
|
|
return true;
|
2013-05-16 19:00:33 +00:00
|
|
|
}
|
|
|
|
|
2013-05-31 04:53:04 +00:00
|
|
|
// NOTE! This loses trip selection, because while we remember the
|
|
|
|
// dives, we don't remember the trips (see the "currentSelectedDives"
|
|
|
|
// list). I haven't figured out how to look up the trip from the
|
|
|
|
// index. TRIP_ROLE vs DIVE_ROLE?
|
2013-05-29 20:43:14 +00:00
|
|
|
void DiveListView::headerClicked(int i)
|
2013-05-28 20:07:43 +00:00
|
|
|
{
|
2014-02-28 04:09:57 +00:00
|
|
|
DiveTripModel::Layout newLayout = i == (int)DiveTripModel::NR ? DiveTripModel::TREE : DiveTripModel::LIST;
|
2013-11-16 20:41:47 +00:00
|
|
|
rememberSelection();
|
2013-06-05 08:28:22 +00:00
|
|
|
unselectDives();
|
2013-05-31 04:53:04 +00:00
|
|
|
/* No layout change? Just re-sort, and scroll to first selection, making sure all selections are expanded */
|
|
|
|
if (currentLayout == newLayout) {
|
2013-11-05 05:09:08 +00:00
|
|
|
currentOrder = (currentOrder == Qt::DescendingOrder) ? Qt::AscendingOrder : Qt::DescendingOrder;
|
|
|
|
sortByColumn(i, currentOrder);
|
2013-05-31 04:53:04 +00:00
|
|
|
} else {
|
|
|
|
// clear the model, repopulate with new indexes.
|
2014-01-16 04:50:56 +00:00
|
|
|
if (currentLayout == DiveTripModel::TREE) {
|
2013-11-08 23:08:14 +00:00
|
|
|
backupExpandedRows();
|
|
|
|
}
|
2013-05-31 04:53:04 +00:00
|
|
|
reload(newLayout, false);
|
2013-11-05 05:09:08 +00:00
|
|
|
currentOrder = Qt::DescendingOrder;
|
|
|
|
sortByColumn(i, currentOrder);
|
2014-01-16 04:50:56 +00:00
|
|
|
if (newLayout == DiveTripModel::TREE) {
|
2013-11-08 23:08:14 +00:00
|
|
|
restoreExpandedRows();
|
|
|
|
}
|
2013-05-31 04:53:04 +00:00
|
|
|
}
|
2013-11-16 20:41:47 +00:00
|
|
|
restoreSelection();
|
2014-05-25 13:19:05 +00:00
|
|
|
// remember the new sort column
|
|
|
|
sortColumn = i;
|
2013-05-28 20:07:43 +00:00
|
|
|
}
|
|
|
|
|
2013-05-29 17:03:36 +00:00
|
|
|
void DiveListView::reload(DiveTripModel::Layout layout, bool forceSort)
|
2013-05-16 19:00:33 +00:00
|
|
|
{
|
2013-06-06 02:22:08 +00:00
|
|
|
if (layout == DiveTripModel::CURRENT)
|
|
|
|
layout = currentLayout;
|
|
|
|
else
|
|
|
|
currentLayout = layout;
|
2015-05-24 15:47:07 +00:00
|
|
|
|
2014-01-15 08:30:39 +00:00
|
|
|
header()->setSectionsClickable(true);
|
2013-05-28 20:46:40 +00:00
|
|
|
connect(header(), SIGNAL(sectionPressed(int)), this, SLOT(headerClicked(int)), Qt::UniqueConnection);
|
|
|
|
|
2018-07-25 19:23:19 +00:00
|
|
|
DiveTripModel *tripModel = DiveTripModel::instance();
|
|
|
|
tripModel->setLayout(layout); // Note: setLayout() resets the whole model
|
2013-05-29 17:03:36 +00:00
|
|
|
|
2014-01-16 04:50:56 +00:00
|
|
|
if (!forceSort)
|
2013-05-29 17:03:36 +00:00
|
|
|
return;
|
|
|
|
|
2018-07-25 19:23:19 +00:00
|
|
|
QSortFilterProxyModel *m = qobject_cast<QSortFilterProxyModel *>(model());
|
2013-11-05 05:09:08 +00:00
|
|
|
sortByColumn(sortColumn, currentOrder);
|
2013-11-13 04:59:02 +00:00
|
|
|
if (amount_selected && current_dive != NULL) {
|
2013-11-16 20:41:47 +00:00
|
|
|
selectDive(selected_dive, true);
|
2013-06-04 12:56:20 +00:00
|
|
|
} else {
|
2014-02-28 04:09:57 +00:00
|
|
|
QModelIndex firstDiveOrTrip = m->index(0, 0);
|
2013-06-04 12:56:20 +00:00
|
|
|
if (firstDiveOrTrip.isValid()) {
|
2014-02-28 04:09:57 +00:00
|
|
|
if (m->index(0, 0, firstDiveOrTrip).isValid())
|
|
|
|
setCurrentIndex(m->index(0, 0, firstDiveOrTrip));
|
2013-06-04 12:56:20 +00:00
|
|
|
else
|
|
|
|
setCurrentIndex(firstDiveOrTrip);
|
|
|
|
}
|
2013-05-16 19:00:33 +00:00
|
|
|
}
|
2014-01-16 04:50:56 +00:00
|
|
|
if (selectedIndexes().count()) {
|
2013-11-15 14:01:56 +00:00
|
|
|
QModelIndex curr = selectedIndexes().first();
|
|
|
|
curr = curr.parent().isValid() ? curr.parent() : curr;
|
2014-01-16 04:50:56 +00:00
|
|
|
if (!isExpanded(curr)) {
|
2013-11-15 14:01:56 +00:00
|
|
|
setAnimated(false);
|
|
|
|
expand(curr);
|
When scrolling to dive, scroll to trip first
The behavior at startup is actually very annoying: we select the latest
dive, and expand the trip it is in, but since we use "scrollTo()" on
just the dive, and it's not initially visible, the startup will make the
first dive be at the top of the list view.
Which means that the actual _trip_ detail is not visible at all, since
it will have been scrolled off the list view entirely.
Fix this by first scrolling to the trip, and only then scrolling to the
actual dive (using the default "EnsureVisible" policy). Obviously, if
it's a trip with lots of dives, scrolling to the dive may end up
scrolling away from the trip header again, but at least that never
happens at startup, and at that point you have to scroll away from the
trip just to show the dive.
Do this same dance when changing the dive selection (mainly noticeable
when picking dives on the globe view).
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2014-04-28 17:39:06 +00:00
|
|
|
scrollTo(curr);
|
2013-11-15 14:01:56 +00:00
|
|
|
setAnimated(true);
|
|
|
|
}
|
|
|
|
}
|
2013-05-26 17:12:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DiveListView::reloadHeaderActions()
|
|
|
|
{
|
2013-05-21 19:51:49 +00:00
|
|
|
// Populate the context menu of the headers that will show
|
|
|
|
// the menu to show / hide columns.
|
2013-05-23 04:25:05 +00:00
|
|
|
if (!header()->actions().size()) {
|
2013-05-21 19:51:49 +00:00
|
|
|
QSettings s;
|
|
|
|
s.beginGroup("DiveListColumnState");
|
2014-02-28 04:09:57 +00:00
|
|
|
for (int i = 0; i < model()->columnCount(); i++) {
|
2013-05-29 21:18:10 +00:00
|
|
|
QString title = QString("%1").arg(model()->headerData(i, Qt::Horizontal).toString());
|
2013-05-26 17:12:45 +00:00
|
|
|
QString settingName = QString("showColumn%1").arg(i);
|
2013-05-21 19:51:49 +00:00
|
|
|
QAction *a = new QAction(title, header());
|
2014-08-27 00:25:15 +00:00
|
|
|
bool showHeaderFirstRun = !(i == DiveTripModel::MAXCNS ||
|
|
|
|
i == DiveTripModel::GAS ||
|
|
|
|
i == DiveTripModel::OTU ||
|
|
|
|
i == DiveTripModel::TEMPERATURE ||
|
|
|
|
i == DiveTripModel::TOTALWEIGHT ||
|
|
|
|
i == DiveTripModel::SUIT ||
|
|
|
|
i == DiveTripModel::CYLINDER ||
|
2018-04-04 06:18:29 +00:00
|
|
|
i == DiveTripModel::SAC ||
|
|
|
|
i == DiveTripModel::TAGS);
|
2013-10-04 17:28:49 +00:00
|
|
|
bool shown = s.value(settingName, showHeaderFirstRun).toBool();
|
2013-05-21 19:51:49 +00:00
|
|
|
a->setCheckable(true);
|
2013-05-26 17:12:45 +00:00
|
|
|
a->setChecked(shown);
|
2013-05-21 19:51:49 +00:00
|
|
|
a->setProperty("index", i);
|
2013-05-26 17:12:45 +00:00
|
|
|
a->setProperty("settingName", settingName);
|
|
|
|
connect(a, SIGNAL(triggered(bool)), this, SLOT(toggleColumnVisibilityByIndex()));
|
2013-05-21 19:51:49 +00:00
|
|
|
header()->addAction(a);
|
2013-05-26 17:12:45 +00:00
|
|
|
setColumnHidden(i, !shown);
|
2013-05-21 19:51:49 +00:00
|
|
|
}
|
|
|
|
s.endGroup();
|
2013-06-09 23:09:39 +00:00
|
|
|
} else {
|
2014-02-28 04:09:57 +00:00
|
|
|
for (int i = 0; i < model()->columnCount(); i++) {
|
2013-06-09 23:09:39 +00:00
|
|
|
QString title = QString("%1").arg(model()->headerData(i, Qt::Horizontal).toString());
|
2013-12-04 16:20:33 +00:00
|
|
|
header()->actions()[i]->setText(title);
|
2013-06-09 23:09:39 +00:00
|
|
|
}
|
2013-05-21 19:51:49 +00:00
|
|
|
}
|
2013-05-14 01:14:59 +00:00
|
|
|
}
|
|
|
|
|
2013-05-26 17:12:45 +00:00
|
|
|
void DiveListView::toggleColumnVisibilityByIndex()
|
2013-05-14 01:14:59 +00:00
|
|
|
{
|
2014-02-28 04:09:57 +00:00
|
|
|
QAction *action = qobject_cast<QAction *>(sender());
|
2013-05-21 19:51:49 +00:00
|
|
|
if (!action)
|
|
|
|
return;
|
2018-08-26 20:46:46 +00:00
|
|
|
const int idx = action->property("index").toInt();
|
|
|
|
|
|
|
|
// Count the number of visible columns.
|
|
|
|
int totalVisible = 0, lastVisibleIdx = -1;
|
|
|
|
for (int i = 0; i < model()->columnCount(); i++) {
|
|
|
|
if (isColumnHidden(i))
|
|
|
|
continue;
|
|
|
|
totalVisible++;
|
|
|
|
lastVisibleIdx = i;
|
|
|
|
}
|
|
|
|
// If there is only one visible column and we are performing an action on it,
|
|
|
|
// don't hide the column and revert the action back to checked == true.
|
|
|
|
// This keeps one column visible at all times.
|
|
|
|
if (totalVisible == 1 && idx == lastVisibleIdx) {
|
|
|
|
action->setChecked(true);
|
|
|
|
return;
|
|
|
|
}
|
2013-05-21 19:51:49 +00:00
|
|
|
|
|
|
|
QSettings s;
|
|
|
|
s.beginGroup("DiveListColumnState");
|
2013-05-26 17:12:45 +00:00
|
|
|
s.setValue(action->property("settingName").toString(), action->isChecked());
|
2013-05-21 19:51:49 +00:00
|
|
|
s.endGroup();
|
2013-05-22 02:07:19 +00:00
|
|
|
s.sync();
|
2018-08-26 20:46:46 +00:00
|
|
|
setColumnHidden(idx, !action->isChecked());
|
2013-11-13 18:59:56 +00:00
|
|
|
setColumnWidth(lastVisibleColumn(), 10);
|
2013-05-14 01:14:59 +00:00
|
|
|
}
|
|
|
|
|
2018-05-21 16:09:09 +00:00
|
|
|
void DiveListView::currentChanged(const QModelIndex ¤t, const QModelIndex&)
|
2013-05-14 11:18:26 +00:00
|
|
|
{
|
2015-05-10 23:01:58 +00:00
|
|
|
if (!isVisible())
|
|
|
|
return;
|
2013-05-14 11:18:26 +00:00
|
|
|
if (!current.isValid())
|
|
|
|
return;
|
2013-06-05 07:46:23 +00:00
|
|
|
scrollTo(current);
|
2013-05-14 11:18:26 +00:00
|
|
|
}
|
|
|
|
|
2014-02-28 04:09:57 +00:00
|
|
|
void DiveListView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
|
2013-05-14 01:14:59 +00:00
|
|
|
{
|
2013-05-29 17:03:36 +00:00
|
|
|
QItemSelection newSelected = selected.size() ? selected : selectionModel()->selection();
|
|
|
|
QItemSelection newDeselected = deselected;
|
|
|
|
|
2014-02-28 04:09:57 +00:00
|
|
|
disconnect(selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(selectionChanged(QItemSelection, QItemSelection)));
|
|
|
|
disconnect(selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), this, SLOT(currentChanged(QModelIndex, QModelIndex)));
|
2013-05-29 17:03:36 +00:00
|
|
|
|
2014-05-22 18:40:22 +00:00
|
|
|
Q_FOREACH (const QModelIndex &index, newDeselected.indexes()) {
|
2013-05-31 12:05:33 +00:00
|
|
|
if (index.column() != 0)
|
2013-05-29 17:03:36 +00:00
|
|
|
continue;
|
2013-05-14 01:14:59 +00:00
|
|
|
const QAbstractItemModel *model = index.model();
|
2014-02-28 04:09:57 +00:00
|
|
|
struct dive *dive = (struct dive *)model->data(index, DiveTripModel::DIVE_ROLE).value<void *>();
|
2014-05-24 20:19:05 +00:00
|
|
|
if (!dive) // it's a trip!
|
|
|
|
deselect_dives_in_trip((dive_trip_t *)model->data(index, DiveTripModel::TRIP_ROLE).value<void *>());
|
|
|
|
else
|
2013-11-16 21:21:54 +00:00
|
|
|
deselect_dive(get_divenr(dive));
|
2013-05-31 12:05:33 +00:00
|
|
|
}
|
2014-05-22 18:40:22 +00:00
|
|
|
Q_FOREACH (const QModelIndex &index, newSelected.indexes()) {
|
2013-05-31 12:05:33 +00:00
|
|
|
if (index.column() != 0)
|
|
|
|
continue;
|
2013-06-14 13:17:38 +00:00
|
|
|
|
2013-05-31 12:05:33 +00:00
|
|
|
const QAbstractItemModel *model = index.model();
|
2014-02-28 04:09:57 +00:00
|
|
|
struct dive *dive = (struct dive *)model->data(index, DiveTripModel::DIVE_ROLE).value<void *>();
|
2013-05-31 12:05:33 +00:00
|
|
|
if (!dive) { // it's a trip!
|
|
|
|
if (model->rowCount(index)) {
|
2013-06-14 13:17:38 +00:00
|
|
|
QItemSelection selection;
|
2014-05-24 20:19:05 +00:00
|
|
|
select_dives_in_trip((dive_trip_t *)model->data(index, DiveTripModel::TRIP_ROLE).value<void *>());
|
2014-02-28 04:09:57 +00:00
|
|
|
selection.select(index.child(0, 0), index.child(model->rowCount(index) - 1, 0));
|
2013-06-14 13:17:38 +00:00
|
|
|
selectionModel()->select(selection, QItemSelectionModel::Select | QItemSelectionModel::Rows);
|
|
|
|
selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select | QItemSelectionModel::NoUpdate);
|
|
|
|
if (!isExpanded(index))
|
|
|
|
expand(index);
|
2013-05-31 12:05:33 +00:00
|
|
|
}
|
|
|
|
} else {
|
2013-11-16 21:21:54 +00:00
|
|
|
select_dive(get_divenr(dive));
|
2013-05-14 01:14:59 +00:00
|
|
|
}
|
|
|
|
}
|
2013-05-29 17:03:36 +00:00
|
|
|
QTreeView::selectionChanged(selectionModel()->selection(), newDeselected);
|
2014-02-28 04:09:57 +00:00
|
|
|
connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(selectionChanged(QItemSelection, QItemSelection)));
|
|
|
|
connect(selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), this, SLOT(currentChanged(QModelIndex, QModelIndex)));
|
2014-09-17 22:39:49 +00:00
|
|
|
if (!dontEmitDiveChangedSignal)
|
2018-07-30 18:41:33 +00:00
|
|
|
emit currentDiveChanged();
|
2013-04-12 07:24:07 +00:00
|
|
|
}
|
2013-06-05 18:29:11 +00:00
|
|
|
|
2015-05-18 08:52:03 +00:00
|
|
|
enum asked_user {NOTYET, MERGE, DONTMERGE};
|
|
|
|
|
|
|
|
static bool can_merge(const struct dive *a, const struct dive *b, enum asked_user *have_asked)
|
2013-11-16 22:46:08 +00:00
|
|
|
{
|
|
|
|
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 */
|
2015-09-22 19:32:27 +00:00
|
|
|
if (dive_endtime(a) + 30 * 60 < b->when) {
|
2015-05-18 08:52:03 +00:00
|
|
|
if (*have_asked == NOTYET) {
|
|
|
|
if (QMessageBox::warning(MainWindow::instance(),
|
2017-04-29 20:54:13 +00:00
|
|
|
MainWindow::tr("Warning"),
|
|
|
|
MainWindow::tr("Trying to merge dives with %1min interval in between").arg(
|
2015-09-22 19:32:27 +00:00
|
|
|
(b->when - dive_endtime(a)) / 60),
|
2015-05-18 08:52:03 +00:00
|
|
|
QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) {
|
|
|
|
*have_asked = DONTMERGE;
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
*have_asked = MERGE;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return *have_asked == MERGE ? true : false;
|
|
|
|
}
|
|
|
|
}
|
2013-11-16 22:46:08 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-09-24 04:57:28 +00:00
|
|
|
void DiveListView::mergeDives()
|
|
|
|
{
|
|
|
|
int i;
|
2013-10-15 11:37:31 +00:00
|
|
|
struct dive *dive, *maindive = NULL;
|
2015-05-18 08:52:03 +00:00
|
|
|
enum asked_user have_asked = NOTYET;
|
2013-09-24 04:57:28 +00:00
|
|
|
|
2014-05-22 18:40:22 +00:00
|
|
|
for_each_dive (i, dive) {
|
2013-10-15 11:37:31 +00:00
|
|
|
if (dive->selected) {
|
2015-05-18 08:52:03 +00:00
|
|
|
if (!can_merge(maindive, dive, &have_asked)) {
|
2013-09-24 04:57:28 +00:00
|
|
|
maindive = dive;
|
2013-11-05 06:38:41 +00:00
|
|
|
} else {
|
2013-09-24 04:57:28 +00:00
|
|
|
maindive = merge_two_dives(maindive, dive);
|
2013-11-05 06:38:41 +00:00
|
|
|
i--; // otherwise we skip a dive in the freshly changed list
|
|
|
|
}
|
2013-10-15 11:37:31 +00:00
|
|
|
}
|
|
|
|
}
|
2015-09-23 02:59:58 +00:00
|
|
|
MainWindow::instance()->refreshProfile();
|
2014-02-12 14:22:54 +00:00
|
|
|
MainWindow::instance()->refreshDisplay();
|
2013-09-24 04:57:28 +00:00
|
|
|
}
|
|
|
|
|
2015-10-02 01:17:37 +00:00
|
|
|
void DiveListView::splitDives()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct dive *dive;
|
|
|
|
|
|
|
|
for_each_dive (i, dive) {
|
|
|
|
if (dive->selected)
|
|
|
|
split_dive(dive);
|
|
|
|
}
|
|
|
|
MainWindow::instance()->refreshProfile();
|
|
|
|
MainWindow::instance()->refreshDisplay();
|
|
|
|
}
|
|
|
|
|
2014-05-19 21:11:32 +00:00
|
|
|
void DiveListView::renumberDives()
|
|
|
|
{
|
|
|
|
RenumberDialog::instance()->renumberOnlySelected();
|
|
|
|
RenumberDialog::instance()->show();
|
|
|
|
}
|
|
|
|
|
2013-06-27 05:16:40 +00:00
|
|
|
void DiveListView::merge_trip(const QModelIndex &a, int offset)
|
|
|
|
{
|
|
|
|
int i = a.row() + offset;
|
2014-02-28 04:09:57 +00:00
|
|
|
QModelIndex b = a.sibling(i, 0);
|
2013-06-27 05:16:40 +00:00
|
|
|
|
2014-02-28 04:09:57 +00:00
|
|
|
dive_trip_t *trip_a = (dive_trip_t *)a.data(DiveTripModel::TRIP_ROLE).value<void *>();
|
|
|
|
dive_trip_t *trip_b = (dive_trip_t *)b.data(DiveTripModel::TRIP_ROLE).value<void *>();
|
2013-06-27 05:16:40 +00:00
|
|
|
if (trip_a == trip_b || !trip_a || !trip_b)
|
|
|
|
return;
|
2014-05-24 21:02:08 +00:00
|
|
|
combine_trips(trip_a, trip_b);
|
2013-11-17 22:37:10 +00:00
|
|
|
rememberSelection();
|
2013-06-27 05:16:40 +00:00
|
|
|
reload(currentLayout, false);
|
2013-11-17 22:37:10 +00:00
|
|
|
restoreSelection();
|
2014-01-15 08:30:42 +00:00
|
|
|
mark_divelist_changed(true);
|
2014-04-17 02:56:42 +00:00
|
|
|
//TODO: emit a signal to signalize that the divelist changed?
|
2013-06-27 05:16:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DiveListView::mergeTripAbove()
|
|
|
|
{
|
|
|
|
merge_trip(contextMenuIndex, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DiveListView::mergeTripBelow()
|
|
|
|
{
|
|
|
|
merge_trip(contextMenuIndex, +1);
|
|
|
|
}
|
|
|
|
|
2013-06-06 00:06:42 +00:00
|
|
|
void DiveListView::removeFromTrip()
|
2013-06-05 18:29:11 +00:00
|
|
|
{
|
2014-04-17 02:56:42 +00:00
|
|
|
//TODO: move this to C-code.
|
2013-11-15 05:11:55 +00:00
|
|
|
int i;
|
2013-12-08 05:05:54 +00:00
|
|
|
struct dive *d;
|
2015-04-10 06:39:51 +00:00
|
|
|
QMap<struct dive*, dive_trip*> divesToRemove;
|
2014-05-22 18:40:22 +00:00
|
|
|
for_each_dive (i, d) {
|
2018-08-29 18:30:01 +00:00
|
|
|
if (d->selected && d->divetrip)
|
2015-04-10 06:39:51 +00:00
|
|
|
divesToRemove.insert(d, d->divetrip);
|
2013-11-15 05:11:55 +00:00
|
|
|
}
|
2018-08-29 18:30:01 +00:00
|
|
|
if (divesToRemove.isEmpty())
|
|
|
|
return;
|
|
|
|
|
2015-04-10 06:39:51 +00:00
|
|
|
UndoRemoveDivesFromTrip *undoCommand = new UndoRemoveDivesFromTrip(divesToRemove);
|
|
|
|
MainWindow::instance()->undoStack->push(undoCommand);
|
|
|
|
|
2013-11-17 23:36:57 +00:00
|
|
|
rememberSelection();
|
2013-06-06 01:37:18 +00:00
|
|
|
reload(currentLayout, false);
|
2013-11-17 23:36:57 +00:00
|
|
|
restoreSelection();
|
2014-01-15 08:30:42 +00:00
|
|
|
mark_divelist_changed(true);
|
2013-06-06 01:37:18 +00:00
|
|
|
}
|
|
|
|
|
2013-11-15 02:32:19 +00:00
|
|
|
void DiveListView::newTripAbove()
|
|
|
|
{
|
2014-02-28 04:09:57 +00:00
|
|
|
struct dive *d = (struct dive *)contextMenuIndex.data(DiveTripModel::DIVE_ROLE).value<void *>();
|
2013-11-15 02:32:19 +00:00
|
|
|
if (!d) // shouldn't happen as we only are setting up this action if this is a dive
|
|
|
|
return;
|
2014-04-17 02:56:42 +00:00
|
|
|
//TODO: port to c-code.
|
|
|
|
dive_trip_t *trip;
|
|
|
|
int idx;
|
2013-11-15 02:32:19 +00:00
|
|
|
rememberSelection();
|
|
|
|
trip = create_and_hookup_trip_from_dive(d);
|
2014-05-22 18:40:22 +00:00
|
|
|
for_each_dive (idx, d) {
|
2013-11-15 05:11:55 +00:00
|
|
|
if (d->selected)
|
2013-11-15 02:32:19 +00:00
|
|
|
add_dive_to_trip(d, trip);
|
|
|
|
}
|
|
|
|
trip->expanded = 1;
|
2013-11-17 23:36:57 +00:00
|
|
|
reload(currentLayout, false);
|
2014-01-15 08:30:42 +00:00
|
|
|
mark_divelist_changed(true);
|
2013-11-17 23:36:57 +00:00
|
|
|
restoreSelection();
|
|
|
|
}
|
|
|
|
|
2014-03-20 06:42:20 +00:00
|
|
|
void DiveListView::addToTripBelow()
|
|
|
|
{
|
2014-08-21 19:20:03 +00:00
|
|
|
addToTrip(1);
|
2014-03-20 06:42:20 +00:00
|
|
|
}
|
|
|
|
|
2013-11-17 23:36:57 +00:00
|
|
|
void DiveListView::addToTripAbove()
|
2014-03-20 06:42:20 +00:00
|
|
|
{
|
2014-08-21 19:20:03 +00:00
|
|
|
addToTrip(-1);
|
2014-03-20 06:42:20 +00:00
|
|
|
}
|
|
|
|
|
2014-08-21 19:20:03 +00:00
|
|
|
void DiveListView::addToTrip(int delta)
|
2013-11-17 23:36:57 +00:00
|
|
|
{
|
2017-10-22 12:43:55 +00:00
|
|
|
// d points to the row that has (mouse-)pointer focus, and there are nr rows selected
|
2014-04-17 02:56:42 +00:00
|
|
|
struct dive *d = (struct dive *)contextMenuIndex.data(DiveTripModel::DIVE_ROLE).value<void *>();
|
2017-10-22 12:43:55 +00:00
|
|
|
int nr = selectionModel()->selectedRows().count();
|
|
|
|
QModelIndex t;
|
|
|
|
dive_trip_t *trip = NULL;
|
|
|
|
|
|
|
|
// now look for the trip to add to, for this, loop over the selected dives and
|
|
|
|
// check if its sibling is a trip.
|
|
|
|
for (int i = 1; i <= nr; i++) {
|
|
|
|
t = contextMenuIndex.sibling(contextMenuIndex.row() + (delta > 0 ? i: i * -1), 0);
|
|
|
|
trip = (dive_trip_t *)t.data(DiveTripModel::TRIP_ROLE).value<void *>();
|
|
|
|
if (trip)
|
|
|
|
break;
|
|
|
|
}
|
2014-04-17 02:56:42 +00:00
|
|
|
|
2014-08-21 19:20:03 +00:00
|
|
|
if (!trip || !d)
|
|
|
|
// no dive, no trip? get me out of here
|
2013-11-17 23:36:57 +00:00
|
|
|
return;
|
2014-08-21 19:20:03 +00:00
|
|
|
|
|
|
|
rememberSelection();
|
|
|
|
|
2013-11-17 23:36:57 +00:00
|
|
|
add_dive_to_trip(d, trip);
|
|
|
|
if (d->selected) { // there are possibly other selected dives that we should add
|
2014-08-21 19:20:03 +00:00
|
|
|
int idx;
|
2014-05-22 18:40:22 +00:00
|
|
|
for_each_dive (idx, d) {
|
2013-11-17 23:36:57 +00:00
|
|
|
if (d->selected)
|
|
|
|
add_dive_to_trip(d, trip);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
trip->expanded = 1;
|
2014-01-15 08:30:42 +00:00
|
|
|
mark_divelist_changed(true);
|
2014-08-21 19:20:03 +00:00
|
|
|
|
2013-11-15 02:32:19 +00:00
|
|
|
reload(currentLayout, false);
|
|
|
|
restoreSelection();
|
|
|
|
}
|
|
|
|
|
2013-12-07 01:40:59 +00:00
|
|
|
void DiveListView::markDiveInvalid()
|
|
|
|
{
|
|
|
|
int i;
|
2014-02-28 04:09:57 +00:00
|
|
|
struct dive *d = (struct dive *)contextMenuIndex.data(DiveTripModel::DIVE_ROLE).value<void *>();
|
2013-12-07 01:40:59 +00:00
|
|
|
if (!d)
|
|
|
|
return;
|
2014-05-22 18:40:22 +00:00
|
|
|
for_each_dive (i, d) {
|
2013-12-07 01:40:59 +00:00
|
|
|
if (!d->selected)
|
|
|
|
continue;
|
2014-04-17 02:56:42 +00:00
|
|
|
//TODO: this should be done in the future
|
2013-12-07 01:40:59 +00:00
|
|
|
// now mark the dive invalid... how do we do THAT?
|
|
|
|
// d->invalid = true;
|
|
|
|
}
|
|
|
|
if (amount_selected == 0) {
|
2014-02-12 14:22:54 +00:00
|
|
|
MainWindow::instance()->cleanUpEmpty();
|
2013-12-07 01:40:59 +00:00
|
|
|
}
|
2014-01-15 08:30:42 +00:00
|
|
|
mark_divelist_changed(true);
|
2014-02-12 14:22:54 +00:00
|
|
|
MainWindow::instance()->refreshDisplay();
|
2014-01-16 04:50:56 +00:00
|
|
|
if (prefs.display_invalid_dives == false) {
|
2013-12-07 01:40:59 +00:00
|
|
|
clearSelection();
|
|
|
|
// select top dive that isn't marked invalid
|
|
|
|
rememberSelection();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-06 01:37:18 +00:00
|
|
|
void DiveListView::deleteDive()
|
|
|
|
{
|
2014-02-28 04:09:57 +00:00
|
|
|
struct dive *d = (struct dive *)contextMenuIndex.data(DiveTripModel::DIVE_ROLE).value<void *>();
|
2013-11-15 05:11:55 +00:00
|
|
|
if (!d)
|
|
|
|
return;
|
2014-04-17 02:56:42 +00:00
|
|
|
|
|
|
|
int i;
|
2013-11-16 20:52:14 +00:00
|
|
|
int lastDiveNr = -1;
|
2015-02-11 07:57:56 +00:00
|
|
|
QList<struct dive*> deletedDives; //a list of all deleted dives to be stored in the undo command
|
2014-05-19 21:37:19 +00:00
|
|
|
for_each_dive (i, d) {
|
2013-11-16 20:41:47 +00:00
|
|
|
if (!d->selected)
|
|
|
|
continue;
|
2015-10-02 18:50:12 +00:00
|
|
|
deletedDives.append(d);
|
2013-11-16 20:52:14 +00:00
|
|
|
lastDiveNr = i;
|
2013-11-16 20:41:47 +00:00
|
|
|
}
|
2015-10-02 18:50:12 +00:00
|
|
|
// the actual dive deletion is happening in the redo command that is implicitly triggered
|
2015-02-11 07:57:56 +00:00
|
|
|
UndoDeleteDive *undoEntry = new UndoDeleteDive(deletedDives);
|
|
|
|
MainWindow::instance()->undoStack->push(undoEntry);
|
2013-11-15 05:11:55 +00:00
|
|
|
if (amount_selected == 0) {
|
2014-02-12 14:22:54 +00:00
|
|
|
MainWindow::instance()->cleanUpEmpty();
|
2013-11-15 05:11:55 +00:00
|
|
|
}
|
2014-01-15 08:30:42 +00:00
|
|
|
mark_divelist_changed(true);
|
2014-02-12 14:22:54 +00:00
|
|
|
MainWindow::instance()->refreshDisplay();
|
2014-01-16 04:50:56 +00:00
|
|
|
if (lastDiveNr != -1) {
|
2013-11-16 20:52:14 +00:00
|
|
|
clearSelection();
|
|
|
|
selectDive(lastDiveNr);
|
|
|
|
rememberSelection();
|
|
|
|
}
|
2013-06-06 00:06:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DiveListView::contextMenuEvent(QContextMenuEvent *event)
|
|
|
|
{
|
|
|
|
QAction *collapseAction = NULL;
|
|
|
|
// let's remember where we are
|
|
|
|
contextMenuIndex = indexAt(event->pos());
|
2014-02-28 04:09:57 +00:00
|
|
|
struct dive *d = (struct dive *)contextMenuIndex.data(DiveTripModel::DIVE_ROLE).value<void *>();
|
|
|
|
dive_trip_t *trip = (dive_trip_t *)contextMenuIndex.data(DiveTripModel::TRIP_ROLE).value<void *>();
|
2013-06-05 18:29:11 +00:00
|
|
|
QMenu popup(this);
|
2013-06-05 21:51:15 +00:00
|
|
|
if (currentLayout == DiveTripModel::TREE) {
|
2015-05-10 20:08:24 +00:00
|
|
|
// verify if there is a node that`s not expanded.
|
|
|
|
bool needs_expand = false;
|
|
|
|
bool needs_collapse = false;
|
2015-05-10 20:19:34 +00:00
|
|
|
uint expanded_nodes = 0;
|
2015-05-10 20:08:24 +00:00
|
|
|
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;
|
|
|
|
|
2015-05-10 20:19:34 +00:00
|
|
|
if (!isExpanded(idx)) {
|
2015-05-10 20:08:24 +00:00
|
|
|
needs_expand = true;
|
2015-05-10 20:19:34 +00:00
|
|
|
} else {
|
2015-05-10 20:08:24 +00:00
|
|
|
needs_collapse = true;
|
2015-05-10 20:19:34 +00:00
|
|
|
expanded_nodes ++;
|
|
|
|
}
|
2015-05-10 20:08:24 +00:00
|
|
|
}
|
|
|
|
if (needs_expand)
|
|
|
|
popup.addAction(tr("Expand all"), this, SLOT(expandAll()));
|
|
|
|
if (needs_collapse)
|
|
|
|
popup.addAction(tr("Collapse all"), this, SLOT(collapseAll()));
|
|
|
|
|
2015-05-10 20:19:34 +00:00
|
|
|
// verify if there`s a need for collapse others
|
|
|
|
if (expanded_nodes > 1)
|
|
|
|
collapseAction = popup.addAction(tr("Collapse others"), this, SLOT(collapseAll()));
|
|
|
|
|
|
|
|
|
2013-06-06 00:06:42 +00:00
|
|
|
if (d) {
|
2014-07-11 08:21:36 +00:00
|
|
|
popup.addAction(tr("Remove dive(s) from trip"), this, SLOT(removeFromTrip()));
|
|
|
|
popup.addAction(tr("Create new trip above"), this, SLOT(newTripAbove()));
|
2014-05-24 15:27:42 +00:00
|
|
|
if (!d->divetrip) {
|
2014-05-24 17:28:11 +00:00
|
|
|
struct dive *top = d;
|
|
|
|
struct dive *bottom = d;
|
|
|
|
if (d->selected) {
|
|
|
|
if (currentOrder == Qt::AscendingOrder) {
|
|
|
|
top = first_selected_dive();
|
|
|
|
bottom = last_selected_dive();
|
|
|
|
} else {
|
|
|
|
top = last_selected_dive();
|
|
|
|
bottom = first_selected_dive();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (is_trip_before_after(top, (currentOrder == Qt::AscendingOrder)))
|
2014-07-11 08:21:36 +00:00
|
|
|
popup.addAction(tr("Add dive(s) to trip immediately above"), this, SLOT(addToTripAbove()));
|
2014-05-24 17:28:11 +00:00
|
|
|
if (is_trip_before_after(bottom, (currentOrder == Qt::DescendingOrder)))
|
2014-07-11 08:21:36 +00:00
|
|
|
popup.addAction(tr("Add dive(s) to trip immediately below"), this, SLOT(addToTripBelow()));
|
2014-05-24 15:27:42 +00:00
|
|
|
}
|
2013-06-06 00:06:42 +00:00
|
|
|
}
|
2013-06-27 05:16:40 +00:00
|
|
|
if (trip) {
|
2014-07-11 08:21:36 +00:00
|
|
|
popup.addAction(tr("Merge trip with trip above"), this, SLOT(mergeTripAbove()));
|
|
|
|
popup.addAction(tr("Merge trip with trip below"), this, SLOT(mergeTripBelow()));
|
2013-06-27 05:16:40 +00:00
|
|
|
}
|
2013-06-05 21:51:15 +00:00
|
|
|
}
|
2013-12-07 01:40:59 +00:00
|
|
|
if (d) {
|
2014-07-11 08:21:36 +00:00
|
|
|
popup.addAction(tr("Delete dive(s)"), this, SLOT(deleteDive()));
|
2013-12-07 01:40:59 +00:00
|
|
|
#if 0
|
2014-07-11 08:21:36 +00:00
|
|
|
popup.addAction(tr("Mark dive(s) invalid", this, SLOT(markDiveInvalid())));
|
2013-12-07 01:40:59 +00:00
|
|
|
#endif
|
|
|
|
}
|
2013-09-24 10:42:40 +00:00
|
|
|
if (amount_selected > 1 && consecutive_selected())
|
2014-07-11 08:21:36 +00:00
|
|
|
popup.addAction(tr("Merge selected dives"), this, SLOT(mergeDives()));
|
2013-10-19 17:35:36 +00:00
|
|
|
if (amount_selected >= 1) {
|
2014-07-11 08:21:36 +00:00
|
|
|
popup.addAction(tr("Renumber dive(s)"), this, SLOT(renumberDives()));
|
2015-04-25 11:08:16 +00:00
|
|
|
popup.addAction(tr("Shift dive times"), this, SLOT(shiftTimes()));
|
2015-10-02 01:17:37 +00:00
|
|
|
popup.addAction(tr("Split selected dives"), this, SLOT(splitDives()));
|
2015-04-25 11:08:16 +00:00
|
|
|
popup.addAction(tr("Load image(s) from file(s)"), this, SLOT(loadImages()));
|
2017-01-11 09:29:59 +00:00
|
|
|
popup.addAction(tr("Load image from web"), this, SLOT(loadWebImages()));
|
2013-10-19 17:35:36 +00:00
|
|
|
}
|
2014-12-24 15:23:31 +00:00
|
|
|
|
2013-06-05 21:39:54 +00:00
|
|
|
// "collapse all" really closes all trips,
|
2013-06-05 21:51:15 +00:00
|
|
|
// "collapse" keeps the trip with the selected dive open
|
2014-02-28 04:09:57 +00:00
|
|
|
QAction *actionTaken = popup.exec(event->globalPos());
|
2013-06-06 00:06:42 +00:00
|
|
|
if (actionTaken == collapseAction && collapseAction) {
|
|
|
|
this->setAnimated(false);
|
2013-11-16 20:41:47 +00:00
|
|
|
selectDive(selected_dive, true);
|
2013-11-15 12:51:39 +00:00
|
|
|
scrollTo(selectedIndexes().first());
|
2013-06-06 00:06:42 +00:00
|
|
|
this->setAnimated(true);
|
|
|
|
}
|
|
|
|
event->accept();
|
2013-06-05 18:29:11 +00:00
|
|
|
}
|
2013-10-18 00:50:08 +00:00
|
|
|
|
2013-11-18 13:53:05 +00:00
|
|
|
void DiveListView::shiftTimes()
|
|
|
|
{
|
|
|
|
ShiftTimesDialog::instance()->show();
|
|
|
|
}
|
2013-12-05 16:28:28 +00:00
|
|
|
|
2014-01-27 13:44:26 +00:00
|
|
|
void DiveListView::loadImages()
|
|
|
|
{
|
2018-07-13 19:06:28 +00:00
|
|
|
QStringList m_filters = mediaExtensionFilters();
|
|
|
|
QStringList i_filters = imageExtensionFilters();
|
|
|
|
QStringList v_filters = videoExtensionFilters();
|
2017-05-05 19:13:46 +00:00
|
|
|
QStringList fileNames = QFileDialog::getOpenFileNames(this,
|
2018-07-13 18:46:45 +00:00
|
|
|
tr("Open media files"),
|
2017-05-05 19:13:46 +00:00
|
|
|
lastUsedImageDir(),
|
2018-07-13 19:06:28 +00:00
|
|
|
QString("%1 (%2);;%3 (%4);;%5 (%6);;%7 (*.*)")
|
|
|
|
.arg(tr("Media files"), m_filters.join(" ")
|
|
|
|
, tr("Image files"), i_filters.join(" ")
|
|
|
|
, tr("Video files"), v_filters.join(" ")
|
|
|
|
, tr("All files")));
|
2017-05-05 19:13:46 +00:00
|
|
|
|
2014-01-27 13:44:26 +00:00
|
|
|
if (fileNames.isEmpty())
|
|
|
|
return;
|
|
|
|
updateLastUsedImageDir(QFileInfo(fileNames[0]).dir().path());
|
2015-04-24 15:10:55 +00:00
|
|
|
matchImagesToDives(fileNames);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DiveListView::matchImagesToDives(QStringList fileNames)
|
|
|
|
{
|
2015-03-14 16:44:24 +00:00
|
|
|
ShiftImageTimesDialog shiftDialog(this, fileNames);
|
2014-02-12 15:46:17 +00:00
|
|
|
shiftDialog.setOffset(lastImageTimeOffset());
|
2015-03-14 16:44:24 +00:00
|
|
|
if (!shiftDialog.exec())
|
|
|
|
return;
|
2014-02-12 15:46:17 +00:00
|
|
|
updateLastImageTimeOffset(shiftDialog.amount());
|
2014-01-27 13:44:26 +00:00
|
|
|
|
2014-09-17 22:39:49 +00:00
|
|
|
Q_FOREACH (const QString &fileName, fileNames) {
|
2014-01-27 13:44:26 +00:00
|
|
|
int j = 0;
|
|
|
|
struct dive *dive;
|
2014-05-22 18:40:22 +00:00
|
|
|
for_each_dive (j, dive) {
|
2014-01-27 13:44:26 +00:00
|
|
|
if (!dive->selected)
|
|
|
|
continue;
|
2018-02-28 22:37:09 +00:00
|
|
|
dive_create_picture(dive, copy_qstring(fileName), shiftDialog.amount(), shiftDialog.matchAll());
|
2014-01-27 13:44:26 +00:00
|
|
|
}
|
|
|
|
}
|
2014-06-02 21:28:02 +00:00
|
|
|
|
2014-05-30 16:46:26 +00:00
|
|
|
mark_divelist_changed(true);
|
2014-08-04 17:06:58 +00:00
|
|
|
copy_dive(current_dive, &displayed_dive);
|
|
|
|
DivePictureModel::instance()->updateDivePictures();
|
2014-01-27 13:44:26 +00:00
|
|
|
}
|
|
|
|
|
2015-04-24 15:10:55 +00:00
|
|
|
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);
|
2017-01-11 09:50:52 +00:00
|
|
|
if (image.isNull()) {
|
2015-04-24 15:10:55 +00:00
|
|
|
// 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;
|
2017-10-26 13:53:47 +00:00
|
|
|
report_error(qPrintable(tr("%1 does not appear to be an image").arg(url.toString())));
|
2015-04-24 15:10:55 +00:00
|
|
|
return;
|
2017-01-11 09:50:52 +00:00
|
|
|
}
|
2015-04-24 15:10:55 +00:00
|
|
|
|
|
|
|
QCryptographicHash hash(QCryptographicHash::Sha1);
|
2018-06-10 14:39:47 +00:00
|
|
|
hash.addData(url.toString().toUtf8());
|
2015-04-24 15:10:55 +00:00
|
|
|
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();
|
2018-06-02 16:03:03 +00:00
|
|
|
learnPictureFilename(url.toString(), imageFile.fileName());
|
2015-04-24 15:10:55 +00:00
|
|
|
matchImagesToDives(QStringList(url.toString()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-27 13:44:26 +00:00
|
|
|
QString DiveListView::lastUsedImageDir()
|
|
|
|
{
|
|
|
|
QSettings settings;
|
|
|
|
QString lastImageDir = QDir::homePath();
|
|
|
|
|
|
|
|
settings.beginGroup("FileDialog");
|
|
|
|
if (settings.contains("LastImageDir"))
|
2017-12-04 07:49:28 +00:00
|
|
|
if (QDir(settings.value("LastImageDir").toString()).exists())
|
2017-12-04 07:44:07 +00:00
|
|
|
lastImageDir = settings.value("LastImageDir").toString();
|
2014-01-27 13:44:26 +00:00
|
|
|
return lastImageDir;
|
|
|
|
}
|
|
|
|
|
2014-02-28 04:09:57 +00:00
|
|
|
void DiveListView::updateLastUsedImageDir(const QString &dir)
|
2014-01-27 13:44:26 +00:00
|
|
|
{
|
|
|
|
QSettings s;
|
|
|
|
s.beginGroup("FileDialog");
|
|
|
|
s.setValue("LastImageDir", dir);
|
|
|
|
}
|
2014-02-12 15:46:17 +00:00
|
|
|
|
|
|
|
int DiveListView::lastImageTimeOffset()
|
|
|
|
{
|
|
|
|
QSettings settings;
|
|
|
|
int offset = 0;
|
|
|
|
|
|
|
|
settings.beginGroup("MainWindow");
|
|
|
|
if (settings.contains("LastImageTimeOffset"))
|
|
|
|
offset = settings.value("LastImageTimeOffset").toInt();
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DiveListView::updateLastImageTimeOffset(const int offset)
|
|
|
|
{
|
|
|
|
QSettings s;
|
|
|
|
s.beginGroup("MainWindow");
|
|
|
|
s.setValue("LastImageTimeOffset", offset);
|
|
|
|
}
|
2017-01-28 11:16:45 +00:00
|
|
|
|
2018-05-22 06:04:28 +00:00
|
|
|
void DiveListView::mouseDoubleClickEvent(QMouseEvent*)
|
2017-01-28 11:16:45 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|