summaryrefslogtreecommitdiffstats
path: root/desktop-widgets/tab-widgets
diff options
context:
space:
mode:
authorGravatar Tomaz Canabrava <tcanabrava@kde.org>2017-04-04 19:21:30 +0200
committerGravatar Dirk Hohndel <dirk@hohndel.org>2017-04-06 18:23:25 -0700
commit1fc4fba69fed8607d9b961086a88bcf362a482a5 (patch)
treed063efda90ddcd0324f648ab4be48d2282f5c1ff /desktop-widgets/tab-widgets
parent8a71196e4e58dfb3534fda5f00856a0223726689 (diff)
downloadsubsurface-1fc4fba69fed8607d9b961086a88bcf362a482a5.tar.gz
Break down MainTab into smaller classes
Maintab is one of our most complex classes, and it's something I'm not actually proud of. But it currently works and the idea of splitting it was in my head for quite a while. This is the third or fourth tentative of splitting it, and this time I let the most complex part of it untouched, the Notes and Equipment tab are way too complex to untangle right now on my limited time. A new class 'TabBase' should be used for any new tab that we may create, and added on the MainTab (see the new lines on the MainTab constructor). Also, Extra Info, Information, Photos and Statistics where ported to this new way helping reduce the number of lines and functions on the MainTab quite a bit. Overall this is a step in the right direction for the future. Signed-off-by: Tomaz Canabrava <tcanabrava@kde.org> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Diffstat (limited to 'desktop-widgets/tab-widgets')
-rw-r--r--desktop-widgets/tab-widgets/TabBase.cpp6
-rw-r--r--desktop-widgets/tab-widgets/TabBase.h17
-rw-r--r--desktop-widgets/tab-widgets/TabDiveExtraInfo.cpp29
-rw-r--r--desktop-widgets/tab-widgets/TabDiveExtraInfo.h24
-rw-r--r--desktop-widgets/tab-widgets/TabDiveExtraInfo.ui24
-rw-r--r--desktop-widgets/tab-widgets/TabDiveInformation.cpp100
-rw-r--r--desktop-widgets/tab-widgets/TabDiveInformation.h23
-rw-r--r--desktop-widgets/tab-widgets/TabDiveInformation.ui340
-rw-r--r--desktop-widgets/tab-widgets/TabDivePhotos.cpp98
-rw-r--r--desktop-widgets/tab-widgets/TabDivePhotos.h33
-rw-r--r--desktop-widgets/tab-widgets/TabDivePhotos.ui35
-rw-r--r--desktop-widgets/tab-widgets/TabDiveStatistics.cpp125
-rw-r--r--desktop-widgets/tab-widgets/TabDiveStatistics.h22
-rw-r--r--desktop-widgets/tab-widgets/TabDiveStatistics.ui222
-rw-r--r--desktop-widgets/tab-widgets/maintab.cpp1522
-rw-r--r--desktop-widgets/tab-widgets/maintab.h129
-rw-r--r--desktop-widgets/tab-widgets/maintab.ui657
17 files changed, 3406 insertions, 0 deletions
diff --git a/desktop-widgets/tab-widgets/TabBase.cpp b/desktop-widgets/tab-widgets/TabBase.cpp
new file mode 100644
index 000000000..127cce6e4
--- /dev/null
+++ b/desktop-widgets/tab-widgets/TabBase.cpp
@@ -0,0 +1,6 @@
+#include "TabBase.h"
+
+TabBase::TabBase(QWidget *parent) : QWidget(parent)
+{
+}
+
diff --git a/desktop-widgets/tab-widgets/TabBase.h b/desktop-widgets/tab-widgets/TabBase.h
new file mode 100644
index 000000000..c5f3c4dcc
--- /dev/null
+++ b/desktop-widgets/tab-widgets/TabBase.h
@@ -0,0 +1,17 @@
+#ifndef TAB_BASE_H
+#define TAB_BASE_H
+
+#include <QWidget>
+
+struct dive;
+
+class TabBase : public QWidget {
+ Q_OBJECT
+
+public:
+ TabBase(QWidget *parent);
+ virtual void updateData() = 0;
+ virtual void clear() = 0;
+};
+
+#endif
diff --git a/desktop-widgets/tab-widgets/TabDiveExtraInfo.cpp b/desktop-widgets/tab-widgets/TabDiveExtraInfo.cpp
new file mode 100644
index 000000000..0560da7b3
--- /dev/null
+++ b/desktop-widgets/tab-widgets/TabDiveExtraInfo.cpp
@@ -0,0 +1,29 @@
+#include "TabDiveExtraInfo.h"
+#include "ui_TabDiveExtraInfo.h"
+
+#include <qt-models/divecomputerextradatamodel.h>
+
+TabDiveExtraInfo::TabDiveExtraInfo(QWidget *parent) :
+ TabBase(parent),
+ ui(new Ui::TabDiveExtraInfo()),
+ extraDataModel(new ExtraDataModel())
+{
+ ui->setupUi(this);
+ ui->extraData->setModel(extraDataModel);
+}
+
+TabDiveExtraInfo::~TabDiveExtraInfo()
+{
+ delete ui;
+}
+
+void TabDiveExtraInfo::updateData()
+{
+ extraDataModel->updateDive();
+}
+
+void TabDiveExtraInfo::clear()
+{
+ extraDataModel->updateDive();
+}
+
diff --git a/desktop-widgets/tab-widgets/TabDiveExtraInfo.h b/desktop-widgets/tab-widgets/TabDiveExtraInfo.h
new file mode 100644
index 000000000..194e6cfd3
--- /dev/null
+++ b/desktop-widgets/tab-widgets/TabDiveExtraInfo.h
@@ -0,0 +1,24 @@
+#ifndef TAB_DIVE_EXTRA_INFO_H
+#define TAB_DIVE_EXTRA_INFO_H
+
+#include "TabBase.h"
+
+namespace Ui {
+ class TabDiveExtraInfo;
+};
+
+class ExtraDataModel;
+
+class TabDiveExtraInfo : public TabBase {
+ Q_OBJECT
+public:
+ TabDiveExtraInfo(QWidget *parent);
+ ~TabDiveExtraInfo();
+ void updateData() override;
+ void clear() override;
+private:
+ Ui::TabDiveExtraInfo *ui;
+ ExtraDataModel *extraDataModel;
+};
+
+#endif
diff --git a/desktop-widgets/tab-widgets/TabDiveExtraInfo.ui b/desktop-widgets/tab-widgets/TabDiveExtraInfo.ui
new file mode 100644
index 000000000..0e3008d1a
--- /dev/null
+++ b/desktop-widgets/tab-widgets/TabDiveExtraInfo.ui
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>TabDiveExtraInfo</class>
+ <widget class="QWidget" name="TabDiveExtraInfo">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Extra Info</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QTableView" name="extraData"/>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/desktop-widgets/tab-widgets/TabDiveInformation.cpp b/desktop-widgets/tab-widgets/TabDiveInformation.cpp
new file mode 100644
index 000000000..c9f9a4c44
--- /dev/null
+++ b/desktop-widgets/tab-widgets/TabDiveInformation.cpp
@@ -0,0 +1,100 @@
+#include "TabDiveInformation.h"
+#include "ui_TabDiveInformation.h"
+#include "../tagwidget.h"
+
+#include <core/helpers.h>
+#include <core/statistics.h>
+#include <core/display.h>
+
+TabDiveInformation::TabDiveInformation(QWidget *parent) : TabBase(parent), ui(new Ui::TabDiveInformation())
+{
+ ui->setupUi(this);
+}
+
+TabDiveInformation::~TabDiveInformation()
+{
+ delete ui;
+}
+
+void TabDiveInformation::clear()
+{
+ ui->sacText->clear();
+ ui->otuText->clear();
+ ui->maxcnsText->clear();
+ ui->oxygenHeliumText->clear();
+ ui->gasUsedText->clear();
+ ui->dateText->clear();
+ ui->diveTimeText->clear();
+ ui->surfaceIntervalText->clear();
+ ui->maximumDepthText->clear();
+ ui->averageDepthText->clear();
+ ui->waterTemperatureText->clear();
+ ui->airTemperatureText->clear();
+ ui->airPressureText->clear();
+ ui->salinityText->clear();
+}
+
+void TabDiveInformation::updateData()
+{
+ clear();
+
+ ui->maxcnsText->setText(QString("%1\%").arg(displayed_dive.maxcns));
+ ui->otuText->setText(QString("%1").arg(displayed_dive.otu));
+ ui->maximumDepthText->setText(get_depth_string(displayed_dive.maxdepth, true));
+ ui->averageDepthText->setText(get_depth_string(displayed_dive.meandepth, true));
+ ui->dateText->setText(get_short_dive_date_string(displayed_dive.when));
+ ui->waterTemperatureText->setText(get_temperature_string(displayed_dive.watertemp, true));
+ ui->airTemperatureText->setText(get_temperature_string(displayed_dive.airtemp, true));
+
+ volume_t gases[MAX_CYLINDERS] = {};
+ get_gas_used(&displayed_dive, gases);
+ QString volumes;
+ int mean[MAX_CYLINDERS], duration[MAX_CYLINDERS];
+ per_cylinder_mean_depth(&displayed_dive, select_dc(&displayed_dive), mean, duration);
+ volume_t sac;
+ QString gaslist, SACs, separator;
+
+ gaslist = ""; SACs = ""; volumes = ""; separator = "";
+ for (int i = 0; i < MAX_CYLINDERS; i++) {
+ if (!is_cylinder_used(&displayed_dive, i))
+ continue;
+ gaslist.append(separator); volumes.append(separator); SACs.append(separator);
+ separator = "\n";
+
+ gaslist.append(gasname(&displayed_dive.cylinder[i].gasmix));
+ if (!gases[i].mliter)
+ continue;
+ volumes.append(get_volume_string(gases[i], true));
+ if (duration[i]) {
+ sac.mliter = gases[i].mliter / (depth_to_atm(mean[i], &displayed_dive) * duration[i] / 60);
+ SACs.append(get_volume_string(sac, true).append(tr("/min")));
+ }
+ }
+ ui->gasUsedText->setText(volumes);
+ ui->oxygenHeliumText->setText(gaslist);
+
+ int sum = displayed_dive.dc.divemode != FREEDIVE ? 30 : 0;
+ ui->diveTimeText->setText(get_time_string_s(displayed_dive.duration.seconds + sum, 0, false));
+
+ struct dive *prevd;
+ process_all_dives(&displayed_dive, &prevd);
+
+ if (prevd)
+ ui->surfaceIntervalText->setText(get_time_string_s(displayed_dive.when - (prevd->when + prevd->duration.seconds), 4,
+ (displayed_dive.dc.divemode == FREEDIVE)));
+ else
+ ui->surfaceIntervalText->clear();
+
+ ui->sacText->setText( mean[0] ? SACs : QString());
+
+ if (displayed_dive.surface_pressure.mbar) /* this is ALWAYS displayed in mbar */
+ ui->airPressureText->setText(QString("%1mbar").arg(displayed_dive.surface_pressure.mbar));
+ else
+ ui->airPressureText->clear();
+
+ if (displayed_dive.salinity)
+ ui->salinityText->setText(QString("%1g/l").arg(displayed_dive.salinity / 10.0));
+ else
+ ui->salinityText->clear();
+
+}
diff --git a/desktop-widgets/tab-widgets/TabDiveInformation.h b/desktop-widgets/tab-widgets/TabDiveInformation.h
new file mode 100644
index 000000000..e67b981c9
--- /dev/null
+++ b/desktop-widgets/tab-widgets/TabDiveInformation.h
@@ -0,0 +1,23 @@
+#ifndef TAB_DIVE_INFORMATION_H
+#define TAB_DIVE_INFORMATION_H
+
+#include "TabBase.h"
+
+namespace Ui {
+ class TabDiveInformation;
+};
+
+class TabDiveInformation : public TabBase {
+ Q_OBJECT
+public:
+ TabDiveInformation(QWidget *parent);
+ ~TabDiveInformation();
+ void updateData() override;
+ void clear() override;
+
+private:
+ Ui::TabDiveInformation *ui;
+};
+
+
+#endif
diff --git a/desktop-widgets/tab-widgets/TabDiveInformation.ui b/desktop-widgets/tab-widgets/TabDiveInformation.ui
new file mode 100644
index 000000000..feb547334
--- /dev/null
+++ b/desktop-widgets/tab-widgets/TabDiveInformation.ui
@@ -0,0 +1,340 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>TabDiveInformation</class>
+ <widget class="QWidget" name="TabDiveInformation">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>421</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Information</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QScrollArea" name="scrollArea_3">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="widgetResizable">
+ <bool>true</bool>
+ </property>
+ <widget class="QWidget" name="scrollAreaWidgetContents_3">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>388</width>
+ <height>409</height>
+ </rect>
+ </property>
+ <layout class="QGridLayout" name="diveInfoScrollAreaLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>2</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QGroupBox" name="groupBox_5">
+ <property name="title">
+ <string>Date</string>
+ </property>
+ <layout class="QHBoxLayout" name="diveInfoDateLayout">
+ <item>
+ <widget class="QLabel" name="dateText">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QGroupBox" name="groupBox_12">
+ <property name="title">
+ <string>Interval</string>
+ </property>
+ <layout class="QHBoxLayout" name="diveInfoSurfintervallLayout">
+ <item>
+ <widget class="QLabel" name="surfaceIntervalText">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QGroupBox" name="groupBox_3">
+ <property name="title">
+ <string>Gases used</string>
+ </property>
+ <layout class="QHBoxLayout" name="diveInfoGasesUsedLayout">
+ <item>
+ <widget class="QLabel" name="oxygenHeliumText">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QGroupBox" name="groupBox_4">
+ <property name="title">
+ <string>Gas consumed</string>
+ </property>
+ <layout class="QHBoxLayout" name="diveInfoGasConsumedLayout">
+ <item>
+ <widget class="QLabel" name="gasUsedText">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>SAC</string>
+ </property>
+ <layout class="QHBoxLayout" name="diveInfoSacLayout">
+ <item>
+ <widget class="QLabel" name="sacText">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QGroupBox" name="groupBox_15">
+ <property name="title">
+ <string>CNS</string>
+ </property>
+ <layout class="QHBoxLayout" name="diveInfoCnsLayout">
+ <item>
+ <widget class="QLabel" name="maxcnsText">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>OTU</string>
+ </property>
+ <layout class="QHBoxLayout" name="diveInfoOtuLayout">
+ <item>
+ <widget class="QLabel" name="otuText">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QGroupBox" name="groupBox_6">
+ <property name="title">
+ <string>Max. depth</string>
+ </property>
+ <layout class="QHBoxLayout" name="diveInfoMaxDepthLayout">
+ <item>
+ <widget class="QLabel" name="maximumDepthText">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QGroupBox" name="groupBox_7">
+ <property name="title">
+ <string>Avg. depth</string>
+ </property>
+ <layout class="QHBoxLayout" name="diveInfoAvgDepthLayout">
+ <item>
+ <widget class="QLabel" name="averageDepthText">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QGroupBox" name="groupBox_10">
+ <property name="title">
+ <string>Air pressure</string>
+ </property>
+ <layout class="QHBoxLayout" name="diveInfoAirPressureLayout">
+ <item>
+ <widget class="QLabel" name="airPressureText">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QGroupBox" name="groupBox_9">
+ <property name="title">
+ <string>Air temp.</string>
+ </property>
+ <layout class="QHBoxLayout" name="diveInfoAirTempLayout">
+ <item>
+ <widget class="QLabel" name="airTemperatureText">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QGroupBox" name="groupBox_8">
+ <property name="title">
+ <string>Water temp.</string>
+ </property>
+ <layout class="QHBoxLayout" name="diveInfoWaterTempLayout">
+ <item>
+ <widget class="QLabel" name="waterTemperatureText">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QGroupBox" name="groupBox_11">
+ <property name="title">
+ <string>Dive time</string>
+ </property>
+ <layout class="QHBoxLayout" name="diveInfoDiveTimeLayout">
+ <item>
+ <widget class="QLabel" name="diveTimeText">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QGroupBox" name="groupBox_1">
+ <property name="title">
+ <string>Salinity</string>
+ </property>
+ <layout class="QHBoxLayout" name="diveInfoSalinityLayout">
+ <item>
+ <widget class="QLabel" name="salinityText">
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Expanding</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/desktop-widgets/tab-widgets/TabDivePhotos.cpp b/desktop-widgets/tab-widgets/TabDivePhotos.cpp
new file mode 100644
index 000000000..7f8b3a8b0
--- /dev/null
+++ b/desktop-widgets/tab-widgets/TabDivePhotos.cpp
@@ -0,0 +1,98 @@
+#include "TabDivePhotos.h"
+#include "ui_TabDivePhotos.h"
+
+#include <qt-models/divepicturemodel.h>
+
+#include <QDesktopServices>
+#include <QContextMenuEvent>
+#include <QMenu>
+#include <QUrl>
+#include <QMessageBox>
+
+//TODO: Remove those in the future.
+#include "../mainwindow.h"
+#include "../divelistview.h"
+
+TabDivePhotos::TabDivePhotos(QWidget *parent)
+ : TabBase(parent),
+ ui(new Ui::TabDivePhotos()),
+ divePictureModel(DivePictureModel::instance())
+{
+ ui->setupUi(this);
+ ui->photosView->setModel(divePictureModel);
+ ui->photosView->setSelectionMode(QAbstractItemView::MultiSelection);
+
+ connect(ui->photosView, &DivePictureWidget::photoDoubleClicked,
+ [](const QString& path) {
+ QDesktopServices::openUrl(QUrl::fromLocalFile(path));
+ }
+ );
+}
+
+TabDivePhotos::~TabDivePhotos()
+{
+ delete ui;
+}
+
+void TabDivePhotos::clear()
+{
+ updateData();
+}
+
+void TabDivePhotos::contextMenuEvent(QContextMenuEvent *event)
+{
+ QMenu popup(this);
+ popup.addAction(tr("Load image(s) from file(s)"), this, &TabDivePhotos::addPhotosFromFile);
+ popup.addAction(tr("Load image(s) from web"), this, &TabDivePhotos::addPhotosFromURL);
+ popup.addSeparator();
+ popup.addAction(tr("Delete selected images"), this, &TabDivePhotos::removeSelectedPhotos);
+ popup.addAction(tr("Delete all images"), this, &TabDivePhotos::removeAllPhotos);
+ popup.exec(event->globalPos());
+ event->accept();
+}
+
+void TabDivePhotos::removeSelectedPhotos()
+{
+ bool last = false;
+ if (!ui->photosView->selectionModel()->hasSelection())
+ return;
+ QModelIndexList indexes = ui->photosView->selectionModel()->selectedRows();
+ if (indexes.count() == 0)
+ indexes = ui->photosView->selectionModel()->selectedIndexes();
+ QModelIndex photo = indexes.first();
+ do {
+ photo = indexes.first();
+ last = indexes.count() == 1;
+ if (photo.isValid()) {
+ QString fileUrl = photo.data(Qt::DisplayPropertyRole).toString();
+ if (fileUrl.length() > 0)
+ DivePictureModel::instance()->removePicture(fileUrl, last);
+ }
+ indexes.removeFirst();
+ } while(!indexes.isEmpty());
+}
+
+//TODO: This looks overly wrong. We shouldn't call MainWindow to retrieve the DiveList to add Images.
+void TabDivePhotos::addPhotosFromFile()
+{
+ MainWindow::instance()->dive_list()->loadImages();
+}
+
+void TabDivePhotos::addPhotosFromURL()
+{
+ MainWindow::instance()->dive_list()->loadWebImages();
+}
+
+void TabDivePhotos::removeAllPhotos()
+{
+ if (QMessageBox::warning(this, tr("Deleting Images"), tr("Are you sure you want to delete all images?"), QMessageBox::Cancel | QMessageBox::Ok, QMessageBox::Cancel) != QMessageBox::Cancel ) {
+ ui->photosView->selectAll();
+ removeSelectedPhotos();
+ }
+}
+
+void TabDivePhotos::updateData()
+{
+ divePictureModel->updateDivePictures();
+}
+
diff --git a/desktop-widgets/tab-widgets/TabDivePhotos.h b/desktop-widgets/tab-widgets/TabDivePhotos.h
new file mode 100644
index 000000000..f2c73b572
--- /dev/null
+++ b/desktop-widgets/tab-widgets/TabDivePhotos.h
@@ -0,0 +1,33 @@
+#ifndef TAB_DIVE_PHOTOS_H
+#define TAB_DIVE_PHOTOS_H
+
+#include "TabBase.h"
+
+namespace Ui {
+ class TabDivePhotos;
+};
+
+class DivePictureModel;
+
+class TabDivePhotos : public TabBase {
+ Q_OBJECT
+public:
+ TabDivePhotos(QWidget *parent);
+ ~TabDivePhotos();
+ void updateData() override;
+ void clear() override;
+
+protected:
+ void contextMenuEvent(QContextMenuEvent *ev) override;
+
+private:
+ void addPhotosFromFile();
+ void addPhotosFromURL();
+ void removeAllPhotos();
+ void removeSelectedPhotos();
+
+ Ui::TabDivePhotos *ui;
+ DivePictureModel *divePictureModel;
+};
+
+#endif
diff --git a/desktop-widgets/tab-widgets/TabDivePhotos.ui b/desktop-widgets/tab-widgets/TabDivePhotos.ui
new file mode 100644
index 000000000..35cfd375a
--- /dev/null
+++ b/desktop-widgets/tab-widgets/TabDivePhotos.ui
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>TabDivePhotos</class>
+ <widget class="QWidget" name="TabDivePhotos">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Photos</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="DivePictureWidget" name="photosView">
+ <property name="viewMode">
+ <enum>QListView::IconMode</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>DivePictureWidget</class>
+ <extends>QListView</extends>
+ <header>desktop-widgets/divepicturewidget.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/desktop-widgets/tab-widgets/TabDiveStatistics.cpp b/desktop-widgets/tab-widgets/TabDiveStatistics.cpp
new file mode 100644
index 000000000..6f8c207b1
--- /dev/null
+++ b/desktop-widgets/tab-widgets/TabDiveStatistics.cpp
@@ -0,0 +1,125 @@
+#include "TabDiveStatistics.h"
+#include "ui_TabDiveStatistics.h"
+
+#include <core/helpers.h>
+#include <core/display.h>
+#include <core/statistics.h>
+
+TabDiveStatistics::TabDiveStatistics(QWidget *parent) : TabBase(parent), ui(new Ui::TabDiveStatistics())
+{
+ ui->setupUi(this);
+ ui->sacLimits->overrideMaxToolTipText(tr("Highest total SAC of a dive"));
+ ui->sacLimits->overrideMinToolTipText(tr("Lowest total SAC of a dive"));
+ ui->sacLimits->overrideAvgToolTipText(tr("Average total SAC of all selected dives"));
+ ui->tempLimits->overrideMaxToolTipText(tr("Highest temperature"));
+ ui->tempLimits->overrideMinToolTipText(tr("Lowest temperature"));
+ ui->tempLimits->overrideAvgToolTipText(tr("Average temperature of all selected dives"));
+ ui->depthLimits->overrideMaxToolTipText(tr("Deepest dive"));
+ ui->depthLimits->overrideMinToolTipText(tr("Shallowest dive"));
+ ui->timeLimits->overrideMaxToolTipText(tr("Longest dive"));
+ ui->timeLimits->overrideMinToolTipText(tr("Shortest dive"));
+ ui->timeLimits->overrideAvgToolTipText(tr("Average length of all selected dives"));
+
+ Q_FOREACH (QObject *obj, children()) {
+ if (QLabel *label = qobject_cast<QLabel *>(obj))
+ label->setAlignment(Qt::AlignHCenter);
+ }
+}
+
+TabDiveStatistics::~TabDiveStatistics()
+{
+ delete ui;
+}
+
+void TabDiveStatistics::clear()
+{
+ ui->depthLimits->clear();
+ ui->sacLimits->clear();
+ ui->divesAllText->clear();
+ ui->tempLimits->clear();
+ ui->totalTimeAllText->clear();
+ ui->timeLimits->clear();
+}
+
+void TabDiveStatistics::updateData()
+{
+ clear();
+ ui->depthLimits->setMaximum(get_depth_string(stats_selection.max_depth, true));
+ ui->depthLimits->setMinimum(get_depth_string(stats_selection.min_depth, true));
+ // the overall average depth is really confusing when listed between the
+ // deepest and shallowest dive - let's just not set it
+ // ui->depthLimits->setAverage(get_depth_string(stats_selection.avg_depth, true));
+
+
+ if (amount_selected > 1 && stats_selection.max_sac.mliter)
+ ui->sacLimits->setMaximum(get_volume_string(stats_selection.max_sac, true).append(tr("/min")));
+ else
+ ui->sacLimits->setMaximum("");
+ if (amount_selected > 1 && stats_selection.min_sac.mliter)
+ ui->sacLimits->setMinimum(get_volume_string(stats_selection.min_sac, true).append(tr("/min")));
+ else
+ ui->sacLimits->setMinimum("");
+ if (stats_selection.avg_sac.mliter)
+ ui->sacLimits->setAverage(get_volume_string(stats_selection.avg_sac, true).append(tr("/min")));
+ else
+ ui->sacLimits->setAverage("");
+
+ temperature_t temp;
+ temp.mkelvin = stats_selection.max_temp;
+ ui->tempLimits->setMaximum(get_temperature_string(temp, true));
+ temp.mkelvin = stats_selection.min_temp;
+ ui->tempLimits->setMinimum(get_temperature_string(temp, true));
+ if (stats_selection.combined_temp && stats_selection.combined_count) {
+ const char *unit;
+ get_temp_units(0, &unit);
+ ui->tempLimits->setAverage(QString("%1%2").arg(stats_selection.combined_temp / stats_selection.combined_count, 0, 'f', 1).arg(unit));
+ }
+
+
+ ui->divesAllText->setText(QString::number(stats_selection.selection_size));
+ ui->totalTimeAllText->setText(get_time_string_s(stats_selection.total_time.seconds, 0, (displayed_dive.dc.divemode == FREEDIVE)));
+ int seconds = stats_selection.total_time.seconds;
+ if (stats_selection.selection_size)
+ seconds /= stats_selection.selection_size;
+ ui->timeLimits->setAverage(get_time_string_s(seconds, 0,(displayed_dive.dc.divemode == FREEDIVE)));
+ if (amount_selected > 1) {
+ ui->timeLimits->setMaximum(get_time_string_s(stats_selection.longest_time.seconds, 0, (displayed_dive.dc.divemode == FREEDIVE)));
+ ui->timeLimits->setMinimum(get_time_string_s(stats_selection.shortest_time.seconds, 0, (displayed_dive.dc.divemode == FREEDIVE)));
+ } else {
+ ui->timeLimits->setMaximum("");
+ ui->timeLimits->setMinimum("");
+ }
+
+ QVector<QPair<QString, int> > gasUsed;
+ QString gasUsedString;
+ volume_t vol;
+ selectedDivesGasUsed(gasUsed);
+ for (int j = 0; j < 20; j++) {
+ if (gasUsed.isEmpty())
+ break;
+ QPair<QString, int> gasPair = gasUsed.last();
+ gasUsed.pop_back();
+ vol.mliter = gasPair.second;
+ gasUsedString.append(gasPair.first).append(": ").append(get_volume_string(vol, true)).append("\n");
+ }
+ if (!gasUsed.isEmpty())
+ gasUsedString.append("...");
+ volume_t o2_tot = {}, he_tot = {};
+ selected_dives_gas_parts(&o2_tot, &he_tot);
+
+ /* No need to show the gas mixing information if diving
+ * with pure air, and only display the he / O2 part when
+ * it is used.
+ */
+ if (he_tot.mliter || o2_tot.mliter) {
+ gasUsedString.append(tr("These gases could be\nmixed from Air and using:\n"));
+ if (he_tot.mliter)
+ gasUsedString.append(QString("He: %1").arg(get_volume_string(he_tot, true)));
+ if (he_tot.mliter && o2_tot.mliter)
+ gasUsedString.append(tr(" and "));
+ if (o2_tot.mliter)
+ gasUsedString.append(QString("O2: %2\n").arg(get_volume_string(o2_tot, true)));
+ }
+ ui->gasConsumption->setText(gasUsedString);
+}
+
diff --git a/desktop-widgets/tab-widgets/TabDiveStatistics.h b/desktop-widgets/tab-widgets/TabDiveStatistics.h
new file mode 100644
index 000000000..fd0d4ec0d
--- /dev/null
+++ b/desktop-widgets/tab-widgets/TabDiveStatistics.h
@@ -0,0 +1,22 @@
+#ifndef TAB_DIVE_STATISTICS_H
+#define TAB_DIVE_STATISTICS_H
+
+#include "TabBase.h"
+
+namespace Ui {
+ class TabDiveStatistics;
+};
+
+class TabDiveStatistics : public TabBase {
+ Q_OBJECT
+public:
+ TabDiveStatistics(QWidget *parent);
+ ~TabDiveStatistics();
+ void updateData() override;
+ void clear() override;
+
+private:
+ Ui::TabDiveStatistics *ui;
+};
+
+#endif
diff --git a/desktop-widgets/tab-widgets/TabDiveStatistics.ui b/desktop-widgets/tab-widgets/TabDiveStatistics.ui
new file mode 100644
index 000000000..251983611
--- /dev/null
+++ b/desktop-widgets/tab-widgets/TabDiveStatistics.ui
@@ -0,0 +1,222 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>TabDiveStatistics</class>
+ <widget class="QWidget" name="TabDiveStatistics">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Statistics</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QScrollArea" name="scrollArea_4">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="widgetResizable">
+ <bool>true</bool>
+ </property>
+ <widget class="QWidget" name="scrollAreaWidgetContents_4">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>388</width>
+ <height>288</height>
+ </rect>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <item>
+ <widget class="QGroupBox" name="groupBoxb">
+ <property name="title">
+ <string>Depth</string>
+ </property>
+ <layout class="QHBoxLayout" name="statsDepthLayout">
+ <item>
+ <widget class="MinMaxAvgWidget" name="depthLimits" native="true"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_14">
+ <property name="title">
+ <string>Duration</string>
+ </property>
+ <layout class="QHBoxLayout" name="statsDurationLayout">
+ <item>
+ <widget class="MinMaxAvgWidget" name="timeLimits" native="true"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_6">
+ <item>
+ <widget class="QGroupBox" name="groupBox_8b">
+ <property name="title">
+ <string>Temperature</string>
+ </property>
+ <layout class="QHBoxLayout" name="statsTempLayout">
+ <item>
+ <widget class="MinMaxAvgWidget" name="tempLimits" native="true"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_11b">
+ <property name="title">
+ <string>Total time</string>
+ </property>
+ <layout class="QHBoxLayout" name="statsTotalTimeLayout">
+ <item>
+ <widget class="QLabel" name="totalTimeAllText">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_7b">
+ <property name="title">
+ <string>Dives</string>
+ </property>
+ <layout class="QHBoxLayout" name="statsDivesLayout">
+ <item>
+ <widget class="QLabel" name="divesAllText">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_7">
+ <item>
+ <widget class="QGroupBox" name="groupBox_4b">
+ <property name="title">
+ <string>SAC</string>
+ </property>
+ <layout class="QHBoxLayout" name="statsSacLayout">
+ <item>
+ <widget class="MinMaxAvgWidget" name="sacLimits" native="true"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_13">
+ <property name="title">
+ <string>Gas consumption</string>
+ </property>
+ <layout class="QHBoxLayout" name="statsGasConsumptionLayout">
+ <item>
+ <widget class="QLabel" name="gasConsumption">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>MinMaxAvgWidget</class>
+ <extends>QWidget</extends>
+ <header>desktop-widgets/simplewidgets.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/desktop-widgets/tab-widgets/maintab.cpp b/desktop-widgets/tab-widgets/maintab.cpp
new file mode 100644
index 000000000..0b712c8fb
--- /dev/null
+++ b/desktop-widgets/tab-widgets/maintab.cpp
@@ -0,0 +1,1522 @@
+/*
+ * maintab.cpp
+ *
+ * classes for the "notebook" area of the main window of Subsurface
+ *
+ */
+#include "desktop-widgets/tab-widgets/maintab.h"
+#include "desktop-widgets/mainwindow.h"
+#include "desktop-widgets/globe.h"
+#include "core/helpers.h"
+#include "core/statistics.h"
+#include "desktop-widgets/modeldelegates.h"
+#include "qt-models/diveplannermodel.h"
+#include "desktop-widgets/divelistview.h"
+#include "core/display.h"
+#include "profile-widget/profilewidget2.h"
+#include "desktop-widgets/diveplanner.h"
+#include "core/divesitehelpers.h"
+#include "qt-models/cylindermodel.h"
+#include "qt-models/weightmodel.h"
+#include "qt-models/divecomputerextradatamodel.h"
+#include "qt-models/divelocationmodel.h"
+#include "core/divesite.h"
+#include "desktop-widgets/locationinformation.h"
+
+#include "TabDiveExtraInfo.h"
+#include "TabDiveInformation.h"
+#include "TabDivePhotos.h"
+#include "TabDiveStatistics.h"
+
+#include <QCompleter>
+#include <QSettings>
+#include <QScrollBar>
+#include <QShortcut>
+#include <QMessageBox>
+#include <QDesktopServices>
+#include <QStringList>
+
+MainTab::MainTab(QWidget *parent) : QTabWidget(parent),
+ weightModel(new WeightModel(this)),
+ cylindersModel(new CylindersModel(this)),
+ editMode(NONE),
+ copyPaste(false),
+ currentTrip(0)
+{
+ ui.setupUi(this);
+
+ extraWidgets << new TabDiveExtraInfo(this);
+ addTab(extraWidgets.last(), "Extra Info");
+ extraWidgets << new TabDiveInformation(this);
+ addTab(extraWidgets.last(), "Information");
+ extraWidgets << new TabDiveStatistics(this);
+ addTab(extraWidgets.last(), "Statistics");
+ extraWidgets << new TabDivePhotos(this);
+ addTab(extraWidgets.last(), "Photos");
+
+ ui.dateEdit->setDisplayFormat(prefs.date_format);
+
+ memset(&displayed_dive, 0, sizeof(displayed_dive));
+ memset(&displayedTrip, 0, sizeof(displayedTrip));
+
+ ui.cylinders->setModel(cylindersModel);
+ ui.weights->setModel(weightModel);
+ closeMessage();
+
+ connect(ui.editDiveSiteButton, SIGNAL(clicked()), MainWindow::instance(), SIGNAL(startDiveSiteEdit()));
+#ifndef NO_MARBLE
+ connect(ui.location, &DiveLocationLineEdit::entered, GlobeGPS::instance(), &GlobeGPS::centerOnIndex);
+ connect(ui.location, &DiveLocationLineEdit::currentChanged, GlobeGPS::instance(), &GlobeGPS::centerOnIndex);
+#endif
+
+ 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()));
+
+ if (qApp->style()->objectName() == "oxygen")
+ setDocumentMode(true);
+ else
+ setDocumentMode(false);
+
+ // we start out with the fields read-only; once things are
+ // filled from a dive, they are made writeable
+ setEnabled(false);
+
+ ui.cylinders->setTitle(tr("Cylinders"));
+ ui.cylinders->setBtnToolTip(tr("Add cylinder"));
+ connect(ui.cylinders, SIGNAL(addButtonClicked()), this, SLOT(addCylinder_clicked()));
+
+ ui.weights->setTitle(tr("Weights"));
+ ui.weights->setBtnToolTip(tr("Add weight system"));
+ connect(ui.weights, SIGNAL(addButtonClicked()), this, SLOT(addWeight_clicked()));
+
+ // This needs to be the same order as enum dive_comp_type in dive.h!
+ ui.DiveType->insertItems(0, QStringList() << tr("OC") << tr("CCR") << tr("pSCR") << tr("Freedive"));
+ connect(ui.DiveType, SIGNAL(currentIndexChanged(int)), this, SLOT(divetype_Changed(int)));
+
+ connect(ui.cylinders->view(), SIGNAL(clicked(QModelIndex)), this, SLOT(editCylinderWidget(QModelIndex)));
+ connect(ui.weights->view(), SIGNAL(clicked(QModelIndex)), this, SLOT(editWeightWidget(QModelIndex)));
+
+ ui.cylinders->view()->setItemDelegateForColumn(CylindersModel::TYPE, new TankInfoDelegate(this));
+ ui.cylinders->view()->setItemDelegateForColumn(CylindersModel::USE, new TankUseDelegate(this));
+ ui.weights->view()->setItemDelegateForColumn(WeightModel::TYPE, new WSInfoDelegate(this));
+ ui.cylinders->view()->setColumnHidden(CylindersModel::DEPTH, true);
+ completers.buddy = new QCompleter(&buddyModel, ui.buddy);
+ completers.divemaster = new QCompleter(&diveMasterModel, ui.divemaster);
+ completers.suit = new QCompleter(&suitModel, ui.suit);
+ completers.tags = new QCompleter(&tagModel, ui.tagWidget);
+ completers.buddy->setCaseSensitivity(Qt::CaseInsensitive);
+ completers.divemaster->setCaseSensitivity(Qt::CaseInsensitive);
+ completers.suit->setCaseSensitivity(Qt::CaseInsensitive);
+ completers.tags->setCaseSensitivity(Qt::CaseInsensitive);
+ ui.buddy->setCompleter(completers.buddy);
+ ui.divemaster->setCompleter(completers.divemaster);
+ ui.suit->setCompleter(completers.suit);
+ ui.tagWidget->setCompleter(completers.tags);
+ ui.diveNotesMessage->hide();
+ ui.diveEquipmentMessage->hide();
+ ui.depth->hide();
+ ui.depthLabel->hide();
+ ui.duration->hide();
+ ui.durationLabel->hide();
+ setMinimumHeight(0);
+ setMinimumWidth(0);
+
+ // Current display of things on Gnome3 looks like shit, so
+ // let`s fix that.
+ if (isGnome3Session()) {
+ QPalette p;
+ p.setColor(QPalette::Window, QColor(Qt::white));
+ ui.scrollArea->viewport()->setPalette(p);
+ ui.scrollArea_2->viewport()->setPalette(p);
+
+ // GroupBoxes in Gnome3 looks like I'v drawn them...
+ static const QString gnomeCss(
+ "QGroupBox {"
+ " background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,"
+ " stop: 0 #E0E0E0, stop: 1 #FFFFFF);"
+ " border: 2px solid gray;"
+ " border-radius: 5px;"
+ " margin-top: 1ex;"
+ "}"
+ "QGroupBox::title {"
+ " subcontrol-origin: margin;"
+ " subcontrol-position: top center;"
+ " padding: 0 3px;"
+ " background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,"
+ " stop: 0 #E0E0E0, stop: 1 #FFFFFF);"
+ "}");
+ Q_FOREACH (QGroupBox *box, findChildren<QGroupBox *>()) {
+ box->setStyleSheet(gnomeCss);
+ }
+ }
+ // QLineEdit and QLabels should have minimal margin on the left and right but not waste vertical space
+ QMargins margins(3, 2, 1, 0);
+ Q_FOREACH (QLabel *label, findChildren<QLabel *>()) {
+ label->setContentsMargins(margins);
+ }
+ ui.cylinders->view()->horizontalHeader()->setContextMenuPolicy(Qt::ActionsContextMenu);
+ ui.weights->view()->horizontalHeader()->setContextMenuPolicy(Qt::ActionsContextMenu);
+
+ QSettings s;
+ s.beginGroup("cylinders_dialog");
+ for (int i = 0; i < CylindersModel::COLUMNS; i++) {
+ if ((i == CylindersModel::REMOVE) || (i == CylindersModel::TYPE))
+ continue;
+ bool checked = s.value(QString("column%1_hidden").arg(i)).toBool();
+ action = new QAction(cylindersModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString(), ui.cylinders->view());
+ action->setCheckable(true);
+ action->setData(i);
+ action->setChecked(!checked);
+ connect(action, SIGNAL(triggered(bool)), this, SLOT(toggleTriggeredColumn()));
+ ui.cylinders->view()->setColumnHidden(i, checked);
+ ui.cylinders->view()->horizontalHeader()->addAction(action);
+ }
+
+ 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);
+
+ connect(ReverseGeoLookupThread::instance(), SIGNAL(finished()),
+ LocationInformationModel::instance(), SLOT(update()));
+
+ connect(ReverseGeoLookupThread::instance(), &QThread::finished,
+ this, &MainTab::setCurrentLocationIndex);
+
+ connect(ui.diveNotesMessage, &KMessageWidget::showAnimationFinished,
+ ui.location, &DiveLocationLineEdit::fixPopupPosition);
+
+ // enable URL clickability in notes:
+ new TextHyperlinkEventFilter(ui.notes);//destroyed when ui.notes is destroyed
+
+ acceptingEdit = false;
+
+ ui.diveTripLocation->hide();
+
+
+}
+
+MainTab::~MainTab()
+{
+ QSettings s;
+ s.beginGroup("cylinders_dialog");
+ for (int i = 0; i < CylindersModel::COLUMNS; i++) {
+ if ((i == CylindersModel::REMOVE) || (i == CylindersModel::TYPE))
+ continue;
+ s.setValue(QString("column%1_hidden").arg(i), ui.cylinders->view()->isColumnHidden(i));
+ }
+}
+
+void MainTab::setCurrentLocationIndex()
+{
+ if (current_dive) {
+ struct dive_site *ds = get_dive_site_by_uuid(current_dive->dive_site_uuid);
+ if (ds)
+ ui.location->setCurrentDiveSiteUuid(ds->uuid);
+ else
+ ui.location->clear();
+ }
+}
+
+void MainTab::enableGeoLookupEdition()
+{
+ ui.waitingSpinner->stop();
+}
+
+void MainTab::disableGeoLookupEdition()
+{
+ ui.waitingSpinner->start();
+}
+
+void MainTab::toggleTriggeredColumn()
+{
+ QAction *action = qobject_cast<QAction *>(sender());
+ int col = action->data().toInt();
+ QTableView *view = ui.cylinders->view();
+
+ if (action->isChecked()) {
+ view->showColumn(col);
+ if (view->columnWidth(col) <= 15)
+ view->setColumnWidth(col, 80);
+ } else
+ view->hideColumn(col);
+}
+
+void MainTab::addDiveStarted()
+{
+ enableEdition(ADD);
+}
+
+void MainTab::addMessageAction(QAction *action)
+{
+ ui.diveEquipmentMessage->addAction(action);
+ ui.diveNotesMessage->addAction(action);
+}
+
+void MainTab::hideMessage()
+{
+ ui.diveNotesMessage->animatedHide();
+ ui.diveEquipmentMessage->animatedHide();
+ updateTextLabels(false);
+}
+
+void MainTab::closeMessage()
+{
+ hideMessage();
+ ui.diveNotesMessage->setCloseButtonVisible(false);
+ ui.diveEquipmentMessage->setCloseButtonVisible(false);
+}
+
+void MainTab::displayMessage(QString str)
+{
+ ui.diveNotesMessage->setCloseButtonVisible(false);
+ ui.diveEquipmentMessage->setCloseButtonVisible(false);
+ ui.diveNotesMessage->setText(str);
+ ui.diveNotesMessage->animatedShow();
+ ui.diveEquipmentMessage->setText(str);
+ ui.diveEquipmentMessage->animatedShow();
+ updateTextLabels();
+}
+
+void MainTab::updateTextLabels(bool showUnits)
+{
+ if (showUnits) {
+ ui.airTempLabel->setText(tr("Air temp. [%1]").arg(get_temp_unit()));
+ ui.waterTempLabel->setText(tr("Water temp. [%1]").arg(get_temp_unit()));
+ } else {
+ ui.airTempLabel->setText(tr("Air temp."));
+ ui.waterTempLabel->setText(tr("Water temp."));
+ }
+}
+
+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
+ // so exit this function here after editCurrentDive() returns
+
+
+
+ // FIXME : can we get rid of this recursive crap?
+
+
+
+ MainWindow::instance()->editCurrentDive();
+ return;
+ }
+
+ ui.editDiveSiteButton->setEnabled(false);
+ MainWindow::instance()->dive_list()->setEnabled(false);
+ MainWindow::instance()->setEnabledToolbar(false);
+
+ if (isTripEdit) {
+ // we are editing trip location and notes
+ displayMessage(tr("This trip is being edited."));
+ currentTrip = current_dive->divetrip;
+ ui.dateEdit->setEnabled(false);
+ editMode = TRIP;
+ } else {
+ ui.dateEdit->setEnabled(true);
+ if (amount_selected > 1) {
+ displayMessage(tr("Multiple dives are being edited."));
+ } else {
+ displayMessage(tr("This dive is being edited."));
+ }
+ editMode = newEditMode != NONE ? newEditMode : DIVE;
+ }
+}
+
+void MainTab::clearEquipment()
+{
+ cylindersModel->clear();
+ weightModel->clear();
+}
+
+void MainTab::nextInputField(QKeyEvent *event)
+{
+ keyPressEvent(event);
+}
+
+#define UPDATE_TEXT(d, field) \
+ if (clear || !d.field) \
+ ui.field->setText(QString()); \
+ else \
+ ui.field->setText(d.field)
+
+#define UPDATE_TEMP(d, field) \
+ if (clear || d.field.mkelvin == 0) \
+ ui.field->setText(""); \
+ else \
+ ui.field->setText(get_temperature_string(d.field, true))
+
+bool MainTab::isEditing()
+{
+ return editMode != NONE;
+}
+
+void MainTab::showLocation()
+{
+ if (get_dive_site_by_uuid(displayed_dive.dive_site_uuid))
+ ui.location->setCurrentDiveSiteUuid(displayed_dive.dive_site_uuid);
+ else
+ ui.location->clear();
+}
+
+// Seems wrong, since we can also call updateDiveInfo(), but since the updateDiveInfo
+// has a parameter on it's definition it didn't worked on the signal slot connection.
+void MainTab::refreshDiveInfo()
+{
+ updateDiveInfo();
+}
+
+void MainTab::updateDepthDuration()
+{
+ ui.depth->setVisible(true);
+ ui.depthLabel->setVisible(true);
+ ui.duration->setVisible(true);
+ ui.durationLabel->setVisible(true);
+ ui.duration->setText(QDateTime::fromTime_t(displayed_dive.duration.seconds).toUTC().toString("h:mm"));
+ ui.depth->setText(get_depth_string(displayed_dive.maxdepth, true));
+}
+
+void MainTab::updateDiveInfo(bool clear)
+{
+ ui.location->refreshDiveSiteCache();
+ EditMode rememberEM = editMode;
+ // don't execute this while adding / planning a dive
+ if (editMode == ADD || editMode == MANUALLY_ADDED_DIVE || MainWindow::instance()->graphics()->isPlanner())
+ return;
+ if (!isEnabled() && !clear )
+ setEnabled(true);
+ if (isEnabled() && clear)
+ setEnabled(false);
+ editMode = IGNORE; // don't trigger on changes to the widgets
+
+ // This method updates ALL tabs whenever a new dive or trip is
+ // selected.
+ // If exactly one trip has been selected, we show the location / notes
+ // for the trip in the Info tab, otherwise we show the info of the
+ // selected_dive
+ struct dive *prevd;
+ char buf[1024];
+
+ process_selected_dives();
+ process_all_dives(&displayed_dive, &prevd);
+
+ for (auto widget : extraWidgets) {
+ widget->updateData();
+ }
+
+ ui.notes->setText(QString());
+ if (!clear) {
+ QString tmp(displayed_dive.notes);
+ if (tmp.indexOf("<table") != -1)
+ ui.notes->setHtml(tmp);
+ else
+ ui.notes->setPlainText(tmp);
+ }
+ UPDATE_TEXT(displayed_dive, notes);
+ UPDATE_TEXT(displayed_dive, suit);
+ UPDATE_TEXT(displayed_dive, divemaster);
+ UPDATE_TEXT(displayed_dive, buddy);
+ UPDATE_TEMP(displayed_dive, airtemp);
+ UPDATE_TEMP(displayed_dive, watertemp);
+ ui.DiveType->setCurrentIndex(get_dive_dc(&displayed_dive, dc_number)->divemode);
+
+ if (!clear) {
+ struct dive_site *ds = NULL;
+ // if we are showing a dive and editing it, let's refer to the displayed_dive_site as that
+ // already may contain changes, otherwise start with the dive site referred to by the displayed
+ // dive
+ if (rememberEM == DIVE) {
+ ds = &displayed_dive_site;
+ } else {
+ ds = get_dive_site_by_uuid(displayed_dive.dive_site_uuid);
+ if (ds)
+ copy_dive_site(ds, &displayed_dive_site);
+ }
+
+ if (ds) {
+ ui.location->setCurrentDiveSiteUuid(ds->uuid);
+ ui.locationTags->setText(constructLocationTags(ds->uuid));
+ } else {
+ ui.location->clear();
+ clear_dive_site(&displayed_dive_site);
+ }
+
+ // 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::fromMSecsSinceEpoch(1000*displayed_dive.when, Qt::UTC);
+ localTime.setTimeSpec(Qt::UTC);
+ ui.dateEdit->setDate(localTime.date());
+ ui.timeEdit->setTime(localTime.time());
+ if (MainWindow::instance() && MainWindow::instance()->dive_list()->selectedTrips().count() == 1) {
+ setTabText(0, tr("Trip notes"));
+ currentTrip = *MainWindow::instance()->dive_list()->selectedTrips().begin();
+ // only use trip relevant fields
+ ui.divemaster->setVisible(false);
+ ui.DivemasterLabel->setVisible(false);
+ ui.buddy->setVisible(false);
+ ui.BuddyLabel->setVisible(false);
+ ui.suit->setVisible(false);
+ ui.SuitLabel->setVisible(false);
+ ui.rating->setVisible(false);
+ ui.RatingLabel->setVisible(false);
+ ui.visibility->setVisible(false);
+ ui.visibilityLabel->setVisible(false);
+ ui.tagWidget->setVisible(false);
+ ui.TagLabel->setVisible(false);
+ ui.airTempLabel->setVisible(false);
+ ui.airtemp->setVisible(false);
+ ui.DiveType->setVisible(false);
+ ui.TypeLabel->setVisible(false);
+ ui.waterTempLabel->setVisible(false);
+ ui.watertemp->setVisible(false);
+ ui.dateEdit->setReadOnly(true);
+ ui.label->setVisible(false);
+ ui.timeEdit->setVisible(false);
+ ui.diveTripLocation->show();
+ ui.location->hide();
+ ui.editDiveSiteButton->hide();
+ // rename the remaining fields and fill data from selected trip
+ ui.LocationLabel->setText(tr("Trip location"));
+ ui.diveTripLocation->setText(currentTrip->location);
+ ui.locationTags->clear();
+ //TODO: Fix this.
+ //ui.location->setText(currentTrip->location);
+ ui.NotesLabel->setText(tr("Trip notes"));
+ ui.notes->setText(currentTrip->notes);
+ clearEquipment();
+ ui.equipmentTab->setEnabled(false);
+ ui.depth->setVisible(false);
+ ui.depthLabel->setVisible(false);
+ ui.duration->setVisible(false);
+ ui.durationLabel->setVisible(false);
+ } else {
+ setTabText(0, tr("Notes"));
+ currentTrip = NULL;
+ // make all the fields visible writeable
+ ui.diveTripLocation->hide();
+ ui.location->show();
+ ui.editDiveSiteButton->show();
+ ui.divemaster->setVisible(true);
+ ui.buddy->setVisible(true);
+ ui.suit->setVisible(true);
+ ui.SuitLabel->setVisible(true);
+ ui.rating->setVisible(true);
+ ui.RatingLabel->setVisible(true);
+ ui.visibility->setVisible(true);
+ ui.visibilityLabel->setVisible(true);
+ ui.BuddyLabel->setVisible(true);
+ ui.DivemasterLabel->setVisible(true);
+ ui.TagLabel->setVisible(true);
+ ui.tagWidget->setVisible(true);
+ ui.airTempLabel->setVisible(true);
+ ui.airtemp->setVisible(true);
+ ui.TypeLabel->setVisible(true);
+ ui.DiveType->setVisible(true);
+ ui.waterTempLabel->setVisible(true);
+ ui.watertemp->setVisible(true);
+ ui.dateEdit->setReadOnly(false);
+ ui.label->setVisible(true);
+ ui.timeEdit->setVisible(true);
+ /* and fill them from the dive */
+ ui.rating->setCurrentStars(displayed_dive.rating);
+ ui.visibility->setCurrentStars(displayed_dive.visibility);
+ // reset labels in case we last displayed trip notes
+ ui.LocationLabel->setText(tr("Location"));
+ ui.NotesLabel->setText(tr("Notes"));
+ ui.equipmentTab->setEnabled(true);
+ cylindersModel->updateDive();
+ weightModel->updateDive();
+ taglist_get_tagstring(displayed_dive.tag_list, buf, 1024);
+ ui.tagWidget->setText(QString(buf));
+ bool isManual = !current_dive || same_string(current_dive->dc.model, "manually added dive");
+ ui.depth->setVisible(isManual);
+ ui.depthLabel->setVisible(isManual);
+ ui.duration->setVisible(isManual);
+ ui.durationLabel->setVisible(isManual);
+ }
+ ui.duration->setText(QDateTime::fromTime_t(displayed_dive.duration.seconds).toUTC().toString("h:mm"));
+ ui.depth->setText(get_depth_string(displayed_dive.maxdepth, true));
+ ui.DiveType->setCurrentIndex(get_dive_dc(&displayed_dive, dc_number)->divemode);
+
+ volume_t gases[MAX_CYLINDERS] = {};
+ get_gas_used(&displayed_dive, gases);
+ QString volumes;
+ int mean[MAX_CYLINDERS], duration[MAX_CYLINDERS];
+ per_cylinder_mean_depth(&displayed_dive, select_dc(&displayed_dive), mean, duration);
+
+ // now let's get some gas use statistics
+ QVector<QPair<QString, int> > gasUsed;
+ QString gasUsedString;
+ volume_t vol;
+ selectedDivesGasUsed(gasUsed);
+ for (int j = 0; j < 20; j++) {
+ if (gasUsed.isEmpty())
+ break;
+ QPair<QString, int> gasPair = gasUsed.last();
+ gasUsed.pop_back();
+ vol.mliter = gasPair.second;
+ gasUsedString.append(gasPair.first).append(": ").append(get_volume_string(vol, true)).append("\n");
+ }
+ if (!gasUsed.isEmpty())
+ gasUsedString.append("...");
+ volume_t o2_tot = {}, he_tot = {};
+ selected_dives_gas_parts(&o2_tot, &he_tot);
+
+ if(ui.locationTags->text().isEmpty())
+ ui.locationTags->hide();
+ else
+ ui.locationTags->show();
+ /* unset the special value text for date and time, just in case someone dove at midnight */
+ ui.dateEdit->setSpecialValueText(QString(""));
+ ui.timeEdit->setSpecialValueText(QString(""));
+
+ } else {
+ /* clear the fields */
+ clearTabs();
+ ui.rating->setCurrentStars(0);
+ ui.visibility->setCurrentStars(0);
+ ui.location->clear();
+ /* set date and time to minimums which triggers showing the special value text */
+ ui.dateEdit->setSpecialValueText(QString("-"));
+ ui.dateEdit->setMinimumDate(QDate(1, 1, 1));
+ ui.dateEdit->setDate(QDate(1, 1, 1));
+ ui.timeEdit->setSpecialValueText(QString("-"));
+ ui.timeEdit->setMinimumTime(QTime(0, 0, 0, 0));
+ ui.timeEdit->setTime(QTime(0, 0, 0, 0));
+ }
+ editMode = rememberEM;
+ ui.cylinders->view()->hideColumn(CylindersModel::DEPTH);
+ if (get_dive_dc(&displayed_dive, dc_number)->divemode == CCR)
+ ui.cylinders->view()->showColumn(CylindersModel::USE);
+ else
+ ui.cylinders->view()->hideColumn(CylindersModel::USE);
+
+ if (verbose)
+ qDebug() << "Set the current dive site:" << displayed_dive.dive_site_uuid;
+ emit diveSiteChanged(get_dive_site_by_uuid(displayed_dive.dive_site_uuid));
+}
+
+void MainTab::addCylinder_clicked()
+{
+ if (editMode == NONE)
+ enableEdition();
+ cylindersModel->add();
+}
+
+void MainTab::addWeight_clicked()
+{
+ if (editMode == NONE)
+ enableEdition();
+ weightModel->add();
+}
+
+void MainTab::reload()
+{
+ suitModel.updateModel();
+ buddyModel.updateModel();
+ diveMasterModel.updateModel();
+ tagModel.updateModel();
+ LocationInformationModel::instance()->update();
+}
+
+// tricky little macro to edit all the selected dives
+// loop over all dives, for each selected dive do WHAT, but do it
+// last for the current dive; this is required in case the invocation
+// wants to compare things to the original value in current_dive like it should
+#define MODIFY_SELECTED_DIVES(WHAT) \
+ do { \
+ struct dive *mydive = NULL; \
+ int _i; \
+ for_each_dive (_i, mydive) { \
+ if (!mydive->selected || mydive == cd) \
+ continue; \
+ \
+ WHAT; \
+ } \
+ mydive = cd; \
+ WHAT; \
+ mark_divelist_changed(true); \
+ } while (0)
+
+#define EDIT_TEXT(what) \
+ if (same_string(mydive->what, cd->what) || copyPaste) { \
+ free(mydive->what); \
+ mydive->what = copy_string(displayed_dive.what); \
+ }
+
+MainTab::EditMode MainTab::getEditMode() const
+{
+ return editMode;
+}
+
+#define EDIT_VALUE(what) \
+ if (mydive->what == cd->what || copyPaste) { \
+ mydive->what = displayed_dive.what; \
+ }
+
+void MainTab::refreshDisplayedDiveSite()
+{
+ if (displayed_dive_site.uuid) {
+ copy_dive_site(get_dive_site_by_uuid(displayed_dive_site.uuid), &displayed_dive_site);
+ ui.location->setCurrentDiveSiteUuid(displayed_dive_site.uuid);
+ }
+}
+
+// when this is called we already have updated the current_dive and know that it exists
+// there is no point in calling this function if there is no current dive
+uint32_t MainTab::updateDiveSite(uint32_t pickedUuid, int divenr)
+{
+ struct dive *cd = get_dive(divenr);
+ if (!cd)
+ return 0;
+
+ if (ui.location->text().isEmpty())
+ return 0;
+
+ if (pickedUuid == 0)
+ return 0;
+
+ const uint32_t origUuid = cd->dive_site_uuid;
+ struct dive_site *origDs = get_dive_site_by_uuid(origUuid);
+ struct dive_site *newDs = NULL;
+ bool createdNewDive = false;
+
+ if (pickedUuid == origUuid)
+ return origUuid;
+
+ if (pickedUuid == RECENTLY_ADDED_DIVESITE) {
+ pickedUuid = create_dive_site(ui.location->text().isEmpty() ? qPrintable(tr("New dive site")) : qPrintable(ui.location->text()), displayed_dive.when);
+ createdNewDive = true;
+ }
+
+ newDs = get_dive_site_by_uuid(pickedUuid);
+
+ // Copy everything from the displayed_dive_site, so we have the latitude, longitude, notes, etc.
+ // The user *might* be using wrongly the 'choose dive site' just to edit the name of it, sigh.
+ if (origDs) {
+ if(createdNewDive) {
+ copy_dive_site(origDs, newDs);
+ free(newDs->name);
+ newDs->name = copy_string(qPrintable(ui.location->text().constData()));
+ newDs->uuid = pickedUuid;
+ qDebug() << "Creating and copying dive site";
+ } else if (newDs->latitude.udeg == 0 && newDs->longitude.udeg == 0) {
+ newDs->latitude.udeg = origDs->latitude.udeg;
+ newDs->longitude.udeg = origDs->longitude.udeg;
+ qDebug() << "Copying GPS information";
+ }
+ }
+
+ if (origDs && pickedUuid != origDs->uuid && same_string(origDs->notes, "SubsurfaceWebservice")) {
+ if (!is_dive_site_used(origDs->uuid, false)) {
+ if (verbose)
+ qDebug() << "delete the autogenerated dive site" << origDs->name;
+ delete_dive_site(origDs->uuid);
+ }
+ }
+
+ cd->dive_site_uuid = pickedUuid;
+ qDebug() << "Setting the dive site id on the dive:" << pickedUuid;
+ return pickedUuid;
+}
+
+void MainTab::acceptChanges()
+{
+ int i, addedId = -1;
+ struct dive *d;
+ bool do_replot = false;
+
+ if(ui.location->hasFocus()) {
+ this->setFocus();
+ }
+
+ acceptingEdit = true;
+ tabBar()->setTabIcon(0, QIcon()); // Notes
+ tabBar()->setTabIcon(1, QIcon()); // Equipment
+ ui.dateEdit->setEnabled(true);
+ hideMessage();
+ ui.equipmentTab->setEnabled(true);
+ if (editMode == ADD) {
+ // We need to add the dive we just created to the dive list and select it.
+ // Easy, right?
+ struct dive *added_dive = clone_dive(&displayed_dive);
+ record_dive(added_dive);
+ addedId = added_dive->id;
+ // make sure that the dive site is handled as well
+ updateDiveSite(ui.location->currDiveSiteUuid(), get_idx_by_uniq_id(added_dive->id));
+
+ // unselect everything as far as the UI is concerned and select the new
+ // dive - we'll have to undo/redo this later after we resort the dive_table
+ // but we need the dive selected for the middle part of this function - this
+ // way we can reuse the code used for editing dives
+ MainWindow::instance()->dive_list()->unselectDives();
+ selected_dive = get_divenr(added_dive);
+ amount_selected = 1;
+ } else if (MainWindow::instance() && MainWindow::instance()->dive_list()->selectedTrips().count() == 1) {
+ /* now figure out if things have changed */
+ if (displayedTrip.notes && !same_string(displayedTrip.notes, currentTrip->notes)) {
+ currentTrip->notes = copy_string(displayedTrip.notes);
+ mark_divelist_changed(true);
+ }
+ if (displayedTrip.location && !same_string(displayedTrip.location, currentTrip->location)) {
+ currentTrip->location = copy_string(displayedTrip.location);
+ mark_divelist_changed(true);
+ }
+ currentTrip = NULL;
+ ui.dateEdit->setEnabled(true);
+ } else {
+ if (editMode == MANUALLY_ADDED_DIVE) {
+ // preserve any changes to the profile
+ free(current_dive->dc.sample);
+ copy_samples(&displayed_dive.dc, &current_dive->dc);
+ addedId = displayed_dive.id;
+ }
+ struct dive *cd = current_dive;
+ struct divecomputer *displayed_dc = get_dive_dc(&displayed_dive, dc_number);
+ // 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.suit, cd->suit))
+ MODIFY_SELECTED_DIVES(EDIT_TEXT(suit));
+ if (!same_string(displayed_dive.notes, cd->notes))
+ MODIFY_SELECTED_DIVES(EDIT_TEXT(notes));
+ if (displayed_dive.rating != cd->rating)
+ MODIFY_SELECTED_DIVES(EDIT_VALUE(rating));
+ if (displayed_dive.visibility != cd->visibility)
+ MODIFY_SELECTED_DIVES(EDIT_VALUE(visibility));
+ if (displayed_dive.airtemp.mkelvin != cd->airtemp.mkelvin)
+ MODIFY_SELECTED_DIVES(EDIT_VALUE(airtemp.mkelvin));
+ if (displayed_dc->divemode != current_dc->divemode) {
+ MODIFY_SELECTED_DIVES(
+ if (get_dive_dc(mydive, dc_number)->divemode == current_dc->divemode || copyPaste) {
+ get_dive_dc(mydive, dc_number)->divemode = displayed_dc->divemode;
+ }
+ );
+ MODIFY_SELECTED_DIVES(update_setpoint_events(mydive, get_dive_dc(mydive, dc_number)));
+ do_replot = true;
+ }
+ if (displayed_dive.watertemp.mkelvin != cd->watertemp.mkelvin)
+ MODIFY_SELECTED_DIVES(EDIT_VALUE(watertemp.mkelvin));
+ if (displayed_dive.when != cd->when) {
+ time_t offset = cd->when - displayed_dive.when;
+ MODIFY_SELECTED_DIVES(mydive->when -= offset;);
+ }
+
+ 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) {
+ mark_divelist_changed(true);
+ MODIFY_SELECTED_DIVES(
+ for (int i = 0; i < MAX_CYLINDERS; i++) {
+ if (mydive != cd) {
+ if (same_string(mydive->cylinder[i].type.description, cd->cylinder[i].type.description) || copyPaste) {
+ // if we started out with the same cylinder description (for multi-edit) or if we do copt & paste
+ // make sure that we have the same cylinder type and copy the gasmix, but DON'T copy the start
+ // and end pressures (those are per dive after all)
+ if (!same_string(mydive->cylinder[i].type.description, displayed_dive.cylinder[i].type.description)) {
+ free((void*)mydive->cylinder[i].type.description);
+ mydive->cylinder[i].type.description = copy_string(displayed_dive.cylinder[i].type.description);
+ }
+ mydive->cylinder[i].type.size = displayed_dive.cylinder[i].type.size;
+ mydive->cylinder[i].type.workingpressure = displayed_dive.cylinder[i].type.workingpressure;
+ mydive->cylinder[i].gasmix = displayed_dive.cylinder[i].gasmix;
+ mydive->cylinder[i].cylinder_use = displayed_dive.cylinder[i].cylinder_use;
+ mydive->cylinder[i].depth = displayed_dive.cylinder[i].depth;
+ }
+ }
+ }
+ );
+ for (int i = 0; i < MAX_CYLINDERS; i++) {
+ // copy the cylinder but make sure we have our own copy of the strings
+ free((void*)cd->cylinder[i].type.description);
+ 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;
+ }
+
+ if (weightModel->changed) {
+ mark_divelist_changed(true);
+ MODIFY_SELECTED_DIVES(
+ for (int i = 0; i < MAX_WEIGHTSYSTEMS; i++) {
+ if (mydive != cd && (copyPaste || same_string(mydive->weightsystem[i].description, cd->weightsystem[i].description))) {
+ mydive->weightsystem[i] = displayed_dive.weightsystem[i];
+ mydive->weightsystem[i].description = copy_string(displayed_dive.weightsystem[i].description);
+ }
+ }
+ );
+ for (int i = 0; i < MAX_WEIGHTSYSTEMS; i++) {
+ cd->weightsystem[i] = displayed_dive.weightsystem[i];
+ cd->weightsystem[i].description = copy_string(displayed_dive.weightsystem[i].description);
+ }
+ }
+
+ // update the dive site for the selected dives that had the same dive site as the current dive
+ uint32_t oldUuid = cd->dive_site_uuid;
+ uint32_t newUuid = 0;
+ MODIFY_SELECTED_DIVES(
+ if (mydive->dive_site_uuid == current_dive->dive_site_uuid) {
+ newUuid = updateDiveSite(newUuid == 0 ? ui.location->currDiveSiteUuid() : newUuid, get_idx_by_uniq_id(mydive->id));
+ }
+ );
+ if (!is_dive_site_used(oldUuid, false)) {
+ if (verbose) {
+ struct dive_site *ds = get_dive_site_by_uuid(oldUuid);
+ qDebug() << "delete now unused dive site" << ((ds && ds->name) ? ds->name : "without name");
+ }
+ delete_dive_site(oldUuid);
+ GlobeGPS::instance()->reload();
+ }
+ // the code above can change the correct uuid for the displayed dive site - and the
+ // code below triggers an update of the display without re-initializing displayed_dive
+ // so let's make sure here that our data is consistent now that we have handled the
+ // dive sites
+ displayed_dive.dive_site_uuid = current_dive->dive_site_uuid;
+ struct dive_site *ds = get_dive_site_by_uuid(displayed_dive.dive_site_uuid);
+ if (ds)
+ copy_dive_site(ds, &displayed_dive_site);
+
+ // each dive that was selected might have had the temperatures in its active divecomputer changed
+ // so re-populate the temperatures - easiest way to do this is by calling fixup_dive
+ for_each_dive (i, d) {
+ if (d->selected) {
+ fixup_dive(d);
+ invalidate_dive_cache(d);
+ }
+ }
+ }
+ if (editMode != TRIP && current_dive->divetrip) {
+ current_dive->divetrip->when = current_dive->when;
+ find_new_trip_start_time(current_dive->divetrip);
+ }
+ if (editMode == ADD || editMode == MANUALLY_ADDED_DIVE) {
+ // we just added or edited the dive, let fixup_dive() make
+ // sure we get the max. depth right
+ current_dive->maxdepth.mm = current_dc->maxdepth.mm = 0;
+ fixup_dive(current_dive);
+ set_dive_nr_for_current_dive();
+ MainWindow::instance()->showProfile();
+ mark_divelist_changed(true);
+ DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::NOTHING);
+ }
+ int scrolledBy = MainWindow::instance()->dive_list()->verticalScrollBar()->sliderPosition();
+ resetPallete();
+ if (editMode == ADD || editMode == MANUALLY_ADDED_DIVE) {
+ // since a newly added dive could be in the middle of the dive_table we need
+ // to resort the dive list and make sure the newly added dive gets selected again
+ sort_table(&dive_table);
+ MainWindow::instance()->dive_list()->reload(DiveTripModel::CURRENT, true);
+ int newDiveNr = get_divenr(get_dive_by_uniq_id(addedId));
+ MainWindow::instance()->dive_list()->unselectDives();
+ MainWindow::instance()->dive_list()->selectDive(newDiveNr, true);
+ editMode = NONE;
+ MainWindow::instance()->refreshDisplay();
+ MainWindow::instance()->graphics()->replot();
+ emit addDiveFinished();
+ } else {
+ editMode = NONE;
+ if (do_replot)
+ MainWindow::instance()->graphics()->replot();
+ MainWindow::instance()->dive_list()->rememberSelection();
+ sort_table(&dive_table);
+ MainWindow::instance()->refreshDisplay();
+ MainWindow::instance()->dive_list()->restoreSelection();
+ }
+ DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::NOTHING);
+ MainWindow::instance()->dive_list()->verticalScrollBar()->setSliderPosition(scrolledBy);
+ MainWindow::instance()->dive_list()->setFocus();
+ cylindersModel->changed = false;
+ weightModel->changed = false;
+ MainWindow::instance()->setEnabledToolbar(true);
+ acceptingEdit = false;
+ ui.editDiveSiteButton->setEnabled(true);
+}
+
+void MainTab::resetPallete()
+{
+ QPalette p;
+ ui.buddy->setPalette(p);
+ ui.notes->setPalette(p);
+ ui.location->setPalette(p);
+ ui.divemaster->setPalette(p);
+ ui.suit->setPalette(p);
+ ui.airtemp->setPalette(p);
+ ui.DiveType->setPalette(p);
+ ui.watertemp->setPalette(p);
+ ui.dateEdit->setPalette(p);
+ ui.timeEdit->setPalette(p);
+ ui.tagWidget->setPalette(p);
+ ui.diveTripLocation->setPalette(p);
+ ui.duration->setPalette(p);
+ ui.depth->setPalette(p);
+}
+
+#define EDIT_TEXT2(what, text) \
+ textByteArray = text.toUtf8(); \
+ free(what); \
+ what = strdup(textByteArray.data());
+
+#define FREE_IF_DIFFERENT(what) \
+ if (displayed_dive.what != cd->what) \
+ free(displayed_dive.what)
+
+void MainTab::rejectChanges()
+{
+ EditMode lastMode = editMode;
+
+ if (lastMode != NONE && current_dive &&
+ (modified ||
+ memcmp(&current_dive->cylinder[0], &displayed_dive.cylinder[0], sizeof(cylinder_t) * MAX_CYLINDERS) ||
+ memcmp(&current_dive->cylinder[0], &displayed_dive.weightsystem[0], sizeof(weightsystem_t) * MAX_WEIGHTSYSTEMS))) {
+ if (QMessageBox::warning(MainWindow::instance(), TITLE_OR_TEXT(tr("Discard the changes?"),
+ tr("You are about to discard your changes.")),
+ QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Discard) != QMessageBox::Discard) {
+ return;
+ }
+ }
+ ui.dateEdit->setEnabled(true);
+ editMode = NONE;
+ tabBar()->setTabIcon(0, QIcon()); // Notes
+ tabBar()->setTabIcon(1, QIcon()); // Equipment
+ hideMessage();
+ resetPallete();
+ // no harm done to call cancelPlan even if we were not in ADD or PLAN mode...
+ DivePlannerPointsModel::instance()->cancelPlan();
+ if(lastMode == ADD)
+ MainWindow::instance()->dive_list()->restoreSelection();
+
+ // now make sure that the correct dive is displayed
+ if (selected_dive >= 0)
+ copy_dive(current_dive, &displayed_dive);
+ else
+ clear_dive(&displayed_dive);
+ updateDiveInfo(selected_dive < 0);
+
+ for (auto widget : extraWidgets) {
+ widget->updateData();
+ }
+ // the user could have edited the location and then canceled the edit
+ // let's get the correct location back in view
+#ifndef NO_MARBLE
+ GlobeGPS::instance()->centerOnDiveSite(get_dive_site_by_uuid(displayed_dive.dive_site_uuid));
+#endif
+ // show the profile and dive info
+ MainWindow::instance()->graphics()->replot();
+ MainWindow::instance()->setEnabledToolbar(true);
+ cylindersModel->changed = false;
+ weightModel->changed = false;
+ cylindersModel->updateDive();
+ weightModel->updateDive();
+ ui.editDiveSiteButton->setEnabled(true);
+}
+#undef EDIT_TEXT2
+
+void MainTab::markChangedWidget(QWidget *w)
+{
+ QPalette p;
+ qreal h, s, l, a;
+ 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 MainTab::on_buddy_textChanged()
+{
+ if (editMode == IGNORE || acceptingEdit == true)
+ return;
+
+ if (same_string(displayed_dive.buddy, ui.buddy->toPlainText().toUtf8().data()))
+ return;
+
+ QStringList text_list = ui.buddy->toPlainText().split(",", QString::SkipEmptyParts);
+ for (int i = 0; i < text_list.size(); i++)
+ text_list[i] = text_list[i].trimmed();
+ QString text = text_list.join(", ");
+ free(displayed_dive.buddy);
+ displayed_dive.buddy = strdup(text.toUtf8().data());
+ markChangedWidget(ui.buddy);
+}
+
+void MainTab::on_divemaster_textChanged()
+{
+ if (editMode == IGNORE || acceptingEdit == true)
+ return;
+
+ if (same_string(displayed_dive.divemaster, ui.divemaster->toPlainText().toUtf8().data()))
+ return;
+
+ QStringList text_list = ui.divemaster->toPlainText().split(",", QString::SkipEmptyParts);
+ for (int i = 0; i < text_list.size(); i++)
+ text_list[i] = text_list[i].trimmed();
+ QString text = text_list.join(", ");
+ free(displayed_dive.divemaster);
+ displayed_dive.divemaster = strdup(text.toUtf8().data());
+ markChangedWidget(ui.divemaster);
+}
+
+void MainTab::on_duration_textChanged(const QString &text)
+{
+ if (editMode == IGNORE || acceptingEdit == true)
+ return;
+ // parse this
+ MainWindow::instance()->graphics()->setReplot(false);
+ if (!isEditing())
+ enableEdition();
+ displayed_dive.dc.duration.seconds = parseDurationToSeconds(text);
+ displayed_dive.duration = displayed_dive.dc.duration;
+ displayed_dive.dc.meandepth.mm = 0;
+ displayed_dive.dc.samples = 0;
+ DivePlannerPointsModel::instance()->loadFromDive(&displayed_dive);
+ markChangedWidget(ui.duration);
+ MainWindow::instance()->graphics()->setReplot(true);
+ MainWindow::instance()->graphics()->plotDive();
+
+}
+
+void MainTab::on_depth_textChanged(const QString &text)
+{
+ if (editMode == IGNORE || acceptingEdit == true)
+ return;
+ // don't replot until we set things up the way we want them
+ MainWindow::instance()->graphics()->setReplot(false);
+ if (!isEditing())
+ enableEdition();
+ displayed_dive.dc.maxdepth.mm = parseLengthToMm(text);
+ displayed_dive.maxdepth = displayed_dive.dc.maxdepth;
+ displayed_dive.dc.meandepth.mm = 0;
+ displayed_dive.dc.samples = 0;
+ DivePlannerPointsModel::instance()->loadFromDive(&displayed_dive);
+ markChangedWidget(ui.depth);
+ MainWindow::instance()->graphics()->setReplot(true);
+ MainWindow::instance()->graphics()->plotDive();
+}
+
+void MainTab::on_airtemp_textChanged(const QString &text)
+{
+ if (editMode == IGNORE || acceptingEdit == true)
+ return;
+ displayed_dive.airtemp.mkelvin = parseTemperatureToMkelvin(text);
+ markChangedWidget(ui.airtemp);
+ validate_temp_field(ui.airtemp, text);
+}
+
+void MainTab::divetype_Changed(int index)
+{
+ if (editMode == IGNORE)
+ return;
+ struct divecomputer *displayed_dc = get_dive_dc(&displayed_dive, dc_number);
+ displayed_dc->divemode = (enum dive_comp_type) index;
+ update_setpoint_events(&displayed_dive, displayed_dc);
+ markChangedWidget(ui.DiveType);
+ MainWindow::instance()->graphics()->recalcCeiling();
+}
+
+void MainTab::on_watertemp_textChanged(const QString &text)
+{
+ if (editMode == IGNORE || acceptingEdit == true)
+ return;
+ displayed_dive.watertemp.mkelvin = parseTemperatureToMkelvin(text);
+ markChangedWidget(ui.watertemp);
+ validate_temp_field(ui.watertemp, text);
+}
+
+void MainTab::validate_temp_field(QLineEdit *tempField, const QString &text)
+{
+ static bool missing_unit = false;
+ static bool missing_precision = false;
+ if (!text.contains(QRegExp("^[-+]{0,1}[0-9]+([,.][0-9]+){0,1}(°[CF]){0,1}$")) &&
+ !text.isEmpty() &&
+ !text.contains(QRegExp("^[-+]$"))) {
+ if (text.contains(QRegExp("^[-+]{0,1}[0-9]+([,.][0-9]+){0,1}(°)$")) && !missing_unit) {
+ if (!missing_unit) {
+ missing_unit = true;
+ return;
+ }
+ }
+ if (text.contains(QRegExp("^[-+]{0,1}[0-9]+([,.]){0,1}(°[CF]){0,1}$")) && !missing_precision) {
+ if (!missing_precision) {
+ missing_precision = true;
+ return;
+ }
+ }
+ QPalette p;
+ p.setBrush(QPalette::Base, QColor(Qt::red).lighter());
+ tempField->setPalette(p);
+ } else {
+ missing_unit = false;
+ missing_precision = false;
+ }
+}
+
+void MainTab::on_dateEdit_dateChanged(const QDate &date)
+{
+ if (editMode == IGNORE || acceptingEdit == true)
+ return;
+ markChangedWidget(ui.dateEdit);
+ QDateTime dateTime = QDateTime::fromMSecsSinceEpoch(1000*displayed_dive.when, Qt::UTC);
+ dateTime.setTimeSpec(Qt::UTC);
+ dateTime.setDate(date);
+ DivePlannerPointsModel::instance()->getDiveplan().when = displayed_dive.when = dateTime.toTime_t();
+ emit dateTimeChanged();
+}
+
+void MainTab::on_timeEdit_timeChanged(const QTime &time)
+{
+ if (editMode == IGNORE || acceptingEdit == true)
+ return;
+ markChangedWidget(ui.timeEdit);
+ QDateTime dateTime = QDateTime::fromMSecsSinceEpoch(1000*displayed_dive.when, Qt::UTC);
+ dateTime.setTimeSpec(Qt::UTC);
+ dateTime.setTime(time);
+ DivePlannerPointsModel::instance()->getDiveplan().when = displayed_dive.when = dateTime.toTime_t();
+ emit dateTimeChanged();
+}
+
+// changing the tags on multiple dives is semantically strange - what's the right thing to do?
+// here's what I think... add the tags that were added to the displayed dive and remove the tags
+// that were removed from it
+void MainTab::saveTags()
+{
+ struct dive *cd = current_dive;
+ struct tag_entry *added_list = NULL;
+ struct tag_entry *removed_list = NULL;
+ struct tag_entry *tl;
+
+ taglist_free(displayed_dive.tag_list);
+ displayed_dive.tag_list = NULL;
+ Q_FOREACH (const QString& tag, ui.tagWidget->getBlockStringList())
+ taglist_add_tag(&displayed_dive.tag_list, tag.toUtf8().data());
+ taglist_cleanup(&displayed_dive.tag_list);
+
+ // figure out which tags were added and which tags were removed
+ added_list = taglist_added(cd->tag_list, displayed_dive.tag_list);
+ removed_list = taglist_added(displayed_dive.tag_list, cd->tag_list);
+ // dump_taglist("added tags:", added_list);
+ // dump_taglist("removed tags:", removed_list);
+
+ // we need to check if the tags were changed before just overwriting them
+ if (added_list == NULL && removed_list == NULL)
+ return;
+
+ MODIFY_SELECTED_DIVES(
+ // create a new tag list and all the existing tags that were not
+ // removed and then all the added tags
+ struct tag_entry *new_tag_list;
+ new_tag_list = NULL;
+ tl = mydive->tag_list;
+ while (tl) {
+ if (!taglist_contains(removed_list, tl->tag->name))
+ taglist_add_tag(&new_tag_list, tl->tag->name);
+ tl = tl->next;
+ }
+ tl = added_list;
+ while (tl) {
+ taglist_add_tag(&new_tag_list, tl->tag->name);
+ tl = tl->next;
+ }
+ taglist_free(mydive->tag_list);
+ mydive->tag_list = new_tag_list;
+ );
+ taglist_free(added_list);
+ taglist_free(removed_list);
+}
+
+// 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];
+
+ if (editMode == IGNORE || acceptingEdit == true)
+ return;
+
+ taglist_get_tagstring(displayed_dive.tag_list, buf, 1024);
+ if (same_string(buf, ui.tagWidget->toPlainText().toUtf8().data()))
+ return;
+
+ markChangedWidget(ui.tagWidget);
+}
+
+void MainTab::on_location_textChanged()
+{
+ if (editMode == IGNORE)
+ return;
+
+ // we don't want to act on the edit until editing is finished,
+ // but we want to mark the field so it's obvious it is being edited
+ QString currentLocation;
+ struct dive_site *ds = get_dive_site_by_uuid(displayed_dive.dive_site_uuid);
+ if (ds)
+ currentLocation = ds->name;
+ if (ui.location->text() != currentLocation)
+ markChangedWidget(ui.location);
+}
+
+void MainTab::on_location_diveSiteSelected()
+{
+ if (editMode == IGNORE || acceptingEdit == true)
+ return;
+
+ if (ui.location->text().isEmpty()) {
+ displayed_dive.dive_site_uuid = 0;
+ markChangedWidget(ui.location);
+ emit diveSiteChanged(0);
+ return;
+ } else {
+ if (ui.location->currDiveSiteUuid() != displayed_dive.dive_site_uuid) {
+ markChangedWidget(ui.location);
+ } else {
+ QPalette p;
+ ui.location->setPalette(p);
+ }
+ }
+}
+
+void MainTab::on_diveTripLocation_textEdited(const QString& text)
+{
+ if (currentTrip) {
+ free(displayedTrip.location);
+ displayedTrip.location = strdup(qPrintable(text));
+ markChangedWidget(ui.diveTripLocation);
+ }
+}
+
+void MainTab::on_suit_textChanged(const QString &text)
+{
+ if (editMode == IGNORE || acceptingEdit == true)
+ return;
+ free(displayed_dive.suit);
+ displayed_dive.suit = strdup(text.toUtf8().data());
+ markChangedWidget(ui.suit);
+}
+
+void MainTab::on_notes_textChanged()
+{
+ if (editMode == IGNORE || acceptingEdit == true)
+ return;
+ if (currentTrip) {
+ if (same_string(displayedTrip.notes, ui.notes->toPlainText().toUtf8().data()))
+ return;
+ free(displayedTrip.notes);
+ displayedTrip.notes = strdup(ui.notes->toPlainText().toUtf8().data());
+ } else {
+ if (same_string(displayed_dive.notes, ui.notes->toPlainText().toUtf8().data()))
+ return;
+ free(displayed_dive.notes);
+ if (ui.notes->toHtml().indexOf("<table") != -1)
+ displayed_dive.notes = strdup(ui.notes->toHtml().toUtf8().data());
+ else
+ displayed_dive.notes = strdup(ui.notes->toPlainText().toUtf8().data());
+ }
+ markChangedWidget(ui.notes);
+}
+
+void MainTab::on_rating_valueChanged(int value)
+{
+ if (acceptingEdit == true)
+ return;
+ if (displayed_dive.rating != value) {
+ displayed_dive.rating = value;
+ modified = true;
+ enableEdition();
+ }
+}
+
+void MainTab::on_visibility_valueChanged(int value)
+{
+ if (acceptingEdit == true)
+ return;
+ if (displayed_dive.visibility != value) {
+ displayed_dive.visibility = value;
+ modified = true;
+ enableEdition();
+ }
+}
+
+#undef MODIFY_SELECTED_DIVES
+#undef EDIT_TEXT
+#undef EDIT_VALUE
+
+void MainTab::editCylinderWidget(const QModelIndex &index)
+{
+ // we need a local copy or bad things happen when enableEdition() is called
+ QModelIndex editIndex = index;
+ if (cylindersModel->changed && editMode == NONE) {
+ enableEdition();
+ return;
+ }
+ if (editIndex.isValid() && editIndex.column() != CylindersModel::REMOVE) {
+ if (editMode == NONE)
+ enableEdition();
+ ui.cylinders->edit(editIndex);
+ }
+}
+
+void MainTab::editWeightWidget(const QModelIndex &index)
+{
+ if (editMode == NONE)
+ enableEdition();
+
+ if (index.isValid() && index.column() != WeightModel::REMOVE)
+ ui.weights->edit(index);
+}
+
+void MainTab::escDetected()
+{
+ if (editMode != NONE)
+ rejectChanges();
+}
+
+void MainTab::clearTabs() {
+ for (auto widget : extraWidgets) {
+ widget->clear();
+ }
+ clearEquipment();
+}
+
+#define SHOW_SELECTIVE(_component) \
+ if (what._component) \
+ ui._component->setText(displayed_dive._component);
+
+void MainTab::showAndTriggerEditSelective(struct dive_components what)
+{
+ // take the data in our copyPasteDive and apply it to selected dives
+ enableEdition();
+ copyPaste = true;
+ SHOW_SELECTIVE(buddy);
+ SHOW_SELECTIVE(divemaster);
+ SHOW_SELECTIVE(suit);
+ if (what.notes) {
+ QString tmp(displayed_dive.notes);
+ if (tmp.contains("<table"))
+ ui.notes->setHtml(tmp);
+ else
+ ui.notes->setPlainText(tmp);
+ }
+ if (what.rating)
+ ui.rating->setCurrentStars(displayed_dive.rating);
+ if (what.visibility)
+ ui.visibility->setCurrentStars(displayed_dive.visibility);
+ if (what.divesite)
+ ui.location->setCurrentDiveSiteUuid(displayed_dive.dive_site_uuid);
+ if (what.tags) {
+ char buf[1024];
+ taglist_get_tagstring(displayed_dive.tag_list, buf, 1024);
+ ui.tagWidget->setText(QString(buf));
+ }
+ if (what.cylinders) {
+ cylindersModel->updateDive();
+ cylindersModel->changed = true;
+ }
+ if (what.weights) {
+ weightModel->updateDive();
+ weightModel->changed = true;
+ }
+}
diff --git a/desktop-widgets/tab-widgets/maintab.h b/desktop-widgets/tab-widgets/maintab.h
new file mode 100644
index 000000000..a33d6053b
--- /dev/null
+++ b/desktop-widgets/tab-widgets/maintab.h
@@ -0,0 +1,129 @@
+/*
+ * maintab.h
+ *
+ * header file for the main tab of Subsurface
+ *
+ */
+#ifndef MAINTAB_H
+#define MAINTAB_H
+
+#include <QTabWidget>
+#include <QDialog>
+#include <QMap>
+#include <QUuid>
+
+#include "ui_maintab.h"
+#include "qt-models/completionmodels.h"
+#include "qt-models/divelocationmodel.h"
+#include "core/dive.h"
+
+class WeightModel;
+class CylindersModel;
+class ExtraDataModel;
+class DivePictureModel;
+class QCompleter;
+
+struct Completers {
+ QCompleter *divemaster;
+ QCompleter *buddy;
+ QCompleter *suit;
+ QCompleter *tags;
+};
+
+class TabBase;
+class MainTab : public QTabWidget {
+ Q_OBJECT
+public:
+ enum EditMode {
+ NONE,
+ DIVE,
+ TRIP,
+ ADD,
+ MANUALLY_ADDED_DIVE,
+ IGNORE
+ };
+
+ MainTab(QWidget *parent = 0);
+ ~MainTab();
+ void clearTabs();
+ void clearEquipment();
+ void reload();
+ void initialUiSetup();
+ bool isEditing();
+ void updateCoordinatesText(qreal lat, qreal lon);
+ void refreshDisplayedDiveSite();
+ void nextInputField(QKeyEvent *event);
+ void showAndTriggerEditSelective(struct dive_components what);
+
+signals:
+ void addDiveFinished();
+ void dateTimeChanged();
+ void diveSiteChanged(struct dive_site * ds);
+public
+slots:
+ void addCylinder_clicked();
+ void addWeight_clicked();
+ void refreshDiveInfo();
+ void updateDiveInfo(bool clear = false);
+ void updateDepthDuration();
+ void acceptChanges();
+ void rejectChanges();
+ void on_location_diveSiteSelected();
+ void on_location_textChanged();
+ void on_divemaster_textChanged();
+ void on_buddy_textChanged();
+ void on_suit_textChanged(const QString &text);
+ void on_diveTripLocation_textEdited(const QString& text);
+ void on_notes_textChanged();
+ void on_airtemp_textChanged(const QString &text);
+ void on_duration_textChanged(const QString &text);
+ void on_depth_textChanged(const QString &text);
+ void divetype_Changed(int);
+ void on_watertemp_textChanged(const QString &text);
+ void validate_temp_field(QLineEdit *tempField, const QString &text);
+ void on_dateEdit_dateChanged(const QDate &date);
+ void on_timeEdit_timeChanged(const QTime & time);
+ void on_rating_valueChanged(int value);
+ void on_visibility_valueChanged(int value);
+ void on_tagWidget_textChanged();
+ void editCylinderWidget(const QModelIndex &index);
+ void editWeightWidget(const QModelIndex &index);
+ void addDiveStarted();
+ void addMessageAction(QAction *action);
+ void hideMessage();
+ void closeMessage();
+ void displayMessage(QString str);
+ void enableEdition(EditMode newEditMode = NONE);
+ void toggleTriggeredColumn();
+ void updateTextLabels(bool showUnits = true);
+ void escDetected(void);
+ void showLocation();
+ void enableGeoLookupEdition();
+ void disableGeoLookupEdition();
+ void setCurrentLocationIndex();
+ EditMode getEditMode() const;
+private:
+ Ui::MainTab ui;
+ WeightModel *weightModel;
+ CylindersModel *cylindersModel;
+ EditMode editMode;
+ BuddyCompletionModel buddyModel;
+ DiveMasterCompletionModel diveMasterModel;
+ SuitCompletionModel suitModel;
+ TagCompletionModel tagModel;
+ Completers completers;
+ bool modified;
+ 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;
+ bool acceptingEdit;
+ uint32_t updateDiveSite(uint32_t pickedUuid, int divenr);
+ QList<TabBase*> extraWidgets;
+};
+
+#endif // MAINTAB_H
diff --git a/desktop-widgets/tab-widgets/maintab.ui b/desktop-widgets/tab-widgets/maintab.ui
new file mode 100644
index 000000000..e1e29d052
--- /dev/null
+++ b/desktop-widgets/tab-widgets/maintab.ui
@@ -0,0 +1,657 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainTab</class>
+ <widget class="QTabWidget" name="MainTab">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>463</width>
+ <height>815</height>
+ </rect>
+ </property>
+ <property name="currentIndex">
+ <number>1</number>
+ </property>
+ <widget class="QWidget" name="notesTab">
+ <attribute name="title">
+ <string>Notes</string>
+ </attribute>
+ <attribute name="toolTip">
+ <string>General notes about the current selection</string>
+ </attribute>
+ <layout class="QGridLayout" name="diveNotesLayout">
+ <property name="leftMargin">
+ <number>5</number>
+ </property>
+ <property name="topMargin">
+ <number>5</number>
+ </property>
+ <property name="rightMargin">
+ <number>5</number>
+ </property>
+ <property name="bottomMargin">
+ <number>5</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item row="2" column="1">
+ <widget class="KMessageWidget" name="diveNotesMessage"/>
+ </item>
+ <item row="3" column="1">
+ <widget class="QScrollArea" name="scrollArea">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="widgetResizable">
+ <bool>true</bool>
+ </property>
+ <widget class="QWidget" name="scrollAreaWidgetContents">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>445</width>
+ <height>726</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="leftMargin">
+ <number>5</number>
+ </property>
+ <property name="rightMargin">
+ <number>5</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>8</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+ <item row="2" column="3">
+ <widget class="QLabel" name="durationLabel">
+ <property name="text">
+ <string>Duration</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Date</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Time</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="airTempLabel">
+ <property name="text">
+ <string>Air temp.</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QLabel" name="waterTempLabel">
+ <property name="text">
+ <string>Water temp.</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+ </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="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <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>
+ <item row="2" column="2">
+ <widget class="QLabel" name="depthLabel">
+ <property name="text">
+ <string>Depth</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QLineEdit" name="depth"/>
+ </item>
+ <item row="3" column="3">
+ <widget class="QLineEdit" name="duration"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>5</number>
+ </property>
+ <property name="rightMargin">
+ <number>5</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="LocationLayout" stretch="0,1">
+ <item>
+ <widget class="QLabel" name="LocationLabel">
+ <property name="text">
+ <string>Location</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="locationTags">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::RichText</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="spacing">
+ <number>2</number>
+ </property>
+ <item>
+ <widget class="DiveLocationLineEdit" name="location"/>
+ </item>
+ <item>
+ <widget class="QToolButton" name="editDiveSiteButton">
+ <property name="toolTip">
+ <string>Edit dive site</string>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../../subsurface.qrc">
+ <normaloff>:/geocode</normaloff>:/geocode</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QtWaitingSpinner" name="waitingSpinner" native="true"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="diveTripLocation"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <property name="leftMargin">
+ <number>5</number>
+ </property>
+ <property name="rightMargin">
+ <number>5</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>5</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="DivemasterLabel">
+ <property name="text">
+ <string>Divemaster</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="BuddyLabel">
+ <property name="text">
+ <string>Buddy</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="TagWidget" name="divemaster">
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="TagWidget" name="buddy">
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout_3" columnstretch="0,0,1">
+ <property name="leftMargin">
+ <number>5</number>
+ </property>
+ <property name="rightMargin">
+ <number>5</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>5</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="RatingLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Rating</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="visibilityLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Visibility</string>
+ </property>
+ </widget>
+ </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" alignment="Qt::AlignVCenter">
+ <widget class="StarWidget" name="rating" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::StrongFocus</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" alignment="Qt::AlignVCenter">
+ <widget class="StarWidget" name="visibility" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>Qt::StrongFocus</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLineEdit" name="suit">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <property name="horizontalSpacing">
+ <number>5</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+ <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>
+ <property name="alignment">
+ <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="TypeLabel">
+ <property name="text">
+ <string>Dive mode</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+ </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>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="NotesLabel">
+ <property name="text">
+ <string>Notes</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="notesAndSocialNetworksLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QTextEdit" name="notes">
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="socialNetworks" native="true">
+ <layout class="QVBoxLayout" name="socialNetworksLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="equipmentTab">
+ <attribute name="title">
+ <string>Equipment</string>
+ </attribute>
+ <attribute name="toolTip">
+ <string>Used equipment in the current selection</string>
+ </attribute>
+ <layout class="QGridLayout" name="equiptmentTabLayout">
+ <property name="leftMargin">
+ <number>5</number>
+ </property>
+ <property name="topMargin">
+ <number>5</number>
+ </property>
+ <property name="rightMargin">
+ <number>5</number>
+ </property>
+ <property name="bottomMargin">
+ <number>5</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="KMessageWidget" name="diveEquipmentMessage"/>
+ </item>
+ <item row="1" column="0">
+ <widget class="QScrollArea" name="scrollArea_2">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="widgetResizable">
+ <bool>true</bool>
+ </property>
+ <widget class="QWidget" name="scrollAreaWidgetContents_2">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>445</width>
+ <height>720</height>
+ </rect>
+ </property>
+ <layout class="QGridLayout" name="equipmentTabScrollAreaLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>2</number>
+ </property>
+ <item row="1" column="0">
+ <widget class="QWidget" name="widget" native="true">
+ <layout class="QVBoxLayout" name="cylinderWeightsLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QSplitter" name="splitter">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <widget class="TableView" name="cylinders" native="true"/>
+ <widget class="TableView" name="weights" native="true"/>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>KMessageWidget</class>
+ <extends>QFrame</extends>
+ <header>desktop-widgets/kmessagewidget.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
+ <class>StarWidget</class>
+ <extends>QWidget</extends>
+ <header>desktop-widgets/starwidget.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
+ <class>TableView</class>
+ <extends>QWidget</extends>
+ <header>desktop-widgets/tableview.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
+ <class>TagWidget</class>
+ <extends>QPlainTextEdit</extends>
+ <header>desktop-widgets/tagwidget.h</header>
+ </customwidget>
+ <customwidget>
+ <class>QtWaitingSpinner</class>
+ <extends>QWidget</extends>
+ <header>desktop-widgets/qtwaitingspinner.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
+ <class>DiveLocationLineEdit</class>
+ <extends>QLineEdit</extends>
+ <header>desktop-widgets/locationinformation.h</header>
+ </customwidget>
+ </customwidgets>
+ <tabstops>
+ <tabstop>dateEdit</tabstop>
+ <tabstop>timeEdit</tabstop>
+ <tabstop>airtemp</tabstop>
+ <tabstop>watertemp</tabstop>
+ <tabstop>divemaster</tabstop>
+ <tabstop>buddy</tabstop>
+ <tabstop>rating</tabstop>
+ <tabstop>visibility</tabstop>
+ <tabstop>suit</tabstop>
+ <tabstop>notes</tabstop>
+ </tabstops>
+ <resources>
+ <include location="../../subsurface.qrc"/>
+ </resources>
+ <connections/>
+</ui>