summaryrefslogtreecommitdiffstats
path: root/qt-ui
diff options
context:
space:
mode:
Diffstat (limited to 'qt-ui')
-rw-r--r--qt-ui/about.cpp4
-rw-r--r--qt-ui/completionmodels.cpp14
-rw-r--r--qt-ui/configuredivecomputerdialog.cpp2
-rw-r--r--qt-ui/divecomponentselection.ui57
-rw-r--r--qt-ui/divecomputermanagementdialog.cpp1
-rw-r--r--qt-ui/divelistview.cpp172
-rw-r--r--qt-ui/divelistview.h5
-rw-r--r--qt-ui/divelogexportdialog.cpp18
-rw-r--r--qt-ui/divelogexportdialog.ui61
-rw-r--r--qt-ui/divelogimportdialog.cpp67
-rw-r--r--qt-ui/divelogimportdialog.h1
-rw-r--r--qt-ui/divepicturewidget.cpp120
-rw-r--r--qt-ui/divepicturewidget.h22
-rw-r--r--qt-ui/diveplanner.cpp138
-rw-r--r--qt-ui/diveplanner.h20
-rw-r--r--qt-ui/diveshareexportdialog.cpp3
-rw-r--r--qt-ui/downloadfromdivecomputer.cpp2
-rw-r--r--qt-ui/filtermodels.cpp17
-rw-r--r--qt-ui/filtermodels.h2
-rw-r--r--qt-ui/globe.cpp59
-rw-r--r--qt-ui/globe.h2
-rw-r--r--qt-ui/locationInformation.ui149
-rw-r--r--qt-ui/locationinformation.cpp241
-rw-r--r--qt-ui/locationinformation.h50
-rw-r--r--qt-ui/maintab.cpp235
-rw-r--r--qt-ui/maintab.h13
-rw-r--r--qt-ui/maintab.ui381
-rw-r--r--qt-ui/mainwindow.cpp548
-rw-r--r--qt-ui/mainwindow.h38
-rw-r--r--qt-ui/mainwindow.ui241
-rw-r--r--qt-ui/modeldelegates.cpp7
-rw-r--r--qt-ui/models.cpp30
-rw-r--r--qt-ui/notificationwidget.cpp37
-rw-r--r--qt-ui/notificationwidget.h31
-rw-r--r--qt-ui/plannerDetails.ui89
-rw-r--r--qt-ui/plannerSettings.ui115
-rw-r--r--qt-ui/preferences.cpp6
-rw-r--r--qt-ui/preferences.ui283
-rw-r--r--qt-ui/printlayout.cpp5
-rw-r--r--qt-ui/profile/divecartesianaxis.cpp2
-rw-r--r--qt-ui/profile/diveeventitem.cpp4
-rw-r--r--qt-ui/profile/diveprofileitem.cpp48
-rw-r--r--qt-ui/profile/divetextitem.cpp1
-rw-r--r--qt-ui/profile/profilewidget2.cpp43
-rw-r--r--qt-ui/profile/ruleritem.cpp8
-rw-r--r--qt-ui/qtwaitingspinner.cpp288
-rw-r--r--qt-ui/qtwaitingspinner.h103
-rw-r--r--qt-ui/shiftimagetimes.ui25
-rw-r--r--qt-ui/simplewidgets.cpp102
-rw-r--r--qt-ui/simplewidgets.h16
-rw-r--r--qt-ui/socialnetworks.cpp2
-rw-r--r--qt-ui/subsurfacewebservices.cpp99
-rw-r--r--qt-ui/tableview.cpp5
-rw-r--r--qt-ui/tableview.h1
-rw-r--r--qt-ui/tagwidget.cpp1
-rw-r--r--qt-ui/undocommands.cpp123
-rw-r--r--qt-ui/undocommands.h50
-rw-r--r--qt-ui/updatemanager.cpp13
-rw-r--r--qt-ui/urldialog.ui91
-rw-r--r--qt-ui/usersurvey.cpp30
-rw-r--r--qt-ui/usersurvey.h1
61 files changed, 3244 insertions, 1098 deletions
diff --git a/qt-ui/about.cpp b/qt-ui/about.cpp
index 361031599..203357010 100644
--- a/qt-ui/about.cpp
+++ b/qt-ui/about.cpp
@@ -1,5 +1,5 @@
#include "about.h"
-#include "ssrf-version.h"
+#include "version.h"
#include <QDesktopServices>
#include <QUrl>
#include <QShortcut>
@@ -9,7 +9,7 @@ SubsurfaceAbout::SubsurfaceAbout(QWidget *parent, Qt::WindowFlags f) : QDialog(p
ui.setupUi(this);
setWindowModality(Qt::ApplicationModal);
- QString versionString(GIT_VERSION_STRING);
+ QString versionString(subsurface_git_version());
QStringList readableVersions = QStringList() << "4.3.950" << "4.4 Beta 1" <<
"4.3.960" << "4.4 Beta 2" <<
"4.3.970" << "4.4 Beta 3";
diff --git a/qt-ui/completionmodels.cpp b/qt-ui/completionmodels.cpp
index fd3cc7504..f2e70afd1 100644
--- a/qt-ui/completionmodels.cpp
+++ b/qt-ui/completionmodels.cpp
@@ -40,9 +40,21 @@
CREATE_CSV_UPDATE_METHOD(BuddyCompletionModel, buddy);
CREATE_CSV_UPDATE_METHOD(DiveMasterCompletionModel, divemaster);
-CREATE_UPDATE_METHOD(LocationCompletionModel, location);
CREATE_UPDATE_METHOD(SuitCompletionModel, suit);
+void LocationCompletionModel::updateModel()
+{
+ QStringList list;
+ struct dive_site *ds;
+ int i = 0;
+ for_each_dive_site(i, ds) {
+ if (!list.contains(ds->name))
+ list.append(ds->name);
+ }
+ std::sort(list.begin(), list.end());
+ setStringList(list);
+}
+
void TagCompletionModel::updateModel()
{
if (g_tag_list == NULL)
diff --git a/qt-ui/configuredivecomputerdialog.cpp b/qt-ui/configuredivecomputerdialog.cpp
index 95bc0f882..172a1a480 100644
--- a/qt-ui/configuredivecomputerdialog.cpp
+++ b/qt-ui/configuredivecomputerdialog.cpp
@@ -2,6 +2,8 @@
#include "helpers.h"
#include "mainwindow.h"
+#include "display.h"
+
#include <QFileDialog>
#include <QMessageBox>
#include <QSettings>
diff --git a/qt-ui/divecomponentselection.ui b/qt-ui/divecomponentselection.ui
index dbd0839ba..7eade039b 100644
--- a/qt-ui/divecomponentselection.ui
+++ b/qt-ui/divecomponentselection.ui
@@ -9,8 +9,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>308</width>
- <height>263</height>
+ <width>401</width>
+ <height>317</height>
</rect>
</property>
<property name="sizePolicy">
@@ -41,9 +41,9 @@
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
- <widget class="QCheckBox" name="location">
+ <widget class="QCheckBox" name="divesite">
<property name="text">
- <string>Location</string>
+ <string>Dive site</string>
</property>
</widget>
</item>
@@ -54,34 +54,6 @@
</property>
</widget>
</item>
- <item row="1" column="0">
- <widget class="QCheckBox" name="gps">
- <property name="text">
- <string>GPS coordinates</string>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QCheckBox" name="divemaster">
- <property name="text">
- <string>Divemaster</string>
- </property>
- </widget>
- </item>
- <item row="3" column="0">
- <widget class="QCheckBox" name="buddy">
- <property name="text">
- <string>Buddy</string>
- </property>
- </widget>
- </item>
- <item row="4" column="0">
- <widget class="QCheckBox" name="rating">
- <property name="text">
- <string>Rating</string>
- </property>
- </widget>
- </item>
<item row="5" column="0">
<widget class="QCheckBox" name="visibility">
<property name="text">
@@ -117,6 +89,27 @@
</property>
</widget>
</item>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="divemaster">
+ <property name="text">
+ <string>Divemaster</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="buddy">
+ <property name="text">
+ <string>Buddy</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QCheckBox" name="rating">
+ <property name="text">
+ <string>Rating</string>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
diff --git a/qt-ui/divecomputermanagementdialog.cpp b/qt-ui/divecomputermanagementdialog.cpp
index 742facdcb..552f6058f 100644
--- a/qt-ui/divecomputermanagementdialog.cpp
+++ b/qt-ui/divecomputermanagementdialog.cpp
@@ -1,6 +1,7 @@
#include "divecomputermanagementdialog.h"
#include "mainwindow.h"
#include "helpers.h"
+#include "models.h"
#include <QMessageBox>
#include <QShortcut>
diff --git a/qt-ui/divelistview.cpp b/qt-ui/divelistview.cpp
index d4e744237..16033dca9 100644
--- a/qt-ui/divelistview.cpp
+++ b/qt-ui/divelistview.cpp
@@ -4,13 +4,22 @@
* classes for the divelist of Subsurface
*
*/
-#include "divelistview.h"
#include "filtermodels.h"
#include "modeldelegates.h"
#include "mainwindow.h"
+#include "divepicturewidget.h"
+#include "display.h"
+#include <unistd.h>
#include <QSettings>
+#include <QKeyEvent>
#include <QFileDialog>
+#include <QNetworkAccessManager>
+#include <QNetworkReply>
+#include <QStandardPaths>
+#include <QMessageBox>
#include "qthelper.h"
+#include "undocommands.h"
+#include "divelistview.h"
// # Date Rtg Dpth Dur Tmp Wght Suit Cyl Gas SAC OTU CNS Loc
static int defaultWidth[] = { 70, 140, 90, 50, 50, 50, 50, 70, 50, 50, 70, 50, 50, 500};
@@ -30,6 +39,7 @@ DiveListView::DiveListView(QWidget *parent) : QTreeView(parent), mouseClickSelec
setSortingEnabled(false);
setContextMenuPolicy(Qt::DefaultContextMenu);
+ setSelectionMode(ExtendedSelection);
header()->setContextMenuPolicy(Qt::ActionsContextMenu);
const QFontMetrics metrics(defaultModelFont());
@@ -39,7 +49,7 @@ DiveListView::DiveListView(QWidget *parent) : QTreeView(parent), mouseClickSelec
// Fixes for the layout needed for mac
#ifdef Q_OS_MAC
int ht = metrics.height();
- header()->setMinimumHeight(ht + 10);
+ header()->setMinimumHeight(ht + 4);
#endif
// TODO FIXME we need this to get the header names
@@ -84,6 +94,8 @@ DiveListView::DiveListView(QWidget *parent) : QTreeView(parent), mouseClickSelec
header()->setStretchLastSection(true);
+
+ installEventFilter(this);
}
DiveListView::~DiveListView()
@@ -175,8 +187,13 @@ void DiveListView::rememberSelection()
if (index.column() != 0) // We only care about the dives, so, let's stick to rows and discard columns.
continue;
struct dive *d = (struct dive *)index.data(DiveTripModel::DIVE_ROLE).value<void *>();
- if (d)
+ if (d) {
selectedDives.insert(d->divetrip, get_divenr(d));
+ } else {
+ struct dive_trip *t = (struct dive_trip *)index.data(DiveTripModel::TRIP_ROLE).value<void *>();
+ if (t)
+ selectedDives.insert(t, -1);
+ }
}
selectionSaved = true;
}
@@ -195,8 +212,10 @@ void DiveListView::restoreSelection()
QList<int> selectedDivesOnTrip = selectedDives.values(trip);
// Only select trip if all of its dives were selected
- if (trip != NULL && divesOnTrip.count() == selectedDivesOnTrip.count())
+ if(selectedDivesOnTrip.contains(-1)) {
selectTrip(trip);
+ selectedDivesOnTrip.removeAll(-1);
+ }
selectDives(selectedDivesOnTrip);
}
}
@@ -343,6 +362,10 @@ bool DiveListView::eventFilter(QObject *, QEvent *event)
if (event->type() != QEvent::KeyPress)
return false;
QKeyEvent *keyEv = static_cast<QKeyEvent *>(event);
+ if (keyEv->key() == Qt::Key_Delete) {
+ contextMenuIndex = currentIndex();
+ deleteDive();
+ }
if (keyEv->key() != Qt::Key_Escape)
return false;
return true;
@@ -492,6 +515,8 @@ void DiveListView::toggleColumnVisibilityByIndex()
void DiveListView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
{
+ if (!isVisible())
+ return;
if (!current.isValid())
return;
scrollTo(current);
@@ -542,15 +567,32 @@ void DiveListView::selectionChanged(const QItemSelection &selected, const QItemS
Q_EMIT currentDiveChanged(selected_dive);
}
-static bool can_merge(const struct dive *a, const struct dive *b)
+enum asked_user {NOTYET, MERGE, DONTMERGE};
+
+static bool can_merge(const struct dive *a, const struct dive *b, enum asked_user *have_asked)
{
if (!a || !b)
return false;
if (a->when > b->when)
return false;
/* Don't merge dives if there's more than half an hour between them */
- if (a->when + a->duration.seconds + 30 * 60 < b->when)
- return false;
+ if (a->when + a->duration.seconds + 30 * 60 < b->when) {
+ if (*have_asked == NOTYET) {
+ if (QMessageBox::warning(MainWindow::instance(),
+ MainWindow::instance()->tr("Warning"),
+ MainWindow::instance()->tr("Trying to merge dives with %1min interval in between").arg(
+ (b->when - a->when - a->duration.seconds) / 60),
+ QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) {
+ *have_asked = DONTMERGE;
+ return false;
+ } else {
+ *have_asked = MERGE;
+ return true;
+ }
+ } else {
+ return *have_asked == MERGE ? true : false;
+ }
+ }
return true;
}
@@ -558,10 +600,11 @@ void DiveListView::mergeDives()
{
int i;
struct dive *dive, *maindive = NULL;
+ enum asked_user have_asked = NOTYET;
for_each_dive (i, dive) {
if (dive->selected) {
- if (!can_merge(maindive, dive)) {
+ if (!can_merge(maindive, dive, &have_asked)) {
maindive = dive;
} else {
maindive = merge_two_dives(maindive, dive);
@@ -611,10 +654,14 @@ void DiveListView::removeFromTrip()
//TODO: move this to C-code.
int i;
struct dive *d;
+ QMap<struct dive*, dive_trip*> divesToRemove;
for_each_dive (i, d) {
if (d->selected)
- remove_dive_from_trip(d, false);
+ divesToRemove.insert(d, d->divetrip);
}
+ UndoRemoveDivesFromTrip *undoCommand = new UndoRemoveDivesFromTrip(divesToRemove);
+ MainWindow::instance()->undoStack->push(undoCommand);
+
rememberSelection();
reload(currentLayout, false);
fixMessyQtModelBehaviour();
@@ -721,13 +768,19 @@ void DiveListView::deleteDive()
// so instead of using the for_each_dive macro I'm using an explicit for loop
// to make this easier to understand
int lastDiveNr = -1;
+ QList<struct dive*> deletedDives; //a list of all deleted dives to be stored in the undo command
for_each_dive (i, d) {
if (!d->selected)
continue;
+ struct dive* undo_entry = alloc_dive();
+ copy_dive(get_dive(i), undo_entry);
+ deletedDives.append(undo_entry);
delete_single_dive(i);
i--; // so the next dive isn't skipped... it's now #i
lastDiveNr = i;
}
+ UndoDeleteDive *undoEntry = new UndoDeleteDive(deletedDives);
+ MainWindow::instance()->undoStack->push(undoEntry);
if (amount_selected == 0) {
MainWindow::instance()->cleanUpEmpty();
}
@@ -765,9 +818,32 @@ void DiveListView::contextMenuEvent(QContextMenuEvent *event)
dive_trip_t *trip = (dive_trip_t *)contextMenuIndex.data(DiveTripModel::TRIP_ROLE).value<void *>();
QMenu popup(this);
if (currentLayout == DiveTripModel::TREE) {
- popup.addAction(tr("Expand all"), this, SLOT(expandAll()));
- popup.addAction(tr("Collapse all"), this, SLOT(collapseAll()));
- collapseAction = popup.addAction(tr("Collapse others"), this, SLOT(collapseAll()));
+ // verify if there is a node that`s not expanded.
+ bool needs_expand = false;
+ bool needs_collapse = false;
+ uint expanded_nodes = 0;
+ for(int i = 0, end = model()->rowCount(); i < end; i++) {
+ QModelIndex idx = model()->index(i, 0);
+ if (idx.data(DiveTripModel::DIVE_ROLE).value<void *>())
+ continue;
+
+ if (!isExpanded(idx)) {
+ needs_expand = true;
+ } else {
+ needs_collapse = true;
+ expanded_nodes ++;
+ }
+ }
+ if (needs_expand)
+ popup.addAction(tr("Expand all"), this, SLOT(expandAll()));
+ if (needs_collapse)
+ popup.addAction(tr("Collapse all"), this, SLOT(collapseAll()));
+
+ // verify if there`s a need for collapse others
+ if (expanded_nodes > 1)
+ collapseAction = popup.addAction(tr("Collapse others"), this, SLOT(collapseAll()));
+
+
if (d) {
popup.addAction(tr("Remove dive(s) from trip"), this, SLOT(removeFromTrip()));
popup.addAction(tr("Create new trip above"), this, SLOT(newTripAbove()));
@@ -804,8 +880,9 @@ void DiveListView::contextMenuEvent(QContextMenuEvent *event)
popup.addAction(tr("Merge selected dives"), this, SLOT(mergeDives()));
if (amount_selected >= 1) {
popup.addAction(tr("Renumber dive(s)"), this, SLOT(renumberDives()));
- popup.addAction(tr("Shift times"), this, SLOT(shiftTimes()));
- popup.addAction(tr("Load images"), this, SLOT(loadImages()));
+ popup.addAction(tr("Shift dive times"), this, SLOT(shiftTimes()));
+ popup.addAction(tr("Load image(s) from file(s)"), this, SLOT(loadImages()));
+ popup.addAction(tr("Load image(s) from web"), this, SLOT(loadWebImages()));
}
// "collapse all" really closes all trips,
@@ -831,11 +908,16 @@ void DiveListView::loadImages()
QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Open image files"), lastUsedImageDir(), tr("Image files (*.jpg *.jpeg *.pnm *.tif *.tiff)"));
if (fileNames.isEmpty())
return;
-
updateLastUsedImageDir(QFileInfo(fileNames[0]).dir().path());
- ShiftImageTimesDialog shiftDialog(this);
+ matchImagesToDives(fileNames);
+}
+
+void DiveListView::matchImagesToDives(QStringList fileNames)
+{
+ ShiftImageTimesDialog shiftDialog(this, fileNames);
shiftDialog.setOffset(lastImageTimeOffset());
- shiftDialog.exec();
+ if (!shiftDialog.exec())
+ return;
updateLastImageTimeOffset(shiftDialog.amount());
Q_FOREACH (const QString &fileName, fileNames) {
@@ -844,7 +926,7 @@ void DiveListView::loadImages()
for_each_dive (j, dive) {
if (!dive->selected)
continue;
- dive_create_picture(dive, qstrdup(fileName.toUtf8().data()), shiftDialog.amount());
+ dive_create_picture(dive, copy_string(fileName.toUtf8().data()), shiftDialog.amount());
}
}
@@ -853,6 +935,60 @@ void DiveListView::loadImages()
DivePictureModel::instance()->updateDivePictures();
}
+void DiveListView::loadWebImages()
+{
+ URLDialog urlDialog(this);
+ if (!urlDialog.exec())
+ return;
+ loadImageFromURL(QUrl::fromUserInput(urlDialog.url()));
+
+}
+
+void DiveListView::loadImageFromURL(QUrl url)
+{
+ if (url.isValid()) {
+ QEventLoop loop;
+ QNetworkRequest request(url);
+ QNetworkReply *reply = manager.get(request);
+ while (reply->isRunning()) {
+ loop.processEvents();
+ sleep(1);
+ }
+ QByteArray imageData = reply->readAll();
+
+ QImage image = QImage();
+ image.loadFromData(imageData);
+ if (image.isNull())
+ // If this is not an image, maybe it's an html file and Miika can provide some xslr magic to extract images.
+ // In this case we would call the function recursively on the list of image source urls;
+ return;
+
+ // Since we already downloaded the image we can cache it as well.
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ hash.addData(imageData);
+ QString path = QStandardPaths::standardLocations(QStandardPaths::CacheLocation).first();
+ QDir dir(path);
+ if (!dir.exists())
+ dir.mkpath(path);
+ QFile imageFile(path.append("/").append(hash.result().toHex()));
+ if (imageFile.open(QIODevice::WriteOnly)) {
+ QDataStream stream(&imageFile);
+ stream.writeRawData(imageData.data(), imageData.length());
+ imageFile.waitForBytesWritten(-1);
+ imageFile.close();
+ add_hash(imageFile.fileName(), hash.result());
+ struct picture picture;
+ picture.hash = NULL;
+ picture.filename = strdup(url.toString().toUtf8().data());
+ learnHash(&picture, hash.result());
+ matchImagesToDives(QStringList(url.toString()));
+ }
+ }
+
+
+}
+
+
QString DiveListView::lastUsedImageDir()
{
QSettings settings;
diff --git a/qt-ui/divelistview.h b/qt-ui/divelistview.h
index a6522fa9a..6e9a18215 100644
--- a/qt-ui/divelistview.h
+++ b/qt-ui/divelistview.h
@@ -13,6 +13,7 @@
#include <QTreeView>
#include <QLineEdit>
+#include <QNetworkAccessManager>
#include "models.h"
class DiveListView : public QTreeView {
@@ -51,6 +52,7 @@ slots:
void renumberDives();
void shiftTimes();
void loadImages();
+ void loadWebImages();
static QString lastUsedImageDir();
signals:
@@ -78,6 +80,9 @@ private:
void updateLastImageTimeOffset(int offset);
int lastImageTimeOffset();
void addToTrip(int delta);
+ void matchImagesToDives(QStringList fileNames);
+ void loadImageFromURL(QUrl url);
+ QNetworkAccessManager manager;
};
#endif // DIVELISTVIEW_H
diff --git a/qt-ui/divelogexportdialog.cpp b/qt-ui/divelogexportdialog.cpp
index 43c41550f..12a8c320f 100644
--- a/qt-ui/divelogexportdialog.cpp
+++ b/qt-ui/divelogexportdialog.cpp
@@ -9,6 +9,7 @@
#include "subsurfacewebservices.h"
#include "worldmap-save.h"
#include "save-html.h"
+#include "mainwindow.h"
#define GET_UNIT(name, field, f, t) \
v = settings.value(QString(name)); \
@@ -55,6 +56,9 @@ DiveLogExportDialog::DiveLogExportDialog(QWidget *parent) : QDialog(parent),
if (settings.contains("listOnly")) {
ui->exportListOnly->setChecked(settings.value("listOnly").toBool());
}
+ if (settings.contains("exportPhotos")) {
+ ui->exportPhotos->setChecked(settings.value("exportPhotos").toBool());
+ }
settings.endGroup();
}
@@ -104,8 +108,12 @@ void DiveLogExportDialog::exportHtmlInit(const QString &filename)
QString json_settings = exportFiles + QDir::separator() + "settings.js";
QString translation = exportFiles + QDir::separator() + "translation.js";
QString stat_file = exportFiles + QDir::separator() + "stat.js";
- QString photos_directory = exportFiles + QDir::separator() + "photos" + QDir::separator();
- mainDir.mkdir(photos_directory);
+
+ QString photos_directory;
+ if (ui->exportPhotos->isChecked()) {
+ photos_directory = exportFiles + QDir::separator() + "photos" + QDir::separator();
+ mainDir.mkdir(photos_directory);
+ }
exportFiles += "/";
exportHTMLsettings(json_settings);
@@ -142,6 +150,7 @@ void DiveLogExportDialog::exportHTMLsettings(const QString &filename)
settings.setValue("subsurfaceNumbers", ui->exportSubsurfaceNumber->isChecked());
settings.setValue("yearlyStatistics", ui->exportStatistics->isChecked());
settings.setValue("listOnly", ui->exportListOnly->isChecked());
+ settings.setValue("exportPhotos", ui->exportPhotos->isChecked());
settings.endGroup();
QString fontSize = ui->fontSizeSelection->currentText();
@@ -312,8 +321,11 @@ void DiveLogExportDialog::on_buttonBox_accepted()
settings.setValue("LastDir", fileInfo.dir().path());
settings.endGroup();
// the non XSLT exports are called directly above, the XSLT based ons are called here
- if (!stylesheet.isEmpty())
+ if (!stylesheet.isEmpty()) {
future = QtConcurrent::run(export_dives_xslt, filename.toUtf8(), ui->exportSelected->isChecked(), ui->CSVUnits_2->currentIndex(), stylesheet.toUtf8());
+ MainWindow::instance()->getNotificationWidget()->showNotification(tr("Please Wait, Exporting..."), KMessageWidget::Information);
+ MainWindow::instance()->getNotificationWidget()->setFuture(future);
+ }
}
}
diff --git a/qt-ui/divelogexportdialog.ui b/qt-ui/divelogexportdialog.ui
index 7514a6551..1f3675ef4 100644
--- a/qt-ui/divelogexportdialog.ui
+++ b/qt-ui/divelogexportdialog.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>507</width>
- <height>423</height>
+ <height>468</height>
</rect>
</property>
<property name="windowTitle">
@@ -233,9 +233,9 @@
</layout>
</widget>
<widget class="QGroupBox" name="groupBox">
- <property name="enabled">
- <bool>false</bool>
- </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
<property name="geometry">
<rect>
<x>0</x>
@@ -338,13 +338,23 @@
</attribute>
</widget>
</item>
- <item row="2" column="0">
+ <item row="3" column="0">
<widget class="QCheckBox" name="exportListOnly">
<property name="text">
<string>Export list only</string>
</property>
</widget>
</item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="exportPhotos">
+ <property name="text">
+ <string>Export photos</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
</item>
@@ -363,6 +373,9 @@
<bool>false</bool>
</property>
<layout class="QFormLayout" name="formLayout">
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
+ </property>
<item row="0" column="0">
<widget class="QLabel" name="fontLabel">
<property name="text">
@@ -513,20 +526,40 @@
</hints>
</connection>
<connection>
- <sender>exportCSV</sender>
- <signal>toggled(bool)</signal>
- <receiver>groupBox</receiver>
- <slot>setEnabled(bool)</slot>
+ <sender>exportCSV</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>groupBox</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>20</x>
+ <y>20</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>20</x>
+ <y>20</y>
+ </hint>
+ </hints>
</connection>
<connection>
- <sender>exportCSVDetails</sender>
- <signal>toggled(bool)</signal>
- <receiver>groupBox</receiver>
- <slot>setEnabled(bool)</slot>
+ <sender>exportCSVDetails</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>groupBox</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>20</x>
+ <y>20</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>20</x>
+ <y>20</y>
+ </hint>
+ </hints>
</connection>
</connections>
<buttongroups>
- <buttongroup name="exportGroup"/>
<buttongroup name="buttonGroup"/>
+ <buttongroup name="exportGroup"/>
</buttongroups>
</ui>
diff --git a/qt-ui/divelogimportdialog.cpp b/qt-ui/divelogimportdialog.cpp
index bb4701ce3..409064833 100644
--- a/qt-ui/divelogimportdialog.cpp
+++ b/qt-ui/divelogimportdialog.cpp
@@ -1,5 +1,6 @@
#include "divelogimportdialog.h"
#include "mainwindow.h"
+#include "color.h"
#include "ui_divelogimportdialog.h"
#include <QShortcut>
#include <QDrag>
@@ -23,7 +24,7 @@ const DiveLogImportDialog::CSVAppConfig DiveLogImportDialog::CSVApps[CSVAPPS] =
ColumnNameProvider::ColumnNameProvider(QObject *parent) : QAbstractListModel(parent)
{
columnNames << tr("Dive #") << tr("Date") << tr("Time") << tr("Duration") << tr("Location") << tr("GPS") << tr("Weight") << tr("Cyl. size") << tr("Start pressure") <<
- tr("End pressure") << tr("Max. depth") << tr("Avg. depth") << tr("Divemaster") << tr("Buddy") << tr("Notes") << tr("Tags") << tr("Air temp.") << tr("Water temp.") <<
+ tr("End pressure") << tr("Max. depth") << tr("Avg. depth") << tr("Divemaster") << tr("Buddy") << tr("Suit") << tr("Notes") << tr("Tags") << tr("Air temp.") << tr("Water temp.") <<
tr("Oâ‚‚") << tr("He") << tr("Sample time") << tr("Sample depth") << tr("Sample temperature") << tr("Sample pOâ‚‚") << tr("Sample CNS") << tr("Sample NDL") <<
tr("Sample TTS") << tr("Sample stopdepth") << tr("Sample pressure");
}
@@ -315,6 +316,7 @@ DiveLogImportDialog::DiveLogImportDialog(QStringList fn, QWidget *parent) : QDia
ui->setupUi(this);
fileNames = fn;
column = 0;
+ delta = "0";
/* Add indexes of XSLTs requiring special handling to the list */
specialCSV << 3;
@@ -374,7 +376,53 @@ void DiveLogImportDialog::loadFileContents(int value, whatChanged triggeredBy)
QString firstLine = f.readLine();
if (firstLine.contains("SEABEAR")) {
seabear = true;
- firstLine = "Sample time;Sample depth;Sample NDL;Sample TTS;Sample stopdepth;Sample temperature;Sample pressure";
+
+ /*
+ * Parse header - currently only interested in sample
+ * interval, or if we have old format (if interval value
+ * is missing from the header).
+ */
+
+ while ((firstLine = f.readLine()).length() > 3 && !f.atEnd()) {
+ if (firstLine.contains("//Log interval: "))
+ delta = firstLine.remove(QString::fromLatin1("//Log interval: ")).trimmed();
+ }
+
+ /*
+ * Parse CSV fields
+ * The pO2 values from CCR diving are ignored later on.
+ */
+
+ firstLine = f.readLine().trimmed();
+
+ currColumns = firstLine.split(';');
+ Q_FOREACH (QString columnText, currColumns) {
+ if (columnText == "Time") {
+ headers.append("Sample time");
+ } else if (columnText == "Depth") {
+ headers.append("Sample depth");
+ } else if (columnText == "Temperature") {
+ headers.append("Sample temperature");
+ } else if (columnText == "NDT") {
+ headers.append("Sample NDL");
+ } else if (columnText == "TTS") {
+ headers.append("Sample TTS");
+ } else if (columnText == "pO2_1") {
+ headers.append("Sample pO2_1");
+ } else if (columnText == "pO2_2") {
+ headers.append("Sample pO2_2");
+ } else if (columnText == "pO2_3") {
+ headers.append("Sample pO2_3");
+ } else if (columnText == "Ceiling") {
+ headers.append("Sample ceiling");
+ } else {
+ // We do not know about this value
+ qDebug() << "Seabear import found an un-handled field: " << columnText;
+ headers.append("");
+ }
+ }
+
+ firstLine = headers.join(";");
blockSignals(true);
ui->knownImports->setCurrentText("Seabear CSV");
blockSignals(false);
@@ -513,7 +561,7 @@ void DiveLogImportDialog::loadFileContents(int value, whatChanged triggeredBy)
}
while (rows < 10 && !f.atEnd()) {
- QString currLine = f.readLine();
+ QString currLine = f.readLine().trimmed();
currColumns = currLine.split(separator);
fileColumns.append(currColumns);
rows += 1;
@@ -526,6 +574,7 @@ void DiveLogImportDialog::loadFileContents(int value, whatChanged triggeredBy)
void DiveLogImportDialog::on_buttonBox_accepted()
{
+ imported_via_xslt = true;
QStringList r = resultModel->result();
if (ui->knownImports->currentText() != "Manual import") {
for (int i = 0; i < fileNames.size(); ++i) {
@@ -542,10 +591,12 @@ void DiveLogImportDialog::on_buttonBox_accepted()
r.indexOf(tr("Sample pressure")),
ui->CSVSeparator->currentIndex(),
specialCSV.contains(ui->knownImports->currentIndex()) ? CSVApps[ui->knownImports->currentIndex()].name.toUtf8().data() : "csv",
- ui->CSVUnits->currentIndex()
- ) < 0)
+ ui->CSVUnits->currentIndex(),
+ delta.toUtf8().data()
+ ) < 0) {
+ imported_via_xslt = false;
return;
-
+ }
// Seabear CSV stores NDL and TTS in Minutes, not seconds
struct dive *dive = dive_table.dives[dive_table.nr - 1];
for(int s_nr = 0 ; s_nr <= dive->dc.samples ; s_nr++) {
@@ -585,9 +636,10 @@ void DiveLogImportDialog::on_buttonBox_accepted()
r.indexOf(tr("Location")),
r.indexOf(tr("GPS")),
r.indexOf(tr("Max. depth")),
- r.indexOf(tr("Mean depth")),
+ r.indexOf(tr("Avg. depth")),
r.indexOf(tr("Divemaster")),
r.indexOf(tr("Buddy")),
+ r.indexOf(tr("Suit")),
r.indexOf(tr("Notes")),
r.indexOf(tr("Weight")),
r.indexOf(tr("Tags")),
@@ -618,6 +670,7 @@ void DiveLogImportDialog::on_buttonBox_accepted()
}
process_dives(true, false);
MainWindow::instance()->refreshDisplay();
+ imported_via_xslt = false;
}
TagDragDelegate::TagDragDelegate(QObject *parent) : QStyledItemDelegate(parent)
diff --git a/qt-ui/divelogimportdialog.h b/qt-ui/divelogimportdialog.h
index 9281b2b10..e7d068a81 100644
--- a/qt-ui/divelogimportdialog.h
+++ b/qt-ui/divelogimportdialog.h
@@ -95,6 +95,7 @@ private:
QList<int> specialCSV;
int column;
ColumnNameResult *resultModel;
+ QString delta;
struct CSVAppConfig {
QString name;
diff --git a/qt-ui/divepicturewidget.cpp b/qt-ui/divepicturewidget.cpp
index 92695b6a6..8c1cb3571 100644
--- a/qt-ui/divepicturewidget.cpp
+++ b/qt-ui/divepicturewidget.cpp
@@ -2,8 +2,90 @@
#include "metrics.h"
#include "dive.h"
#include "divelist.h"
+#include <unistd.h>
#include <QtConcurrentMap>
+#include <QtConcurrentRun>
+#include <QFuture>
#include <QDir>
+#include <QCryptographicHash>
+#include <QNetworkAccessManager>
+#include <QNetworkReply>
+#include <mainwindow.h>
+#include <qthelper.h>
+#include <QStandardPaths>
+
+void loadPicuture(struct picture *picture)
+{
+ ImageDownloader download(picture);
+ download.load();
+}
+
+SHashedImage::SHashedImage(struct picture *picture) : QImage()
+{
+ QUrl url = QUrl::fromUserInput(QString(picture->filename));
+ if(url.isLocalFile())
+ load(url.toLocalFile());
+ if (isNull()) {
+ // Hash lookup.
+ load(fileFromHash(picture->hash));
+ if (!isNull()) {
+ QtConcurrent::run(updateHash, picture);
+ } else {
+ QtConcurrent::run(loadPicuture, picture);
+ }
+ } else {
+ QByteArray hash = hashFile(url.toLocalFile());
+ free(picture->hash);
+ picture->hash = strdup(hash.toHex().data());
+ }
+}
+
+ImageDownloader::ImageDownloader(struct picture *pic)
+{
+ picture = pic;
+}
+
+void ImageDownloader::load(){
+ QUrl url = QUrl::fromUserInput(QString(picture->filename));
+ if (url.isValid()) {
+ QEventLoop loop;
+ QNetworkRequest request(url);
+ connect(&manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(saveImage(QNetworkReply *)));
+ QNetworkReply *reply = manager.get(request);
+ while (reply->isRunning()) {
+ loop.processEvents();
+ sleep(1);
+ }
+ }
+
+}
+
+void ImageDownloader::saveImage(QNetworkReply *reply)
+{
+ QByteArray imageData = reply->readAll();
+ QImage image = QImage();
+ image.loadFromData(imageData);
+ if (image.isNull())
+ return;
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ hash.addData(imageData);
+ QString path = QStandardPaths::standardLocations(QStandardPaths::CacheLocation).first();
+ QDir dir(path);
+ if (!dir.exists())
+ dir.mkpath(path);
+ QFile imageFile(path.append("/").append(hash.result().toHex()));
+ if (imageFile.open(QIODevice::WriteOnly)) {
+ QDataStream stream(&imageFile);
+ stream.writeRawData(imageData.data(), imageData.length());
+ imageFile.waitForBytesWritten(-1);
+ imageFile.close();
+ add_hash(imageFile.fileName(), hash.result());
+ learnHash(picture, hash.result());
+ DivePictureModel::instance()->updateDivePictures();
+ }
+ reply->manager()->deleteLater();
+ reply->deleteLater();
+}
DivePictureModel *DivePictureModel::instance()
{
@@ -15,25 +97,36 @@ DivePictureModel::DivePictureModel() : numberOfPictures(0)
{
}
-typedef QPair<QString, QImage> SPixmap;
-typedef QList<SPixmap> SPixmapList;
+typedef struct picture *picturepointer;
+typedef QPair<picturepointer, QImage> SPixmap;
+typedef QList<struct picture *> SPictureList;
-SPixmap scaleImages(const QString &s)
+SPixmap scaleImages(picturepointer picture)
{
static QHash <QString, QImage > cache;
SPixmap ret;
- ret.first = s;
- if (cache.contains(s)) {
- ret.second = cache.value(s);
+ ret.first = picture;
+ if (cache.contains(picture->filename) && !cache.value(picture->filename).isNull()) {
+ ret.second = cache.value(picture->filename);
} else {
int dim = defaultIconMetrics().sz_pic;
- QImage p = QImage(s).scaled(dim, dim, Qt::KeepAspectRatio);
- cache.insert(s, p);
+ QImage p = SHashedImage(picture);
+ if(!p.isNull())
+ p = p.scaled(dim, dim, Qt::KeepAspectRatio);
+ cache.insert(picture->filename, p);
ret.second = p;
}
return ret;
}
+void DivePictureModel::updateDivePicturesWhenDone(QList<QFuture<void> > futures)
+{
+ Q_FOREACH (QFuture<void> f, futures) {
+ f.waitForFinished();
+ }
+ updateDivePictures();
+}
+
void DivePictureModel::updateDivePictures()
{
if (numberOfPictures != 0) {
@@ -49,14 +142,15 @@ void DivePictureModel::updateDivePictures()
}
stringPixmapCache.clear();
- QStringList pictures;
+ SPictureList pictures;
FOR_EACH_PICTURE_NON_PTR(displayed_dive) {
stringPixmapCache[QString(picture->filename)].offsetSeconds = picture->offset.seconds;
- pictures.push_back(QString(picture->filename));
+ pictures.push_back(picture);
}
- Q_FOREACH (const SPixmap &pixmap, QtConcurrent::blockingMapped<SPixmapList>(pictures, scaleImages))
- stringPixmapCache[pixmap.first].image = pixmap.second;
+ QList<SPixmap> list = QtConcurrent::blockingMapped(pictures, scaleImages);
+ Q_FOREACH (const SPixmap &pixmap, list)
+ stringPixmapCache[pixmap.first->filename].image = pixmap.second;
beginInsertRows(QModelIndex(), 0, numberOfPictures - 1);
endInsertRows();
@@ -121,5 +215,5 @@ DivePictureWidget::DivePictureWidget(QWidget *parent) : QListView(parent)
void DivePictureWidget::doubleClicked(const QModelIndex &index)
{
QString filePath = model()->data(index, Qt::DisplayPropertyRole).toString();
- emit photoDoubleClicked(filePath);
+ emit photoDoubleClicked(localFilePath(filePath));
}
diff --git a/qt-ui/divepicturewidget.h b/qt-ui/divepicturewidget.h
index aa524e1a6..2ce228daf 100644
--- a/qt-ui/divepicturewidget.h
+++ b/qt-ui/divepicturewidget.h
@@ -4,12 +4,33 @@
#include <QAbstractTableModel>
#include <QListView>
#include <QThread>
+#include <QFuture>
+#include <QNetworkReply>
+
+typedef QPair<QString, QByteArray> SHashedFilename;
struct PhotoHelper {
QImage image;
int offsetSeconds;
};
+class SHashedImage : public QImage {
+public:
+ SHashedImage(struct picture *picture);
+};
+
+class ImageDownloader : public QObject {
+ Q_OBJECT;
+public:
+ ImageDownloader(struct picture *picture);
+ void load();
+private:
+ struct picture *picture;
+ QNetworkAccessManager manager;
+private slots:
+ void saveImage(QNetworkReply *reply);
+};
+
class DivePictureModel : public QAbstractTableModel {
Q_OBJECT
public:
@@ -18,6 +39,7 @@ public:
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
void updateDivePictures();
+ void updateDivePicturesWhenDone(QList<QFuture<void> >);
void removePicture(const QString& fileUrl);
private:
diff --git a/qt-ui/diveplanner.cpp b/qt-ui/diveplanner.cpp
index 7831cc6d9..c82bc0463 100644
--- a/qt-ui/diveplanner.cpp
+++ b/qt-ui/diveplanner.cpp
@@ -3,6 +3,8 @@
#include "mainwindow.h"
#include "planner.h"
#include "helpers.h"
+#include "models.h"
+#include "profile/profilewidget2.h"
#include <QGraphicsSceneMouseEvent>
#include <QMessageBox>
@@ -42,15 +44,6 @@ void DivePlannerPointsModel::removeSelectedPoints(const QVector<int> &rows)
QVector<int> v2 = rows;
std::sort(v2.begin(), v2.end(), intLessThan);
- /*
- * If we end up having divepoints that are not within the dive
- * profile, we need to just skip the removal to prevent
- * crashing due to index out of range.
- */
-
- if (rowCount() >= divepoints.count())
- return;
-
beginRemoveRows(QModelIndex(), firstRow, rowCount() - 1);
for (int i = v2.count() - 1; i >= 0; i--) {
divepoints.remove(v2[i]);
@@ -74,7 +67,7 @@ void DivePlannerPointsModel::createSimpleDive()
if (!prefs.drop_stone_mode)
plannerModel->addStop(M_OR_FT(15, 45), 1 * 60, &gas, 0, true);
- plannerModel->addStop(M_OR_FT(15, 45), 40 * 60, &gas, 0, true);
+ plannerModel->addStop(M_OR_FT(15, 45), 20 * 60, &gas, 0, true);
if (!isPlanner()) {
plannerModel->addStop(M_OR_FT(5, 15), 42 * 60, &gas, 0, true);
plannerModel->addStop(M_OR_FT(5, 15), 45 * 60, &gas, 0, true);
@@ -99,10 +92,13 @@ void DivePlannerPointsModel::setupStartTime()
void DivePlannerPointsModel::loadFromDive(dive *d)
{
+ int depthsum = 0;
+ int samplecount = 0;
bool oldRec = recalc;
recalc = false;
CylindersModel::instance()->updateDive();
duration_t lasttime = {};
+ duration_t newtime = {};
struct gasmix gas;
free_dps(&diveplan);
diveplan.when = d->when;
@@ -111,13 +107,27 @@ void DivePlannerPointsModel::loadFromDive(dive *d)
// if it is we only add the manually entered samples as waypoints to the diveplan
// otherwise we have to add all of them
bool hasMarkedSamples = d->dc.sample[0].manually_entered;
- for (int i = 0; i < d->dc.samples - 1; i++) {
- const sample &s = d->dc.sample[i];
- if (s.time.seconds == 0 || (hasMarkedSamples && !s.manually_entered))
- continue;
- get_gas_at_time(d, &d->dc, lasttime, &gas);
- plannerModel->addStop(s.depth.mm, s.time.seconds, &gas, 0, true);
- lasttime = s.time;
+ // if this dive has more than 100 samples (so it is probably a logged dive),
+ // average samples so we end up with a total of 100 samples.
+ int plansamples = d->dc.samples <= 100 ? d->dc.samples : 100;
+ int j = 0;
+ for (int i = 0; i < plansamples - 1; i++) {
+ while (j * plansamples <= i * d->dc.samples) {
+ const sample &s = d->dc.sample[j];
+ if (s.time.seconds != 0 && (!hasMarkedSamples || s.manually_entered)) {
+ depthsum += s.depth.mm;
+ ++samplecount;
+ newtime = s.time;
+ }
+ j++;
+ }
+ if (samplecount) {
+ get_gas_at_time(d, &d->dc, lasttime, &gas);
+ plannerModel->addStop(depthsum / samplecount, newtime.seconds, &gas, 0, true);
+ lasttime = newtime;
+ depthsum = 0;
+ samplecount = 0;
+ }
}
recalc = oldRec;
emitDataChanged();
@@ -231,7 +241,7 @@ void DiveHandler::changeGas()
{
QAction *action = qobject_cast<QAction *>(sender());
QModelIndex index = plannerModel->index(parentIndex(), DivePlannerPointsModel::GAS);
- plannerModel->setData(index, action->text());
+ plannerModel->gaschange(index.sibling(index.row() + 1, index.column()), action->text());
}
void DiveHandler::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
@@ -383,6 +393,15 @@ void PlannerSettingsWidget::decoSacChanged(const double decosac)
plannerModel->setDecoSac(decosac);
}
+void PlannerSettingsWidget::disableDecoElements(bool value)
+{
+ ui.lastStop->setDisabled(value);
+ ui.backgasBreaks->setDisabled(value);
+ ui.bottompo2->setDisabled(value);
+ ui.decopo2->setDisabled(value);
+ ui.reserve_gas->setDisabled(!value);
+}
+
void DivePlannerWidget::printDecoPlan()
{
MainWindow::instance()->printPlan();
@@ -395,6 +414,14 @@ PlannerSettingsWidget::PlannerSettingsWidget(QWidget *parent, Qt::WindowFlags f)
QSettings s;
QStringList rebreater_modes;
s.beginGroup("Planner");
+ prefs.last_stop = s.value("last_stop", prefs.last_stop).toBool();
+ prefs.verbatim_plan = s.value("verbatim_plan", prefs.verbatim_plan).toBool();
+ prefs.display_duration = s.value("display_duration", prefs.display_duration).toBool();
+ prefs.display_runtime = s.value("display_runtime", prefs.display_runtime).toBool();
+ prefs.display_transitions = s.value("display_transitions", prefs.display_transitions).toBool();
+ prefs.recreational_mode = s.value("recreational_mode", prefs.recreational_mode).toBool();
+ prefs.safetystop = s.value("safetystop", prefs.safetystop).toBool();
+ prefs.reserve_gas = s.value("reserve_gas", prefs.reserve_gas).toInt();
prefs.ascrate75 = s.value("ascrate75", prefs.ascrate75).toInt();
prefs.ascrate50 = s.value("ascrate50", prefs.ascrate50).toInt();
prefs.ascratestops = s.value("ascratestops", prefs.ascratestops).toInt();
@@ -411,6 +438,14 @@ PlannerSettingsWidget::PlannerSettingsWidget(QWidget *parent, Qt::WindowFlags f)
s.endGroup();
updateUnitsUI();
+ ui.lastStop->setChecked(prefs.last_stop);
+ ui.verbatim_plan->setChecked(prefs.verbatim_plan);
+ ui.display_duration->setChecked(prefs.display_duration);
+ ui.display_runtime->setChecked(prefs.display_runtime);
+ ui.display_transitions->setChecked(prefs.display_transitions);
+ ui.recreational_mode->setChecked(prefs.recreational_mode);
+ ui.safetystop->setChecked(prefs.safetystop);
+ ui.reserve_gas->setValue(prefs.reserve_gas / 1000);
ui.bottompo2->setValue(prefs.bottompo2 / 1000.0);
ui.decopo2->setValue(prefs.decopo2 / 1000.0);
ui.backgasBreaks->setChecked(prefs.doo2breaks);
@@ -424,6 +459,9 @@ PlannerSettingsWidget::PlannerSettingsWidget(QWidget *parent, Qt::WindowFlags f)
connect(ui.display_duration, SIGNAL(toggled(bool)), plannerModel, SLOT(setDisplayDuration(bool)));
connect(ui.display_runtime, SIGNAL(toggled(bool)), plannerModel, SLOT(setDisplayRuntime(bool)));
connect(ui.display_transitions, SIGNAL(toggled(bool)), plannerModel, SLOT(setDisplayTransitions(bool)));
+ connect(ui.safetystop, SIGNAL(toggled(bool)), plannerModel, SLOT(setSafetyStop(bool)));
+ connect(ui.recreational_mode, SIGNAL(toggled(bool)), plannerModel, SLOT(setRecreationalMode(bool)));
+ connect(ui.reserve_gas, SIGNAL(valueChanged(int)), plannerModel, SLOT(setReserveGas(int)));
connect(ui.ascRate75, SIGNAL(valueChanged(int)), this, SLOT(setAscRate75(int)));
connect(ui.ascRate75, SIGNAL(valueChanged(int)), plannerModel, SLOT(emitDataChanged()));
connect(ui.ascRate50, SIGNAL(valueChanged(int)), this, SLOT(setAscRate50(int)));
@@ -445,6 +483,8 @@ PlannerSettingsWidget::PlannerSettingsWidget(QWidget *parent, Qt::WindowFlags f)
connect(ui.gflow, SIGNAL(editingFinished()), plannerModel, SLOT(triggerGFLow()));
connect(ui.backgasBreaks, SIGNAL(toggled(bool)), this, SLOT(setBackgasBreaks(bool)));
connect(ui.rebreathermode, SIGNAL(currentIndexChanged(int)), plannerModel, SLOT(setRebreatherMode(int)));
+ connect(DivePlannerPointsModel::instance(), SIGNAL(recreationChanged(bool)), this, SLOT(disableDecoElements(bool)));
+
settingsChanged();
ui.gflow->setValue(prefs.gflow);
ui.gfhigh->setValue(prefs.gfhigh);
@@ -466,6 +506,14 @@ PlannerSettingsWidget::~PlannerSettingsWidget()
{
QSettings s;
s.beginGroup("Planner");
+ s.setValue("last_stop", prefs.last_stop);
+ s.setValue("verbatim_plan", prefs.verbatim_plan);
+ s.setValue("display_duration", prefs.display_duration);
+ s.setValue("display_runtime", prefs.display_runtime);
+ s.setValue("display_transitions", prefs.display_transitions);
+ s.setValue("recreational_mode", prefs.recreational_mode);
+ s.setValue("safetystop", prefs.safetystop);
+ s.setValue("reserve_gas", prefs.reserve_gas);
s.setValue("ascrate75", prefs.ascrate75);
s.setValue("ascrate50", prefs.ascrate50);
s.setValue("ascratestops", prefs.ascratestops);
@@ -693,6 +741,18 @@ bool DivePlannerPointsModel::setData(const QModelIndex &index, const QVariant &v
return QAbstractItemModel::setData(index, value, role);
}
+void DivePlannerPointsModel::gaschange(const QModelIndex &index, QString newgas)
+{
+ int i = index.row();
+ gasmix oldgas = divepoints[i].gasmix;
+ gasmix gas = { 0 };
+ if (!validate_gas(newgas.toUtf8().data(), &gas))
+ return;
+ while (i < plannerModel->rowCount() && gasmix_distance(&oldgas, &divepoints[i].gasmix) == 0)
+ divepoints[i++].gasmix = gas;
+ emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, COLUMNS - 1));
+}
+
QVariant DivePlannerPointsModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
@@ -729,6 +789,7 @@ int DivePlannerPointsModel::rowCount(const QModelIndex &parent) const
DivePlannerPointsModel::DivePlannerPointsModel(QObject *parent) : QAbstractTableModel(parent),
mode(NOTHING),
+ recalc(false),
tempGFHigh(100),
tempGFLow(100)
{
@@ -820,30 +881,54 @@ int DivePlannerPointsModel::getSurfacePressure()
void DivePlannerPointsModel::setLastStop6m(bool value)
{
set_last_stop(value);
+ prefs.last_stop = value;
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, COLUMNS - 1));
}
void DivePlannerPointsModel::setVerbatim(bool value)
{
set_verbatim(value);
+ prefs.verbatim_plan = value;
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, COLUMNS - 1));
}
void DivePlannerPointsModel::setDisplayRuntime(bool value)
{
set_display_runtime(value);
+ prefs.display_runtime = value;
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, COLUMNS - 1));
}
void DivePlannerPointsModel::setDisplayDuration(bool value)
{
set_display_duration(value);
+ prefs.display_duration = value;
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, COLUMNS - 1));
}
void DivePlannerPointsModel::setDisplayTransitions(bool value)
{
set_display_transitions(value);
+ prefs.display_transitions = value;
+ emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, COLUMNS - 1));
+}
+
+void DivePlannerPointsModel::setRecreationalMode(bool value)
+{
+ prefs.recreational_mode = value;
+ emit recreationChanged(value);
+ emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, COLUMNS -1));
+}
+
+void DivePlannerPointsModel::setSafetyStop(bool value)
+{
+ prefs.safetystop = value;
+ emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, COLUMNS -1));
+}
+
+void DivePlannerPointsModel::setReserveGas(int reserve)
+{
+ prefs.reserve_gas = reserve * 1000;
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, COLUMNS - 1));
}
@@ -1110,7 +1195,7 @@ bool DivePlannerPointsModel::tankInUse(struct gasmix gasmix)
continue;
if (!p.entered) // removing deco gases is ok
continue;
- if (gasmix_distance(&p.gasmix, &gasmix) < 200)
+ if (gasmix_distance(&p.gasmix, &gasmix) < 100)
return true;
}
return false;
@@ -1137,7 +1222,7 @@ void DivePlannerPointsModel::tanksUpdated()
struct gasmix gas;
gas.o2.permille = oldGases.at(i).first;
gas.he.permille = oldGases.at(i).second;
- if (gasmix_distance(&gas, &p.gasmix) < 200) {
+ if (gasmix_distance(&gas, &p.gasmix) < 100) {
p.gasmix.o2.permille = gases.at(i).first;
p.gasmix.he.permille = gases.at(i).second;
}
@@ -1237,7 +1322,7 @@ void DivePlannerPointsModel::createPlan(bool replanCopy)
plannerModel->setRecalc(oldRecalc);
//TODO: C-based function here?
- plan(&diveplan, &cache, isPlanner(), true);
+ bool did_deco = plan(&diveplan, &cache, isPlanner(), true);
if (!current_dive || displayed_dive.id != current_dive->id) {
// we were planning a new dive, not re-planning an existing on
record_dive(clone_dive(&displayed_dive));
@@ -1255,6 +1340,12 @@ void DivePlannerPointsModel::createPlan(bool replanCopy)
if (current_dive->divetrip)
add_dive_to_trip(copy, current_dive->divetrip);
record_dive(copy);
+ QString oldnotes(current_dive->notes);
+ if (oldnotes.indexOf(QString(disclaimer)) >= 0)
+ oldnotes.truncate(oldnotes.indexOf(QString(disclaimer)));
+ if (did_deco)
+ oldnotes.append(displayed_dive.notes);
+ displayed_dive.notes = strdup(oldnotes.toUtf8().data());
}
copy_dive(&displayed_dive, current_dive);
}
@@ -1266,3 +1357,8 @@ void DivePlannerPointsModel::createPlan(bool replanCopy)
setPlanMode(NOTHING);
planCreated();
}
+
+PlannerDetails::PlannerDetails(QWidget *parent) : QWidget(parent)
+{
+ ui.setupUi(this);
+}
diff --git a/qt-ui/diveplanner.h b/qt-ui/diveplanner.h
index 4093bacd1..42e0dc44a 100644
--- a/qt-ui/diveplanner.h
+++ b/qt-ui/diveplanner.h
@@ -35,6 +35,7 @@ public:
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
virtual Qt::ItemFlags flags(const QModelIndex &index) const;
+ void gaschange(const QModelIndex &index, QString newgas);
void removeSelectedPoints(const QVector<int> &rows);
void setPlanMode(Mode mode);
bool isPlanner();
@@ -82,6 +83,8 @@ slots:
void setDisplayRuntime(bool value);
void setDisplayDuration(bool value);
void setDisplayTransitions(bool value);
+ void setRecreationalMode(bool value);
+ void setSafetyStop(bool value);
void savePlan();
void saveDuplicatePlan();
void remove(const QModelIndex &index);
@@ -91,12 +94,14 @@ slots:
void loadFromDive(dive *d);
void emitDataChanged();
void setRebreatherMode(int mode);
+ void setReserveGas(int reserve);
signals:
void planCreated();
void planCanceled();
void cylinderModelEdited();
void startTimeChanged(QDateTime);
+ void recreationChanged(bool);
private:
explicit DivePlannerPointsModel(QObject *parent = 0);
@@ -180,12 +185,27 @@ slots:
void setBottomPo2(double po2);
void setDecoPo2(double po2);
void setBackgasBreaks(bool dobreaks);
+ void disableDecoElements(bool value);
private:
Ui::plannerSettingsWidget ui;
void updateUnitsUI();
};
+#include "ui_plannerDetails.h"
+
+class PlannerDetails : public QWidget {
+ Q_OBJECT
+public:
+ explicit PlannerDetails(QWidget *parent = 0);
+ QPushButton *printPlan() const { return ui.printPlan; }
+ QTextEdit *divePlanOutput() const { return ui.divePlanOutput; }
+
+private:
+ Ui::plannerDetails ui;
+};
+
+
QString dpGasToStr(const divedatapoint &p);
#endif // DIVEPLANNER_H
diff --git a/qt-ui/diveshareexportdialog.cpp b/qt-ui/diveshareexportdialog.cpp
index 9e8e69ad6..40670d7fc 100644
--- a/qt-ui/diveshareexportdialog.cpp
+++ b/qt-ui/diveshareexportdialog.cpp
@@ -4,6 +4,7 @@
#include "save-html.h"
#include "usersurvey.h"
#include "subsurfacewebservices.h"
+#include "helpers.h"
#include <QDesktopServices>
#include <QSettings>
@@ -130,7 +131,7 @@ void DiveShareExportDialog::doUpload()
else
request.setUrl(QUrl(DIVESHARE_BASE_URI "/upload"));
- request.setRawHeader("User-Agent", UserSurvey::getUserAgent().toUtf8());
+ request.setRawHeader("User-Agent", getUserAgent().toUtf8());
if (ui->txtUID->text().length() != 0)
request.setRawHeader("X-UID", ui->txtUID->text().toUtf8());
diff --git a/qt-ui/downloadfromdivecomputer.cpp b/qt-ui/downloadfromdivecomputer.cpp
index c59fb0d7b..e6b51a513 100644
--- a/qt-ui/downloadfromdivecomputer.cpp
+++ b/qt-ui/downloadfromdivecomputer.cpp
@@ -557,8 +557,8 @@ void DownloadThread::run()
}
DiveImportedModel::DiveImportedModel(QObject *o) : QAbstractTableModel(o),
- lastIndex(-1),
firstIndex(0),
+ lastIndex(-1),
checkStates(0)
{
}
diff --git a/qt-ui/filtermodels.cpp b/qt-ui/filtermodels.cpp
index e2597a634..be26a253b 100644
--- a/qt-ui/filtermodels.cpp
+++ b/qt-ui/filtermodels.cpp
@@ -1,5 +1,8 @@
#include "filtermodels.h"
#include "mainwindow.h"
+#include "models.h"
+#include "divelistview.h"
+#include "display.h"
#define CREATE_INSTANCE_METHOD( CLASS ) \
CLASS *CLASS::instance() \
@@ -61,12 +64,12 @@ CREATE_MODEL_SET_DATA_METHOD( CLASS ); \
CREATE_INSTANCE_METHOD( CLASS ); \
CREATE_DATA_METHOD( CLASS, COUNTER_FUNCTION )
-CREATE_COMMON_METHODS_FOR_FILTER(TagFilterModel, count_dives_with_tag);
-CREATE_COMMON_METHODS_FOR_FILTER(BuddyFilterModel, count_dives_with_person);
-CREATE_COMMON_METHODS_FOR_FILTER(LocationFilterModel, count_dives_with_location);
-CREATE_COMMON_METHODS_FOR_FILTER(SuitsFilterModel, count_dives_with_suit);
+CREATE_COMMON_METHODS_FOR_FILTER(TagFilterModel, count_dives_with_tag)
+CREATE_COMMON_METHODS_FOR_FILTER(BuddyFilterModel, count_dives_with_person)
+CREATE_COMMON_METHODS_FOR_FILTER(LocationFilterModel, count_dives_with_location)
+CREATE_COMMON_METHODS_FOR_FILTER(SuitsFilterModel, count_dives_with_suit)
-CREATE_INSTANCE_METHOD(MultiFilterSortModel);
+CREATE_INSTANCE_METHOD(MultiFilterSortModel)
SuitsFilterModel::SuitsFilterModel(QObject *parent) : QStringListModel(parent)
{
@@ -246,7 +249,7 @@ bool LocationFilterModel::doFilter(struct dive *d, QModelIndex &index0, QAbstrac
return true;
}
// Checked means 'Show', Unchecked means 'Hide'.
- QString location(d->location);
+ QString location(get_dive_location(d));
// only show empty location dives if the user checked that.
if (location.isEmpty()) {
if (rowCount() > 0)
@@ -274,7 +277,7 @@ void LocationFilterModel::repopulate()
struct dive *dive;
int i = 0;
for_each_dive (i, dive) {
- QString location(dive->location);
+ QString location(get_dive_location(dive));
if (!location.isEmpty() && !list.contains(location)) {
list.append(location);
}
diff --git a/qt-ui/filtermodels.h b/qt-ui/filtermodels.h
index 1406b8272..9d8724173 100644
--- a/qt-ui/filtermodels.h
+++ b/qt-ui/filtermodels.h
@@ -6,7 +6,7 @@
class MultiFilterInterface {
public:
- MultiFilterInterface() : checkState(NULL){};
+ MultiFilterInterface() : checkState(NULL), anyChecked(false) {}
virtual bool doFilter(struct dive *d, QModelIndex &index0, QAbstractItemModel *sourceModel) const = 0;
virtual void clearFilter() = 0;
bool *checkState;
diff --git a/qt-ui/globe.cpp b/qt-ui/globe.cpp
index 1e4639d5c..ea0c0f231 100644
--- a/qt-ui/globe.cpp
+++ b/qt-ui/globe.cpp
@@ -2,7 +2,13 @@
#ifndef NO_MARBLE
#include "mainwindow.h"
#include "helpers.h"
+#include "divelistview.h"
+#include "maintab.h"
+#include "display.h"
+
#include <QTimer>
+#include <QContextMenuEvent>
+#include <QMouseEvent>
#include <marble/AbstractFloatItem.h>
#include <marble/GeoDataPlacemark.h>
@@ -158,10 +164,11 @@ void GlobeGPS::mouseClicked(qreal lon, qreal lat, GeoDataCoordinates::Unit unit)
QList<int> selectedDiveIds;
for_each_dive (idx, dive) {
long lat_diff, lon_diff;
- if (!dive_has_gps_location(dive))
+ struct dive_site *ds = get_dive_site_for_dive(dive);
+ if (!dive_site_has_gps_location(ds))
continue;
- lat_diff = labs(dive->latitude.udeg - lat_udeg);
- lon_diff = labs(dive->longitude.udeg - lon_udeg);
+ lat_diff = labs(ds->latitude.udeg - lat_udeg);
+ lon_diff = labs(ds->longitude.udeg - lon_udeg);
if (lat_diff > 180000000)
lat_diff = 360000000 - lat_diff;
if (lon_diff > 180000000)
@@ -180,6 +187,7 @@ void GlobeGPS::mouseClicked(qreal lon, qreal lat, GeoDataCoordinates::Unit unit)
void GlobeGPS::repopulateLabels()
{
+ struct dive_site *ds;
if (loadedDives) {
model()->treeModel()->removeDocument(loadedDives);
delete loadedDives;
@@ -198,12 +206,16 @@ void GlobeGPS::repopulateLabels()
// don't show that flag, it's either already shown as displayed_dive
// or it's the one that we are moving right now...
continue;
- if (dive_has_gps_location(dive)) {
- GeoDataPlacemark *place = new GeoDataPlacemark(dive->location);
- place->setCoordinate(dive->longitude.udeg / 1000000.0, dive->latitude.udeg / 1000000.0, 0, GeoDataCoordinates::Degree);
+ if (idx == -1)
+ ds = &displayed_dive_site;
+ else
+ ds = get_dive_site_for_dive(dive);
+ if (dive_site_has_gps_location(ds)) {
+ GeoDataPlacemark *place = new GeoDataPlacemark(ds->name);
+ place->setCoordinate(ds->longitude.udeg / 1000000.0, ds->latitude.udeg / 1000000.0, 0, GeoDataCoordinates::Degree);
// don't add dive locations twice, unless they are at least 50m apart
- if (locationMap[QString(dive->location)]) {
- GeoDataCoordinates existingLocation = locationMap[QString(dive->location)]->coordinate();
+ if (locationMap[QString(ds->name)]) {
+ GeoDataCoordinates existingLocation = locationMap[QString(ds->name)]->coordinate();
GeoDataLineString segment = GeoDataLineString();
segment.append(existingLocation);
GeoDataCoordinates newLocation = place->coordinate();
@@ -214,7 +226,7 @@ void GlobeGPS::repopulateLabels()
if (dist < 0.05)
continue;
}
- locationMap[QString(dive->location)] = place;
+ locationMap[QString(ds->name)] = place;
loadedDives->append(place);
}
}
@@ -230,23 +242,23 @@ void GlobeGPS::reload()
void GlobeGPS::centerOnCurrentDive()
{
- struct dive *dive = current_dive;
+ struct dive_site *ds = get_dive_site_for_dive(current_dive);
// dive has changed, if we had the 'editingDive', hide it.
- if (messageWidget->isVisible() && (!dive || dive_has_gps_location(dive) || amount_selected != 1))
+ if (messageWidget->isVisible() && (!ds || dive_site_has_gps_location(ds) || amount_selected != 1))
messageWidget->hide();
editingDiveLocation = false;
- if (!dive)
+ if (!ds)
return;
- qreal longitude = dive->longitude.udeg / 1000000.0;
- qreal latitude = dive->latitude.udeg / 1000000.0;
+ qreal longitude = ds->longitude.udeg / 1000000.0;
+ qreal latitude = ds->latitude.udeg / 1000000.0;
- if ((!dive_has_gps_location(dive) || MainWindow::instance()->information()->isEditing()) && amount_selected == 1) {
+ if ((!dive_site_has_gps_location(ds) || MainWindow::instance()->information()->isEditing()) && amount_selected == 1) {
prepareForGetDiveCoordinates();
return;
}
- if (!dive_has_gps_location(dive)) {
+ if (!dive_site_has_gps_location(ds)) {
zoomOutForNoGPS();
return;
}
@@ -282,7 +294,7 @@ void GlobeGPS::zoomOutForNoGPS()
// we show a dive with GPS location we need to zoom in again
if (fixZoomTimer->isActive())
fixZoomTimer->stop();
- setZoom(1200, Marble::Automatic);
+ setZoom(0, Marble::Automatic);
if (!needResetZoom) {
needResetZoom = true;
currentZoomLevel = zoom();
@@ -303,8 +315,10 @@ void GlobeGPS::prepareForGetDiveCoordinates()
}
}
+// This needs to update the dive site, not just this dive
void GlobeGPS::changeDiveGeoPosition(qreal lon, qreal lat, GeoDataCoordinates::Unit unit)
{
+ struct dive_site *ds;
messageWidget->hide();
if (MainWindow::instance()->dive_list()->selectionModel()->selection().isEmpty())
@@ -318,8 +332,8 @@ void GlobeGPS::changeDiveGeoPosition(qreal lon, qreal lat, GeoDataCoordinates::U
centerOn(lon, lat, true);
// change the location of the displayed_dive and put the UI in edit mode
- displayed_dive.latitude.udeg = lrint(lat * 1000000.0);
- displayed_dive.longitude.udeg = lrint(lon * 1000000.0);
+ displayed_dive_site.latitude.udeg = lrint(lat * 1000000.0);
+ displayed_dive_site.longitude.udeg = lrint(lon * 1000000.0);
emit(coordinatesChanged());
repopulateLabels();
editingDiveLocation = false;
@@ -335,7 +349,12 @@ void GlobeGPS::mousePressEvent(QMouseEvent *event)
// there could be two scenarios that got us here; let's check if we are editing a dive
if (MainWindow::instance()->information()->isEditing() && clickOnGlobe) {
- MainWindow::instance()->information()->updateCoordinatesText(lat, lon);
+ //
+ // FIXME
+ // TODO
+ //
+ // this needs to do this on the dive site screen
+ // MainWindow::instance()->information()->updateCoordinatesText(lat, lon);
repopulateLabels();
} else if (clickOnGlobe) {
changeDiveGeoPosition(lon, lat, GeoDataCoordinates::Degree);
diff --git a/qt-ui/globe.h b/qt-ui/globe.h
index b6a33bbbe..4f9d7c611 100644
--- a/qt-ui/globe.h
+++ b/qt-ui/globe.h
@@ -19,7 +19,7 @@ class GlobeGPS : public MarbleWidget {
Q_OBJECT
public:
using MarbleWidget::centerOn;
- GlobeGPS(QWidget *parent);
+ GlobeGPS(QWidget *parent = 0);
void reload();
void repopulateLabels();
void centerOnCurrentDive();
diff --git a/qt-ui/locationInformation.ui b/qt-ui/locationInformation.ui
new file mode 100644
index 000000000..38eb36145
--- /dev/null
+++ b/qt-ui/locationInformation.ui
@@ -0,0 +1,149 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>LocationInformation</class>
+ <widget class="QGroupBox" name="LocationInformation">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>556</width>
+ <height>584</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>GroupBox</string>
+ </property>
+ <property name="title">
+ <string>Dive Site</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="bottomMargin">
+ <number>12</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>2</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>-1</number>
+ </property>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Current Location</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>1</number>
+ </property>
+ <item>
+ <widget class="QComboBox" name="currentLocation"/>
+ </item>
+ <item>
+ <widget class="QToolButton" name="addLocation">
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../subsurface.qrc">
+ <normaloff>:/plus</normaloff>:/plus</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="editLocation">
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../subsurface.qrc">
+ <normaloff>:/edit</normaloff>:/edit</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="removeLocation">
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../subsurface.qrc">
+ <normaloff>:/trash</normaloff>:/trash</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Name</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLineEdit" name="diveSiteName"/>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Coordinates</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLineEdit" name="diveSiteCoordinates"/>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Description</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLineEdit" name="diveSiteDescription"/>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Notes</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1" rowspan="2" colspan="2">
+ <widget class="QPlainTextEdit" name="diveSiteNotes"/>
+ </item>
+ <item row="6" column="0">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="0" colspan="3">
+ <widget class="KMessageWidget" name="diveSiteMessage" native="true"/>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>KMessageWidget</class>
+ <extends>QWidget</extends>
+ <header>kmessagewidget.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources>
+ <include location="../subsurface.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/qt-ui/locationinformation.cpp b/qt-ui/locationinformation.cpp
new file mode 100644
index 000000000..18f780983
--- /dev/null
+++ b/qt-ui/locationinformation.cpp
@@ -0,0 +1,241 @@
+#include "locationinformation.h"
+#include "dive.h"
+#include "mainwindow.h"
+#include "divelistview.h"
+#include "qthelper.h"
+
+#include <QDebug>
+#include <QShowEvent>
+
+LocationInformationModel::LocationInformationModel(QObject *obj) : QAbstractListModel(obj), internalRowCount(0)
+{
+}
+
+int LocationInformationModel::rowCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent);
+ return internalRowCount;
+}
+
+QVariant LocationInformationModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+ struct dive_site *ds = get_dive_site(index.row());
+
+ switch(role) {
+ case Qt::DisplayRole : return qPrintable(ds->name);
+ }
+
+ return QVariant();
+}
+
+void LocationInformationModel::update()
+{
+ int i;
+ struct dive_site *ds;
+ for_each_dive_site (i, ds);
+
+ if (rowCount()) {
+ beginRemoveRows(QModelIndex(), 0, rowCount()-1);
+ endRemoveRows();
+ }
+ if (i) {
+ beginInsertRows(QModelIndex(), 0, i);
+ internalRowCount = i;
+ endInsertRows();
+ }
+}
+
+LocationInformationWidget::LocationInformationWidget(QWidget *parent) : QGroupBox(parent), modified(false)
+{
+ ui.setupUi(this);
+ ui.diveSiteMessage->setCloseButtonVisible(false);
+ ui.diveSiteMessage->show();
+
+ // create the three buttons and only show the close button for now
+ closeAction = new QAction(tr("Close"), this);
+ connect(closeAction, SIGNAL(triggered(bool)), this, SLOT(rejectChanges()));
+
+ acceptAction = new QAction(tr("Apply changes"), this);
+ connect(acceptAction, SIGNAL(triggered(bool)), this, SLOT(acceptChanges()));
+
+ rejectAction = new QAction(tr("Discard changes"), this);
+ connect(rejectAction, SIGNAL(triggered(bool)), this, SLOT(rejectChanges()));
+
+ ui.diveSiteMessage->setText(tr("Dive site management"));
+ ui.diveSiteMessage->addAction(closeAction);
+
+ ui.currentLocation->setModel(new LocationInformationModel());
+ connect(ui.currentLocation, SIGNAL(currentIndexChanged(int)), this, SLOT(setCurrentDiveSite(int)));
+}
+
+void LocationInformationWidget::setCurrentDiveSite(int dive_nr)
+{
+ currentDs = get_dive_site(dive_nr);
+ if (currentDs)
+ setLocationId(currentDs->uuid);
+ else
+ setLocationId(displayed_dive.dive_site_uuid);
+}
+
+void LocationInformationWidget::setLocationId(uint32_t uuid)
+{
+ currentDs = get_dive_site_by_uuid(uuid);
+
+ if (!currentDs) {
+ currentDs = get_dive_site_by_uuid(create_dive_site(""));
+ displayed_dive.dive_site_uuid = currentDs->uuid;
+ ui.diveSiteName->clear();
+ ui.diveSiteDescription->clear();
+ ui.diveSiteNotes->clear();
+ ui.diveSiteCoordinates->clear();
+ }
+ displayed_dive_site = *currentDs;
+ if (displayed_dive_site.name)
+ ui.diveSiteName->setText(displayed_dive_site.name);
+ else
+ ui.diveSiteName->clear();
+ if (displayed_dive_site.description)
+ ui.diveSiteDescription->setText(displayed_dive_site.description);
+ else
+ ui.diveSiteDescription->clear();
+ if (displayed_dive_site.notes)
+ ui.diveSiteNotes->setPlainText(displayed_dive_site.notes);
+ else
+ ui.diveSiteNotes->clear();
+ if (displayed_dive_site.latitude.udeg || displayed_dive_site.longitude.udeg)
+ ui.diveSiteCoordinates->setText(printGPSCoords(displayed_dive_site.latitude.udeg, displayed_dive_site.longitude.udeg));
+ else
+ ui.diveSiteCoordinates->clear();
+}
+
+void LocationInformationWidget::updateGpsCoordinates()
+{
+ ui.diveSiteCoordinates->setText(printGPSCoords(displayed_dive_site.latitude.udeg, displayed_dive_site.longitude.udeg));
+ MainWindow::instance()->setApplicationState("EditDiveSite");
+}
+
+void LocationInformationWidget::acceptChanges()
+{
+ char *uiString;
+ currentDs->latitude = displayed_dive_site.latitude;
+ currentDs->longitude = displayed_dive_site.longitude;
+ uiString = ui.diveSiteName->text().toUtf8().data();
+ if (!same_string(uiString, currentDs->name)) {
+ free(currentDs->name);
+ currentDs->name = copy_string(uiString);
+ }
+ uiString = ui.diveSiteDescription->text().toUtf8().data();
+ if (!same_string(uiString, currentDs->description)) {
+ free(currentDs->description);
+ currentDs->description = copy_string(uiString);
+ }
+ uiString = ui.diveSiteNotes->document()->toPlainText().toUtf8().data();
+ if (!same_string(uiString, currentDs->notes)) {
+ free(currentDs->notes);
+ currentDs->notes = copy_string(uiString);
+ }
+ if (dive_site_is_empty(currentDs)) {
+ delete_dive_site(currentDs->uuid);
+ displayed_dive.dive_site_uuid = 0;
+ setLocationId(0);
+ } else {
+ setLocationId(currentDs->uuid);
+ }
+ mark_divelist_changed(true);
+ resetState();
+ emit informationManagementEnded();
+}
+
+void LocationInformationWidget::rejectChanges()
+{
+ Q_ASSERT(currentDs != NULL);
+ if (dive_site_is_empty(currentDs)) {
+ delete_dive_site(currentDs->uuid);
+ displayed_dive.dive_site_uuid = 0;
+ setLocationId(0);
+ } else {
+ setLocationId(currentDs->uuid);
+ }
+ resetState();
+ emit informationManagementEnded();
+}
+
+void LocationInformationWidget::showEvent(QShowEvent *ev)
+{
+ LocationInformationModel *m = (LocationInformationModel*) ui.currentLocation->model();
+ ui.diveSiteMessage->setCloseButtonVisible(false);
+ m->update();
+ QGroupBox::showEvent(ev);
+
+}
+
+void LocationInformationWidget::markChangedWidget(QWidget *w)
+{
+ QPalette p;
+ qreal h, s, l, a;
+ if (!modified)
+ enableEdition();
+ qApp->palette().color(QPalette::Text).getHslF(&h, &s, &l, &a);
+ p.setBrush(QPalette::Base, (l <= 0.3) ? QColor(Qt::yellow).lighter() : (l <= 0.6) ? QColor(Qt::yellow).light() : /* else */ QColor(Qt::yellow).darker(300));
+ w->setPalette(p);
+ modified = true;
+}
+
+void LocationInformationWidget::resetState()
+{
+ modified = false;
+ resetPallete();
+ MainWindow::instance()->dive_list()->setEnabled(true);
+ MainWindow::instance()->setEnabledToolbar(true);
+ ui.diveSiteMessage->setText(tr("Dive site management"));
+ ui.diveSiteMessage->addAction(closeAction);
+ ui.diveSiteMessage->removeAction(acceptAction);
+ ui.diveSiteMessage->removeAction(rejectAction);
+ ui.diveSiteMessage->setCloseButtonVisible(false);
+}
+
+void LocationInformationWidget::enableEdition()
+{
+ MainWindow::instance()->dive_list()->setEnabled(false);
+ MainWindow::instance()->setEnabledToolbar(false);
+ ui.diveSiteMessage->setText(tr("You are editing a dive site"));
+ ui.diveSiteMessage->removeAction(closeAction);
+ ui.diveSiteMessage->addAction(acceptAction);
+ ui.diveSiteMessage->addAction(rejectAction);
+ ui.diveSiteMessage->setCloseButtonVisible(false);
+}
+
+void LocationInformationWidget::on_diveSiteCoordinates_textChanged(const QString& text)
+{
+ if (!same_string(qPrintable(text), printGPSCoords(currentDs->latitude.udeg, currentDs->longitude.udeg)))
+ markChangedWidget(ui.diveSiteCoordinates);
+}
+
+void LocationInformationWidget::on_diveSiteDescription_textChanged(const QString& text)
+{
+ if (!same_string(qPrintable(text), currentDs->description))
+ markChangedWidget(ui.diveSiteDescription);
+}
+
+void LocationInformationWidget::on_diveSiteName_textChanged(const QString& text)
+{
+ if (!same_string(qPrintable(text), currentDs->name))
+ markChangedWidget(ui.diveSiteName);
+}
+
+void LocationInformationWidget::on_diveSiteNotes_textChanged()
+{
+ if (!same_string(qPrintable(ui.diveSiteNotes->toPlainText()), currentDs->notes))
+ markChangedWidget(ui.diveSiteNotes);
+}
+
+void LocationInformationWidget::resetPallete()
+{
+ QPalette p;
+ ui.diveSiteCoordinates->setPalette(p);
+ ui.diveSiteDescription->setPalette(p);
+ ui.diveSiteName->setPalette(p);
+ ui.diveSiteNotes->setPalette(p);
+}
diff --git a/qt-ui/locationinformation.h b/qt-ui/locationinformation.h
new file mode 100644
index 000000000..82105c333
--- /dev/null
+++ b/qt-ui/locationinformation.h
@@ -0,0 +1,50 @@
+#ifndef LOCATIONINFORMATION_H
+#define LOCATIONINFORMATION_H
+
+#include "ui_locationInformation.h"
+#include <stdint.h>
+#include <QAbstractListModel>
+
+class LocationInformationModel : public QAbstractListModel {
+Q_OBJECT
+public:
+ LocationInformationModel(QObject *obj = 0);
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index = QModelIndex(), int role = Qt::DisplayRole) const;
+ void update();
+private:
+ int internalRowCount;
+};
+
+class LocationInformationWidget : public QGroupBox {
+Q_OBJECT
+public:
+ LocationInformationWidget(QWidget *parent = 0);
+protected:
+ void showEvent(QShowEvent *);
+\
+public slots:
+ void acceptChanges();
+ void rejectChanges();
+ void setLocationId(uint32_t uuid);
+ void updateGpsCoordinates(void);
+ void markChangedWidget(QWidget *w);
+ void enableEdition();
+ void resetState();
+ void resetPallete();
+ void setCurrentDiveSite(int dive_nr);
+ void on_diveSiteCoordinates_textChanged(const QString& text);
+ void on_diveSiteDescription_textChanged(const QString& text);
+ void on_diveSiteName_textChanged(const QString& text);
+ void on_diveSiteNotes_textChanged();
+signals:
+ void informationManagementEnded();
+
+private:
+ struct dive_site *currentDs;
+ Ui::LocationInformation ui;
+ bool modified;
+ QAction *closeAction, *acceptAction, *rejectAction;
+};
+
+#endif
diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp
index 2fafb65ff..fd760445b 100644
--- a/qt-ui/maintab.cpp
+++ b/qt-ui/maintab.cpp
@@ -6,9 +6,16 @@
*/
#include "maintab.h"
#include "mainwindow.h"
+#include "globe.h"
#include "helpers.h"
#include "statistics.h"
#include "modeldelegates.h"
+#include "models.h"
+#include "divelistview.h"
+#include "display.h"
+#include "profile/profilewidget2.h"
+#include "diveplanner.h"
+#include "divesitehelpers.h"
#if defined(FBSUPPORT)
#include "socialnetworks.h"
@@ -20,6 +27,7 @@
#include <QShortcut>
#include <QMessageBox>
#include <QDesktopServices>
+#include <QStringList>
MainTab::MainTab(QWidget *parent) : QTabWidget(parent),
weightModel(new WeightModel(this)),
@@ -43,18 +51,19 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent),
ui.extraData->setModel(extraDataModel);
closeMessage();
+ connect(ui.manageDiveSite, SIGNAL(clicked()), this, SLOT(prepareDiveSiteEdit()));
+
QAction *action = new QAction(tr("Apply changes"), this);
connect(action, SIGNAL(triggered(bool)), this, SLOT(acceptChanges()));
addMessageAction(action);
action = new QAction(tr("Discard changes"), this);
connect(action, SIGNAL(triggered(bool)), this, SLOT(rejectChanges()));
+ addMessageAction(action);
QShortcut *closeKey = new QShortcut(QKeySequence(Qt::Key_Escape), this);
connect(closeKey, SIGNAL(activated()), this, SLOT(escDetected()));
- addMessageAction(action);
-
if (qApp->style()->objectName() == "oxygen")
setDocumentMode(true);
else
@@ -64,21 +73,6 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent),
// filled from a dive, they are made writeable
setEnabled(false);
- ui.location->installEventFilter(this);
- ui.coordinates->installEventFilter(this);
- ui.divemaster->installEventFilter(this);
- ui.buddy->installEventFilter(this);
- ui.suit->installEventFilter(this);
- ui.notes->viewport()->installEventFilter(this);
- ui.rating->installEventFilter(this);
- ui.visibility->installEventFilter(this);
- ui.airtemp->installEventFilter(this);
- ui.watertemp->installEventFilter(this);
- ui.dateEdit->installEventFilter(this);
- ui.timeEdit->installEventFilter(this);
- ui.tagWidget->installEventFilter(this);
- ui.DiveType->installEventFilter(this);
-
Q_FOREACH (QObject *obj, ui.statisticsTab->children()) {
QLabel *label = qobject_cast<QLabel *>(obj);
if (label)
@@ -192,6 +186,15 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent),
ui.socialNetworks->setVisible(false);
#endif
+ ui.waitingSpinner->setRoundness(70.0);
+ ui.waitingSpinner->setMinimumTrailOpacity(15.0);
+ ui.waitingSpinner->setTrailFadePercentage(70.0);
+ ui.waitingSpinner->setNumberOfLines(8);
+ ui.waitingSpinner->setLineLength(5);
+ ui.waitingSpinner->setLineWidth(3);
+ ui.waitingSpinner->setInnerRadius(5);
+ ui.waitingSpinner->setRevolutionsPerSecond(1);
+
acceptingEdit = false;
}
@@ -206,6 +209,22 @@ MainTab::~MainTab()
}
}
+void MainTab::enableGeoLoockupEdition()
+{
+ ui.waitingSpinner->stop();
+ ui.manageDiveSite->show();
+}
+
+void MainTab::disableGeoLoockupEdition()
+{
+ ui.waitingSpinner->start();
+ ui.manageDiveSite->hide();
+}
+
+void MainTab::prepareDiveSiteEdit() {
+ emit requestDiveSiteEdit(displayed_dive.dive_site_uuid);
+}
+
void MainTab::toggleTriggeredColumn()
{
QAction *action = qobject_cast<QAction *>(sender());
@@ -284,11 +303,15 @@ void MainTab::updateTextLabels(bool showUnits)
void MainTab::enableEdition(EditMode newEditMode)
{
+ const bool isTripEdit = MainWindow::instance() &&
+ MainWindow::instance()->dive_list()->selectedTrips().count() == 1;
+
if (((newEditMode == DIVE || newEditMode == NONE) && current_dive == NULL) || editMode != NONE)
return;
modified = false;
copyPaste = false;
if ((newEditMode == DIVE || newEditMode == NONE) &&
+ !isTripEdit &&
current_dive->dc.model &&
strcmp(current_dive->dc.model, "manually added dive") == 0) {
// editCurrentDive will call enableEdition with newEditMode == MANUALLY_ADDED_DIVE
@@ -310,7 +333,7 @@ void MainTab::enableEdition(EditMode newEditMode)
if (amount_selected == 1 && newEditMode != ADD)
MainWindow::instance()->globe()->prepareForGetDiveCoordinates();
- if (MainWindow::instance() && MainWindow::instance()->dive_list()->selectedTrips().count() == 1) {
+ if (isTripEdit) {
// we are editing trip location and notes
displayMessage(tr("This trip is being edited."));
memset(&displayedTrip, 0, sizeof(displayedTrip));
@@ -385,12 +408,17 @@ bool MainTab::isEditing()
return editMode != NONE;
}
+void MainTab::showLocation()
+{
+ ui.location->setText(get_dive_location(&displayed_dive));
+}
+
void MainTab::updateDiveInfo(bool clear)
{
// don't execute this while adding / planning a dive
if (editMode == ADD || editMode == MANUALLY_ADDED_DIVE || MainWindow::instance()->graphics()->isPlanner())
return;
- if (!isEnabled() && !clear)
+ if (!isEnabled() && !clear )
setEnabled(true);
if (isEnabled() && clear)
setEnabled(false);
@@ -418,9 +446,7 @@ void MainTab::updateDiveInfo(bool clear)
else
ui.notes->setPlainText(tmp);
}
-
UPDATE_TEXT(displayed_dive, notes);
- UPDATE_TEXT(displayed_dive, location);
UPDATE_TEXT(displayed_dive, suit);
UPDATE_TEXT(displayed_dive, divemaster);
UPDATE_TEXT(displayed_dive, buddy);
@@ -429,7 +455,11 @@ void MainTab::updateDiveInfo(bool clear)
ui.DiveType->setCurrentIndex(displayed_dive.dc.divemode);
if (!clear) {
- updateGpsCoordinates();
+ struct dive_site *ds = get_dive_site_by_uuid(displayed_dive.dive_site_uuid);
+ if (ds)
+ ui.location->setText(ds->name);
+ else
+ ui.location->clear();
// Subsurface always uses "local time" as in "whatever was the local time at the location"
// so all time stamps have no time zone information and are in UTC
QDateTime localTime = QDateTime::fromTime_t(displayed_dive.when - gettimezoneoffset(displayed_dive.when));
@@ -440,8 +470,6 @@ void MainTab::updateDiveInfo(bool clear)
setTabText(0, tr("Trip notes"));
currentTrip = *MainWindow::instance()->dive_list()->selectedTrips().begin();
// only use trip relevant fields
- ui.coordinates->setVisible(false);
- ui.CoordinatedLabel->setVisible(false);
ui.divemaster->setVisible(false);
ui.DivemasterLabel->setVisible(false);
ui.buddy->setVisible(false);
@@ -468,11 +496,9 @@ void MainTab::updateDiveInfo(bool clear)
clearEquipment();
ui.equipmentTab->setEnabled(false);
} else {
- setTabText(0, tr("Dive notes"));
+ setTabText(0, tr("Notes"));
currentTrip = NULL;
// make all the fields visible writeable
- ui.coordinates->setVisible(true);
- ui.CoordinatedLabel->setVisible(true);
ui.divemaster->setVisible(true);
ui.buddy->setVisible(true);
ui.suit->setVisible(true);
@@ -641,8 +667,8 @@ void MainTab::updateDiveInfo(bool clear)
clearStats();
clearEquipment();
ui.rating->setCurrentStars(0);
- ui.coordinates->clear();
ui.visibility->setCurrentStars(0);
+ ui.location->clear();
}
editMode = NONE;
ui.cylinders->view()->hideColumn(CylindersModel::DEPTH);
@@ -750,19 +776,13 @@ void MainTab::acceptChanges()
copy_samples(&displayed_dive.dc, &current_dive->dc);
}
struct dive *cd = current_dive;
- //Reset coordinates field, in case it contains garbage.
- updateGpsCoordinates();
// now check if something has changed and if yes, edit the selected dives that
// were identical with the master dive shown (and mark the divelist as changed)
- if (!same_string(displayed_dive.buddy, cd->buddy))
- MODIFY_SELECTED_DIVES(EDIT_TEXT(buddy));
if (!same_string(displayed_dive.suit, cd->suit))
MODIFY_SELECTED_DIVES(EDIT_TEXT(suit));
if (!same_string(displayed_dive.notes, cd->notes))
MODIFY_SELECTED_DIVES(EDIT_TEXT(notes));
if (!same_string(displayed_dive.divemaster, cd->divemaster))
- MODIFY_SELECTED_DIVES(EDIT_TEXT(divemaster));
- if (displayed_dive.rating != cd->rating)
MODIFY_SELECTED_DIVES(EDIT_VALUE(rating));
if (displayed_dive.visibility != cd->visibility)
MODIFY_SELECTED_DIVES(EDIT_VALUE(visibility));
@@ -779,18 +799,12 @@ void MainTab::acceptChanges()
time_t offset = cd->when - displayed_dive.when;
MODIFY_SELECTED_DIVES(mydive->when -= offset;);
}
- if (displayed_dive.latitude.udeg != cd->latitude.udeg ||
- displayed_dive.longitude.udeg != cd->longitude.udeg)
- MODIFY_SELECTED_DIVES(
- if (copyPaste ||
- (same_string(mydive->location, cd->location) &&
- mydive->latitude.udeg == cd->latitude.udeg &&
- mydive->longitude.udeg == cd->longitude.udeg))
- gpsHasChanged(mydive, cd, ui.coordinates->text(), 0);
- );
- if (!same_string(displayed_dive.location, cd->location))
- MODIFY_SELECTED_DIVES(EDIT_TEXT(location));
+ if (displayed_dive.dive_site_uuid != cd->dive_site_uuid)
+ MODIFY_SELECTED_DIVES(EDIT_VALUE(dive_site_uuid));
+ // three text fields are somewhat special and are represented as tags
+ // in the UI - they need somewhat smarter handling
+ saveTaggedStrings();
saveTags();
if (editMode != ADD && cylindersModel->changed) {
@@ -821,6 +835,16 @@ void MainTab::acceptChanges()
cd->cylinder[i] = displayed_dive.cylinder[i];
cd->cylinder[i].type.description = copy_string(displayed_dive.cylinder[i].type.description);
}
+ /* if cylinders changed we may have changed gas change events
+ * - so far this is ONLY supported for a single selected dive */
+ struct divecomputer *tdc = &current_dive->dc;
+ struct divecomputer *sdc = &displayed_dive.dc;
+ while(tdc && sdc) {
+ free_events(tdc->events);
+ copy_events(sdc, tdc);
+ tdc = tdc->next;
+ sdc = sdc->next;
+ }
do_replot = true;
}
@@ -895,7 +919,6 @@ void MainTab::resetPallete()
ui.buddy->setPalette(p);
ui.notes->setPalette(p);
ui.location->setPalette(p);
- ui.coordinates->setPalette(p);
ui.divemaster->setPalette(p);
ui.suit->setPalette(p);
ui.airtemp->setPalette(p);
@@ -970,10 +993,7 @@ void MainTab::markChangedWidget(QWidget *w)
qApp->palette().color(QPalette::Text).getHslF(&h, &s, &l, &a);
p.setBrush(QPalette::Base, (l <= 0.3) ? QColor(Qt::yellow).lighter() : (l <= 0.6) ? QColor(Qt::yellow).light() : /* else */ QColor(Qt::yellow).darker(300));
w->setPalette(p);
- if (!modified) {
- modified = true;
- enableEdition();
- }
+ modified = true;
}
void MainTab::on_buddy_textChanged()
@@ -1137,6 +1157,73 @@ void MainTab::saveTags()
);
}
+// buddy and divemaster are represented in the UI just like the tags, but the internal
+// representation is just a string (with commas as delimiters). So we need to do the same
+// thing we did for tags, just differently
+void MainTab::saveTaggedStrings()
+{
+ QStringList addedList, removedList;
+ struct dive *cd = current_dive;
+
+ diffTaggedStrings(cd->buddy, displayed_dive.buddy, addedList, removedList);
+ MODIFY_SELECTED_DIVES(
+ QStringList oldList = QString(mydive->buddy).split(QRegExp("\\s*,\\s*"), QString::SkipEmptyParts);
+ QString newString;
+ QString comma;
+ Q_FOREACH (const QString tag, oldList) {
+ if (!removedList.contains(tag, Qt::CaseInsensitive)) {
+ newString += comma + tag;
+ comma = ", ";
+ }
+ }
+ Q_FOREACH (const QString tag, addedList) {
+ if (!oldList.contains(tag, Qt::CaseInsensitive)) {
+ newString += comma + tag;
+ comma = ", ";
+ }
+ }
+ free(mydive->buddy);
+ mydive->buddy = copy_string(qPrintable(newString));
+ );
+ addedList.clear();
+ removedList.clear();
+ diffTaggedStrings(cd->divemaster, displayed_dive.divemaster, addedList, removedList);
+ MODIFY_SELECTED_DIVES(
+ QStringList oldList = QString(mydive->divemaster).split(QRegExp("\\s*,\\s*"), QString::SkipEmptyParts);
+ QString newString;
+ QString comma;
+ Q_FOREACH (const QString tag, oldList) {
+ if (!removedList.contains(tag, Qt::CaseInsensitive)) {
+ newString += comma + tag;
+ comma = ", ";
+ }
+ }
+ Q_FOREACH (const QString tag, addedList) {
+ if (!oldList.contains(tag, Qt::CaseInsensitive)) {
+ newString += comma + tag;
+ comma = ", ";
+ }
+ }
+ free(mydive->divemaster);
+ mydive->divemaster = copy_string(qPrintable(newString));
+ );
+}
+
+void MainTab::diffTaggedStrings(QString currentString, QString displayedString, QStringList &addedList, QStringList &removedList)
+{
+ QStringList displayedList, currentList;
+ currentList = currentString.split(',', QString::SkipEmptyParts);
+ displayedList = displayedString.split(',', QString::SkipEmptyParts);
+ Q_FOREACH ( const QString tag, currentList) {
+ if (!displayedList.contains(tag, Qt::CaseInsensitive))
+ removedList << tag.trimmed();
+ }
+ Q_FOREACH (const QString tag, displayedList) {
+ if (!currentList.contains(tag, Qt::CaseInsensitive))
+ addedList << tag.trimmed();
+ }
+}
+
void MainTab::on_tagWidget_textChanged()
{
char buf[1024];
@@ -1158,9 +1245,6 @@ void MainTab::on_location_textChanged(const QString &text)
if (currentTrip) {
free(displayedTrip.location);
displayedTrip.location = strdup(ui.location->text().toUtf8().data());
- } else {
- free(displayed_dive.location);
- displayed_dive.location = strdup(ui.location->text().toUtf8().data());
}
markChangedWidget(ui.location);
}
@@ -1168,25 +1252,13 @@ void MainTab::on_location_textChanged(const QString &text)
// If we have GPS data for the location entered, add it.
void MainTab::on_location_editingFinished()
{
- // if we have a location and no GPS data, look up the GPS data;
- // but if the GPS data was intentionally cleared then don't
- if (!currentTrip &&
- !same_string(displayed_dive.location, "") &&
- ui.coordinates->text().trimmed().isEmpty() &&
- !(editMode == DIVE && dive_has_gps_location(current_dive))) {
- struct dive *dive;
- int i = 0;
- for_each_dive (i, dive) {
- if (same_string(displayed_dive.location, dive->location) &&
- (dive->latitude.udeg || dive->longitude.udeg)) {
- displayed_dive.latitude = dive->latitude;
- displayed_dive.longitude = dive->longitude;
- MainWindow::instance()->globe()->reload();
- updateGpsCoordinates();
- break;
- }
- }
- }
+ // find the dive site or create it
+ const char *name = copy_string(qPrintable(ui.location->text()));
+ uint32_t uuid = get_dive_site_uuid_by_name(name, NULL);
+ if (!uuid)
+ uuid = create_dive_site(name);
+ displayed_dive.dive_site_uuid = uuid;
+ free((void*)name);
}
void MainTab::on_suit_textChanged(const QString &text)
@@ -1219,6 +1291,7 @@ void MainTab::on_notes_textChanged()
markChangedWidget(ui.notes);
}
+#if 0 // we'll need something like this for the dive site management
void MainTab::on_coordinates_textChanged(const QString &text)
{
if (editMode == IGNORE || acceptingEdit == true)
@@ -1235,6 +1308,7 @@ void MainTab::on_coordinates_textChanged(const QString &text)
ui.coordinates->setPalette(p); // marks things red
}
}
+#endif
void MainTab::on_rating_valueChanged(int value)
{
@@ -1286,6 +1360,7 @@ void MainTab::editWeightWidget(const QModelIndex &index)
ui.weights->edit(index);
}
+#if 0 // we'll need this for dive sites
void MainTab::updateCoordinatesText(qreal lat, qreal lon)
{
int ulat = rint(lat * 1000000);
@@ -1298,9 +1373,16 @@ void MainTab::updateGpsCoordinates()
if (editMode == NONE)
enableEdition();
- ui.coordinates->setText(printGPSCoords(displayed_dive.latitude.udeg, displayed_dive.longitude.udeg));
- ui.coordinates->setModified(displayed_dive.latitude.udeg || displayed_dive.longitude.udeg);
+ struct dive_site *ds = get_dive_site_by_uuid(displayed_dive.dive_site_uuid);
+ if (ds && dive_site_has_gps_location(ds)) {
+ ui.coordinates->setText(printGPSCoords(ds->latitude.udeg, ds->longitude.udeg));
+ ui.coordinates->setModified(true);
+ } else if (!ui.coordinates->text().isEmpty()) {
+ ui.coordinates->setModified(true);
+ ui.coordinates->clear();
+ }
}
+#endif
void MainTab::escDetected()
{
@@ -1332,7 +1414,6 @@ void MainTab::showAndTriggerEditSelective(struct dive_components what)
// take the data in our copyPasteDive and apply it to selected dives
enableEdition();
copyPaste = true;
- SHOW_SELECTIVE(location);
SHOW_SELECTIVE(buddy);
SHOW_SELECTIVE(divemaster);
SHOW_SELECTIVE(suit);
@@ -1347,8 +1428,8 @@ void MainTab::showAndTriggerEditSelective(struct dive_components what)
ui.rating->setCurrentStars(displayed_dive.rating);
if (what.visibility)
ui.visibility->setCurrentStars(displayed_dive.visibility);
- if (what.gps)
- updateGpsCoordinates();
+ if (what.divesite)
+ ui.location->setText(get_dive_location(&displayed_dive));
if (what.tags) {
char buf[1024];
taglist_get_tagstring(displayed_dive.tag_list, buf, 1024);
diff --git a/qt-ui/maintab.h b/qt-ui/maintab.h
index 71703e3f1..4a44c03b3 100644
--- a/qt-ui/maintab.h
+++ b/qt-ui/maintab.h
@@ -10,6 +10,7 @@
#include <QTabWidget>
#include <QDialog>
#include <QMap>
+#include <QUuid>
#include "ui_maintab.h"
#include "completionmodels.h"
@@ -40,7 +41,7 @@ public:
IGNORE
};
- MainTab(QWidget *parent);
+ MainTab(QWidget *parent = 0);
~MainTab();
void clearStats();
void clearInfo();
@@ -55,7 +56,7 @@ public:
signals:
void addDiveFinished();
void dateTimeChanged();
-
+ void requestDiveSiteEdit(uint32_t uuid);
public
slots:
void addCylinder_clicked();
@@ -65,7 +66,6 @@ slots:
void rejectChanges();
void on_location_textChanged(const QString &text);
void on_location_editingFinished();
- void on_coordinates_textChanged(const QString &text);
void on_divemaster_textChanged();
void on_buddy_textChanged();
void on_suit_textChanged(const QString &text);
@@ -92,7 +92,10 @@ slots:
void escDetected(void);
void photoDoubleClicked(const QString filePath);
void removeSelectedPhotos();
- void updateGpsCoordinates();
+ void prepareDiveSiteEdit();
+ void showLocation();
+ void enableGeoLoockupEdition();
+ void disableGeoLoockupEdition();
private:
Ui::MainTab ui;
@@ -111,6 +114,8 @@ private:
bool copyPaste;
void resetPallete();
void saveTags();
+ void saveTaggedStrings();
+ void diffTaggedStrings(QString currentString, QString displayedString, QStringList &addedList, QStringList &removedList);
void markChangedWidget(QWidget *w);
dive_trip_t *currentTrip;
dive_trip_t displayedTrip;
diff --git a/qt-ui/maintab.ui b/qt-ui/maintab.ui
index bffbac97a..da8cabecb 100644
--- a/qt-ui/maintab.ui
+++ b/qt-ui/maintab.ui
@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
- <width>443</width>
+ <width>463</width>
<height>815</height>
</rect>
</property>
@@ -15,7 +15,7 @@
</property>
<widget class="QWidget" name="notesTab">
<attribute name="title">
- <string>Dive notes</string>
+ <string>Notes</string>
</attribute>
<layout class="QGridLayout" name="diveNotesLayout">
<property name="spacing">
@@ -40,113 +40,137 @@
<rect>
<x>0</x>
<y>0</y>
- <width>397</width>
+ <width>417</width>
<height>744</height>
</rect>
</property>
- <layout class="QGridLayout" name="diveNotesScrollAreaLayout">
- <property name="spacing">
- <number>0</number>
- </property>
- <item row="4" column="0" colspan="3">
- <layout class="QHBoxLayout" name="horizontalLayout">
- <property name="sizeConstraint">
- <enum>QLayout::SetNoConstraint</enum>
- </property>
- <item>
- <widget class="QLabel" name="CoordinatedLabel">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Date</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Time</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="airTempLabel">
+ <property name="text">
+ <string>Air temp.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QLabel" name="waterTempLabel">
+ <property name="text">
+ <string>Water temp.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QDateEdit" name="dateEdit">
+ <property name="calendarPopup">
+ <bool>true</bool>
+ </property>
+ <property name="timeSpec">
+ <enum>Qt::UTC</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QTimeEdit" name="timeEdit">
<property name="sizePolicy">
- <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="text">
- <string>Coordinates</string>
+ <property name="timeSpec">
+ <enum>Qt::UTC</enum>
</property>
</widget>
</item>
+ <item row="1" column="2">
+ <widget class="QLineEdit" name="airtemp">
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QLineEdit" name="watertemp">
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="LocationLabelLayout">
<item>
- <widget class="QLabel" name="TypeLabel">
+ <widget class="QLabel" name="LocationLabel">
<property name="text">
- <string>Dive mode</string>
+ <string>Location</string>
</property>
</widget>
</item>
</layout>
</item>
- <item row="0" column="0">
- <widget class="QLabel" name="label_2">
- <property name="text">
- <string>Date</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Time</string>
- </property>
- </widget>
- </item>
- <item row="0" column="2">
- <layout class="QHBoxLayout" name="temperatureLabels">
- <property name="spacing">
- <number>0</number>
- </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
- <widget class="QLabel" name="airTempLabel">
- <property name="text">
- <string>Air temp.</string>
+ <widget class="QLineEdit" name="location">
+ <property name="readOnly">
+ <bool>false</bool>
</property>
</widget>
</item>
<item>
- <widget class="QLabel" name="waterTempLabel">
+ <widget class="QPushButton" name="manageDiveSite">
<property name="text">
- <string>Water temp.</string>
+ <string>manage</string>
</property>
</widget>
</item>
+ <item>
+ <widget class="QtWaitingSpinner" name="waitingSpinner" native="true"/>
+ </item>
</layout>
</item>
- <item row="1" column="1">
- <widget class="QTimeEdit" name="timeEdit">
- <property name="sizePolicy">
- <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="timeSpec">
- <enum>Qt::UTC</enum>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QDateEdit" name="dateEdit">
- <property name="calendarPopup">
- <bool>true</bool>
- </property>
- <property name="timeSpec">
- <enum>Qt::UTC</enum>
- </property>
- </widget>
- </item>
- <item row="1" column="2">
- <layout class="QHBoxLayout" name="airWaterTempLayout">
- <property name="spacing">
- <number>0</number>
- </property>
- <item>
- <widget class="QLineEdit" name="airtemp">
+ <item>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <item row="0" column="0">
+ <widget class="QLabel" name="DivemasterLabel">
+ <property name="text">
+ <string>Divemaster</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="BuddyLabel">
+ <property name="text">
+ <string>Buddy</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="TagWidget" name="divemaster">
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
- <item>
- <widget class="QLineEdit" name="watertemp">
+ <item row="1" column="1">
+ <widget class="TagWidget" name="buddy">
<property name="readOnly">
<bool>false</bool>
</property>
@@ -154,47 +178,9 @@
</item>
</layout>
</item>
- <item row="2" column="0">
- <widget class="QLabel" name="LocationLabel">
- <property name="text">
- <string>Location</string>
- </property>
- </widget>
- </item>
- <item row="10" column="0">
- <widget class="QLabel" name="DivemasterLabel">
- <property name="text">
- <string>Divemaster</string>
- </property>
- </widget>
- </item>
- <item row="10" column="2">
- <widget class="QLabel" name="BuddyLabel">
- <property name="text">
- <string>Buddy</string>
- </property>
- </widget>
- </item>
- <item row="11" column="0" colspan="2">
- <widget class="TagWidget" name="divemaster">
- <property name="readOnly">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item row="11" column="2">
- <widget class="TagWidget" name="buddy">
- <property name="readOnly">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item row="12" column="0" colspan="2">
- <layout class="QHBoxLayout" name="ratingVisibilityLabels">
- <property name="spacing">
- <number>0</number>
- </property>
- <item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="0">
<widget class="QLabel" name="RatingLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
@@ -207,7 +193,7 @@
</property>
</widget>
</item>
- <item>
+ <item row="0" column="1">
<widget class="QLabel" name="visibilityLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
@@ -220,28 +206,14 @@
</property>
</widget>
</item>
- </layout>
- </item>
- <item row="12" column="2">
- <widget class="QLabel" name="SuitLabel">
- <property name="text">
- <string>Suit</string>
- </property>
- </widget>
- </item>
- <item row="13" column="2">
- <widget class="QLineEdit" name="suit">
- <property name="readOnly">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item row="13" column="0" colspan="2">
- <layout class="QHBoxLayout" name="ratingVisibilityWidgets">
- <property name="spacing">
- <number>0</number>
- </property>
- <item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="SuitLabel">
+ <property name="text">
+ <string>Suit</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
<widget class="StarWidget" name="rating" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
@@ -254,7 +226,7 @@
</property>
</widget>
</item>
- <item>
+ <item row="1" column="1">
<widget class="StarWidget" name="visibility" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
@@ -267,55 +239,69 @@
</property>
</widget>
</item>
+ <item row="1" column="2">
+ <widget class="QLineEdit" name="suit">
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
</layout>
</item>
- <item row="14" column="0">
- <widget class="QLabel" name="TagLabel">
- <property name="text">
- <string>Tags</string>
- </property>
- </widget>
+ <item>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="1" column="1">
+ <widget class="QComboBox" name="DiveType"/>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="TagLabel">
+ <property name="text">
+ <string>Tags</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="TypeLabel">
+ <property name="text">
+ <string>Dive mode</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="TagWidget" name="tagWidget">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="verticalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ <property name="horizontalScrollBarPolicy">
+ <enum>Qt::ScrollBarAlwaysOff</enum>
+ </property>
+ <property name="lineWrapMode">
+ <enum>QPlainTextEdit::NoWrap</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
- <item row="16" column="0">
+ <item>
<widget class="QLabel" name="NotesLabel">
<property name="text">
<string>Notes</string>
</property>
</widget>
</item>
- <item row="15" column="0" colspan="3">
- <widget class="TagWidget" name="tagWidget">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="verticalScrollBarPolicy">
- <enum>Qt::ScrollBarAlwaysOff</enum>
- </property>
- <property name="horizontalScrollBarPolicy">
- <enum>Qt::ScrollBarAlwaysOff</enum>
- </property>
- <property name="lineWrapMode">
- <enum>QPlainTextEdit::NoWrap</enum>
- </property>
- </widget>
- </item>
- <item row="3" column="0" colspan="3">
- <widget class="QLineEdit" name="location">
- <property name="readOnly">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item row="17" column="0" colspan="3">
+ <item>
<layout class="QHBoxLayout" name="notesAndSocialNetworksLayout">
<property name="spacing">
<number>0</number>
@@ -371,20 +357,6 @@
</item>
</layout>
</item>
- <item row="7" column="0" colspan="3">
- <layout class="QHBoxLayout" name="coordinatesDiveTypeLayout">
- <item>
- <widget class="QLineEdit" name="coordinates">
- <property name="readOnly">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QComboBox" name="DiveType"/>
- </item>
- </layout>
- </item>
</layout>
</widget>
</widget>
@@ -415,8 +387,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>397</width>
- <height>734</height>
+ <width>98</width>
+ <height>55</height>
</rect>
</property>
<layout class="QGridLayout" name="equipmentTabScrollAreaLayout">
@@ -449,7 +421,7 @@
</widget>
<widget class="QWidget" name="infoTab">
<attribute name="title">
- <string>Dive info</string>
+ <string>Info</string>
</attribute>
<layout class="QGridLayout" name="diveInfoLayout">
<item row="0" column="0">
@@ -471,8 +443,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>397</width>
- <height>734</height>
+ <width>330</width>
+ <height>334</height>
</rect>
</property>
<layout class="QGridLayout" name="diveInfoScrollAreaLayout">
@@ -788,8 +760,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>397</width>
- <height>734</height>
+ <width>328</width>
+ <height>208</height>
</rect>
</property>
<layout class="QGridLayout" name="statsScrollAreaLayout">
@@ -1039,6 +1011,12 @@
<extends>QListView</extends>
<header>divepicturewidget.h</header>
</customwidget>
+ <customwidget>
+ <class>QtWaitingSpinner</class>
+ <extends>QWidget</extends>
+ <header>qtwaitingspinner.h</header>
+ <container>1</container>
+ </customwidget>
</customwidgets>
<tabstops>
<tabstop>dateEdit</tabstop>
@@ -1051,7 +1029,6 @@
<tabstop>rating</tabstop>
<tabstop>visibility</tabstop>
<tabstop>suit</tabstop>
- <tabstop>tagWidget</tabstop>
<tabstop>notes</tabstop>
</tabstops>
<resources>
diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp
index 82eaaac57..a06a2c227 100644
--- a/qt-ui/mainwindow.cpp
+++ b/qt-ui/mainwindow.cpp
@@ -11,7 +11,8 @@
#include <QSettings>
#include <QShortcut>
#include <QToolBar>
-#include "ssrf-version.h"
+#include "version.h"
+#include "divelistview.h"
#include "downloadfromdivecomputer.h"
#include "preferences.h"
#include "subsurfacewebservices.h"
@@ -20,6 +21,10 @@
#include "updatemanager.h"
#include "planner.h"
#include "filtermodels.h"
+#include "profile/profilewidget2.h"
+#include "globe.h"
+#include "maintab.h"
+#include "diveplanner.h"
#ifndef NO_PRINTING
#include <QPrintDialog>
#include "printdialog.h"
@@ -27,10 +32,15 @@
#include "divelogimportdialog.h"
#include "divelogexportdialog.h"
#include "usersurvey.h"
+#include "divesitehelpers.h"
+#include "locationinformation.h"
#ifndef NO_USERMANUAL
#include "usermanual.h"
#endif
#include <QNetworkProxy>
+#include <QUndoStack>
+#include <qthelper.h>
+#include <QtConcurrentRun>
MainWindow *MainWindow::m_Instance = NULL;
@@ -44,7 +54,26 @@ MainWindow::MainWindow() : QMainWindow(),
Q_ASSERT_X(m_Instance == NULL, "MainWindow", "MainWindow recreated!");
m_Instance = this;
ui.setupUi(this);
- ui.multiFilter->hide();
+ read_hashes();
+ // Define the States of the Application Here, Currently the states are situations where the different
+ // widgets will change on the mainwindow.
+
+ // for the "default" mode
+ MainTab *mainTab = new MainTab();
+ DiveListView *diveListView = new DiveListView();
+ ProfileWidget2 *profileWidget = new ProfileWidget2();
+
+#ifndef NO_MARBLE
+ GlobeGPS *globeGps = new GlobeGPS();
+#else
+ QWidget *globeGps = NULL;
+#endif
+
+ PlannerSettingsWidget *plannerSettings = new PlannerSettingsWidget();
+ DivePlannerWidget *plannerWidget = new DivePlannerWidget();
+ PlannerDetails *plannerDetails = new PlannerDetails();
+ LocationInformationWidget *locationInformation = new LocationInformationWidget();
+
// what is a sane order for those icons? we should have the ones the user is
// most likely to want towards the top so they are always visible
// and the ones that someone likely sets and then never touches again towards the bottom
@@ -57,69 +86,82 @@ MainWindow::MainWindow() : QMainWindow(),
ui.profEad << ui.profSAC <<
ui.profHR << // very few dive computers support this
ui.profTissues; // maybe less frequently used
+
+ QToolBar *toolBar = new QToolBar();
+ Q_FOREACH (QAction *a, profileToolbarActions)
+ toolBar->addAction(a);
+ toolBar->setOrientation(Qt::Vertical);
+ toolBar->setIconSize(QSize(24,24));
+
+ QWidget *profileContainer = new QWidget();
+ QHBoxLayout *profLayout = new QHBoxLayout();
+ profLayout->addWidget(toolBar);
+ profLayout->addWidget(profileWidget);
+ profileContainer->setLayout(profLayout);
+
+ registerApplicationState("Default", mainTab, profileContainer, diveListView, globeGps );
+ registerApplicationState("AddDive", mainTab, profileContainer, diveListView, globeGps );
+ registerApplicationState("EditDive", mainTab, profileContainer, diveListView, globeGps );
+ registerApplicationState("PlanDive", plannerWidget, profileContainer, plannerSettings, plannerDetails );
+ registerApplicationState("EditPlannedDive", plannerWidget, profileContainer, diveListView, globeGps );
+ registerApplicationState("EditDiveSite",locationInformation, profileContainer, diveListView, globeGps );
+
+ setApplicationState("Default");
+
+ ui.multiFilter->hide();
+
setWindowIcon(QIcon(":subsurface-icon"));
if (!QIcon::hasThemeIcon("window-close")) {
QIcon::setThemeName("subsurface");
}
- connect(ui.ListWidget, SIGNAL(currentDiveChanged(int)), this, SLOT(current_dive_changed(int)));
+ connect(dive_list(), SIGNAL(currentDiveChanged(int)), this, SLOT(current_dive_changed(int)));
connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), this, SLOT(readSettings()));
- connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), ui.ListWidget, SLOT(update()));
- connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), ui.ListWidget, SLOT(reloadHeaderActions()));
- connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), ui.InfoWidget, SLOT(updateDiveInfo()));
- connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), ui.divePlannerWidget, SLOT(settingsChanged()));
- connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), ui.plannerSettingsWidget, SLOT(settingsChanged()));
+ connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), diveListView, SLOT(update()));
+ connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), diveListView, SLOT(reloadHeaderActions()));
+ connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), information(), SLOT(updateDiveInfo()));
+ connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), divePlannerWidget(), SLOT(settingsChanged()));
+ connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), divePlannerSettingsWidget(), SLOT(settingsChanged()));
connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), TankInfoModel::instance(), SLOT(update()));
connect(ui.actionRecent1, SIGNAL(triggered(bool)), this, SLOT(recentFileTriggered(bool)));
connect(ui.actionRecent2, SIGNAL(triggered(bool)), this, SLOT(recentFileTriggered(bool)));
connect(ui.actionRecent3, SIGNAL(triggered(bool)), this, SLOT(recentFileTriggered(bool)));
connect(ui.actionRecent4, SIGNAL(triggered(bool)), this, SLOT(recentFileTriggered(bool)));
- connect(information(), SIGNAL(addDiveFinished()), ui.newProfile, SLOT(setProfileState()));
+ connect(information(), SIGNAL(addDiveFinished()), graphics(), SLOT(setProfileState()));
connect(DivePlannerPointsModel::instance(), SIGNAL(planCreated()), this, SLOT(planCreated()));
connect(DivePlannerPointsModel::instance(), SIGNAL(planCanceled()), this, SLOT(planCanceled()));
- connect(ui.printPlan, SIGNAL(pressed()), ui.divePlannerWidget, SLOT(printDecoPlan()));
+ connect(plannerDetails->printPlan(), SIGNAL(pressed()), divePlannerWidget(), SLOT(printDecoPlan()));
+ connect(mainTab, SIGNAL(requestDiveSiteEdit(uint32_t)), this, SLOT(enableDiveSiteEdit(uint32_t)));
+ connect(locationInformation, SIGNAL(informationManagementEnded()), this, SLOT(setDefaultState()));
+ connect(locationInformation, SIGNAL(informationManagementEnded()), information(), SLOT(showLocation()));
+
#ifdef NO_PRINTING
- ui.printPlan->hide();
+ plannerDetails->printPlan()->hide();
+ ui.menuFile->removeAction(ui.actionPrint);
#endif
ui.mainErrorMessage->hide();
- ui.newProfile->setEmptyState();
+ graphics()->setEmptyState();
initialUiSetup();
readSettings();
- ui.ListWidget->reload(DiveTripModel::TREE);
- ui.ListWidget->reloadHeaderActions();
- ui.ListWidget->setFocus();
- ui.globe->reload();
- ui.ListWidget->expand(ui.ListWidget->model()->index(0, 0));
- ui.ListWidget->scrollTo(ui.ListWidget->model()->index(0, 0), QAbstractItemView::PositionAtCenter);
- ui.divePlannerWidget->settingsChanged();
- ui.plannerSettingsWidget->settingsChanged();
+ diveListView->reload(DiveTripModel::TREE);
+ diveListView->reloadHeaderActions();
+ diveListView->setFocus();
+ globe()->reload();
+ diveListView->expand(dive_list()->model()->index(0, 0));
+ diveListView->scrollTo(dive_list()->model()->index(0, 0), QAbstractItemView::PositionAtCenter);
+ divePlannerWidget()->settingsChanged();
+ divePlannerSettingsWidget()->settingsChanged();
#ifdef NO_MARBLE
- ui.globePane->hide();
ui.menuView->removeAction(ui.actionViewGlobe);
#else
- connect(ui.globe, SIGNAL(coordinatesChanged()), ui.InfoWidget, SLOT(updateGpsCoordinates()));
+ connect(globe(), SIGNAL(coordinatesChanged()), locationInformation, SLOT(updateGpsCoordinates()));
#endif
#ifdef NO_USERMANUAL
ui.menuHelp->removeAction(ui.actionUserManual);
#endif
-#ifdef NO_PRINTING
- ui.menuFile->removeAction(ui.actionPrint);
-#endif
memset(&copyPasteDive, 0, sizeof(copyPasteDive));
memset(&what, 0, sizeof(what));
- QToolBar *toolBar = new QToolBar();
- Q_FOREACH (QAction *a, profileToolbarActions)
- toolBar->addAction(a);
- toolBar->setOrientation(Qt::Vertical);
- toolBar->setIconSize(QSize(24,24));
- // since I'm adding the toolBar by hand, because designer
- // has no concept of "toolbar" for a non-mainwindow widget (...)
- // I need to take the current item that's in the toolbar Position
- // and reposition it alongside the grid layout.
- QLayoutItem *p = ui.profileInnerLayout->takeAt(0);
- ui.profileInnerLayout->addWidget(toolBar, 0, 0);
- ui.profileInnerLayout->addItem(p, 0, 1);
// and now for some layout hackery
// this gets us consistent margins everywhere and a much more balanced look
@@ -146,19 +188,52 @@ MainWindow::MainWindow() : QMainWindow(),
else
layout->setContentsMargins(margins);
}
- margins = QMargins(0, 5, 5, 5);
- ui.profileInnerLayout->setContentsMargins(margins);
- ui.profileInnerLayout->setSpacing(0);
toolBar->setContentsMargins(zeroMargins);
updateManager = new UpdateManager(this);
+
+ undoStack = new QUndoStack(this);
+ QAction *undoAction = undoStack->createUndoAction(this, tr("&Undo"));
+ QAction *redoAction = undoStack->createRedoAction(this, tr("&Redo"));
+ undoAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Z));
+ redoAction->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Z));
+ QList<QAction*>undoRedoActions;
+ undoRedoActions.append(undoAction);
+ undoRedoActions.append(redoAction);
+ ui.menu_Edit->addActions(undoRedoActions);
+
+ ReverseGeoLoockupThread *geoLoockup = ReverseGeoLoockupThread::instance();
+ connect(geoLoockup, SIGNAL(started()),information(), SLOT(disableGeoLoockupEdition()));
+ connect(geoLoockup, SIGNAL(finished()), information(), SLOT(enableGeoLoockupEdition()));
}
MainWindow::~MainWindow()
{
+ write_hashes();
m_Instance = NULL;
}
+PlannerDetails *MainWindow::plannerDetails() const {
+ return qobject_cast<PlannerDetails*>(applicationState["PlanDive"].bottomRight);
+}
+
+PlannerSettingsWidget *MainWindow::divePlannerSettingsWidget() {
+ return qobject_cast<PlannerSettingsWidget*>(applicationState["PlanDive"].bottomLeft);
+}
+
+LocationInformationWidget *MainWindow::locationInformationWidget() {
+ return qobject_cast<LocationInformationWidget*>(applicationState["EditDiveSite"].topLeft);
+}
+
+void MainWindow::enableDiveSiteEdit(uint32_t id) {
+ locationInformationWidget()->setLocationId(displayed_dive.dive_site_uuid);
+ setApplicationState("EditDiveSite");
+}
+
+void MainWindow::setDefaultState() {
+ setApplicationState("Default");
+}
+
void MainWindow::setLoadedWithFiles(bool f)
{
filesAsArguments = f;
@@ -177,19 +252,16 @@ MainWindow *MainWindow::instance()
// this gets called after we download dives from a divecomputer
void MainWindow::refreshDisplay(bool doRecreateDiveList)
{
- showError(get_error_string());
- ui.InfoWidget->reload();
+ getNotificationWidget()->showNotification(get_error_string(), KMessageWidget::Error);
+ information()->reload();
TankInfoModel::instance()->update();
- ui.globe->reload();
+ globe()->reload();
if (doRecreateDiveList)
recreateDiveList();
- ui.diveListPane->setCurrentIndex(0); // switch to the dive list
-#ifdef NO_MARBLE
- ui.globePane->hide();
-#endif
- ui.globePane->setCurrentIndex(0);
- ui.ListWidget->setEnabled(true);
- ui.ListWidget->setFocus();
+
+ setApplicationState("Default");
+ dive_list()->setEnabled(true);
+ dive_list()->setFocus();
WSInfoModel::instance()->updateInfo();
if (amount_selected == 0)
cleanUpEmpty();
@@ -197,7 +269,7 @@ void MainWindow::refreshDisplay(bool doRecreateDiveList)
void MainWindow::recreateDiveList()
{
- ui.ListWidget->reload(DiveTripModel::CURRENT);
+ dive_list()->reload(DiveTripModel::CURRENT);
TagFilterModel::instance()->repopulate();
BuddyFilterModel::instance()->repopulate();
LocationFilterModel::instance()->repopulate();
@@ -208,10 +280,11 @@ void MainWindow::current_dive_changed(int divenr)
{
if (divenr >= 0) {
select_dive(divenr);
- ui.globe->centerOnCurrentDive();
+ globe()->centerOnCurrentDive();
}
- ui.newProfile->plotDive();
- ui.InfoWidget->updateDiveInfo();
+ graphics()->plotDive();
+ information()->updateDiveInfo();
+ locationInformationWidget()->setLocationId(displayed_dive.dive_site_uuid);
}
void MainWindow::on_actionNew_triggered()
@@ -252,20 +325,49 @@ void MainWindow::on_actionSaveAs_triggered()
file_save_as();
}
+void learnImageDirs(QStringList dirnames)
+{
+ QList<QFuture<void> > futures;
+ foreach (QString dir, dirnames) {
+ futures << QtConcurrent::run(learnImages, QDir(dir), 10, false);
+ }
+ DivePictureModel::instance()->updateDivePicturesWhenDone(futures);
+}
+
+void MainWindow::on_actionHash_images_triggered()
+{
+ QFuture<void> future;
+ QFileDialog dialog(this, tr("Traverse image directories"), lastUsedDir(), filter());
+ dialog.setFileMode(QFileDialog::Directory);
+ dialog.setViewMode(QFileDialog::Detail);
+ dialog.setLabelText(QFileDialog::Accept, tr("Scan"));
+ dialog.setLabelText(QFileDialog::Reject, tr("Cancel"));
+ QStringList dirnames;
+ if (dialog.exec())
+ dirnames = dialog.selectedFiles();
+ if (dirnames.isEmpty())
+ return;
+ future = QtConcurrent::run(learnImageDirs,dirnames);
+ MainWindow::instance()->getNotificationWidget()->showNotification(tr("Scanning images...(this can take a while)"), KMessageWidget::Information);
+ MainWindow::instance()->getNotificationWidget()->setFuture(future);
+
+}
+
ProfileWidget2 *MainWindow::graphics() const
{
- return ui.newProfile;
+ return qobject_cast<ProfileWidget2*>(applicationState["Default"].topRight->layout()->itemAt(1)->widget());
}
void MainWindow::cleanUpEmpty()
{
- ui.InfoWidget->clearStats();
- ui.InfoWidget->clearInfo();
- ui.InfoWidget->clearEquipment();
- ui.InfoWidget->updateDiveInfo(true);
- ui.newProfile->setEmptyState();
- ui.ListWidget->reload(DiveTripModel::TREE);
- ui.globe->reload();
+ information()->clearStats();
+ information()->clearInfo();
+ information()->clearEquipment();
+ information()->updateDiveInfo(true);
+ graphics()->setEmptyState();
+ dive_list()->reload(DiveTripModel::TREE);
+ locationInformationWidget()->setLocationId(0);
+ globe()->reload();
if (!existing_filename)
setTitle(MWTF_DEFAULT);
disableShortcuts();
@@ -274,7 +376,8 @@ void MainWindow::cleanUpEmpty()
bool MainWindow::okToClose(QString message)
{
if (DivePlannerPointsModel::instance()->currentMode() != DivePlannerPointsModel::NOTHING ||
- ui.InfoWidget->isEditing()) {
+ information()->isEditing() ||
+ currentApplicationState == "EditDiveSite") {
QMessageBox::warning(this, tr("Warning"), message);
return false;
}
@@ -286,11 +389,13 @@ bool MainWindow::okToClose(QString message)
void MainWindow::closeCurrentFile()
{
- ui.newProfile->setEmptyState();
+ graphics()->setEmptyState();
/* free the dives and trips */
clear_git_id();
while (dive_table.nr)
delete_single_dive(0);
+ while (dive_site_table.nr)
+ delete_dive_site(get_dive_site(0)->uuid);
free((void *)existing_filename);
existing_filename = NULL;
@@ -362,8 +467,8 @@ void MainWindow::enableShortcuts()
void MainWindow::showProfile()
{
enableShortcuts();
- ui.newProfile->setProfileState();
- ui.infoPane->setCurrentIndex(MAINTAB);
+ graphics()->setProfileState();
+ setApplicationState("Default");
}
void MainWindow::on_actionPreferences_triggered()
@@ -373,9 +478,9 @@ void MainWindow::on_actionPreferences_triggered()
void MainWindow::on_actionQuit_triggered()
{
- if (ui.InfoWidget->isEditing()) {
- ui.InfoWidget->rejectChanges();
- if (ui.InfoWidget->isEditing())
+ if (information()->isEditing()) {
+ information()->rejectChanges();
+ if (information()->isEditing())
// didn't discard the edits
return;
}
@@ -421,7 +526,7 @@ void MainWindow::on_actionEditDeviceNames_triggered()
bool MainWindow::plannerStateClean()
{
if (DivePlannerPointsModel::instance()->currentMode() != DivePlannerPointsModel::NOTHING ||
- ui.InfoWidget->isEditing()) {
+ information()->isEditing()) {
QMessageBox::warning(this, tr("Warning"), tr("Please save or cancel the current dive edit before trying to add a dive."));
return false;
}
@@ -433,16 +538,16 @@ void MainWindow::planCanceled()
// while planning we might have modified the displayed_dive
// let's refresh what's shown on the profile
showProfile();
- ui.newProfile->replot();
+ graphics()->replot();
refreshDisplay(false);
- ui.newProfile->plotDive(get_dive(selected_dive));
+ graphics()->plotDive(get_dive(selected_dive));
DivePictureModel::instance()->updateDivePictures();
}
void MainWindow::planCreated()
{
// get the new dive selected and assign a number if reasonable
- ui.newProfile->setProfileState();
+ graphics()->setProfileState();
if (displayed_dive.id == 0) {
// we might have added a new dive (so displayed_dive was cleared out by clone_dive()
dive_list()->unselectDives();
@@ -451,20 +556,20 @@ void MainWindow::planCreated()
set_dive_nr_for_current_dive();
}
// make sure our UI is in a consistent state
- ui.InfoWidget->updateDiveInfo();
+ information()->updateDiveInfo();
showProfile();
refreshDisplay();
}
void MainWindow::setPlanNotes(const char *notes)
{
- ui.divePlanOutput->setHtml(notes);
+ plannerDetails()->divePlanOutput()->setHtml(notes);
}
void MainWindow::printPlan()
{
#ifndef NO_PRINTING
- QString diveplan = ui.divePlanOutput->toHtml();
+ QString diveplan = plannerDetails()->divePlanOutput()->toHtml();
QString withDisclaimer = QString("<img height=50 src=\":subsurface-icon\"> ") + diveplan + QString(disclaimer);
QPrinter printer;
@@ -473,9 +578,9 @@ void MainWindow::printPlan()
if (dialog->exec() != QDialog::Accepted)
return;
- ui.divePlanOutput->setHtml(withDisclaimer);
- ui.divePlanOutput->print(&printer);
- ui.divePlanOutput->setHtml(diveplan);
+ plannerDetails()->divePlanOutput()->setHtml(withDisclaimer);
+ plannerDetails()->divePlanOutput()->print(&printer);
+ plannerDetails()->divePlanOutput()->setHtml(diveplan);
#endif
}
@@ -489,31 +594,28 @@ void MainWindow::setupForAddAndPlan(const char *model)
// setup the dive cylinders
DivePlannerPointsModel::instance()->clear();
DivePlannerPointsModel::instance()->setupCylinders();
+ locationInformationWidget()->setLocationId(0);
}
void MainWindow::on_actionReplanDive_triggered()
{
- if (!plannerStateClean())
- return;
- if (!current_dive || !current_dive->dc.model || strcmp(current_dive->dc.model, "planned dive")) {
- qDebug() << "trying to replan a dive that's not a planned dive:" << current_dive->dc.model;
+ if (!plannerStateClean() || !current_dive || !current_dive->dc.model)
return;
+ else if (strcmp(current_dive->dc.model, "planned dive")) {
+ if (QMessageBox::warning(this, tr("Warning"), tr("trying to replan a dive that's not a planned dive."),
+ QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel)
+ return;
}
// put us in PLAN mode
DivePlannerPointsModel::instance()->clear();
DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::PLAN);
- ui.newProfile->setPlanState();
- ui.newProfile->clearHandlers();
- ui.infoPane->setCurrentIndex(PLANNERWIDGET);
- ui.divePlannerWidget->setReplanButton(true);
+ graphics()->setPlanState();
+ graphics()->clearHandlers();
+ setApplicationState("PlanDive");
+ divePlannerWidget()->setReplanButton(true);
DivePlannerPointsModel::instance()->loadFromDive(current_dive);
reset_cylinders(&displayed_dive, true);
- ui.diveListPane->setCurrentIndex(1); // switch to the plan output
- ui.globePane->setCurrentIndex(1);
-#ifdef NO_MARBLE
- ui.globePane->show();
-#endif
}
void MainWindow::on_actionDivePlanner_triggered()
@@ -523,22 +625,20 @@ void MainWindow::on_actionDivePlanner_triggered()
// put us in PLAN mode
DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::PLAN);
+ setApplicationState("PlanDive");
- ui.newProfile->setPlanState();
- ui.infoPane->setCurrentIndex(PLANNERWIDGET);
+ graphics()->setPlanState();
- // create a simple starting dive, using the first gas from the just copied cylidners
+ // create a simple starting dive, using the first gas from the just copied cylinders
setupForAddAndPlan("planned dive"); // don't translate, stored in XML file
DivePlannerPointsModel::instance()->setupStartTime();
DivePlannerPointsModel::instance()->createSimpleDive();
DivePictureModel::instance()->updateDivePictures();
- ui.divePlannerWidget->setReplanButton(false);
+ divePlannerWidget()->setReplanButton(false);
+}
- ui.diveListPane->setCurrentIndex(1); // switch to the plan output
- ui.globePane->setCurrentIndex(1);
-#ifdef NO_MARBLE
- ui.globePane->show();
-#endif
+DivePlannerWidget* MainWindow::divePlannerWidget() {
+ return qobject_cast<DivePlannerWidget*>(applicationState["PlanDive"].topLeft);
}
void MainWindow::on_actionAddDive_triggered()
@@ -551,23 +651,45 @@ void MainWindow::on_actionAddDive_triggered()
dive_list()->clearSelection();
}
+ setApplicationState("AddDive");
DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::ADD);
// setup things so we can later create our starting dive
setupForAddAndPlan("manually added dive"); // don't translate, stored in the XML file
// now show the mostly empty main tab
- ui.InfoWidget->updateDiveInfo();
+ information()->updateDiveInfo();
// show main tab
- ui.InfoWidget->setCurrentIndex(0);
+ information()->setCurrentIndex(0);
- ui.InfoWidget->addDiveStarted();
- ui.infoPane->setCurrentIndex(MAINTAB);
+ information()->addDiveStarted();
- ui.newProfile->setAddState();
+ graphics()->setAddState();
DivePlannerPointsModel::instance()->createSimpleDive();
- ui.newProfile->plotDive();
+ graphics()->plotDive();
+}
+
+void MainWindow::on_actionEditDive_triggered()
+{
+ if (information()->isEditing() || DivePlannerPointsModel::instance()->currentMode() != DivePlannerPointsModel::NOTHING) {
+ QMessageBox::warning(this, tr("Warning"), tr("Please, first finish the current edition before trying to do another."));
+ return;
+ }
+
+ const bool isTripEdit = dive_list()->selectedTrips().count() >= 1;
+ if (!current_dive || isTripEdit || strcmp(current_dive->dc.model, "manually added dive")) {
+ QMessageBox::warning(this, tr("Warning"), tr("Trying to edit a dive that's not a manually added dive."));
+ return;
+ }
+
+ DivePlannerPointsModel::instance()->clear();
+ disableShortcuts();
+ DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::ADD);
+ graphics()->setAddState();
+ setApplicationState("EditDive");
+ DivePlannerPointsModel::instance()->loadFromDive(current_dive);
+ information()->enableEdition(MainTab::MANUALLY_ADDED_DIVE);
}
void MainWindow::on_actionRenumber_triggered()
@@ -612,16 +734,16 @@ void MainWindow::on_actionYearlyStatistics_triggered()
#define TOGGLE_COLLAPSABLE( X ) \
ui.mainSplitter->setCollapsible(0, X); \
ui.mainSplitter->setCollapsible(1, X); \
- ui.infoProfileSplitter->setCollapsible(0, X); \
- ui.infoProfileSplitter->setCollapsible(1, X); \
- ui.listGlobeSplitter->setCollapsible(0, X); \
- ui.listGlobeSplitter->setCollapsible(1, X);
+ ui.topSplitter->setCollapsible(0, X); \
+ ui.topSplitter->setCollapsible(1, X); \
+ ui.bottomSplitter->setCollapsible(0, X); \
+ ui.bottomSplitter->setCollapsible(1, X);
void MainWindow::on_actionViewList_triggered()
{
TOGGLE_COLLAPSABLE( true );
beginChangeState(LIST_MAXIMIZED);
- ui.listGlobeSplitter->setSizes(BEHAVIOR << EXPANDED << COLLAPSED);
+ ui.topSplitter->setSizes(BEHAVIOR << EXPANDED << COLLAPSED);
ui.mainSplitter->setSizes(BEHAVIOR << COLLAPSED << EXPANDED);
}
@@ -629,7 +751,7 @@ void MainWindow::on_actionViewProfile_triggered()
{
TOGGLE_COLLAPSABLE( true );
beginChangeState(PROFILE_MAXIMIZED);
- ui.infoProfileSplitter->setSizes(BEHAVIOR << COLLAPSED << EXPANDED);
+ ui.topSplitter->setSizes(BEHAVIOR << COLLAPSED << EXPANDED);
ui.mainSplitter->setSizes(BEHAVIOR << EXPANDED << COLLAPSED);
}
@@ -637,7 +759,7 @@ void MainWindow::on_actionViewInfo_triggered()
{
TOGGLE_COLLAPSABLE( true );
beginChangeState(INFO_MAXIMIZED);
- ui.infoProfileSplitter->setSizes(BEHAVIOR << EXPANDED << COLLAPSED);
+ ui.topSplitter->setSizes(BEHAVIOR << EXPANDED << COLLAPSED);
ui.mainSplitter->setSizes(BEHAVIOR << EXPANDED << COLLAPSED);
}
@@ -646,7 +768,7 @@ void MainWindow::on_actionViewGlobe_triggered()
TOGGLE_COLLAPSABLE( true );
beginChangeState(GLOBE_MAXIMIZED);
ui.mainSplitter->setSizes(BEHAVIOR << COLLAPSED << EXPANDED);
- ui.listGlobeSplitter->setSizes(BEHAVIOR << COLLAPSED << EXPANDED);
+ ui.bottomSplitter->setSizes(BEHAVIOR << COLLAPSED << EXPANDED);
}
#undef BEHAVIOR
@@ -677,26 +799,26 @@ void MainWindow::on_actionViewAll_triggered()
settings.beginGroup("MainWindow");
if (settings.value("mainSplitter").isValid()) {
ui.mainSplitter->restoreState(settings.value("mainSplitter").toByteArray());
- ui.infoProfileSplitter->restoreState(settings.value("infoProfileSplitter").toByteArray());
- ui.listGlobeSplitter->restoreState(settings.value("listGlobeSplitter").toByteArray());
+ ui.topSplitter->restoreState(settings.value("topSplitter").toByteArray());
+ ui.bottomSplitter->restoreState(settings.value("bottomSplitter").toByteArray());
if (ui.mainSplitter->sizes().first() == 0 || ui.mainSplitter->sizes().last() == 0)
ui.mainSplitter->setSizes(mainSizes);
- if (ui.infoProfileSplitter->sizes().first() == 0 || ui.infoProfileSplitter->sizes().last() == 0)
- ui.infoProfileSplitter->setSizes(infoProfileSizes);
- if (ui.listGlobeSplitter->sizes().first() == 0 || ui.listGlobeSplitter->sizes().last() == 0)
- ui.listGlobeSplitter->setSizes(listGlobeSizes);
+ if (ui.topSplitter->sizes().first() == 0 || ui.topSplitter->sizes().last() == 0)
+ ui.topSplitter->setSizes(infoProfileSizes);
+ if (ui.bottomSplitter->sizes().first() == 0 || ui.bottomSplitter->sizes().last() == 0)
+ ui.bottomSplitter->setSizes(listGlobeSizes);
} else {
ui.mainSplitter->setSizes(mainSizes);
- ui.infoProfileSplitter->setSizes(infoProfileSizes);
- ui.listGlobeSplitter->setSizes(listGlobeSizes);
+ ui.topSplitter->setSizes(infoProfileSizes);
+ ui.bottomSplitter->setSizes(listGlobeSizes);
}
ui.mainSplitter->setCollapsible(0, false);
ui.mainSplitter->setCollapsible(1, false);
- ui.infoProfileSplitter->setCollapsible(0, false);
- ui.infoProfileSplitter->setCollapsible(1, false);
- ui.listGlobeSplitter->setCollapsible(0,false);
- ui.listGlobeSplitter->setCollapsible(1,false);
+ ui.topSplitter->setCollapsible(0, false);
+ ui.topSplitter->setCollapsible(1, false);
+ ui.bottomSplitter->setCollapsible(0,false);
+ ui.bottomSplitter->setCollapsible(1,false);
}
#undef TOGGLE_COLLAPSABLE
@@ -714,24 +836,24 @@ void MainWindow::saveSplitterSizes()
QSettings settings;
settings.beginGroup("MainWindow");
settings.setValue("mainSplitter", ui.mainSplitter->saveState());
- settings.setValue("infoProfileSplitter", ui.infoProfileSplitter->saveState());
- settings.setValue("listGlobeSplitter", ui.listGlobeSplitter->saveState());
+ settings.setValue("topSplitter", ui.topSplitter->saveState());
+ settings.setValue("bottomSplitter", ui.bottomSplitter->saveState());
}
void MainWindow::on_actionPreviousDC_triggered()
{
unsigned nrdc = number_of_computers(current_dive);
dc_number = (dc_number + nrdc - 1) % nrdc;
- ui.newProfile->plotDive();
- ui.InfoWidget->updateDiveInfo();
+ graphics()->plotDive();
+ information()->updateDiveInfo();
}
void MainWindow::on_actionNextDC_triggered()
{
unsigned nrdc = number_of_computers(current_dive);
dc_number = (dc_number + 1) % nrdc;
- ui.newProfile->plotDive();
- ui.InfoWidget->updateDiveInfo();
+ graphics()->plotDive();
+ information()->updateDiveInfo();
}
void MainWindow::on_actionFullScreen_triggered(bool checked)
@@ -802,6 +924,7 @@ QString MainWindow::filter()
f += "UDDF (*.uddf *.UDDF);;";
f += "XML (*.xml *.XML)";
f += "Divesoft (*.dlf *.DLF)";
+ f += "Datatrak/WLog Files (*.log *.LOG)";
return f;
}
@@ -937,7 +1060,7 @@ void MainWindow::checkSurvey(QSettings *s)
s->setValue("FirstUse42", value);
}
// wait a week for production versions, but not at all for non-tagged builds
- QString ver(VERSION_STRING);
+ QString ver(subsurface_version());
int waitTime = 7;
QDate firstUse42 = s->value("FirstUse42").toDate();
if (run_survey || (firstUse42.daysTo(QDate().currentDate()) > waitTime && !s->contains("SurveyDone"))) {
@@ -965,7 +1088,7 @@ void MainWindow::writeSettings()
void MainWindow::closeEvent(QCloseEvent *event)
{
if (DivePlannerPointsModel::instance()->currentMode() != DivePlannerPointsModel::NOTHING ||
- ui.InfoWidget->isEditing()) {
+ information()->isEditing()) {
on_actionQuit_triggered();
event->ignore();
return;
@@ -994,17 +1117,17 @@ void MainWindow::closeEvent(QCloseEvent *event)
DiveListView *MainWindow::dive_list()
{
- return ui.ListWidget;
+ return qobject_cast<DiveListView*>(applicationState["Default"].bottomLeft);
}
GlobeGPS *MainWindow::globe()
{
- return ui.globe;
+ return qobject_cast<GlobeGPS*>(applicationState["Default"].bottomRight);
}
MainTab *MainWindow::information()
{
- return ui.InfoWidget;
+ return qobject_cast<MainTab*>(applicationState["Default"].topLeft);
}
void MainWindow::loadRecentFiles(QSettings *s)
@@ -1174,20 +1297,31 @@ int MainWindow::file_save_as(void)
{
QString filename;
const char *default_filename = existing_filename;
- filename = QFileDialog::getSaveFileName(this, tr("Save file as"), default_filename,
- tr("Subsurface XML files (*.ssrf *.xml *.XML)"));
+
+ // create a file dialog that allows us to save to a new file
+ QFileDialog selection_dialog(this, tr("Save file as"), default_filename,
+ tr("Subsurface XML files (*.ssrf *.xml *.XML)"));
+ selection_dialog.setAcceptMode(QFileDialog::AcceptSave);
+ selection_dialog.setFileMode(QFileDialog::AnyFile);
+
+ /* if the exit/cancel button is pressed return */
+ if (!selection_dialog.exec())
+ return 0;
+
+ /* get the first selected file */
+ filename = selection_dialog.selectedFiles().at(0);
if (filename.isNull() || filename.isEmpty())
return report_error("No filename to save into");
- if (ui.InfoWidget->isEditing())
- ui.InfoWidget->acceptChanges();
+ if (information()->isEditing())
+ information()->acceptChanges();
if (save_dives(filename.toUtf8().data())) {
- showError(get_error_string());
+ getNotificationWidget()->showNotification(get_error_string(), KMessageWidget::Error);
return -1;
}
- showError(get_error_string());
+ getNotificationWidget()->showNotification(get_error_string(), KMessageWidget::Error);
set_filename(filename.toUtf8().data(), true);
setTitle(MWTF_FILENAME);
mark_divelist_changed(false);
@@ -1202,8 +1336,8 @@ int MainWindow::file_save(void)
if (!existing_filename)
return file_save_as();
- if (ui.InfoWidget->isEditing())
- ui.InfoWidget->acceptChanges();
+ if (information()->isEditing())
+ information()->acceptChanges();
current_default = prefs.default_filename;
if (strcmp(existing_filename, current_default) == 0) {
@@ -1214,23 +1348,18 @@ int MainWindow::file_save(void)
current_def_dir.mkpath(current_def_dir.absolutePath());
}
if (save_dives(existing_filename)) {
- showError(get_error_string());
+ getNotificationWidget()->showNotification(get_error_string(), KMessageWidget::Error);
return -1;
}
- showError(get_error_string());
+ getNotificationWidget()->showNotification(get_error_string(), KMessageWidget::Error);
mark_divelist_changed(false);
addRecentFile(QStringList() << QString(existing_filename));
return 0;
}
-void MainWindow::showError(QString message)
+NotificationWidget *MainWindow::getNotificationWidget()
{
- if (message.isEmpty())
- return;
- ui.mainErrorMessage->setText(message);
- ui.mainErrorMessage->setCloseButtonVisible(true);
- ui.mainErrorMessage->setMessageType(KMessageWidget::Error);
- ui.mainErrorMessage->animatedShow();
+ return ui.mainErrorMessage;
}
void MainWindow::setTitle(enum MainWindowTitleFormat format)
@@ -1284,6 +1413,18 @@ void MainWindow::importTxtFiles(const QStringList fileNames)
refreshDisplay();
}
+void MainWindow::showV2Dialog()
+{
+ // here we need to ask the user if / how they want to do the reverse geo coding
+ // for now this is just a warning that things could take a long time
+ QMessageBox d(QMessageBox::Information,
+ tr("Welcom to Subsurface %1").arg(subsurface_version()),
+ tr("Importing data files from earlier versions of Subsurface can take a significant amount of time"),
+ QMessageBox::Ok,
+ this);
+ d.exec();
+}
+
void MainWindow::loadFiles(const QStringList fileNames)
{
if (fileNames.isEmpty())
@@ -1301,14 +1442,26 @@ void MainWindow::loadFiles(const QStringList fileNames)
set_filename(fileNamePtr.data(), true);
setTitle(MWTF_FILENAME);
} else {
+ if (!v2_question_shown && abort_read_of_old_file) {
+ v2_question_shown = true;
+ abort_read_of_old_file = false;
+ showV2Dialog();
+ getNotificationWidget()->showNotification(tr("Please Wait, Importing your files..."), KMessageWidget::Information);
+ i--; // so we re-try this file
+ continue;
+ }
failedParses.append(fileNames.at(i));
}
}
-
+ getNotificationWidget()->hideNotification();
process_dives(false, false);
addRecentFile(fileNames);
removeRecentFile(failedParses);
+ // searches for geo lookup information in a thread so it doesn`t
+ // freezes the ui.
+ ReverseGeoLoockupThread::instance()->start();
+
refreshDisplay();
ui.actionAutoGroup->setChecked(autogroup);
}
@@ -1316,18 +1469,21 @@ void MainWindow::loadFiles(const QStringList fileNames)
void MainWindow::on_actionImportDiveLog_triggered()
{
QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Open dive log file"), lastUsedDir(),
- tr("Dive log files (*.ssrf *.can *.csv *.db *.dld *.jlb *.lvd *.sde *.udcf *.uddf *.xml *.txt *.dlf *.apd);;"
- "Cochran files (*.can);;"
- "CSV files (*.csv);;"
- "DiveLog.de files (*.dld);;"
- "JDiveLog files (*.jlb);;"
- "Liquivision files (*.lvd);;"
- "MkVI files (*.txt);;"
- "Suunto files (*.sde *.db);;"
- "Divesoft files (*.dlf);;"
- "UDDF/UDCF files (*.uddf *.udcf);;"
- "XML files (*.xml);;"
- "APD log viewer (*.apd);;"
+ tr("Dive log files (*.ssrf *.can *.csv *.db *.dld *.jlb *.lvd *.sde *.udcf *.uddf *.xml *.txt *.dlf *.apd"
+ "*.SSRF *.CAN *.CSV *.DB *.DLD *.JLB *.LVD *.SDE *.UDCF *.UDDF *.xml *.TXT *.DLF *.APD);;"
+ "Cochran files (*.can *.CAN);;"
+ "CSV files (*.csv *.CSV);;"
+ "DiveLog.de files (*.dld *.DLD);;"
+ "JDiveLog files (*.jlb *.JLB);;"
+ "Liquivision files (*.lvd *.LVD);;"
+ "MkVI files (*.txt *.TXT);;"
+ "Suunto files (*.sde *.db *.SDE *.DB);;"
+ "Divesoft files (*.dlf *.DLF);;"
+ "UDDF/UDCF files (*.uddf *.udcf *.UDDF *.UDCF);;"
+ "XML files (*.xml *.XML);;"
+ "APD log viewer (*.apd *.APD);;"
+ "Datatrak/WLog Files (*.log *.LOG);;"
+ "OSTCtools Files (*.dive *.DIVE);;"
"All files (*)"));
if (fileNames.isEmpty())
@@ -1368,20 +1524,16 @@ void MainWindow::editCurrentDive()
if (defaultDC == "manually added dive") {
disableShortcuts();
DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::ADD);
- ui.newProfile->setAddState();
- ui.infoPane->setCurrentIndex(MAINTAB);
+ graphics()->setAddState();
+ setApplicationState("EditDive");
DivePlannerPointsModel::instance()->loadFromDive(d);
- ui.InfoWidget->enableEdition(MainTab::MANUALLY_ADDED_DIVE);
+ information()->enableEdition(MainTab::MANUALLY_ADDED_DIVE);
} else if (defaultDC == "planned dive") {
disableShortcuts();
DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::PLAN);
- //TODO: I BROKE THIS BY COMMENTING THE LINE BELOW
- // and I'm sleepy now, so I think I should not try to fix right away.
- // we don't setCurrentIndex anymore, we ->setPlanState() or ->setAddState() on the ProfileView.
- //ui.stackedWidget->setCurrentIndex(PLANNERPROFILE); // Planner.
- ui.infoPane->setCurrentIndex(PLANNERWIDGET);
+ setApplicationState("EditPlannedDive");
DivePlannerPointsModel::instance()->loadFromDive(d);
- ui.InfoWidget->enableEdition(MainTab::MANUALLY_ADDED_DIVE);
+ information()->enableEdition(MainTab::MANUALLY_ADDED_DIVE);
}
}
@@ -1456,7 +1608,7 @@ void MainWindow::on_paste_triggered()
{
// take the data in our copyPasteDive and apply it to selected dives
selective_copy_dive(&copyPasteDive, &displayed_dive, what, false);
- ui.InfoWidget->showAndTriggerEditSelective(what);
+ information()->showAndTriggerEditSelective(what);
}
void MainWindow::on_actionFilterTags_triggered()
@@ -1466,3 +1618,43 @@ void MainWindow::on_actionFilterTags_triggered()
else
ui.multiFilter->setVisible(true);
}
+
+void MainWindow::registerApplicationState(const QByteArray& state, QWidget *topLeft, QWidget *topRight, QWidget *bottomLeft, QWidget *bottomRight)
+{
+ applicationState[state] = WidgetForQuadrant(topLeft, topRight, bottomLeft, bottomRight);
+ if (ui.topLeft->indexOf(topLeft) == -1 && topLeft) {
+ ui.topLeft->addWidget(topLeft);
+ }
+ if (ui.topRight->indexOf(topRight) == -1 && topRight) {
+ ui.topRight->addWidget(topRight);
+ }
+ if (ui.bottomLeft->indexOf(bottomLeft) == -1 && bottomLeft) {
+ ui.bottomLeft->addWidget(bottomLeft);
+ }
+ if(ui.bottomRight->indexOf(bottomRight) == -1 && bottomRight) {
+ ui.bottomRight->addWidget(bottomRight);
+ }
+}
+
+void MainWindow::setApplicationState(const QByteArray& state) {
+ if (!applicationState.keys().contains(state))
+ return;
+
+ if (currentApplicationState == state)
+ return;
+
+ currentApplicationState = state;
+#define SET_CURRENT_INDEX( X ) \
+ if (applicationState[state].X) { \
+ ui.X->setCurrentWidget( applicationState[state].X); \
+ ui.X->show(); \
+ } else { \
+ ui.X->hide(); \
+ }
+
+ SET_CURRENT_INDEX( topLeft )
+ SET_CURRENT_INDEX( topRight )
+ SET_CURRENT_INDEX( bottomLeft )
+ SET_CURRENT_INDEX( bottomRight )
+#undef SET_CURRENT_INDEX
+}
diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h
index 2364caadc..f963330cc 100644
--- a/qt-ui/mainwindow.h
+++ b/qt-ui/mainwindow.h
@@ -10,8 +10,10 @@
#include <QMainWindow>
#include <QAction>
#include <QUrl>
+#include <QUuid>
#include "ui_mainwindow.h"
+#include "notificationwidget.h"
struct DiveList;
class QSortFilterProxyModel;
@@ -30,6 +32,12 @@ class QWebView;
class QSettings;
class UpdateManager;
class UserManual;
+class DivePlannerWidget;
+class ProfileWidget2;
+class PlannerDetails;
+class PlannerSettingsWidget;
+class QUndoStack;
+class LocationInformationWidget;
enum MainWindowTitleFormat {
MWTF_DEFAULT,
@@ -43,10 +51,7 @@ public:
COLLAPSED,
EXPANDED
};
- enum InfoWidgetIndexes {
- MAINTAB,
- PLANNERWIDGET
- };
+
enum CurrentState {
VIEWALL,
GLOBE_MAXIMIZED,
@@ -64,7 +69,9 @@ public:
void removeRecentFile(QStringList failedFiles);
DiveListView *dive_list();
GlobeGPS *globe();
- void showError(QString message);
+ DivePlannerWidget *divePlannerWidget();
+ PlannerSettingsWidget *divePlannerSettingsWidget();
+ LocationInformationWidget *locationInformationWidget();
void setTitle(enum MainWindowTitleFormat format);
// Some shortcuts like "change DC" or "copy/paste dive components"
@@ -77,11 +84,16 @@ public:
void cleanUpEmpty();
void setToolButtonsEnabled(bool enabled);
ProfileWidget2 *graphics() const;
+ PlannerDetails *plannerDetails() const;
void setLoadedWithFiles(bool filesFromCommandLine);
bool filesFromCommandLine() const;
void setPlanNotes(const char *notes);
void printPlan();
void checkSurvey(QSettings *s);
+ void setApplicationState(const QByteArray& state);
+ void showV2Dialog();
+ QUndoStack *undoStack;
+ NotificationWidget *getNotificationWidget();
private
slots:
/* file menu action */
@@ -94,6 +106,7 @@ slots:
void on_actionPrint_triggered();
void on_actionPreferences_triggered();
void on_actionQuit_triggered();
+ void on_actionHash_images_triggered();
/* log menu actions */
void on_actionDownloadDC_triggered();
@@ -101,6 +114,7 @@ slots:
void on_actionDivelogs_de_triggered();
void on_actionEditDeviceNames_triggered();
void on_actionAddDive_triggered();
+ void on_actionEditDive_triggered();
void on_actionRenumber_triggered();
void on_actionAutoGroup_triggered();
void on_actionYearlyStatistics_triggered();
@@ -151,6 +165,8 @@ slots:
void on_paste_triggered();
void on_actionFilterTags_triggered();
void on_actionConfigure_Dive_Computer_triggered();
+ void enableDiveSiteEdit(uint32_t id);
+ void setDefaultState();
protected:
void closeEvent(QCloseEvent *);
@@ -185,6 +201,7 @@ private:
void saveSplitterSizes();
QString lastUsedDir();
void updateLastUsedDir(const QString &s);
+ void registerApplicationState(const QByteArray& state, QWidget *topLeft, QWidget *topRight, QWidget *bottomLeft, QWidget *bottomRight);
bool filesAsArguments;
UpdateManager *updateManager;
@@ -194,6 +211,17 @@ private:
struct dive copyPasteDive;
struct dive_components what;
QList<QAction *> profileToolbarActions;
+
+ struct WidgetForQuadrant {
+ WidgetForQuadrant(QWidget *tl = 0, QWidget *tr = 0, QWidget *bl = 0, QWidget *br = 0) :
+ topLeft(tl), topRight(tr), bottomLeft(bl), bottomRight(br) {}
+ QWidget *topLeft;
+ QWidget *topRight;
+ QWidget *bottomLeft;
+ QWidget *bottomRight;
+ };
+ QHash<QByteArray, WidgetForQuadrant> applicationState;
+ QByteArray currentApplicationState;
};
#endif // MAINWINDOW_H
diff --git a/qt-ui/mainwindow.ui b/qt-ui/mainwindow.ui
index 8ffb8bbd8..8e98bed6c 100644
--- a/qt-ui/mainwindow.ui
+++ b/qt-ui/mainwindow.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>1682</width>
- <height>1151</height>
+ <width>861</width>
+ <height>800</height>
</rect>
</property>
<widget class="QWidget" name="centralwidget">
@@ -23,169 +23,24 @@
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
- <widget class="QSplitter" name="infoProfileSplitter">
+ <widget class="QSplitter" name="topSplitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
- <widget class="QStackedWidget" name="infoPane">
- <property name="currentIndex">
- <number>0</number>
- </property>
- <widget class="QWidget" name="page">
- <layout class="QHBoxLayout" name="mainTabOuterLayout">
- <property name="spacing">
- <number>0</number>
- </property>
- <item>
- <widget class="MainTab" name="InfoWidget" native="true"/>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="page_2">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <layout class="QHBoxLayout" name="divePlannerLayout">
- <item>
- <widget class="DivePlannerWidget" name="divePlannerWidget" native="true">
- <property name="enabled">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </widget>
- <widget class="QWidget" name="ProfileWidget">
- <layout class="QGridLayout" name="profileInnerLayout">
- <item row="0" column="0" rowspan="3">
- <widget class="ProfileWidget2" name="newProfile"/>
- </item>
- </layout>
- </widget>
+ <widget class="QStackedWidget" name="topLeft"/>
+ <widget class="QStackedWidget" name="topRight"/>
</widget>
- <widget class="QSplitter" name="listGlobeSplitter">
+ <widget class="QSplitter" name="bottomSplitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
- <widget class="QStackedWidget" name="diveListPane">
- <property name="currentIndex">
- <number>0</number>
- </property>
- <widget class="QWidget" name="page_3">
- <layout class="QVBoxLayout" name="diveListLayout">
- <item>
- <widget class="DiveListView" name="ListWidget">
- <property name="selectionMode">
- <enum>QAbstractItemView::ExtendedSelection</enum>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <widget class="PlannerSettingsWidget" name="plannerSettingsWidget"/>
- </widget>
- <widget class="QStackedWidget" name="globePane">
- <property name="currentIndex">
- <number>0</number>
- </property>
- <widget class="QWidget" name="stackedWidgetPage1">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <layout class="QVBoxLayout" name="globeLayout">
- <item>
- <widget class="GlobeGPS" name="globe" native="true"/>
- </item>
- </layout>
- </widget>
- <widget class="QWidget" name="page_5">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_3">
- <item>
- <layout class="QHBoxLayout" name="divePlanLayout">
- <item>
- <widget class="QLabel" name="divePlanOutputLabel">
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>20</height>
- </size>
- </property>
- <property name="text">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Dive plan details&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
- </property>
- <property name="textFormat">
- <enum>Qt::RichText</enum>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="printPlan">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Print</string>
- </property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- <property name="default">
- <bool>false</bool>
- </property>
- <property name="flat">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QTextEdit" name="divePlanOutput">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="styleSheet">
- <string notr="true">font: 13pt &quot;Courier&quot;;</string>
- </property>
- <property name="readOnly">
- <bool>true</bool>
- </property>
- <property name="html">
- <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
-&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
-p, li { white-space: pre-wrap; }
-&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Courier'; font-size:13pt; font-weight:400; font-style:normal;&quot;&gt;
-&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'.Curier New';&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </widget>
+ <widget class="QStackedWidget" name="bottomLeft"/>
+ <widget class="QStackedWidget" name="bottomRight"/>
</widget>
</widget>
</item>
<item>
- <widget class="KMessageWidget" name="mainErrorMessage" native="true"/>
+ <widget class="NotificationWidget" name="mainErrorMessage" native="true"/>
</item>
</layout>
</widget>
@@ -194,8 +49,8 @@ p, li { white-space: pre-wrap; }
<rect>
<x>0</x>
<y>0</y>
- <width>1682</width>
- <height>27</height>
+ <width>861</width>
+ <height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@@ -212,6 +67,7 @@ p, li { white-space: pre-wrap; }
<addaction name="actionPrint"/>
<addaction name="actionPreferences"/>
<addaction name="separator"/>
+ <addaction name="actionHash_images"/>
<addaction name="actionConfigure_Dive_Computer"/>
<addaction name="separator"/>
<addaction name="actionRecent1"/>
@@ -226,6 +82,7 @@ p, li { white-space: pre-wrap; }
<string>&amp;Log</string>
</property>
<addaction name="actionAddDive"/>
+ <addaction name="actionEditDive"/>
<addaction name="actionDivePlanner"/>
<addaction name="actionReplanDive"/>
<addaction name="copy"/>
@@ -271,7 +128,13 @@ p, li { white-space: pre-wrap; }
<addaction name="actionDownloadWeb"/>
<addaction name="actionDivelogs_de"/>
</widget>
+ <widget class="QMenu" name="menu_Edit">
+ <property name="title">
+ <string>&amp;Edit</string>
+ </property>
+ </widget>
<addaction name="menuFile"/>
+ <addaction name="menu_Edit"/>
<addaction name="menuImport"/>
<addaction name="menuLog"/>
<addaction name="menuView"/>
@@ -391,6 +254,11 @@ p, li { white-space: pre-wrap; }
<string>Ctrl++</string>
</property>
</action>
+ <action name="actionEditDive">
+ <property name="text">
+ <string>&amp;Edit dive</string>
+ </property>
+ </action>
<action name="copy">
<property name="text">
<string>&amp;Copy dive components</string>
@@ -590,7 +458,7 @@ p, li { white-space: pre-wrap; }
</action>
<action name="actionReplanDive">
<property name="text">
- <string>Re-plan &amp;dive</string>
+ <string>Edit &amp;dive in planner</string>
</property>
</action>
<action name="profPO2">
@@ -813,46 +681,33 @@ p, li { white-space: pre-wrap; }
<string>User &amp;survey</string>
</property>
</action>
+ <action name="action_Undo">
+ <property name="text">
+ <string>&amp;Undo</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Z</string>
+ </property>
+ </action>
+ <action name="action_Redo">
+ <property name="text">
+ <string>&amp;Redo</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Shift+Z</string>
+ </property>
+ </action>
+ <action name="actionHash_images">
+ <property name="text">
+ <string>Find moved images</string>
+ </property>
+ </action>
</widget>
<customwidgets>
<customwidget>
- <class>KMessageWidget</class>
- <extends>QWidget</extends>
- <header>kmessagewidget.h</header>
- <container>1</container>
- </customwidget>
- <customwidget>
- <class>MainTab</class>
- <extends>QWidget</extends>
- <header>qt-ui/maintab.h</header>
- <container>1</container>
- </customwidget>
- <customwidget>
- <class>DiveListView</class>
- <extends>QTreeView</extends>
- <header>divelistview.h</header>
- </customwidget>
- <customwidget>
- <class>GlobeGPS</class>
- <extends>QWidget</extends>
- <header>globe.h</header>
- <container>1</container>
- </customwidget>
- <customwidget>
- <class>DivePlannerWidget</class>
- <extends>QWidget</extends>
- <header>diveplanner.h</header>
- <container>1</container>
- </customwidget>
- <customwidget>
- <class>ProfileWidget2</class>
- <extends>QGraphicsView</extends>
- <header>qt-ui/profile/profilewidget2.h</header>
- </customwidget>
- <customwidget>
- <class>PlannerSettingsWidget</class>
+ <class>NotificationWidget</class>
<extends>QWidget</extends>
- <header>diveplanner.h</header>
+ <header>notificationwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
diff --git a/qt-ui/modeldelegates.cpp b/qt-ui/modeldelegates.cpp
index ee7dc6cf5..66533b652 100644
--- a/qt-ui/modeldelegates.cpp
+++ b/qt-ui/modeldelegates.cpp
@@ -2,8 +2,13 @@
#include "dive.h"
#include "gettextfromc.h"
#include "mainwindow.h"
+#include "models.h"
+#include "starwidget.h"
+#include "profile/profilewidget2.h"
#include <QCompleter>
+#include <QKeyEvent>
+#include <QTextDocument>
QSize DiveListDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
@@ -247,7 +252,7 @@ TankInfoDelegate::TankInfoDelegate(QObject *parent) : ComboBoxDelegate(TankInfoM
void TankInfoDelegate::reenableReplot(QWidget *widget, QAbstractItemDelegate::EndEditHint hint)
{
MainWindow::instance()->graphics()->setReplot(true);
- // FIXME: We need to replot after a cylidner is selected but the replot below overwrites
+ // FIXME: We need to replot after a cylinder is selected but the replot below overwrites
// the newly selected cylinder.
// MainWindow::instance()->graphics()->replot();
}
diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp
index fec7d02ed..69a276bfb 100644
--- a/qt-ui/models.cpp
+++ b/qt-ui/models.cpp
@@ -14,6 +14,7 @@
#include "qthelper.h"
#include "gettextfromc.h"
#include "display.h"
+#include "color.h"
#include <QCoreApplication>
#include <QDebug>
@@ -387,6 +388,7 @@ Qt::ItemFlags CylindersModel::flags(const QModelIndex &index) const
void CylindersModel::remove(const QModelIndex &index)
{
+ int mapping[MAX_CYLINDERS];
if (index.column() != REMOVE) {
return;
}
@@ -394,6 +396,7 @@ void CylindersModel::remove(const QModelIndex &index)
cylinder_t *cyl = &displayed_dive.cylinder[index.row()];
struct gasmix *mygas = &cyl->gasmix;
for (int i = 0; i < MAX_CYLINDERS; i++) {
+ mapping[i] = i;
if (i == index.row() || cylinder_none(&displayed_dive.cylinder[i]))
continue;
struct gasmix *gas2 = &displayed_dive.cylinder[i].gasmix;
@@ -404,7 +407,7 @@ void CylindersModel::remove(const QModelIndex &index)
((DivePlannerPointsModel::instance()->currentMode() != DivePlannerPointsModel::NOTHING &&
DivePlannerPointsModel::instance()->tankInUse(cyl->gasmix)) ||
(DivePlannerPointsModel::instance()->currentMode() == DivePlannerPointsModel::NOTHING &&
- (cyl->manually_added || is_cylinder_used(&displayed_dive, index.row()))))) {
+ is_cylinder_used(&displayed_dive, index.row())))) {
QMessageBox::warning(MainWindow::instance(), TITLE_OR_TEXT(
tr("Cylinder cannot be removed"),
tr("This gas is in use. Only cylinders that are not used in the dive can be removed.")),
@@ -418,11 +421,24 @@ void CylindersModel::remove(const QModelIndex &index)
// as first gas
memmove(cyl, &displayed_dive.cylinder[same_gas], sizeof(*cyl));
remove_cylinder(&displayed_dive, same_gas);
+ mapping[same_gas] = 0;
+ for (int i = same_gas + 1; i < MAX_CYLINDERS; i++)
+ mapping[i] = i - 1;
} else {
remove_cylinder(&displayed_dive, index.row());
+ if (same_gas > index.row())
+ same_gas--;
+ mapping[index.row()] = same_gas;
+ for (int i = index.row() + 1; i < MAX_CYLINDERS; i++)
+ mapping[i] = i - 1;
}
changed = true;
endRemoveRows();
+ struct divecomputer *dc = &displayed_dive.dc;
+ while (dc) {
+ dc_cylinder_renumber(&displayed_dive, dc, mapping);
+ dc = dc->next;
+ }
}
WeightModel::WeightModel(QObject *parent) : CleanerTableModel(parent),
@@ -1191,7 +1207,7 @@ QVariant DiveItem::data(int column, int role) const
retVal = dive->maxcns;
break;
case LOCATION:
- retVal = QString(dive->location);
+ retVal = QString(get_dive_location(dive));
break;
}
break;
@@ -1232,7 +1248,7 @@ QVariant DiveItem::data(int column, int role) const
retVal = dive->maxcns;
break;
case LOCATION:
- retVal = QString(dive->location);
+ retVal = QString(get_dive_location(dive));
break;
case GAS:
const char *gas_string = get_dive_gas_string(dive);
@@ -1356,15 +1372,19 @@ QString DiveItem::displayDepthWithUnit() const
QString DiveItem::displayDuration() const
{
- int hrs, mins;
+ int hrs, mins, fullmins, secs;
struct dive *dive = get_dive_by_uniq_id(diveId);
mins = (dive->duration.seconds + 59) / 60;
+ fullmins = dive->duration.seconds / 60;
+ secs = dive->duration.seconds - 60 * fullmins;
hrs = mins / 60;
mins -= hrs * 60;
QString displayTime;
if (hrs)
displayTime = QString("%1:%2").arg(hrs).arg(mins, 2, 10, QChar('0'));
+ else if (mins < 15 || dive->dc.divemode == FREEDIVE)
+ displayTime = QString("%1m%2s").arg(fullmins).arg(secs, 2, 10, QChar('0'));
else
displayTime = QString("%1").arg(mins);
return displayTime;
@@ -2110,7 +2130,7 @@ QVariant ProfilePrintModel::data(const QModelIndex &index, int role) const
}
if (row == 1) {
if (col == 0)
- return QString(dive->location);
+ return QString(get_dive_location(dive));
if (col == 3)
return QString(tr("Duration: %1 min")).arg(di.displayDuration());
}
diff --git a/qt-ui/notificationwidget.cpp b/qt-ui/notificationwidget.cpp
new file mode 100644
index 000000000..44e2eed1b
--- /dev/null
+++ b/qt-ui/notificationwidget.cpp
@@ -0,0 +1,37 @@
+#include "notificationwidget.h"
+
+NotificationWidget::NotificationWidget(QWidget *parent) : KMessageWidget(parent)
+{
+ future_watcher = new QFutureWatcher<void>();
+ connect(future_watcher, SIGNAL(finished()), this, SLOT(finish()));
+}
+
+void NotificationWidget::showNotification(QString message, KMessageWidget::MessageType type)
+{
+ if (message.isEmpty())
+ return;
+ setText(message);
+ setCloseButtonVisible(true);
+ setMessageType(type);
+ animatedShow();
+}
+
+void NotificationWidget::hideNotification()
+{
+ animatedHide();
+}
+
+void NotificationWidget::setFuture(const QFuture<void> &future)
+{
+ future_watcher->setFuture(future);
+}
+
+void NotificationWidget::finish()
+{
+ hideNotification();
+}
+
+NotificationWidget::~NotificationWidget()
+{
+ delete future_watcher;
+}
diff --git a/qt-ui/notificationwidget.h b/qt-ui/notificationwidget.h
new file mode 100644
index 000000000..7a3f93b4d
--- /dev/null
+++ b/qt-ui/notificationwidget.h
@@ -0,0 +1,31 @@
+#ifndef NOTIFICATIONWIDGET_H
+#define NOTIFICATIONWIDGET_H
+
+#include <QWidget>
+#include <QFutureWatcher>
+
+#include <kmessagewidget.h>
+
+namespace Ui {
+ class NotificationWidget;
+}
+
+class NotificationWidget : public KMessageWidget {
+ Q_OBJECT
+
+public:
+ explicit NotificationWidget(QWidget *parent = 0);
+ void setFuture(const QFuture<void> &future);
+ void showNotification(QString message, KMessageWidget::MessageType type);
+ void hideNotification();
+ ~NotificationWidget();
+
+private:
+ QFutureWatcher<void> *future_watcher;
+
+private
+slots:
+ void finish();
+};
+
+#endif // NOTIFICATIONWIDGET_H
diff --git a/qt-ui/plannerDetails.ui b/qt-ui/plannerDetails.ui
new file mode 100644
index 000000000..349ec536a
--- /dev/null
+++ b/qt-ui/plannerDetails.ui
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>plannerDetails</class>
+ <widget class="QWidget" name="plannerDetails">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="divePlanOutputLabel">
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>20</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Dive plan details&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="printPlan">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Print</string>
+ </property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ <property name="default">
+ <bool>false</bool>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QTextEdit" name="divePlanOutput">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">font: 13pt &quot;Courier&quot;;</string>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="html">
+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Courier'; font-size:13pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'.Curier New';&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/qt-ui/plannerSettings.ui b/qt-ui/plannerSettings.ui
index af03fd1fb..02d829c42 100644
--- a/qt-ui/plannerSettings.ui
+++ b/qt-ui/plannerSettings.ui
@@ -262,8 +262,22 @@
<property name="spacing">
<number>2</number>
</property>
- <item row="0" column="2">
- <widget class="QSpinBox" name="gflow">
+ <item row="6" column="1">
+ <widget class="QLabel" name="label_16">
+ <property name="text">
+ <string>GF high</string>
+ </property>
+ </widget>
+ </item>
+ <item row="9" column="1" colspan="2">
+ <widget class="QCheckBox" name="backgasBreaks">
+ <property name="text">
+ <string>Plan backgas breaks</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="2">
+ <widget class="QSpinBox" name="gfhigh">
<property name="suffix">
<string>%</string>
</property>
@@ -275,28 +289,44 @@
</property>
</widget>
</item>
- <item row="0" column="1">
- <widget class="QLabel" name="label_15">
+ <item row="8" column="1" colspan="2">
+ <widget class="QCheckBox" name="lastStop">
<property name="text">
- <string>GF low</string>
+ <string>Last stop at 6m</string>
</property>
</widget>
</item>
- <item row="1" column="1">
- <widget class="QLabel" name="label_16">
- <property name="text">
- <string>GF high</string>
+ <item row="10" column="1">
+ <widget class="QComboBox" name="rebreathermode">
+ <property name="currentText">
+ <string/>
+ </property>
+ <property name="maxVisibleItems">
+ <number>6</number>
</property>
</widget>
</item>
- <item row="4" column="1" colspan="2">
- <widget class="QCheckBox" name="backgasBreaks">
+ <item row="5" column="2">
+ <widget class="QSpinBox" name="gflow">
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>150</number>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="1" colspan="2">
+ <widget class="QCheckBox" name="drop_stone_mode">
<property name="text">
- <string>Plan backgas breaks</string>
+ <string>Drop to first depth</string>
</property>
</widget>
</item>
- <item row="6" column="1">
+ <item row="11" column="1">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
@@ -309,40 +339,53 @@
</property>
</spacer>
</item>
- <item row="3" column="1" colspan="2">
- <widget class="QCheckBox" name="lastStop">
+ <item row="5" column="1">
+ <widget class="QLabel" name="label_15">
<property name="text">
- <string>Last stop at 6m</string>
+ <string>GF low</string>
</property>
</widget>
</item>
- <item row="1" column="2">
- <widget class="QSpinBox" name="gfhigh">
- <property name="suffix">
- <string>%</string>
+ <item row="0" column="1">
+ <widget class="QCheckBox" name="recreational_mode">
+ <property name="text">
+ <string>Recreational mode</string>
</property>
- <property name="minimum">
- <number>1</number>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="safetystop">
+ <property name="text">
+ <string>Safety stop</string>
</property>
- <property name="maximum">
- <number>150</number>
+ <property name="tristate">
+ <bool>false</bool>
</property>
</widget>
</item>
- <item row="2" column="1" colspan="2">
- <widget class="QCheckBox" name="drop_stone_mode">
+ <item row="2" column="1">
+ <widget class="QLabel" name="label_3">
<property name="text">
- <string>Drop to first depth</string>
+ <string>Reserve gas</string>
</property>
</widget>
</item>
- <item row="5" column="1">
- <widget class="QComboBox" name="rebreathermode">
- <property name="currentText">
+ <item row="2" column="2">
+ <widget class="QSpinBox" name="reserve_gas">
+ <property name="suffix">
+ <string>bar</string>
+ </property>
+ <property name="prefix">
<string/>
</property>
- <property name="maxVisibleItems">
- <number>6</number>
+ <property name="minimum">
+ <number>10</number>
+ </property>
+ <property name="maximum">
+ <number>99</number>
+ </property>
+ <property name="value">
+ <number>40</number>
</property>
</widget>
</item>
@@ -475,7 +518,7 @@
<item>
<widget class="QGroupBox" name="groupBox_6">
<property name="title">
- <string>Dive notes</string>
+ <string>Notes</string>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<property name="leftMargin">
@@ -501,9 +544,6 @@
<property name="text">
<string>Display runtime</string>
</property>
- <property name="checked">
- <bool>true</bool>
- </property>
</widget>
</item>
<item row="1" column="0">
@@ -517,9 +557,6 @@
<property name="text">
<string>Display segment duration</string>
</property>
- <property name="checked">
- <bool>false</bool>
- </property>
</widget>
</item>
<item row="2" column="0">
diff --git a/qt-ui/preferences.cpp b/qt-ui/preferences.cpp
index ab241f358..aeccc961c 100644
--- a/qt-ui/preferences.cpp
+++ b/qt-ui/preferences.cpp
@@ -1,5 +1,7 @@
#include "preferences.h"
#include "mainwindow.h"
+#include "models.h"
+
#include <QSettings>
#include <QFileDialog>
#include <QMessageBox>
@@ -144,6 +146,8 @@ void PreferencesDialog::setUiFromPrefs()
ui.imperial->setChecked(true);
else
ui.personalize->setChecked(true);
+ ui.gpsTraditional->setChecked(prefs.coordinates_traditional);
+ ui.gpsDecimal->setChecked(!prefs.coordinates_traditional);
ui.celsius->setChecked(prefs.units.temperature == units::CELSIUS);
ui.fahrenheit->setChecked(prefs.units.temperature == units::FAHRENHEIT);
@@ -304,6 +308,7 @@ void PreferencesDialog::syncSettings()
s.setValue("volume", ui.cuft->isChecked() ? units::CUFT : units::LITER);
s.setValue("weight", ui.lbs->isChecked() ? units::LBS : units::KG);
s.setValue("vertical_speed_time", ui.vertical_speed_minutes->isChecked() ? units::MINUTES : units::SECONDS);
+ s.setValue("coordinates", ui.gpsTraditional->isChecked());
s.endGroup();
// Defaults
@@ -382,6 +387,7 @@ void PreferencesDialog::loadSettings()
GET_UNIT("weight", weight, units::LBS, units::KG);
}
GET_UNIT("vertical_speed_time", vertical_speed_time, units::MINUTES, units::SECONDS);
+ GET_BOOL("coordinates", coordinates_traditional);
s.endGroup();
s.beginGroup("TecDetails");
GET_BOOL("po2graph", pp_graphs.po2);
diff --git a/qt-ui/preferences.ui b/qt-ui/preferences.ui
index e67925f56..84c90d7ff 100644
--- a/qt-ui/preferences.ui
+++ b/qt-ui/preferences.ui
@@ -22,127 +22,6 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
- <widget class="QListWidget" name="listWidget">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Expanding">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>80</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>80</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="iconSize">
- <size>
- <width>40</width>
- <height>40</height>
- </size>
- </property>
- <property name="textElideMode">
- <enum>Qt::ElideNone</enum>
- </property>
- <property name="movement">
- <enum>QListView::Static</enum>
- </property>
- <property name="isWrapping" stdset="0">
- <bool>true</bool>
- </property>
- <property name="layoutMode">
- <enum>QListView::Batched</enum>
- </property>
- <property name="spacing">
- <number>0</number>
- </property>
- <property name="gridSize">
- <size>
- <width>70</width>
- <height>60</height>
- </size>
- </property>
- <property name="viewMode">
- <enum>QListView::IconMode</enum>
- </property>
- <property name="uniformItemSizes">
- <bool>false</bool>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- <property name="currentRow">
- <number>-1</number>
- </property>
- <item>
- <property name="text">
- <string>Defaults</string>
- </property>
- <property name="icon">
- <iconset>
- <normalon>:/subsurface-icon</normalon>
- </iconset>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Units</string>
- </property>
- <property name="icon">
- <iconset>
- <normalon>:/units</normalon>
- </iconset>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Graph</string>
- </property>
- <property name="icon">
- <iconset>
- <normalon>:/graph</normalon>
- </iconset>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Language</string>
- </property>
- <property name="icon">
- <iconset>
- <normalon>:/advanced</normalon>
- </iconset>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Network</string>
- </property>
- <property name="icon">
- <iconset>
- <normalon>:/network</normalon>
- </iconset>
- </property>
- </item>
- <item>
- <property name="text">
- <string>Facebook</string>
- </property>
- <property name="icon">
- <iconset>
- <normalon>:/facebook</normalon>
- </iconset>
- </property>
- </item>
- </widget>
- </item>
- <item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
@@ -585,6 +464,42 @@
</layout>
</item>
<item>
+ <widget class="QGroupBox" name="groupBox_11">
+ <property name="title">
+ <string>GPS coordinates</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_12">
+ <item>
+ <widget class="QLabel" name="label_27">
+ <property name="text">
+ <string>Location Display</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="gpsTraditional">
+ <property name="text">
+ <string>traditional (dms)</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">buttonGroup_7</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="gpsDecimal">
+ <property name="text">
+ <string>decimal</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">buttonGroup_7</string>
+ </attribute>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@@ -1077,6 +992,127 @@
</widget>
</widget>
</item>
+ <item>
+ <widget class="QListWidget" name="listWidget">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>80</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>40</width>
+ <height>40</height>
+ </size>
+ </property>
+ <property name="textElideMode">
+ <enum>Qt::ElideNone</enum>
+ </property>
+ <property name="movement">
+ <enum>QListView::Static</enum>
+ </property>
+ <property name="isWrapping" stdset="0">
+ <bool>true</bool>
+ </property>
+ <property name="layoutMode">
+ <enum>QListView::Batched</enum>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="gridSize">
+ <size>
+ <width>70</width>
+ <height>60</height>
+ </size>
+ </property>
+ <property name="viewMode">
+ <enum>QListView::IconMode</enum>
+ </property>
+ <property name="uniformItemSizes">
+ <bool>false</bool>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="currentRow">
+ <number>-1</number>
+ </property>
+ <item>
+ <property name="text">
+ <string>Defaults</string>
+ </property>
+ <property name="icon">
+ <iconset>
+ <normalon>:/subsurface-icon</normalon>
+ </iconset>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Units</string>
+ </property>
+ <property name="icon">
+ <iconset>
+ <normalon>:/units</normalon>
+ </iconset>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Graph</string>
+ </property>
+ <property name="icon">
+ <iconset>
+ <normalon>:/graph</normalon>
+ </iconset>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Language</string>
+ </property>
+ <property name="icon">
+ <iconset>
+ <normalon>:/advanced</normalon>
+ </iconset>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Network</string>
+ </property>
+ <property name="icon">
+ <iconset>
+ <normalon>:/network</normalon>
+ </iconset>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Facebook</string>
+ </property>
+ <property name="icon">
+ <iconset>
+ <normalon>:/facebook</normalon>
+ </iconset>
+ </property>
+ </item>
+ </widget>
+ </item>
</layout>
</item>
<item>
@@ -1431,12 +1467,13 @@
</connection>
</connections>
<buttongroups>
- <buttongroup name="buttonGroup"/>
+ <buttongroup name="verticalSpeed"/>
<buttongroup name="buttonGroup_2"/>
<buttongroup name="buttonGroup_3"/>
<buttongroup name="buttonGroup_4"/>
<buttongroup name="buttonGroup_5"/>
+ <buttongroup name="buttonGroup"/>
<buttongroup name="buttonGroup_6"/>
- <buttongroup name="verticalSpeed"/>
+ <buttongroup name="buttonGroup_7"/>
</buttongroups>
</ui>
diff --git a/qt-ui/printlayout.cpp b/qt-ui/printlayout.cpp
index 6b88f0d29..4be5fef73 100644
--- a/qt-ui/printlayout.cpp
+++ b/qt-ui/printlayout.cpp
@@ -3,11 +3,14 @@
#include <QPicture>
#include <QMessageBox>
#include <QPointer>
+#include <QTableView>
#include "mainwindow.h"
#include "printdialog.h"
#include "printlayout.h"
#include "modeldelegates.h"
+#include "models.h"
+#include "profile/profilewidget2.h"
PrintLayout::PrintLayout(PrintDialog *dialogPtr, QPrinter *printerPtr, struct print_options *optionsPtr)
{
@@ -459,7 +462,7 @@ void PrintLayout::addTablePrintDataRow(TablePrintModel *model, int row, struct d
model->setData(model->index(row, 3), di.displayDuration(), Qt::DisplayRole);
model->setData(model->index(row, 4), dive->divemaster, Qt::DisplayRole);
model->setData(model->index(row, 5), dive->buddy, Qt::DisplayRole);
- model->setData(model->index(row, 6), dive->location, Qt::DisplayRole);
+ model->setData(model->index(row, 6), get_dive_location(dive), Qt::DisplayRole);
}
void PrintLayout::addTablePrintHeadingRow(TablePrintModel *model, int row) const
diff --git a/qt-ui/profile/divecartesianaxis.cpp b/qt-ui/profile/divecartesianaxis.cpp
index 467a8b978..41d94a9a0 100644
--- a/qt-ui/profile/divecartesianaxis.cpp
+++ b/qt-ui/profile/divecartesianaxis.cpp
@@ -5,6 +5,8 @@
#include "diveplotdatamodel.h"
#include "animationfunctions.h"
#include "mainwindow.h"
+#include "divelineitem.h"
+#include "profilewidget2.h"
static QPen gridPen()
{
diff --git a/qt-ui/profile/diveeventitem.cpp b/qt-ui/profile/diveeventitem.cpp
index 700430007..0d81e7b45 100644
--- a/qt-ui/profile/diveeventitem.cpp
+++ b/qt-ui/profile/diveeventitem.cpp
@@ -135,7 +135,9 @@ bool DiveEventItem::shouldBeHidden()
* Don't bother showing those
*/
struct sample *first_sample = &get_dive_dc(&displayed_dive, dc_number)->sample[0];
- if (!strcmp(event->name, "gaschange") && first_sample && event->time.seconds == first_sample->time.seconds)
+ if (!strcmp(event->name, "gaschange") &&
+ (event->time.seconds == 0 ||
+ (first_sample && event->time.seconds == first_sample->time.seconds)))
return true;
for (int i = 0; i < evn_used; i++) {
diff --git a/qt-ui/profile/diveprofileitem.cpp b/qt-ui/profile/diveprofileitem.cpp
index 3bc79832e..7d29d28b4 100644
--- a/qt-ui/profile/diveprofileitem.cpp
+++ b/qt-ui/profile/diveprofileitem.cpp
@@ -9,6 +9,9 @@
#include "helpers.h"
#include "libdivecomputer/parser.h"
#include "mainwindow.h"
+#include "maintab.h"
+#include "profile/profilewidget2.h"
+#include "diveplanner.h"
#include <QSettings>
@@ -166,7 +169,7 @@ void DiveProfileItem::modelDataChanged(const QModelIndex &topLeft, const QModelI
for (int i = 0; i < dataModel->rowCount(); i++, entry++) {
int max = maxCeiling(i);
// Don't scream if we violate the ceiling by a few cm
- if (entry->depth < max - 100) {
+ if (entry->depth < max - 100 && entry->sec > 0) {
profileColor = QColor(Qt::red);
if (!eventAdded) {
add_event(&displayed_dive.dc, entry->sec, SAMPLE_EVENT_CEILING, -1, max / 1000, "planned waypoint above ceiling");
@@ -682,8 +685,8 @@ void DiveGasPressureItem::modelDataChanged(const QModelIndex &topLeft, const QMo
bool offsets_initialised = false;
int o2cyl = -1, dilcyl = -1;
- QFlags<Qt::AlignmentFlag> alignVar, align_dil = Qt::AlignBottom, align_o2 = Qt::AlignBottom;
- double axisRange = (vAxis->maximum() - vAxis->minimum())/1000;
+ QFlags<Qt::AlignmentFlag> alignVar= Qt::AlignTop, align_dil = Qt::AlignBottom, align_o2 = Qt::AlignTop;
+ double axisRange = (vAxis->maximum() - vAxis->minimum())/1000; // Convert axis pressure range to bar
double axisLog = log10(log10(axisRange));
for (int i = 0, count = dataModel->rowCount(); i < count; i++) {
entry = dataModel->data().entry + i;
@@ -691,23 +694,17 @@ void DiveGasPressureItem::modelDataChanged(const QModelIndex &topLeft, const QMo
if (displayed_dive.dc.divemode == CCR && displayed_dive.oxygen_cylinder_index >= 0)
o2mbar = GET_O2CYLINDER_PRESSURE(entry);
- if (o2mbar) {
+ if (o2mbar) { // If there is an o2mbar value then this is a CCR dive. Then do:
// The first time an o2 value is detected, see if the oxygen cyl pressure graph starts above or below the dil graph
if (!offsets_initialised) { // Initialise the parameters for placing the text correctly near the graph line:
o2cyl = displayed_dive.oxygen_cylinder_index;
dilcyl = displayed_dive.diluent_cylinder_index;
if ((o2mbar > mbar)) { // If above, write o2 start cyl pressure above graph and diluent pressure below graph:
- print_y_offset[o2cyl][0] = -7 * axisLog; // y offset for oxygen gas lable (above)
- print_y_offset[o2cyl][1] = -0.5; // y offset for oxygen start pressure value (above)
+ print_y_offset[o2cyl][0] = -7 * axisLog; // y offset for oxygen gas lable (above); pressure offsets=-0.5, already initialised
print_y_offset[dilcyl][0] = 5 * axisLog; // y offset for diluent gas lable (below)
- print_y_offset[dilcyl][1] = 0; // y offset for diluent start pressure value (below)
- align_dil = Qt::AlignBottom;
- align_o2 = Qt::AlignTop;
- } else { // ... else write o2 start cyl pressure below graph:
- print_y_offset[o2cyl][0] = 5 * axisLog; // o2 lable & pressure below graph,
- print_y_offset[o2cyl][1] = 0;
- print_y_offset[dilcyl][0] = -7 * axisLog; // and diluent lable above graph.
- print_y_offset[dilcyl][1] = -0.5; // and diluent pressure above graph.
+ } else { // ... else write o2 start cyl pressure below graph:
+ print_y_offset[o2cyl][0] = 5 * axisLog; // o2 lable & pressure below graph; pressure offsets=-0.5, already initialised
+ print_y_offset[dilcyl][0] = -7.8 * axisLog; // and diluent lable above graph.
align_dil = Qt::AlignTop;
align_o2 = Qt::AlignBottom;
}
@@ -722,9 +719,6 @@ void DiveGasPressureItem::modelDataChanged(const QModelIndex &topLeft, const QMo
last_pressure[displayed_dive.oxygen_cylinder_index] = o2mbar;
last_time[displayed_dive.oxygen_cylinder_index] = entry->sec;
alignVar = align_dil;
- } else {
- alignVar = Qt::AlignBottom;
- align_dil = Qt::AlignTop;
}
if (!mbar)
@@ -733,29 +727,19 @@ void DiveGasPressureItem::modelDataChanged(const QModelIndex &topLeft, const QMo
if (cyl != entry->cylinderindex) { // Pressure value near the left hand edge of the profile - other cylinders:
cyl = entry->cylinderindex; // For each other cylinder, write the gas lable and pressure
if (!seen_cyl[cyl]) {
- plotPressureValue(mbar, entry->sec, alignVar, print_y_offset[cyl][0]);
- plotGasValue(mbar, entry->sec, displayed_dive.cylinder[cyl].gasmix, align_dil, print_y_offset[cyl][1]);
+ plotPressureValue(mbar, entry->sec, alignVar, print_y_offset[cyl][1]);
+ plotGasValue(mbar, entry->sec, displayed_dive.cylinder[cyl].gasmix, align_dil, print_y_offset[cyl][0]);
seen_cyl[cyl] = true;
}
}
last_pressure[cyl] = mbar;
last_time[cyl] = entry->sec;
}
- // Now, for the cylinder pressure written near the right edge of the profile:
- if ((o2cyl >= 0) && (dilcyl >= 0)) { // At first, skip uninitialised values of o2cyl and dilcyl
- if (last_pressure[o2cyl] > last_pressure[dilcyl]) { // If oxygen cyl pressure graph ends above diluent graph:
- align_dil = Qt::AlignTop; // initialise to write diluent cyl end pressure underneath the graph
- align_o2 = Qt::AlignBottom;
- } else {
- align_dil = Qt::AlignBottom; // else initialise to write diluent cyl end pressure above the graph
- align_o2 = Qt::AlignTop;
- }
- }
for (cyl = 0; cyl < MAX_CYLINDERS; cyl++) { // For each cylinder, on right hand side of profile, write cylinder pressure
alignVar = ((o2cyl >= 0) && (cyl == displayed_dive.oxygen_cylinder_index)) ? align_o2 : align_dil;
if (last_time[cyl]) {
- plotPressureValue(last_pressure[cyl], last_time[cyl], (alignVar | Qt::AlignLeft), print_y_offset[cyl][0]);
+ plotPressureValue(last_pressure[cyl], last_time[cyl], (alignVar | Qt::AlignLeft), print_y_offset[cyl][1]);
}
}
}
@@ -808,12 +792,14 @@ DiveCalculatedCeiling::DiveCalculatedCeiling() : is3mIncrement(false), gradientF
gradientFactor->setY(0);
gradientFactor->setBrush(getColor(PRESSURE_TEXT));
gradientFactor->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
- connect(MainWindow::instance()->information(), SIGNAL(dateTimeChanged()), this, SLOT(recalc()));
settingsChanged();
}
void DiveCalculatedCeiling::modelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
+ if (MainWindow::instance()->information())
+ connect(MainWindow::instance()->information(), SIGNAL(dateTimeChanged()), this, SLOT(recalc()), Qt::UniqueConnection);
+
// We don't have enougth data to calculate things, quit.
if (!shouldCalculateStuff(topLeft, bottomRight))
return;
diff --git a/qt-ui/profile/divetextitem.cpp b/qt-ui/profile/divetextitem.cpp
index 85e046638..4c0137177 100644
--- a/qt-ui/profile/divetextitem.cpp
+++ b/qt-ui/profile/divetextitem.cpp
@@ -1,5 +1,6 @@
#include "divetextitem.h"
#include "mainwindow.h"
+#include "profilewidget2.h"
DiveTextItem::DiveTextItem(QGraphicsItem *parent) : QGraphicsItemGroup(parent),
internalAlignFlags(Qt::AlignHCenter | Qt::AlignVCenter),
diff --git a/qt-ui/profile/profilewidget2.cpp b/qt-ui/profile/profilewidget2.cpp
index f04d16b3f..a426ceef2 100644
--- a/qt-ui/profile/profilewidget2.cpp
+++ b/qt-ui/profile/profilewidget2.cpp
@@ -10,12 +10,18 @@
#include "ruleritem.h"
#include "tankitem.h"
#include "pref.h"
+#include "divepicturewidget.h"
+#include "models.h"
+#include "maintab.h"
+#include "diveplanner.h"
+
#include <libdivecomputer/parser.h>
#include <QScrollBar>
#include <QtCore/qmath.h>
#include <QMessageBox>
#include <QInputDialog>
#include <QDebug>
+#include <QWheelEvent>
#ifndef QT_NO_DEBUG
#include <QTableView>
@@ -644,7 +650,7 @@ void ProfileWidget2::plotDive(struct dive *d, bool force)
// so if we are calculation TTS / NDL then let's force that off.
if (measureDuration.elapsed() > 1000 && prefs.calcndltts) {
MainWindow::instance()->turnOffNdlTts();
- MainWindow::instance()->showError(tr("Show NDL / TTS was disabled because of excessive processing time"));
+ MainWindow::instance()->getNotificationWidget()->showNotification(tr("Show NDL / TTS was disabled because of excessive processing time"), KMessageWidget::Error);
}
}
@@ -727,6 +733,8 @@ void ProfileWidget2::resizeEvent(QResizeEvent *event)
void ProfileWidget2::mousePressEvent(QMouseEvent *event)
{
+ if (zoomLevel)
+ return;
QGraphicsView::mousePressEvent(event);
if (currentState == PLAN)
shouldCalculateMaxTime = false;
@@ -734,18 +742,24 @@ void ProfileWidget2::mousePressEvent(QMouseEvent *event)
void ProfileWidget2::divePlannerHandlerClicked()
{
+ if (zoomLevel)
+ return;
shouldCalculateMaxDepth = false;
replot();
}
void ProfileWidget2::divePlannerHandlerReleased()
{
+ if (zoomLevel)
+ return;
shouldCalculateMaxDepth = true;
replot();
}
void ProfileWidget2::mouseReleaseEvent(QMouseEvent *event)
{
+ if (zoomLevel)
+ return;
QGraphicsView::mouseReleaseEvent(event);
if (currentState == PLAN) {
shouldCalculateMaxTime = true;
@@ -1039,6 +1053,7 @@ void ProfileWidget2::clearHandlers()
if (handles.count()) {
foreach (DiveHandler *handle, handles) {
scene()->removeItem(handle);
+ delete handle;
}
handles.clear();
}
@@ -1054,6 +1069,7 @@ void ProfileWidget2::setAddState()
if (currentState == ADD)
return;
+ clearHandlers();
setProfileState();
mouseFollowerHorizontal->setVisible(true);
mouseFollowerVertical->setVisible(true);
@@ -1206,6 +1222,10 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event)
setpointAction->setData(event->globalPos());
QAction *action = m.addAction(tr("Add bookmark"), this, SLOT(addBookmark()));
action->setData(event->globalPos());
+
+ if (same_string(current_dc->model, "manually added dive"))
+ QAction *editProfileAction = m.addAction(tr("Edit the profile"), MainWindow::instance(), SLOT(editCurrentDive()));
+
if (DiveEventItem *item = dynamic_cast<DiveEventItem *>(sceneItem)) {
action = new QAction(&m);
action->setText(tr("Remove event"));
@@ -1375,8 +1395,21 @@ void ProfileWidget2::changeGas()
// backup the things on the dataModel, since we will clear that out.
struct gasmix gasmix;
- int seconds = timeAxis->valueAt(scenePos);
+ qreal sec_val = timeAxis->valueAt(scenePos);
+
+ // no gas changes before the dive starts
+ unsigned int seconds = (sec_val < 0.0) ? 0 : (unsigned int)sec_val;
+ // if there is a gas change at this time stamp, remove it before adding the new one
+ struct event *gasChangeEvent = current_dc->events;
+ while ((gasChangeEvent = get_next_event(gasChangeEvent, "gaschange")) != NULL) {
+ if (gasChangeEvent->time.seconds == seconds) {
+ remove_event(gasChangeEvent);
+ gasChangeEvent = current_dc->events;
+ } else {
+ gasChangeEvent = gasChangeEvent->next;
+ }
+ }
validate_gas(gas.toUtf8().constData(), &gasmix);
QRegExp rx("\\(\\D*(\\d+)");
int tank;
@@ -1504,6 +1537,7 @@ void ProfileWidget2::repositionDiveHandlers()
{
DivePlannerPointsModel *plannerModel = DivePlannerPointsModel::instance();
// Re-position the user generated dive handlers
+ struct gasmix mix, lastmix;
for (int i = 0; i < plannerModel->rowCount(); i++) {
struct divedatapoint datapoint = plannerModel->at(i);
if (datapoint.time == 0) // those are the magic entries for tanks
@@ -1528,8 +1562,9 @@ void ProfileWidget2::repositionDiveHandlers()
QLineF line(p1, p2);
QPointF pos = line.pointAt(0.5);
gases[i]->setPos(pos);
- gases[i]->setVisible(datapoint.entered);
- gases[i]->setText(dpGasToStr(plannerModel->at(i)));
+ gases[i]->setText(dpGasToStr(datapoint));
+ gases[i]->setVisible(datapoint.entered &&
+ (i == 0 || gases[i]->text() != gases[i-1]->text()));
}
}
diff --git a/qt-ui/profile/ruleritem.cpp b/qt-ui/profile/ruleritem.cpp
index c88a3353d..d5742ef1d 100644
--- a/qt-ui/profile/ruleritem.cpp
+++ b/qt-ui/profile/ruleritem.cpp
@@ -1,6 +1,8 @@
#include "ruleritem.h"
#include "preferences.h"
#include "mainwindow.h"
+#include "profilewidget2.h"
+#include "display.h"
#include <qgraphicssceneevent.h>
@@ -81,7 +83,11 @@ void RulerItem2::settingsChanged()
ProfileWidget2 *profWidget = NULL;
if (scene() && scene()->views().count())
profWidget = qobject_cast<ProfileWidget2 *>(scene()->views().first());
- setVisible(profWidget->currentState == ProfileWidget2::PROFILE ? prefs.rulergraph : false);
+
+ if (profWidget && profWidget->currentState == ProfileWidget2::PROFILE)
+ setVisible(prefs.rulergraph);
+ else
+ setVisible(false);
}
void RulerItem2::recalculate()
diff --git a/qt-ui/qtwaitingspinner.cpp b/qt-ui/qtwaitingspinner.cpp
new file mode 100644
index 000000000..14e8669b0
--- /dev/null
+++ b/qt-ui/qtwaitingspinner.cpp
@@ -0,0 +1,288 @@
+
+/* Original Work Copyright (c) 2012-2014 Alexander Turkin
+ Modified 2014 by William Hallatt
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#include <cmath>
+#include <algorithm>
+
+#include <QPainter>
+#include <QTimer>
+
+#include "qtwaitingspinner.h"
+
+/*----------------------------------------------------------------------------*/
+
+// Defaults
+const QColor c_color(Qt::black);
+const qreal c_roundness(70.0);
+const qreal c_minTrailOpacity(15.0);
+const qreal c_trailFadePercentage(70.0);
+const int c_lines(12);
+const int c_lineLength(10);
+const int c_lineWidth(5);
+const int c_innerRadius(10);
+const int c_revPerSec(1);
+
+/*----------------------------------------------------------------------------*/
+
+QtWaitingSpinner::QtWaitingSpinner(QWidget *parent)
+ : QWidget(parent),
+
+ // Configurable settings.
+ m_color(c_color), m_roundness(c_roundness),
+ m_minTrailOpacity(c_minTrailOpacity),
+ m_trailFadePercentage(c_trailFadePercentage), m_revPerSec(c_revPerSec),
+ m_numberOfLines(c_lines), m_lineLength(c_lineLength + c_lineWidth),
+ m_lineWidth(c_lineWidth), m_innerRadius(c_innerRadius),
+
+ // Other
+ m_timer(NULL), m_parent(parent), m_centreOnParent(false),
+ m_currentCounter(0), m_isSpinning(false) {
+ initialise();
+}
+
+/*----------------------------------------------------------------------------*/
+
+QtWaitingSpinner::QtWaitingSpinner(Qt::WindowModality modality, QWidget *parent,
+ bool centreOnParent)
+ : QWidget(parent, Qt::Dialog | Qt::FramelessWindowHint),
+
+ // Configurable settings.
+ m_color(c_color), m_roundness(c_roundness),
+ m_minTrailOpacity(c_minTrailOpacity),
+ m_trailFadePercentage(c_trailFadePercentage), m_revPerSec(c_revPerSec),
+ m_numberOfLines(c_lines), m_lineLength(c_lineLength + c_lineWidth),
+ m_lineWidth(c_lineWidth), m_innerRadius(c_innerRadius),
+
+ // Other
+ m_timer(NULL), m_parent(parent), m_centreOnParent(centreOnParent),
+ m_currentCounter(0) {
+ initialise();
+
+ // We need to set the window modality AFTER we've hidden the
+ // widget for the first time since changing this property while
+ // the widget is visible has no effect.
+ this->setWindowModality(modality);
+ this->setAttribute(Qt::WA_TranslucentBackground);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void QtWaitingSpinner::initialise() {
+ m_timer = new QTimer(this);
+ connect(m_timer, SIGNAL(timeout()), this, SLOT(rotate()));
+ updateSize();
+ updateTimer();
+ this->hide();
+}
+
+/*----------------------------------------------------------------------------*/
+
+void QtWaitingSpinner::paintEvent(QPaintEvent * /*ev*/) {
+ QPainter painter(this);
+ painter.fillRect(this->rect(), Qt::transparent);
+ painter.setRenderHint(QPainter::Antialiasing, true);
+
+ if (m_currentCounter >= m_numberOfLines) {
+ m_currentCounter = 0;
+ }
+ painter.setPen(Qt::NoPen);
+ for (int i = 0; i < m_numberOfLines; ++i) {
+ painter.save();
+ painter.translate(m_innerRadius + m_lineLength,
+ m_innerRadius + m_lineLength);
+ qreal rotateAngle =
+ static_cast<qreal>(360 * i) / static_cast<qreal>(m_numberOfLines);
+ painter.rotate(rotateAngle);
+ painter.translate(m_innerRadius, 0);
+ int distance =
+ lineCountDistanceFromPrimary(i, m_currentCounter, m_numberOfLines);
+ QColor color =
+ currentLineColor(distance, m_numberOfLines, m_trailFadePercentage,
+ m_minTrailOpacity, m_color);
+ painter.setBrush(color);
+ // TODO improve the way rounded rect is painted
+ painter.drawRoundedRect(
+ QRect(0, -m_lineWidth / 2, m_lineLength, m_lineWidth), m_roundness,
+ m_roundness, Qt::RelativeSize);
+ painter.restore();
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+void QtWaitingSpinner::start() {
+ updatePosition();
+ m_isSpinning = true;
+ this->show();
+ if (!m_timer->isActive()) {
+ m_timer->start();
+ m_currentCounter = 0;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+void QtWaitingSpinner::stop() {
+ m_isSpinning = false;
+ this->hide();
+ if (m_timer->isActive()) {
+ m_timer->stop();
+ m_currentCounter = 0;
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+void QtWaitingSpinner::setNumberOfLines(int lines) {
+ m_numberOfLines = lines;
+ m_currentCounter = 0;
+ updateTimer();
+}
+
+/*----------------------------------------------------------------------------*/
+
+void QtWaitingSpinner::setLineLength(int length) {
+ m_lineLength = length;
+ updateSize();
+}
+
+/*----------------------------------------------------------------------------*/
+
+void QtWaitingSpinner::setLineWidth(int width) {
+ m_lineWidth = width;
+ updateSize();
+}
+
+/*----------------------------------------------------------------------------*/
+
+void QtWaitingSpinner::setInnerRadius(int radius) {
+ m_innerRadius = radius;
+ updateSize();
+}
+
+/*----------------------------------------------------------------------------*/
+
+bool QtWaitingSpinner::isSpinning() const { return m_isSpinning; }
+
+/*----------------------------------------------------------------------------*/
+
+void QtWaitingSpinner::setRoundness(qreal roundness) {
+ m_roundness = std::max(0.0, std::min(100.0, roundness));
+}
+
+/*----------------------------------------------------------------------------*/
+
+void QtWaitingSpinner::setColor(QColor color) { m_color = color; }
+
+/*----------------------------------------------------------------------------*/
+
+void QtWaitingSpinner::setRevolutionsPerSecond(int rps) {
+ m_revPerSec = rps;
+ updateTimer();
+}
+
+/*----------------------------------------------------------------------------*/
+
+void QtWaitingSpinner::setTrailFadePercentage(qreal trail) {
+ m_trailFadePercentage = trail;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void QtWaitingSpinner::setMinimumTrailOpacity(qreal minOpacity) {
+ m_minTrailOpacity = minOpacity;
+}
+
+/*----------------------------------------------------------------------------*/
+
+void QtWaitingSpinner::rotate() {
+ ++m_currentCounter;
+ if (m_currentCounter >= m_numberOfLines) {
+ m_currentCounter = 0;
+ }
+ update();
+}
+
+/*----------------------------------------------------------------------------*/
+
+void QtWaitingSpinner::updateSize() {
+ int size = (m_innerRadius + m_lineLength) * 2;
+ setFixedSize(size, size);
+}
+
+/*----------------------------------------------------------------------------*/
+
+void QtWaitingSpinner::updateTimer() {
+ m_timer->setInterval(calculateTimerInterval(m_numberOfLines, m_revPerSec));
+}
+
+/*----------------------------------------------------------------------------*/
+
+void QtWaitingSpinner::updatePosition() {
+ if (m_parent && m_centreOnParent) {
+ this->move(m_parent->frameGeometry().topLeft() + m_parent->rect().center() -
+ this->rect().center());
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+int QtWaitingSpinner::calculateTimerInterval(int lines, int speed) {
+ return 1000 / (lines * speed);
+}
+
+/*----------------------------------------------------------------------------*/
+
+int QtWaitingSpinner::lineCountDistanceFromPrimary(int current, int primary,
+ int totalNrOfLines) {
+ int distance = primary - current;
+ if (distance < 0) {
+ distance += totalNrOfLines;
+ }
+ return distance;
+}
+
+/*----------------------------------------------------------------------------*/
+
+QColor QtWaitingSpinner::currentLineColor(int countDistance, int totalNrOfLines,
+ qreal trailFadePerc, qreal minOpacity,
+ QColor color) {
+ if (countDistance == 0) {
+ return color;
+ }
+ const qreal minAlphaF = minOpacity / 100.0;
+ int distanceThreshold =
+ static_cast<int>(ceil((totalNrOfLines - 1) * trailFadePerc / 100.0));
+ if (countDistance > distanceThreshold) {
+ color.setAlphaF(minAlphaF);
+ } else {
+ qreal alphaDiff = color.alphaF() - minAlphaF;
+ qreal gradient = alphaDiff / static_cast<qreal>(distanceThreshold + 1);
+ qreal resultAlpha = color.alphaF() - gradient * countDistance;
+
+ // If alpha is out of bounds, clip it.
+ resultAlpha = std::min(1.0, std::max(0.0, resultAlpha));
+ color.setAlphaF(resultAlpha);
+ }
+ return color;
+}
+
+/*----------------------------------------------------------------------------*/
diff --git a/qt-ui/qtwaitingspinner.h b/qt-ui/qtwaitingspinner.h
new file mode 100644
index 000000000..254b52ec7
--- /dev/null
+++ b/qt-ui/qtwaitingspinner.h
@@ -0,0 +1,103 @@
+/* Original Work Copyright (c) 2012-2014 Alexander Turkin
+ Modified 2014 by William Hallatt
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#ifndef QTWAITINGSPINNER_H
+#define QTWAITINGSPINNER_H
+
+#include <QWidget>
+
+#include <QTimer>
+#include <QColor>
+
+class QtWaitingSpinner : public QWidget {
+ Q_OBJECT
+public:
+ /*! Constructor for "standard" widget behaviour - use this
+ * constructor if you wish to, e.g. embed your widget in another. */
+ QtWaitingSpinner(QWidget *parent = 0);
+
+ /*! Constructor - use this constructor to automatically create a modal
+ * ("blocking") spinner on top of the calling widget/window. If a valid
+ * parent widget is provided, "centreOnParent" will ensure that
+ * QtWaitingSpinner automatically centres itself on it, if not,
+ * "centreOnParent" is ignored. */
+ QtWaitingSpinner(Qt::WindowModality modality, QWidget *parent = 0,
+ bool centreOnParent = true);
+
+public Q_SLOTS:
+ void start();
+ void stop();
+
+public:
+ void setColor(QColor color);
+ void setRoundness(qreal roundness);
+ void setMinimumTrailOpacity(qreal minOpacity);
+ void setTrailFadePercentage(qreal trail);
+ void setRevolutionsPerSecond(int rps);
+ void setNumberOfLines(int lines);
+ void setLineLength(int length);
+ void setLineWidth(int width);
+ void setInnerRadius(int radius);
+
+ bool isSpinning() const;
+
+private Q_SLOTS:
+ void rotate();
+
+protected:
+ void paintEvent(QPaintEvent *ev);
+
+private:
+ static int calculateTimerInterval(int lines, int speed);
+ static int lineCountDistanceFromPrimary(int current, int primary,
+ int totalNrOfLines);
+ static QColor currentLineColor(int distance, int totalNrOfLines,
+ qreal trailFadePerc, qreal minOpacity,
+ QColor color);
+
+ void initialise();
+ void updateSize();
+ void updateTimer();
+ void updatePosition();
+
+private:
+ // Configurable settings.
+ QColor m_color;
+ qreal m_roundness; // 0..100
+ qreal m_minTrailOpacity;
+ qreal m_trailFadePercentage;
+ int m_revPerSec; // revolutions per second
+ int m_numberOfLines;
+ int m_lineLength;
+ int m_lineWidth;
+ int m_innerRadius;
+
+private:
+ QtWaitingSpinner(const QtWaitingSpinner&);
+ QtWaitingSpinner& operator=(const QtWaitingSpinner&);
+
+ QTimer *m_timer;
+ QWidget *m_parent;
+ bool m_centreOnParent;
+ int m_currentCounter;
+ bool m_isSpinning;
+};
+
+#endif // QTWAITINGSPINNER_H
diff --git a/qt-ui/shiftimagetimes.ui b/qt-ui/shiftimagetimes.ui
index cce51e888..56a222856 100644
--- a/qt-ui/shiftimagetimes.ui
+++ b/qt-ui/shiftimagetimes.ui
@@ -117,6 +117,31 @@
</widget>
</item>
<item>
+ <widget class="QLabel" name="warningLabel">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">color: red;</string>
+ </property>
+ <property name="text">
+ <string>Warning!
+Not all images have timestamps in the range between
+30 minutes before the start and 30 minutes after the end of any selected dive.</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="invalidLabel">
+ <property name="styleSheet">
+ <string notr="true">color: red; </string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
diff --git a/qt-ui/simplewidgets.cpp b/qt-ui/simplewidgets.cpp
index f8f4c2493..430609c7b 100644
--- a/qt-ui/simplewidgets.cpp
+++ b/qt-ui/simplewidgets.cpp
@@ -5,12 +5,17 @@
#include <QFileDialog>
#include <QShortcut>
#include <QCalendarWidget>
+#include <QKeyEvent>
+#include <QAction>
#include "file.h"
#include "mainwindow.h"
#include "helpers.h"
#include "libdivecomputer/parser.h"
-
+#include "divelistview.h"
+#include "display.h"
+#include "profile/profilewidget2.h"
+#include "undocommands.h"
class MinMaxAvgWidgetPrivate {
public:
@@ -137,8 +142,17 @@ void RenumberDialog::renumberOnlySelected(bool selected)
void RenumberDialog::buttonClicked(QAbstractButton *button)
{
- if (ui.buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole)
- renumber_dives(ui.spinBox->value(), selectedOnly);
+ if (ui.buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) {
+ QMap<int,int> renumberedDives;
+ int i;
+ struct dive *dive = NULL;
+ for_each_dive (i, dive) {
+ if (!selectedOnly || dive->selected)
+ renumberedDives.insert(dive->id, dive->number);
+ }
+ UndoRenumberDives *undoCommand = new UndoRenumberDives(renumberedDives, ui.spinBox->value());
+ MainWindow::instance()->undoStack->push(undoCommand);
+ }
}
RenumberDialog::RenumberDialog(QWidget *parent) : QDialog(parent), selectedOnly(false)
@@ -169,7 +183,6 @@ void SetpointDialog::buttonClicked(QAbstractButton *button)
add_event(dc, time, SAMPLE_EVENT_PO2, 0, (int)(1000.0 * ui.spinbox->value()), "SP change");
mark_divelist_changed(true);
MainWindow::instance()->graphics()->replot();
-
}
SetpointDialog::SetpointDialog(QWidget *parent) : QDialog(parent)
@@ -198,7 +211,16 @@ void ShiftTimesDialog::buttonClicked(QAbstractButton *button)
amount *= -1;
if (amount != 0) {
// DANGER, DANGER - this could get our dive_table unsorted...
- shift_times(amount);
+ int i;
+ struct dive *dive;
+ QList<int> affectedDives;
+ for_each_dive (i, dive) {
+ if (!dive->selected)
+ continue;
+
+ affectedDives.append(dive->id);
+ }
+ MainWindow::instance()->undoStack->push(new UndoShiftTime(affectedDives, amount));
sort_table(&dive_table);
mark_divelist_changed(true);
MainWindow::instance()->dive_list()->rememberSelection();
@@ -250,9 +272,6 @@ void ShiftImageTimesDialog::buttonClicked(QAbstractButton *button)
void ShiftImageTimesDialog::syncCameraClicked()
{
- struct memblock mem;
- EXIFInfo exiv;
- int retval;
QPixmap picture;
QDateTime dcDateTime = QDateTime();
QStringList fileNames = QFileDialog::getOpenFileNames(this,
@@ -268,13 +287,8 @@ void ShiftImageTimesDialog::syncCameraClicked()
scene->addPixmap(picture.scaled(ui.DCImage->size()));
ui.DCImage->setScene(scene);
- if (readfile(fileNames.at(0).toUtf8().data(), &mem) <= 0)
- return;
- retval = exiv.parseFrom((const unsigned char *)mem.buffer, (unsigned)mem.size);
- free(mem.buffer);
- if (retval != PARSE_EXIF_SUCCESS)
- return;
- dcImageEpoch = exiv.epoch();
+
+ dcImageEpoch = picture_get_timestamp(fileNames.at(0).toUtf8().data());
dcDateTime.setTime_t(dcImageEpoch);
ui.dcTime->setDateTime(dcDateTime);
connect(ui.dcTime, SIGNAL(dateTimeChanged(const QDateTime &)), this, SLOT(dcDateTimeChanged(const QDateTime &)));
@@ -287,11 +301,12 @@ void ShiftImageTimesDialog::dcDateTimeChanged(const QDateTime &newDateTime)
setOffset(newDateTime.toTime_t() - dcImageEpoch);
}
-ShiftImageTimesDialog::ShiftImageTimesDialog(QWidget *parent) : QDialog(parent), m_amount(0)
+ShiftImageTimesDialog::ShiftImageTimesDialog(QWidget *parent, QStringList fileNames) : QDialog(parent), fileNames(fileNames), m_amount(0)
{
ui.setupUi(this);
connect(ui.buttonBox, SIGNAL(clicked(QAbstractButton *)), this, SLOT(buttonClicked(QAbstractButton *)));
connect(ui.syncCamera, SIGNAL(clicked()), this, SLOT(syncCameraClicked()));
+ connect(ui.timeEdit, SIGNAL(timeChanged(const QTime &)), this, SLOT(timeEditChanged(const QTime &)));
dcImageEpoch = (time_t)0;
}
@@ -311,6 +326,55 @@ void ShiftImageTimesDialog::setOffset(time_t offset)
ui.timeEdit->setTime(QTime(offset / 3600, (offset % 3600) / 60, offset % 60));
}
+void ShiftImageTimesDialog::updateInvalid()
+{
+ timestamp_t timestamp;
+ QDateTime time;
+ bool allValid = true;
+ ui.warningLabel->hide();
+ ui.invalidLabel->hide();
+ ui.invalidLabel->clear();
+
+ Q_FOREACH (const QString &fileName, fileNames) {
+ if (picture_check_valid(fileName.toUtf8().data(), m_amount))
+ continue;
+
+ // We've found invalid image
+ timestamp = picture_get_timestamp(fileName.toUtf8().data());
+ dcImageEpoch = timestamp;
+ time.setTime_t(timestamp + m_amount);
+ ui.invalidLabel->setText(ui.invalidLabel->text() + fileName + " " + time.toString() + "\n");
+ allValid = false;
+ }
+
+ if (!allValid){
+ ui.warningLabel->show();
+ ui.invalidLabel->show();
+ }
+}
+
+void ShiftImageTimesDialog::timeEditChanged(const QTime &time)
+{
+ m_amount = time.hour() * 3600 + time.minute() * 60;
+ if (ui.backwards->isChecked())
+ m_amount *= -1;
+ updateInvalid();
+}
+
+URLDialog::URLDialog(QWidget *parent) : QDialog(parent)
+{
+ ui.setupUi(this);
+ QShortcut *close = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_W), this);
+ connect(close, SIGNAL(activated()), this, SLOT(close()));
+ QShortcut *quit = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q), this);
+ connect(quit, SIGNAL(activated()), parent, SLOT(close()));
+}
+
+QString URLDialog::url() const
+{
+ return ui.urlField->toPlainText();
+}
+
bool isGnome3Session()
{
#if defined(QT_OS_WIW) || defined(QT_OS_MAC)
@@ -454,8 +518,7 @@ DiveComponentSelection::DiveComponentSelection(QWidget *parent, struct dive *tar
{
ui.setupUi(this);
what = _what;
- UI_FROM_COMPONENT(location);
- UI_FROM_COMPONENT(gps);
+ UI_FROM_COMPONENT(divesite);
UI_FROM_COMPONENT(divemaster);
UI_FROM_COMPONENT(buddy);
UI_FROM_COMPONENT(rating);
@@ -475,8 +538,7 @@ DiveComponentSelection::DiveComponentSelection(QWidget *parent, struct dive *tar
void DiveComponentSelection::buttonClicked(QAbstractButton *button)
{
if (ui.buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) {
- COMPONENT_FROM_UI(location);
- COMPONENT_FROM_UI(gps);
+ COMPONENT_FROM_UI(divesite);
COMPONENT_FROM_UI(divemaster);
COMPONENT_FROM_UI(buddy);
COMPONENT_FROM_UI(rating);
diff --git a/qt-ui/simplewidgets.h b/qt-ui/simplewidgets.h
index 8d5b4f73c..17f628392 100644
--- a/qt-ui/simplewidgets.h
+++ b/qt-ui/simplewidgets.h
@@ -6,6 +6,7 @@ class QAbstractButton;
class QNetworkReply;
#include <QWidget>
+#include <QGroupBox>
#include <QDialog>
#include <stdint.h>
@@ -13,6 +14,7 @@ class QNetworkReply;
#include "ui_setpoint.h"
#include "ui_shifttimes.h"
#include "ui_shiftimagetimes.h"
+#include "ui_urldialog.h"
#include "ui_divecomponentselection.h"
#include "ui_listfilter.h"
#include "ui_filterwidget.h"
@@ -96,7 +98,7 @@ private:
class ShiftImageTimesDialog : public QDialog {
Q_OBJECT
public:
- explicit ShiftImageTimesDialog(QWidget *parent);
+ explicit ShiftImageTimesDialog(QWidget *parent, QStringList fileNames);
time_t amount() const;
void setOffset(time_t offset);
private
@@ -104,13 +106,25 @@ slots:
void buttonClicked(QAbstractButton *button);
void syncCameraClicked();
void dcDateTimeChanged(const QDateTime &);
+ void timeEditChanged(const QTime &time);
+ void updateInvalid();
private:
+ QStringList fileNames;
Ui::ShiftImageTimesDialog ui;
time_t m_amount;
time_t dcImageEpoch;
};
+class URLDialog : public QDialog {
+ Q_OBJECT
+public:
+ explicit URLDialog(QWidget *parent);
+ QString url() const;
+private:
+ Ui::URLDialog ui;
+};
+
class QCalendarWidget;
class DateWidget : public QWidget {
diff --git a/qt-ui/socialnetworks.cpp b/qt-ui/socialnetworks.cpp
index 21ccf9354..6a81d5db7 100644
--- a/qt-ui/socialnetworks.cpp
+++ b/qt-ui/socialnetworks.cpp
@@ -302,7 +302,7 @@ void SocialNetworkDialog::selectionChanged()
tr("min", "abbreviation for minutes")));
}
if (ui->Location->isChecked()) {
- fullText += tr("Dive location: %1 \n").arg(d->location);
+ fullText += tr("Dive location: %1 \n").arg(get_dive_location(d));
}
if (ui->Buddy->isChecked()) {
fullText += tr("Buddy: %1 \n").arg(d->buddy);
diff --git a/qt-ui/subsurfacewebservices.cpp b/qt-ui/subsurfacewebservices.cpp
index fe7605ad7..fad542de6 100644
--- a/qt-ui/subsurfacewebservices.cpp
+++ b/qt-ui/subsurfacewebservices.cpp
@@ -1,7 +1,13 @@
#include "subsurfacewebservices.h"
+#include "helpers.h"
#include "webservice.h"
#include "mainwindow.h"
#include "usersurvey.h"
+#include "divelist.h"
+#include "globe.h"
+#include "maintab.h"
+#include "display.h"
+#include "membuffer.h"
#include <errno.h>
#include <QDir>
@@ -26,7 +32,25 @@
#endif
struct dive_table gps_location_table;
-static bool merge_locations_into_dives(void);
+
+// we don't overwrite any existing GPS info in the dive
+// so get the dive site and if there is none or there is one without GPS fix, add it
+static void copy_gps_location(struct dive *from, struct dive *to)
+{
+ struct dive_site *ds = get_dive_site_for_dive(to);
+ if (!ds || !dive_site_has_gps_location(ds)) {
+ struct dive_site *gds = get_dive_site_for_dive(from);
+ if (!ds) {
+ // simply link to the one created for the fake dive
+ to->dive_site_uuid = gds->uuid;
+ } else {
+ ds->latitude = gds->latitude;
+ ds->longitude = gds->longitude;
+ if (same_string(ds->name, ""))
+ ds->name = copy_string(gds->name);
+ }
+ }
+}
#define SAME_GROUP 6 * 3600 // six hours
//TODO: C Code. static functions are not good if we plan to have a test for them.
@@ -39,14 +63,14 @@ static bool merge_locations_into_dives(void)
for_each_dive (i, dive) {
if (!dive_has_gps_location(dive)) {
- for (j = tracer; (gpsfix = get_gps_location(j, &gps_location_table)) !=NULL; j++) {
+ for (j = tracer; (gpsfix = get_dive_from_table(j, &gps_location_table)) !=NULL; j++) {
if (dive_within_time_range (dive, gpsfix->when, SAME_GROUP)) {
/*
* If position is fixed during dive. This is the good one.
* Asign and mark position, and end gps_location loop
*/
if ((dive->when <= gpsfix->when && gpsfix->when <= dive->when + dive->duration.seconds)) {
- copy_gps_location(gpsfix,dive);
+ copy_gps_location(gpsfix, dive);
changed++;
tracer = j;
break;
@@ -54,7 +78,7 @@ static bool merge_locations_into_dives(void)
/*
* If it is not, check if there are more position fixes in SAME_GROUP range
*/
- if ((nextgpsfix = get_gps_location(j+1,&gps_location_table)) &&
+ if ((nextgpsfix = get_dive_from_table(j+1,&gps_location_table)) &&
dive_within_time_range (dive, nextgpsfix->when, SAME_GROUP)) {
/*
* If distance from gpsfix to end of dive is shorter than distance between
@@ -62,7 +86,7 @@ static bool merge_locations_into_dives(void)
* If not, simply fail and nextgpsfix will be evaluated in next iteration.
*/
if ((dive->when + dive->duration.seconds - gpsfix->when) < (nextgpsfix->when - gpsfix->when)) {
- copy_gps_location(gpsfix,dive);
+ copy_gps_location(gpsfix, dive);
tracer = j;
break;
}
@@ -70,7 +94,7 @@ static bool merge_locations_into_dives(void)
* If no more positions in range, the actual is the one. Asign, mark and end loop.
*/
} else {
- copy_gps_location(gpsfix,dive);
+ copy_gps_location(gpsfix, dive);
changed++;
tracer = j;
break;
@@ -122,11 +146,12 @@ bool DivelogsDeWebServices::prepare_dives_for_divelogs(const QString &tempfile,
/* walk the dive list in chronological order */
int i;
struct dive *dive;
+ struct membuffer mb = { 0 };
for_each_dive (i, dive) {
FILE *f;
char filename[PATH_MAX];
int streamsize;
- char *membuf;
+ const char *membuf;
xmlDoc *transformed;
struct zip_source *s;
@@ -136,29 +161,11 @@ bool DivelogsDeWebServices::prepare_dives_for_divelogs(const QString &tempfile,
*/
if (selected && !dive->selected)
continue;
- QString innerTmpFile = tempfile;
- QString tmpSuffix = QString::number(qrand() % 9999) + ".tmp";
- innerTmpFile.replace(".dld", tmpSuffix);
- f = subsurface_fopen(QFile::encodeName(QDir::toNativeSeparators(innerTmpFile)), "w+");
- if (!f) {
- report_error(tr("cannot create temporary file: %s").toUtf8(), qt_error_string().toUtf8().data());
- goto error_close_zip;
- }
- save_dive(f, dive);
- fseek(f, 0, SEEK_END);
- streamsize = ftell(f);
- rewind(f);
-
- membuf = (char *)malloc(streamsize + 1);
- if (!membuf || (streamsize = fread(membuf, 1, streamsize, f)) == 0) {
- report_error(tr("internal error: %s").toUtf8(), qt_error_string().toUtf8().data());
- fclose(f);
- free((void *)membuf);
- goto error_close_zip;
- }
- membuf[streamsize] = 0;
- fclose(f);
- unlink(QFile::encodeName(QDir::toNativeSeparators(innerTmpFile)));
+ /* make sure the buffer is empty and add the dive */
+ mb.len = 0;
+ save_one_dive_to_mb(&mb, dive);
+ membuf = mb_cstring(&mb);
+ streamsize = strlen(membuf);
/*
* Parse the memory buffer into XML document and
* transform it to divelogs.de format, finally dumping
@@ -168,7 +175,6 @@ bool DivelogsDeWebServices::prepare_dives_for_divelogs(const QString &tempfile,
if (!doc) {
qWarning() << errPrefix << "could not parse back into memory the XML file we've just created!";
report_error(tr("internal error").toUtf8());
- free((void *)membuf);
goto error_close_zip;
}
free((void *)membuf);
@@ -210,7 +216,7 @@ WebServices::WebServices(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f
ui.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
timeout.setSingleShot(true);
defaultApplyText = ui.buttonBox->button(QDialogButtonBox::Apply)->text();
- userAgent = UserSurvey::getUserAgent();
+ userAgent = getUserAgent();
}
void WebServices::hidePassword()
@@ -326,10 +332,19 @@ void SubsurfaceWebServices::buttonClicked(QAbstractButton *button)
ui.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
switch (ui.buttonBox->buttonRole(button)) {
case QDialogButtonBox::ApplyRole: {
+ int i;
+ struct dive *d;
+ struct dive_site *ds;
clear_table(&gps_location_table);
QByteArray url = tr("Webservice").toLocal8Bit();
parse_xml_buffer(url.data(), downloadedData.data(), downloadedData.length(), &gps_location_table, NULL);
-
+ // make sure we mark all the dive sites that were created
+ for (i = 0; i < gps_location_table.nr; i++) {
+ d = get_dive_from_table(i, &gps_location_table);
+ ds = get_dive_site_by_uuid(d->dive_site_uuid);
+ if (ds)
+ ds->notes = strdup("SubsurfaceWebservice");
+ }
/* now merge the data in the gps_location table into the dive_table */
if (merge_locations_into_dives()) {
mark_divelist_changed(true);
@@ -358,6 +373,16 @@ void SubsurfaceWebServices::buttonClicked(QAbstractButton *button)
hide();
close();
resetState();
+ /* and now clean up and remove all the extra dive sites that were created */
+ QSet<uint32_t> usedUuids;
+ for_each_dive(i, d) {
+ if (d->dive_site_uuid)
+ usedUuids.insert(d->dive_site_uuid);
+ }
+ for_each_dive_site(i, ds) {
+ if (!usedUuids.contains(ds->uuid) && same_string(ds->notes, "SubsurfaceWebservice"))
+ delete_dive_site(ds->uuid);
+ }
} break;
case QDialogButtonBox::RejectRole:
if (reply != NULL && reply->isOpen()) {
@@ -606,8 +631,10 @@ void DivelogsDeWebServices::prepareDivesForUpload(bool selected)
f.remove();
return;
}
+ } else {
+ report_error("Failed to create upload file %s\n", qPrintable(filename));
}
- MainWindow::instance()->showError(get_error_string());
+ MainWindow::instance()->getNotificationWidget()->showNotification(get_error_string(), KMessageWidget::Error);
}
void DivelogsDeWebServices::uploadDives(QIODevice *dldContent)
@@ -638,7 +665,9 @@ void DivelogsDeWebServices::uploadDives(QIODevice *dldContent)
}
}
-DivelogsDeWebServices::DivelogsDeWebServices(QWidget *parent, Qt::WindowFlags f) : WebServices(parent, f), uploadMode(false)
+DivelogsDeWebServices::DivelogsDeWebServices(QWidget *parent, Qt::WindowFlags f) : WebServices(parent, f),
+ multipart(NULL),
+ uploadMode(false)
{
QSettings s;
ui.userID->setText(s.value("divelogde_user").toString());
diff --git a/qt-ui/tableview.cpp b/qt-ui/tableview.cpp
index 78a0bce10..e412d77e9 100644
--- a/qt-ui/tableview.cpp
+++ b/qt-ui/tableview.cpp
@@ -12,11 +12,9 @@ TableView::TableView(QWidget *parent) : QGroupBox(parent)
QFontMetrics fm(defaultModelFont());
int text_ht = fm.height();
- int text_em = fm.width('m');
metrics.icon = &defaultIconMetrics();
- metrics.col_width = 7*text_em;
metrics.rm_col_width = metrics.icon->sz_small + 2*metrics.icon->spacing;
metrics.header_ht = text_ht + 10; // TODO DPI
@@ -138,7 +136,8 @@ void TableView::edit(const QModelIndex &index)
int TableView::defaultColumnWidth(int col)
{
- return col == CylindersModel::REMOVE ? metrics.rm_col_width : metrics.col_width;
+ QString text = ui.tableView->model()->headerData(col, Qt::Horizontal).toString();
+ return text.isEmpty() ? metrics.rm_col_width : defaultModelFontMetrics().width(text) + 4; // add small margin
}
QTableView *TableView::view()
diff --git a/qt-ui/tableview.h b/qt-ui/tableview.h
index 36eef907a..f72b256ea 100644
--- a/qt-ui/tableview.h
+++ b/qt-ui/tableview.h
@@ -21,7 +21,6 @@ class TableView : public QGroupBox {
struct TableMetrics {
const IconMetrics* icon; // icon metrics
- int col_width; // generic column width
int rm_col_width; // column width of REMOVE column
int header_ht; // height of the header
};
diff --git a/qt-ui/tagwidget.cpp b/qt-ui/tagwidget.cpp
index 8365a2ea7..3b61b492a 100644
--- a/qt-ui/tagwidget.cpp
+++ b/qt-ui/tagwidget.cpp
@@ -1,5 +1,6 @@
#include "tagwidget.h"
#include "mainwindow.h"
+#include "maintab.h"
#include <QCompleter>
TagWidget::TagWidget(QWidget *parent) : GroupedLineEdit(parent), m_completer(NULL), lastFinishedTag(false)
diff --git a/qt-ui/undocommands.cpp b/qt-ui/undocommands.cpp
new file mode 100644
index 000000000..aad264e24
--- /dev/null
+++ b/qt-ui/undocommands.cpp
@@ -0,0 +1,123 @@
+#include "undocommands.h"
+#include "mainwindow.h"
+#include "divelist.h"
+
+UndoDeleteDive::UndoDeleteDive(QList<dive *> deletedDives)
+ : diveList(deletedDives)
+{
+ setText("delete dive");
+ if (diveList.count() > 1)
+ setText(QString("delete %1 dives").arg(QString::number(diveList.count())));
+}
+
+void UndoDeleteDive::undo()
+{
+ for (int i = 0; i < diveList.count(); i++)
+ record_dive(diveList.at(i));
+ mark_divelist_changed(true);
+ MainWindow::instance()->refreshDisplay();
+}
+
+void UndoDeleteDive::redo()
+{
+ QList<struct dive*> newList;
+ for (int i = 0; i < diveList.count(); i++) {
+ //make a copy of the dive before deleting it
+ struct dive* d = alloc_dive();
+ copy_dive(diveList.at(i), d);
+ newList.append(d);
+ //delete the dive
+ delete_single_dive(get_divenr(diveList.at(i)));
+ }
+ mark_divelist_changed(true);
+ MainWindow::instance()->refreshDisplay();
+ diveList.clear();
+ diveList = newList;
+}
+
+
+UndoShiftTime::UndoShiftTime(QList<int> changedDives, int amount)
+ : diveList(changedDives), timeChanged(amount)
+{
+ setText("shift time");
+}
+
+void UndoShiftTime::undo()
+{
+ for (int i = 0; i < diveList.count(); i++) {
+ struct dive* d = get_dive_by_uniq_id(diveList.at(i));
+ d->when -= timeChanged;
+ }
+ mark_divelist_changed(true);
+ MainWindow::instance()->refreshDisplay();
+}
+
+void UndoShiftTime::redo()
+{
+ for (int i = 0; i < diveList.count(); i++) {
+ struct dive* d = get_dive_by_uniq_id(diveList.at(i));
+ d->when += timeChanged;
+ }
+ mark_divelist_changed(true);
+ MainWindow::instance()->refreshDisplay();
+}
+
+
+UndoRenumberDives::UndoRenumberDives(QMap<int, int> originalNumbers, int startNumber)
+{
+ oldNumbers = originalNumbers;
+ start = startNumber;
+ setText("renumber dive");
+ if (oldNumbers.count() > 1)
+ setText(QString("renumber %1 dives").arg(QString::number(oldNumbers.count())));
+}
+
+void UndoRenumberDives::undo()
+{
+ foreach (int key, oldNumbers.keys()) {
+ struct dive* d = get_dive_by_uniq_id(key);
+ d->number = oldNumbers.value(key);
+ }
+ mark_divelist_changed(true);
+ MainWindow::instance()->refreshDisplay();
+}
+
+void UndoRenumberDives::redo()
+{
+ int i = start;
+ foreach (int key, oldNumbers.keys()) {
+ struct dive* d = get_dive_by_uniq_id(key);
+ d->number = i++;
+ }
+ mark_divelist_changed(true);
+ MainWindow::instance()->refreshDisplay();
+}
+
+
+UndoRemoveDivesFromTrip::UndoRemoveDivesFromTrip(QMap<dive *, dive_trip *> removedDives)
+{
+ divesToUndo = removedDives;
+ setText("remove dive(s) from trip");
+}
+
+void UndoRemoveDivesFromTrip::undo()
+{
+ QMapIterator<dive*, dive_trip*> i(divesToUndo);
+ while (i.hasNext()) {
+ i.next();
+ add_dive_to_trip(i.key (), i.value());
+ }
+ mark_divelist_changed(true);
+ MainWindow::instance()->refreshDisplay();
+}
+
+void UndoRemoveDivesFromTrip::redo()
+{
+ QMapIterator<dive*, dive_trip*> i(divesToUndo);
+ while (i.hasNext()) {
+ i.next();
+ remove_dive_from_trip(i.key(), false);
+ }
+ mark_divelist_changed(true);
+ MainWindow::instance()->refreshDisplay();
+}
diff --git a/qt-ui/undocommands.h b/qt-ui/undocommands.h
new file mode 100644
index 000000000..bd8530d77
--- /dev/null
+++ b/qt-ui/undocommands.h
@@ -0,0 +1,50 @@
+#ifndef UNDOCOMMANDS_H
+#define UNDOCOMMANDS_H
+
+#include <QUndoCommand>
+#include <QMap>
+#include "dive.h"
+
+class UndoDeleteDive : public QUndoCommand {
+public:
+ UndoDeleteDive(QList<struct dive*> deletedDives);
+ virtual void undo();
+ virtual void redo();
+
+private:
+ QList<struct dive*> diveList;
+};
+
+class UndoShiftTime : public QUndoCommand {
+public:
+ UndoShiftTime(QList<int> changedDives, int amount);
+ virtual void undo();
+ virtual void redo();
+
+private:
+ QList<int> diveList;
+ int timeChanged;
+};
+
+class UndoRenumberDives : public QUndoCommand {
+public:
+ UndoRenumberDives(QMap<int,int> originalNumbers, int startNumber);
+ virtual void undo();
+ virtual void redo();
+
+private:
+ QMap<int,int> oldNumbers;
+ int start;
+};
+
+class UndoRemoveDivesFromTrip : public QUndoCommand {
+public:
+ UndoRemoveDivesFromTrip(QMap<struct dive*, dive_trip*> removedDives);
+ virtual void undo();
+ virtual void redo();
+
+private:
+ QMap<struct dive*, dive_trip*> divesToUndo;
+};
+
+#endif // UNDOCOMMANDS_H
diff --git a/qt-ui/updatemanager.cpp b/qt-ui/updatemanager.cpp
index 3fff8b45d..8a22400b9 100644
--- a/qt-ui/updatemanager.cpp
+++ b/qt-ui/updatemanager.cpp
@@ -1,10 +1,11 @@
#include "updatemanager.h"
#include "usersurvey.h"
+#include "helpers.h"
#include <QtNetwork>
#include <QMessageBox>
#include <QUuid>
#include "subsurfacewebservices.h"
-#include "ssrf-version.h"
+#include "version.h"
#include "mainwindow.h"
UpdateManager::UpdateManager(QObject *parent) : QObject(parent)
@@ -16,9 +17,9 @@ UpdateManager::UpdateManager(QObject *parent) : QObject(parent)
return;
if (settings.contains("LastVersionUsed")) {
// we have checked at least once before
- if (settings.value("LastVersionUsed").toString() != GIT_VERSION_STRING) {
+ if (settings.value("LastVersionUsed").toString() != subsurface_git_version()) {
// we have just updated - wait two weeks before you check again
- settings.setValue("LastVersionUsed", QString(GIT_VERSION_STRING));
+ settings.setValue("LastVersionUsed", QString(subsurface_git_version()));
settings.setValue("NextCheck", QDateTime::currentDateTime().addDays(14).toString(Qt::ISODate));
} else {
// is it time to check again?
@@ -28,7 +29,7 @@ UpdateManager::UpdateManager(QObject *parent) : QObject(parent)
return;
}
}
- settings.setValue("LastVersionUsed", QString(GIT_VERSION_STRING));
+ settings.setValue("LastVersionUsed", QString(subsurface_git_version()));
settings.setValue("NextCheck", QDateTime::currentDateTime().addDays(14).toString(Qt::ISODate));
checkForUpdates(true);
}
@@ -47,13 +48,13 @@ void UpdateManager::checkForUpdates(bool automatic)
os = "unknown";
#endif
isAutomaticCheck = automatic;
- QString version = CANONICAL_VERSION_STRING;
+ QString version = subsurface_canonical_version();
QString uuidString = getUUID();
QString url = QString("http://subsurface-divelog.org/updatecheck.html?os=%1&version=%2&uuid=%3").arg(os, version, uuidString);
QNetworkRequest request;
request.setUrl(url);
request.setRawHeader("Accept", "text/xml");
- QString userAgent = UserSurvey::getUserAgent();
+ QString userAgent = getUserAgent();
request.setRawHeader("User-Agent", userAgent.toUtf8());
connect(SubsurfaceWebServices::manager()->get(request), SIGNAL(finished()), this, SLOT(requestReceived()), Qt::UniqueConnection);
}
diff --git a/qt-ui/urldialog.ui b/qt-ui/urldialog.ui
new file mode 100644
index 000000000..397f90a64
--- /dev/null
+++ b/qt-ui/urldialog.ui
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>URLDialog</class>
+ <widget class="QDialog" name="URLDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>397</width>
+ <height>103</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="geometry">
+ <rect>
+ <x>40</x>
+ <y>60</y>
+ <width>341</width>
+ <height>32</height>
+ </rect>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ <widget class="QPlainTextEdit" name="urlField">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>30</y>
+ <width>371</width>
+ <height>21</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QLabel" name="label">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>10</y>
+ <width>151</width>
+ <height>16</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Enter URL for images</string>
+ </property>
+ </widget>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>URLDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>URLDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/qt-ui/usersurvey.cpp b/qt-ui/usersurvey.cpp
index 4061d46df..05da582a1 100644
--- a/qt-ui/usersurvey.cpp
+++ b/qt-ui/usersurvey.cpp
@@ -4,7 +4,7 @@
#include "usersurvey.h"
#include "ui_usersurvey.h"
-#include "ssrf-version.h"
+#include "version.h"
#include "subsurfacewebservices.h"
#include "updatemanager.h"
@@ -22,12 +22,12 @@ UserSurvey::UserSurvey(QWidget *parent) : QDialog(parent),
QShortcut *quitKey = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q), this);
connect(quitKey, SIGNAL(activated()), parent, SLOT(close()));
- os = QString("ssrfVers=%1").arg(VERSION_STRING);
+ os = QString("ssrfVers=%1").arg(subsurface_version());
os.append(QString("&prettyOsName=%1").arg(SubsurfaceSysInfo::prettyOsName()));
- QString arch = SubsurfaceSysInfo::cpuArchitecture();
+ QString arch = SubsurfaceSysInfo::buildCpuArchitecture();
os.append(QString("&appCpuArch=%1").arg(arch));
if (arch == "i386") {
- QString osArch = SubsurfaceSysInfo::osArch();
+ QString osArch = SubsurfaceSysInfo::currentCpuArchitecture();
os.append(QString("&osCpuArch=%1").arg(osArch));
}
os.append(QString("&uiLang=%1").arg(uiLanguage(NULL)));
@@ -39,32 +39,16 @@ QString UserSurvey::getVersion()
{
QString arch;
// fill in the system data
- QString sysInfo = QString("Subsurface %1").arg(VERSION_STRING);
+ QString sysInfo = QString("Subsurface %1").arg(subsurface_version());
sysInfo.append(tr("\nOperating system: %1").arg(SubsurfaceSysInfo::prettyOsName()));
- arch = SubsurfaceSysInfo::cpuArchitecture();
+ arch = SubsurfaceSysInfo::buildCpuArchitecture();
sysInfo.append(tr("\nCPU architecture: %1").arg(arch));
if (arch == "i386")
- sysInfo.append(tr("\nOS CPU architecture: %1").arg(SubsurfaceSysInfo::osArch()));
+ sysInfo.append(tr("\nOS CPU architecture: %1").arg(SubsurfaceSysInfo::currentCpuArchitecture()));
sysInfo.append(tr("\nLanguage: %1").arg(uiLanguage(NULL)));
return sysInfo;
}
-QString UserSurvey::getUserAgent()
-{
- QString arch;
- // fill in the system data - use ':' as separator
- // replace all other ':' with ' ' so that this is easy to parse
- QString userAgent = QString("Subsurface:%1:").arg(VERSION_STRING);
- userAgent.append(SubsurfaceSysInfo::prettyOsName().replace(':', ' ') + ":");
- arch = SubsurfaceSysInfo::cpuArchitecture().replace(':', ' ');
- userAgent.append(arch);
- if (arch == "i386")
- userAgent.append("/" + SubsurfaceSysInfo::osArch());
- userAgent.append(":" + uiLanguage(NULL));
- return userAgent;
-
-}
-
UserSurvey::~UserSurvey()
{
delete ui;
diff --git a/qt-ui/usersurvey.h b/qt-ui/usersurvey.h
index 55140521e..1dd5aaab3 100644
--- a/qt-ui/usersurvey.h
+++ b/qt-ui/usersurvey.h
@@ -16,7 +16,6 @@ public:
explicit UserSurvey(QWidget *parent = 0);
~UserSurvey();
static QString getVersion();
- static QString getUserAgent();
private
slots: