2013-04-14 03:44:02 +00:00
|
|
|
/*
|
|
|
|
* divelistview.cpp
|
|
|
|
*
|
|
|
|
* classes for the divelist of Subsurface
|
|
|
|
*
|
|
|
|
*/
|
2013-04-12 07:24:07 +00:00
|
|
|
#include "divelistview.h"
|
2013-04-27 15:27:27 +00:00
|
|
|
#include "models.h"
|
|
|
|
#include "modeldelegates.h"
|
2013-09-23 05:24:28 +00:00
|
|
|
#include "mainwindow.h"
|
2013-12-05 16:28:28 +00:00
|
|
|
#include "subsurfacewebservices.h"
|
2014-05-20 16:33:32 +00:00
|
|
|
#include "divelogexportdialog.h"
|
2013-06-04 12:56:20 +00:00
|
|
|
#include "../display.h"
|
2014-01-27 13:44:26 +00:00
|
|
|
#include "exif.h"
|
|
|
|
#include "../file.h"
|
2013-05-02 22:27:36 +00:00
|
|
|
#include <QApplication>
|
|
|
|
#include <QHeaderView>
|
2013-05-14 01:14:59 +00:00
|
|
|
#include <QDebug>
|
2013-05-21 19:51:49 +00:00
|
|
|
#include <QSettings>
|
2013-05-14 01:14:59 +00:00
|
|
|
#include <QKeyEvent>
|
2013-05-16 19:00:33 +00:00
|
|
|
#include <QSortFilterProxyModel>
|
2013-05-21 19:51:49 +00:00
|
|
|
#include <QAction>
|
2013-05-30 04:04:42 +00:00
|
|
|
#include <QLineEdit>
|
|
|
|
#include <QKeyEvent>
|
2013-06-05 18:29:11 +00:00
|
|
|
#include <QMenu>
|
2013-10-18 00:50:08 +00:00
|
|
|
#include <QFileDialog>
|
2014-01-27 13:44:26 +00:00
|
|
|
#include <string>
|
|
|
|
#include <iostream>
|
2014-05-12 16:53:26 +00:00
|
|
|
#include "../qthelper.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),
|
2014-05-28 22:03:10 +00:00
|
|
|
currentOrder(Qt::DescendingOrder), searchBox(this), dontEmitDiveChangedSignal(false), selectionSaved(false)
|
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));
|
2013-05-16 19:00:33 +00:00
|
|
|
QSortFilterProxyModel *model = new QSortFilterProxyModel(this);
|
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);
|
2013-05-16 19:00:33 +00:00
|
|
|
setModel(model);
|
2013-06-12 17:14:25 +00:00
|
|
|
connect(model, SIGNAL(layoutChanged()), this, SLOT(fixMessyQtModelBehaviour()));
|
|
|
|
|
2013-05-28 19:56:58 +00:00
|
|
|
setSortingEnabled(false);
|
2013-06-06 00:06:42 +00:00
|
|
|
setContextMenuPolicy(Qt::DefaultContextMenu);
|
2013-05-21 19:51:49 +00:00
|
|
|
header()->setContextMenuPolicy(Qt::ActionsContextMenu);
|
2014-04-02 19:30:10 +00:00
|
|
|
#ifdef Q_OS_MAC
|
|
|
|
// Fixes for the layout needed for mac
|
2013-10-01 13:52:23 +00:00
|
|
|
const QFontMetrics metrics(defaultModelFont());
|
|
|
|
header()->setMinimumHeight(metrics.height() + 10);
|
2014-04-02 19:30:10 +00:00
|
|
|
#endif
|
2013-11-13 18:59:56 +00:00
|
|
|
header()->setStretchLastSection(true);
|
2014-07-11 08:21:36 +00:00
|
|
|
QAction *showSearchBox = new QAction(tr("Show search box"), this);
|
2014-02-28 04:09:57 +00:00
|
|
|
showSearchBox->setShortcut(Qt::CTRL + Qt::Key_F);
|
2014-01-09 17:21:28 +00:00
|
|
|
showSearchBox->setShortcutContext(Qt::WindowShortcut);
|
2013-05-30 04:04:42 +00:00
|
|
|
addAction(showSearchBox);
|
|
|
|
|
2014-02-09 17:18:54 +00:00
|
|
|
searchBox.installEventFilter(this);
|
|
|
|
searchBox.hide();
|
2013-05-30 04:04:42 +00:00
|
|
|
connect(showSearchBox, SIGNAL(triggered(bool)), this, SLOT(showSearchEdit()));
|
2014-02-09 17:18:54 +00:00
|
|
|
connect(&searchBox, SIGNAL(textChanged(QString)), model, SLOT(setFilterFixedString(QString)));
|
2013-11-08 22:30:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DiveListView::~DiveListView()
|
|
|
|
{
|
|
|
|
QSettings settings;
|
|
|
|
settings.beginGroup("ListWidget");
|
2014-01-16 04:50:56 +00:00
|
|
|
for (int i = DiveTripModel::NR; i < DiveTripModel::COLUMNS; i++) {
|
2013-11-08 22:30:17 +00:00
|
|
|
if (isColumnHidden(i))
|
|
|
|
continue;
|
|
|
|
settings.setValue(QString("colwidth%1").arg(i), columnWidth(i));
|
|
|
|
}
|
|
|
|
settings.endGroup();
|
|
|
|
}
|
|
|
|
|
2014-01-16 04:50:56 +00:00
|
|
|
void DiveListView::setupUi()
|
|
|
|
{
|
2013-11-08 22:30:17 +00:00
|
|
|
QSettings settings;
|
2013-11-08 22:46:25 +00:00
|
|
|
static bool firstRun = true;
|
2014-01-16 04:50:56 +00:00
|
|
|
if (firstRun)
|
2013-11-08 23:08:14 +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
|
|
|
|
setColumnWidth(i, 100);
|
|
|
|
}
|
|
|
|
settings.endGroup();
|
2014-01-16 04:50:56 +00:00
|
|
|
if (firstRun)
|
2013-11-08 23:08:14 +00:00
|
|
|
restoreExpandedRows();
|
|
|
|
else
|
|
|
|
collapseAll();
|
|
|
|
firstRun = false;
|
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
|
|
|
}
|
2013-06-12 17:14:25 +00:00
|
|
|
void DiveListView::fixMessyQtModelBehaviour()
|
|
|
|
{
|
|
|
|
QAbstractItemModel *m = model();
|
2014-02-28 04:09:57 +00:00
|
|
|
for (int i = 0; i < model()->rowCount(); i++)
|
|
|
|
if (m->rowCount(m->index(i, 0)) != 0)
|
2013-06-12 17:14:25 +00:00
|
|
|
setFirstColumnSpanned(i, QModelIndex(), true);
|
|
|
|
}
|
|
|
|
|
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 *>();
|
2013-11-14 08:52:03 +00:00
|
|
|
if (d)
|
2013-11-26 17:44:18 +00:00
|
|
|
selectedDives.insert(d->divetrip, get_divenr(d));
|
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;
|
2013-11-14 08:52:03 +00:00
|
|
|
unselectDives();
|
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
|
|
|
|
if (trip != NULL && divesOnTrip.count() == selectedDivesOnTrip.count())
|
2013-11-26 17:44:18 +00:00
|
|
|
selectTrip(trip);
|
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);
|
|
|
|
}
|
|
|
|
|
2013-06-05 06:41:52 +00:00
|
|
|
void DiveListView::unselectDives()
|
|
|
|
{
|
|
|
|
selectionModel()->clearSelection();
|
2014-05-24 20:55:18 +00:00
|
|
|
// clearSelection should emit selectionChanged() but sometimes that
|
|
|
|
// appears not to happen
|
|
|
|
int i;
|
|
|
|
struct dive *dive;
|
|
|
|
for_each_dive(i, dive) {
|
|
|
|
deselect_dive(i);
|
|
|
|
}
|
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
|
|
|
{
|
|
|
|
QModelIndexList indexes = selectionModel()->selectedRows();
|
2014-02-28 04:09:57 +00:00
|
|
|
QList<dive_trip_t *> ret;
|
2014-05-22 18:40:22 +00:00
|
|
|
Q_FOREACH (const QModelIndex &index, indexes) {
|
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-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());
|
|
|
|
while (!sortedSelection.isEmpty())
|
|
|
|
selectDive(sortedSelection.takeLast());
|
2013-12-02 19:15:40 +00:00
|
|
|
|
2014-02-28 04:09:57 +00:00
|
|
|
QSortFilterProxyModel *m = qobject_cast<QSortFilterProxyModel *>(model());
|
2014-05-08 04:51:55 +00:00
|
|
|
QModelIndex idx = m->match(m->index(0, 0), DiveTripModel::DIVE_IDX, selected_dive, 2, Qt::MatchRecursive).first();
|
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 (idx.parent().isValid())
|
|
|
|
scrollTo(idx.parent());
|
2013-12-02 19:15:40 +00:00
|
|
|
scrollTo(idx);
|
2014-05-25 03:55:28 +00:00
|
|
|
|
2014-05-25 04:03:18 +00:00
|
|
|
// now that everything is up to date, update the widgets
|
|
|
|
Q_EMIT currentDiveChanged(selected_dive);
|
|
|
|
dontEmitDiveChangedSignal = false;
|
2014-05-25 03:55:28 +00:00
|
|
|
return;
|
2013-12-02 19:15:40 +00:00
|
|
|
}
|
|
|
|
|
2013-05-30 04:04:42 +00:00
|
|
|
void DiveListView::showSearchEdit()
|
|
|
|
{
|
2014-02-09 17:18:54 +00:00
|
|
|
searchBox.show();
|
|
|
|
searchBox.setFocus();
|
2013-05-30 04:04:42 +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);
|
2013-10-11 13:21:04 +00:00
|
|
|
if (keyEv->key() != Qt::Key_Escape)
|
2013-05-30 04:04:42 +00:00
|
|
|
return false;
|
|
|
|
|
2014-02-09 17:18:54 +00:00
|
|
|
searchBox.clear();
|
|
|
|
searchBox.hide();
|
2014-02-28 04:09:57 +00:00
|
|
|
QSortFilterProxyModel *m = qobject_cast<QSortFilterProxyModel *>(model());
|
2013-05-30 04:04:42 +00:00
|
|
|
m->setFilterFixedString(QString());
|
|
|
|
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
|
|
|
{
|
2014-05-25 14:28:22 +00:00
|
|
|
// we want to run setupUi() once we actually are displaying something
|
|
|
|
// in the widget
|
|
|
|
static bool first = true;
|
|
|
|
if (first && dive_table.nr > 0) {
|
|
|
|
setupUi();
|
|
|
|
first = false;
|
|
|
|
}
|
2013-06-06 02:22:08 +00:00
|
|
|
if (layout == DiveTripModel::CURRENT)
|
|
|
|
layout = currentLayout;
|
|
|
|
else
|
|
|
|
currentLayout = layout;
|
2014-02-28 04:09:57 +00:00
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
2013-05-28 20:46:40 +00:00
|
|
|
header()->setClickable(true);
|
2014-01-15 08:30:39 +00:00
|
|
|
#else
|
|
|
|
header()->setSectionsClickable(true);
|
|
|
|
#endif
|
2013-05-28 20:46:40 +00:00
|
|
|
connect(header(), SIGNAL(sectionPressed(int)), this, SLOT(headerClicked(int)), Qt::UniqueConnection);
|
|
|
|
|
2014-02-28 04:09:57 +00:00
|
|
|
QSortFilterProxyModel *m = qobject_cast<QSortFilterProxyModel *>(model());
|
2013-05-16 19:00:33 +00:00
|
|
|
QAbstractItemModel *oldModel = m->sourceModel();
|
2014-01-16 04:50:56 +00:00
|
|
|
if (oldModel) {
|
2013-05-16 21:09:45 +00:00
|
|
|
oldModel->deleteLater();
|
2013-11-16 21:21:54 +00:00
|
|
|
}
|
2013-05-28 19:56:58 +00:00
|
|
|
DiveTripModel *tripModel = new DiveTripModel(this);
|
2013-05-28 20:07:43 +00:00
|
|
|
tripModel->setLayout(layout);
|
2013-05-28 19:56:58 +00:00
|
|
|
|
|
|
|
m->setSourceModel(tripModel);
|
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;
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2014-01-16 04:50:56 +00:00
|
|
|
if (currentLayout == DiveTripModel::TREE) {
|
2013-11-16 20:47:34 +00:00
|
|
|
fixMessyQtModelBehaviour();
|
|
|
|
}
|
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());
|
2013-10-04 17:28:49 +00:00
|
|
|
bool showHeaderFirstRun = !(
|
2014-05-22 18:40:22 +00:00
|
|
|
i == DiveTripModel::MAXCNS || i == DiveTripModel::NITROX || i == DiveTripModel::OTU || i == DiveTripModel::TEMPERATURE || i == DiveTripModel::TOTALWEIGHT || i == DiveTripModel::SUIT || i == DiveTripModel::CYLINDER || i == DiveTripModel::SAC);
|
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;
|
|
|
|
|
|
|
|
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();
|
2013-05-26 17:12:45 +00:00
|
|
|
setColumnHidden(action->property("index").toInt(), !action->isChecked());
|
2013-11-13 18:59:56 +00:00
|
|
|
setColumnWidth(lastVisibleColumn(), 10);
|
2013-05-14 01:14:59 +00:00
|
|
|
}
|
|
|
|
|
2014-02-28 04:09:57 +00:00
|
|
|
void DiveListView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
|
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-05-25 04:03:18 +00:00
|
|
|
if(!dontEmitDiveChangedSignal)
|
|
|
|
Q_EMIT currentDiveChanged(selected_dive);
|
2013-04-12 07:24:07 +00:00
|
|
|
}
|
2013-06-05 18:29:11 +00:00
|
|
|
|
2013-11-16 22:46:08 +00:00
|
|
|
static bool can_merge(const struct dive *a, const struct dive *b)
|
|
|
|
{
|
|
|
|
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 */
|
2014-02-28 04:09:57 +00:00
|
|
|
if (a->when + a->duration.seconds + 30 * 60 < b->when)
|
2013-11-16 22:46:08 +00:00
|
|
|
return false;
|
|
|
|
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;
|
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) {
|
2013-11-16 22:46:08 +00:00
|
|
|
if (!can_merge(maindive, dive)) {
|
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
|
|
|
}
|
|
|
|
}
|
2014-02-12 14:22:54 +00:00
|
|
|
MainWindow::instance()->refreshDisplay();
|
2013-09-24 04:57:28 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
fixMessyQtModelBehaviour();
|
|
|
|
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;
|
2014-05-22 18:40:22 +00:00
|
|
|
for_each_dive (i, d) {
|
2013-11-15 05:11:55 +00:00
|
|
|
if (d->selected)
|
2014-01-15 08:30:42 +00:00
|
|
|
remove_dive_from_trip(d, false);
|
2013-11-15 05:11:55 +00:00
|
|
|
}
|
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
|
|
|
fixMessyQtModelBehaviour();
|
|
|
|
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);
|
|
|
|
fixMessyQtModelBehaviour();
|
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()
|
|
|
|
{
|
|
|
|
addToTrip(true);
|
|
|
|
}
|
|
|
|
|
2013-11-17 23:36:57 +00:00
|
|
|
void DiveListView::addToTripAbove()
|
2014-03-20 06:42:20 +00:00
|
|
|
{
|
|
|
|
addToTrip(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DiveListView::addToTrip(bool below)
|
2013-11-17 23:36:57 +00:00
|
|
|
{
|
2014-05-22 18:40:22 +00:00
|
|
|
int delta = (currentOrder == Qt::AscendingOrder) ? -1 : +1;
|
2014-04-17 02:56:42 +00:00
|
|
|
struct dive *d = (struct dive *)contextMenuIndex.data(DiveTripModel::DIVE_ROLE).value<void *>();
|
|
|
|
rememberSelection();
|
|
|
|
|
|
|
|
//TODO: This part should be moved to C-code.
|
|
|
|
int idx;
|
2013-11-17 23:36:57 +00:00
|
|
|
dive_trip_t *trip = NULL;
|
2013-12-20 17:37:56 +00:00
|
|
|
struct dive *pd = NULL;
|
2014-03-20 06:42:20 +00:00
|
|
|
if (below) // Should we add to the trip below instead?
|
|
|
|
delta *= -1;
|
2014-04-17 02:56:42 +00:00
|
|
|
|
2013-11-17 23:36:57 +00:00
|
|
|
if (d->selected) { // we are right-clicking on one of possibly many selected dive(s)
|
|
|
|
// find the top selected dive, depending on the list order
|
2014-05-24 15:27:42 +00:00
|
|
|
if (delta == 1)
|
|
|
|
d = last_selected_dive();
|
|
|
|
else
|
|
|
|
d = first_selected_dive();
|
2013-11-17 23:36:57 +00:00
|
|
|
}
|
|
|
|
// now find the trip "above" in the dive list
|
|
|
|
if ((pd = get_dive(get_divenr(d) + delta)) != NULL) {
|
|
|
|
trip = pd->divetrip;
|
|
|
|
}
|
|
|
|
if (!pd || !trip)
|
|
|
|
// previous dive wasn't in a trip, so something is wrong
|
|
|
|
return;
|
|
|
|
add_dive_to_trip(d, trip);
|
|
|
|
if (d->selected) { // there are possibly other selected dives that we should add
|
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-04-17 02:56:42 +00:00
|
|
|
//This part stays at C++ code.
|
2013-11-15 02:32:19 +00:00
|
|
|
reload(currentLayout, false);
|
|
|
|
restoreSelection();
|
2013-11-17 23:36:57 +00:00
|
|
|
fixMessyQtModelBehaviour();
|
2013-11-15 02:32:19 +00:00
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
fixMessyQtModelBehaviour();
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
|
|
//TODO: port this to C-code.
|
|
|
|
int i;
|
2013-11-16 20:41:47 +00:00
|
|
|
// after a dive is deleted the ones following it move forward in the dive_table
|
|
|
|
// so instead of using the for_each_dive macro I'm using an explicit for loop
|
|
|
|
// to make this easier to understand
|
2013-11-16 20:52:14 +00:00
|
|
|
int lastDiveNr = -1;
|
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;
|
|
|
|
delete_single_dive(i);
|
|
|
|
i--; // so the next dive isn't skipped... it's now #i
|
2013-11-16 20:52:14 +00:00
|
|
|
lastDiveNr = i;
|
2013-11-16 20:41:47 +00:00
|
|
|
}
|
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-11-17 23:36:57 +00:00
|
|
|
fixMessyQtModelBehaviour();
|
2013-06-06 00:06:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DiveListView::testSlot()
|
|
|
|
{
|
2014-02-28 04:09:57 +00:00
|
|
|
struct dive *d = (struct dive *)contextMenuIndex.data(DiveTripModel::DIVE_ROLE).value<void *>();
|
2013-06-06 00:06:42 +00:00
|
|
|
if (d) {
|
|
|
|
qDebug("testSlot called on dive #%d", d->number);
|
|
|
|
} else {
|
|
|
|
QModelIndex child = contextMenuIndex.child(0, 0);
|
2014-02-28 04:09:57 +00:00
|
|
|
d = (struct dive *)child.data(DiveTripModel::DIVE_ROLE).value<void *>();
|
2013-06-06 00:06:42 +00:00
|
|
|
if (d)
|
|
|
|
qDebug("testSlot called on trip including dive #%d", d->number);
|
|
|
|
else
|
|
|
|
qDebug("testSlot called on trip with no dive");
|
2013-06-05 18:29:11 +00:00
|
|
|
}
|
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) {
|
2014-07-11 08:21:36 +00:00
|
|
|
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()));
|
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()));
|
|
|
|
popup.addAction(tr("Shift times"), this, SLOT(shiftTimes()));
|
|
|
|
popup.addAction(tr("Load images"), this, SLOT(loadImages()));
|
2013-10-19 17:35:36 +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()
|
|
|
|
{
|
2014-07-11 08:21:36 +00:00
|
|
|
QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Open image files"), lastUsedImageDir(), tr("Image files (*.jpg *.jpeg *.pnm *.tif *.tiff)"));
|
2014-01-27 13:44:26 +00:00
|
|
|
if (fileNames.isEmpty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
updateLastUsedImageDir(QFileInfo(fileNames[0]).dir().path());
|
2014-02-08 22:56:47 +00:00
|
|
|
ShiftImageTimesDialog shiftDialog(this);
|
2014-02-12 15:46:17 +00:00
|
|
|
shiftDialog.setOffset(lastImageTimeOffset());
|
2014-02-08 22:56:47 +00:00
|
|
|
shiftDialog.exec();
|
2014-02-12 15:46:17 +00:00
|
|
|
updateLastImageTimeOffset(shiftDialog.amount());
|
2014-01-27 13:44:26 +00:00
|
|
|
|
2014-06-02 21:28:02 +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;
|
2014-06-03 17:51:47 +00:00
|
|
|
dive_create_picture(dive, qstrdup(fileName.toUtf8().data()), shiftDialog.amount());
|
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-07-08 19:52:38 +00:00
|
|
|
// the sequence is somewhat magic - replot re-populates the displayed_dive.
|
|
|
|
// calling refreshDisplay afterwards gets the picture model populated and the thumbnails displayed
|
2014-05-30 16:46:26 +00:00
|
|
|
MainWindow::instance()->graphics()->replot();
|
2014-07-08 19:52:38 +00:00
|
|
|
MainWindow::instance()->refreshDisplay();
|
2014-01-27 13:44:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QString DiveListView::lastUsedImageDir()
|
|
|
|
{
|
|
|
|
QSettings settings;
|
|
|
|
QString lastImageDir = QDir::homePath();
|
|
|
|
|
|
|
|
settings.beginGroup("FileDialog");
|
|
|
|
if (settings.contains("LastImageDir"))
|
|
|
|
if (QDir::setCurrent(settings.value("LastImageDir").toString()))
|
|
|
|
lastImageDir = settings.value("LastIamgeDir").toString();
|
|
|
|
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);
|
|
|
|
}
|
2014-06-26 18:31:01 +00:00
|
|
|
|
|
|
|
void DiveListView::endSearch()
|
|
|
|
{
|
|
|
|
searchBox.clear();
|
|
|
|
searchBox.hide();
|
|
|
|
}
|