summaryrefslogtreecommitdiffstats
path: root/qt-ui
diff options
context:
space:
mode:
Diffstat (limited to 'qt-ui')
-rw-r--r--qt-ui/divelistview.cpp67
-rw-r--r--qt-ui/divelistview.h9
-rw-r--r--qt-ui/mainwindow.cpp14
-rw-r--r--qt-ui/models.h2
-rw-r--r--qt-ui/subsurfacewebservices.cpp239
-rw-r--r--qt-ui/subsurfacewebservices.h38
-rw-r--r--qt-ui/subsurfacewebservices.ui106
7 files changed, 456 insertions, 19 deletions
diff --git a/qt-ui/divelistview.cpp b/qt-ui/divelistview.cpp
index d66313823..f0cb01a82 100644
--- a/qt-ui/divelistview.cpp
+++ b/qt-ui/divelistview.cpp
@@ -29,6 +29,7 @@ DiveListView::DiveListView(QWidget *parent) : QTreeView(parent), mouseClickSelec
model->setFilterKeyColumn(-1); // filter all columns
setModel(model);
setSortingEnabled(false);
+ setContextMenuPolicy(Qt::DefaultContextMenu);
header()->setContextMenuPolicy(Qt::ActionsContextMenu);
QAction *showSearchBox = new QAction(tr("Show Search Box"), this);
showSearchBox->setShortcut( Qt::CTRL + Qt::Key_F);
@@ -127,8 +128,10 @@ void DiveListView::headerClicked(int i)
void DiveListView::reload(DiveTripModel::Layout layout, bool forceSort)
{
- currentLayout = layout;
-
+ if (layout == DiveTripModel::CURRENT)
+ layout = currentLayout;
+ else
+ currentLayout = layout;
header()->setClickable(true);
connect(header(), SIGNAL(sectionPressed(int)), this, SLOT(headerClicked(int)), Qt::UniqueConnection);
@@ -272,17 +275,61 @@ void DiveListView::selectionChanged(const QItemSelection& selected, const QItemS
Q_EMIT currentDiveChanged(selected_dive);
}
-void DiveListView::mousePressEvent(QMouseEvent *event)
+void DiveListView::removeFromTrip()
{
- // all we care about is the unmodified right click
- if ( ! (event->modifiers() == Qt::NoModifier && event->buttons() & Qt::RightButton)) {
- event->ignore();
- QTreeView::mousePressEvent(event);
+ struct dive *d = (struct dive *) contextMenuIndex.data(TreeItemDT::DIVE_ROLE).value<void*>();
+ if (!d) // shouldn't happen as we only are setting up this action if this is a dive
return;
+ remove_dive_from_trip(d);
+ reload(currentLayout, false);
+}
+
+void DiveListView::deleteDive()
+{
+ struct dive *d = (struct dive *) contextMenuIndex.data(TreeItemDT::DIVE_ROLE).value<void*>();
+ if (d)
+ delete_single_dive(get_index_for_dive(d));
+ reload(currentLayout, false);
+}
+
+void DiveListView::testSlot()
+{
+ struct dive *d = (struct dive *) contextMenuIndex.data(TreeItemDT::DIVE_ROLE).value<void*>();
+ if (d) {
+ qDebug("testSlot called on dive #%d", d->number);
+ } else {
+ QModelIndex child = contextMenuIndex.child(0, 0);
+ d = (struct dive *) child.data(TreeItemDT::DIVE_ROLE).value<void*>();
+ if (d)
+ qDebug("testSlot called on trip including dive #%d", d->number);
+ else
+ qDebug("testSlot called on trip with no dive");
}
+}
+
+void DiveListView::contextMenuEvent(QContextMenuEvent *event)
+{
+ QAction *collapseAction = NULL;
+ // let's remember where we are
+ contextMenuIndex = indexAt(event->pos());
+ struct dive *d = (struct dive *) contextMenuIndex.data(TreeItemDT::DIVE_ROLE).value<void*>();
QMenu popup(this);
- popup.addAction(tr("expand all"), this, SLOT(expandAll()));
- QAction *collapseAllAction = popup.addAction(tr("collapse all"), this, SLOT(collapseAll()));
- if (popup.exec(event->globalPos()) == collapseAllAction)
+ if (currentLayout == DiveTripModel::TREE) {
+ popup.addAction(tr("expand all"), this, SLOT(expandAll()));
+ popup.addAction(tr("collapse all"), this, SLOT(collapseAll()));
+ collapseAction = popup.addAction(tr("collapse"), this, SLOT(collapseAll()));
+ if (d) {
+ popup.addAction(tr("remove dive from trip"), this, SLOT(removeFromTrip()));
+ }
+ }
+ popup.addAction(tr("delete dive"), this, SLOT(deleteDive()));
+ // "collapse all" really closes all trips,
+ // "collapse" keeps the trip with the selected dive open
+ QAction * actionTaken = popup.exec(event->globalPos());
+ if (actionTaken == collapseAction && collapseAction) {
+ this->setAnimated(false);
selectDive(current_dive, true);
+ this->setAnimated(true);
+ }
+ event->accept();
}
diff --git a/qt-ui/divelistview.h b/qt-ui/divelistview.h
index 2bce35612..a9b986f97 100644
--- a/qt-ui/divelistview.h
+++ b/qt-ui/divelistview.h
@@ -24,25 +24,30 @@ public:
DiveListView(QWidget *parent = 0);
void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
void currentChanged(const QModelIndex& current, const QModelIndex& previous);
- void reload(DiveTripModel::Layout layout = DiveTripModel::TREE, bool forceSort = true);
+ void reload(DiveTripModel::Layout layout, bool forceSort = true);
bool eventFilter(QObject* , QEvent* );
void unselectDives();
void selectDive(struct dive *, bool scrollto = false);
- void mousePressEvent(QMouseEvent *event);
+ void contextMenuEvent(QContextMenuEvent *event);
public slots:
void toggleColumnVisibilityByIndex();
void reloadHeaderActions();
void headerClicked(int);
void showSearchEdit();
+ void removeFromTrip();
+ void deleteDive();
+ void testSlot();
Q_SIGNALS:
void currentDiveChanged(int divenr);
+
private:
bool mouseClickSelection;
int currentHeaderClicked;
DiveTripModel::Layout currentLayout;
QLineEdit *searchBox;
+ QModelIndex contextMenuIndex;
};
#endif // DIVELISTVIEW_H
diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp
index 3c550439b..286aba43e 100644
--- a/qt-ui/mainwindow.cpp
+++ b/qt-ui/mainwindow.cpp
@@ -28,6 +28,7 @@
#include "models.h"
#include "downloadfromdivecomputer.h"
#include "preferences.h"
+#include "subsurfacewebservices.h"
static MainWindow* instance = 0;
@@ -42,11 +43,11 @@ MainWindow::MainWindow() : ui(new Ui::MainWindow()), helpView(0)
setWindowIcon(QIcon(":subsurface-icon"));
connect(ui->ListWidget, SIGNAL(currentDiveChanged(int)), this, SLOT(current_dive_changed(int)));
connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), this, SLOT(readSettings()));
- connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), this, SLOT(refreshDisplay()));
+ connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), ui->ListWidget, SLOT(update()));
connect(PreferencesDialog::instance(), SIGNAL(settingsChanged()), ui->ProfileWidget, SLOT(refresh()));
ui->mainErrorMessage->hide();
ui->ProfileWidget->setFocusProxy(ui->ListWidget);
- ui->ListWidget->reload();
+ ui->ListWidget->reload(DiveTripModel::TREE);
initialUiSetup();
readSettings();
ui->ListWidget->reloadHeaderActions();
@@ -60,7 +61,7 @@ void MainWindow::refreshDisplay()
{
if (selected_dive == -1)
current_dive_changed(dive_table.nr - 1);
- ui->ListWidget->reload();
+ ui->ListWidget->reload(DiveTripModel::CURRENT, false);
}
void MainWindow::current_dive_changed(int divenr)
@@ -106,7 +107,7 @@ void MainWindow::on_actionOpen_triggered()
ui->InfoWidget->reload();
ui->globe->reload();
- ui->ListWidget->reload();
+ ui->ListWidget->reload(DiveTripModel::TREE);
ui->ListWidget->setFocus();
}
@@ -139,7 +140,7 @@ void MainWindow::on_actionClose_triggered()
ui->InfoWidget->clearEquipment();
ui->InfoWidget->updateDiveInfo(-1);
ui->ProfileWidget->clear();
- ui->ListWidget->reload();
+ ui->ListWidget->reload(DiveTripModel::TREE);
ui->globe->reload();
clear_events();
@@ -181,7 +182,8 @@ void MainWindow::on_actionDownloadDC_triggered()
void MainWindow::on_actionDownloadWeb_triggered()
{
- qDebug("actionDownloadWeb");}
+ SubsurfaceWebServices::instance()->runDialog();
+}
void MainWindow::on_actionEditDeviceNames_triggered()
{
diff --git a/qt-ui/models.h b/qt-ui/models.h
index 99d028aca..a012ec6bd 100644
--- a/qt-ui/models.h
+++ b/qt-ui/models.h
@@ -144,7 +144,7 @@ class DiveTripModel : public QAbstractItemModel
Q_OBJECT
public:
- enum Layout{TREE, LIST};
+ enum Layout{TREE, LIST, CURRENT};
DiveTripModel(QObject *parent = 0);
~DiveTripModel();
diff --git a/qt-ui/subsurfacewebservices.cpp b/qt-ui/subsurfacewebservices.cpp
new file mode 100644
index 000000000..2f82d6d26
--- /dev/null
+++ b/qt-ui/subsurfacewebservices.cpp
@@ -0,0 +1,239 @@
+#include "subsurfacewebservices.h"
+#include "ui_subsurfacewebservices.h"
+#include "../webservice.h"
+
+#include <libxml/parser.h>
+
+#include <QNetworkAccessManager>
+#include <QNetworkReply>
+#include <QDebug>
+#include <QSettings>
+#include <qdesktopservices.h>
+
+#include "../dive.h"
+#include "../divelist.h"
+
+struct dive_table gps_location_table;
+static gboolean merge_locations_into_dives(void);
+
+SubsurfaceWebServices* SubsurfaceWebServices::instance()
+{
+ static SubsurfaceWebServices *self = new SubsurfaceWebServices();
+ return self;
+}
+
+SubsurfaceWebServices::SubsurfaceWebServices(QWidget* parent, Qt::WindowFlags f)
+: ui( new Ui::SubsurfaceWebServices()){
+ ui->setupUi(this);
+ connect(ui->buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonClicked(QAbstractButton*)));
+ connect(ui->download, SIGNAL(clicked(bool)), this, SLOT(startDownload()));
+ ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
+ QSettings s;
+ ui->userID->setText(s.value("webservice_uid").toString());
+}
+
+
+static void clear_table(struct dive_table *table)
+{
+ int i;
+ for (i = 0; i < table->nr; i++)
+ free(table->dives[i]);
+ table->nr = 0;
+}
+
+void SubsurfaceWebServices::buttonClicked(QAbstractButton* button)
+{
+ ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
+ switch(ui->buttonBox->buttonRole(button)){
+ case QDialogButtonBox::ApplyRole:{
+ clear_table(&gps_location_table);
+ QByteArray url = tr("Webservice").toLocal8Bit();
+ parse_xml_buffer(url.data(), downloadedData.data(), downloadedData.length(), &gps_location_table, NULL);
+
+ /* now merge the data in the gps_location table into the dive_table */
+ if (merge_locations_into_dives()) {
+ mark_divelist_changed(TRUE);
+ }
+
+ /* store last entered uid in config */
+ QSettings s;
+ s.setValue("webservice_uid", ui->userID->text());
+ s.sync();
+ }
+ break;
+ case QDialogButtonBox::RejectRole:
+ manager->deleteLater();
+ reply->deleteLater();
+ ui->progressBar->setMaximum(1);
+ break;
+ case QDialogButtonBox::HelpRole:
+ QDesktopServices::openUrl(QUrl("http://api.hohndel.org"));
+ break;
+ default:
+ break;
+ }
+}
+
+void SubsurfaceWebServices::startDownload()
+{
+ QUrl url("http://api.hohndel.org/api/dive/get/");
+ url.setQueryItems( QList<QPair<QString,QString> >() << qMakePair(QString("login"), ui->userID->text()));
+
+ manager = new QNetworkAccessManager(this);
+ QNetworkRequest request;
+ request.setUrl(url);
+ request.setRawHeader("Accept", "text/xml");
+ reply = manager->get(request);
+ ui->status->setText(tr("Wait a bit untill we have something..."));
+ ui->progressBar->setRange(0,0); // this makes the progressbar do an 'infinite spin'
+ ui->download->setEnabled(false);
+ ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
+
+ connect(reply, SIGNAL(finished()), this, SLOT(downloadFinished()));
+ connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
+ this, SLOT(downloadError(QNetworkReply::NetworkError)));
+}
+
+void SubsurfaceWebServices::downloadFinished()
+{
+ ui->progressBar->setRange(0,1);
+ downloadedData = reply->readAll();
+
+ ui->download->setEnabled(true);
+ ui->status->setText(tr("Download Finished"));
+
+ uint resultCode = download_dialog_parse_response(downloadedData);
+ setStatusText(resultCode);
+ if (resultCode == DD_STATUS_OK){
+ ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true);
+ }
+ manager->deleteLater();
+ reply->deleteLater();
+}
+
+void SubsurfaceWebServices::downloadError(QNetworkReply::NetworkError error)
+{
+ ui->download->setEnabled(true);
+ ui->progressBar->setRange(0,1);
+ ui->status->setText(QString::number((int)QNetworkRequest::HttpStatusCodeAttribute));
+ manager->deleteLater();
+ reply->deleteLater();
+}
+
+void SubsurfaceWebServices::setStatusText(int status)
+{
+ QString text;
+ switch (status) {
+ case DD_STATUS_ERROR_CONNECT: text = tr("Connection Error: "); break;
+ case DD_STATUS_ERROR_ID: text = tr("Invalid user identifier!"); break;
+ case DD_STATUS_ERROR_PARSE: text = tr("Cannot parse response!"); break;
+ case DD_STATUS_OK: text = tr("Download Success!"); break;
+ }
+ ui->status->setText(text);
+}
+
+void SubsurfaceWebServices::runDialog()
+{
+ show();
+}
+
+/* requires that there is a <download> or <error> tag under the <root> tag */
+void SubsurfaceWebServices::download_dialog_traverse_xml(xmlNodePtr node, unsigned int *download_status)
+{
+ xmlNodePtr cur_node;
+ for (cur_node = node; cur_node; cur_node = cur_node->next) {
+ if ((!strcmp((const char *)cur_node->name, (const char *)"download")) &&
+ (!strcmp((const char *)xmlNodeGetContent(cur_node), (const char *)"ok"))) {
+ *download_status = DD_STATUS_OK;
+ return;
+ } else if (!strcmp((const char *)cur_node->name, (const char *)"error")) {
+ *download_status = DD_STATUS_ERROR_ID;
+ return;
+ }
+ }
+}
+
+unsigned int SubsurfaceWebServices::download_dialog_parse_response(const QByteArray& xml)
+{
+ xmlNodePtr root;
+ xmlDocPtr doc = xmlParseMemory(xml.data(), xml.length());
+ unsigned int status = DD_STATUS_ERROR_PARSE;
+
+ if (!doc)
+ return DD_STATUS_ERROR_PARSE;
+ root = xmlDocGetRootElement(doc);
+ if (!root) {
+ status = DD_STATUS_ERROR_PARSE;
+ goto end;
+ }
+ if (root->children)
+ download_dialog_traverse_xml(root->children, &status);
+ end:
+ xmlFreeDoc(doc);
+ return status;
+}
+
+static gboolean is_automatic_fix(struct dive *gpsfix)
+{
+ if (gpsfix && gpsfix->location &&
+ (!strcmp(gpsfix->location, "automatic fix") ||
+ !strcmp(gpsfix->location, "Auto-created dive")))
+ return TRUE;
+ return FALSE;
+}
+
+#define SAME_GROUP 6 * 3600 // six hours
+
+static gboolean merge_locations_into_dives(void)
+{
+ int i, nr = 0, changed = 0;
+ struct dive *gpsfix, *last_named_fix = NULL, *dive;
+
+ sort_table(&gps_location_table);
+
+ for_each_gps_location(i, gpsfix) {
+ if (is_automatic_fix(gpsfix)) {
+ dive = find_dive_including(gpsfix->when);
+ if (dive && !dive_has_gps_location(dive)) {
+#if DEBUG_WEBSERVICE
+ struct tm tm;
+ utc_mkdate(gpsfix->when, &tm);
+ printf("found dive named %s @ %04d-%02d-%02d %02d:%02d:%02d\n",
+ gpsfix->location,
+ tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+#endif
+ changed++;
+ copy_gps_location(gpsfix, dive);
+ }
+ } else {
+ if (last_named_fix && dive_within_time_range(last_named_fix, gpsfix->when, SAME_GROUP)) {
+ nr++;
+ } else {
+ nr = 1;
+ last_named_fix = gpsfix;
+ }
+ dive = find_dive_n_near(gpsfix->when, nr, SAME_GROUP);
+ if (dive) {
+ if (!dive_has_gps_location(dive)) {
+ copy_gps_location(gpsfix, dive);
+ changed++;
+ }
+ if (!dive->location) {
+ dive->location = strdup(gpsfix->location);
+ changed++;
+ }
+ } else {
+ struct tm tm;
+ utc_mkdate(gpsfix->when, &tm);
+#if DEBUG_WEBSERVICE
+ printf("didn't find dive matching gps fix named %s @ %04d-%02d-%02d %02d:%02d:%02d\n",
+ gpsfix->location,
+ tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+#endif
+ }
+ }
+ }
+ return changed > 0;
+}
diff --git a/qt-ui/subsurfacewebservices.h b/qt-ui/subsurfacewebservices.h
new file mode 100644
index 000000000..9e85db155
--- /dev/null
+++ b/qt-ui/subsurfacewebservices.h
@@ -0,0 +1,38 @@
+#ifndef SUBSURFACEWEBSERVICES_H
+#define SUBSURFACEWEBSERVICES_H
+
+#include <QDialog>
+#include <QNetworkReply>
+#include <libxml/tree.h>
+
+namespace Ui{
+ class SubsurfaceWebServices;
+};
+class QAbstractButton;
+class QNetworkReply;
+
+class SubsurfaceWebServices : public QDialog {
+ Q_OBJECT
+public:
+ static SubsurfaceWebServices* instance();
+ void runDialog();
+
+private slots:
+ void startDownload();
+ void buttonClicked(QAbstractButton* button);
+ void downloadFinished();
+ void downloadError(QNetworkReply::NetworkError error);
+
+private:
+ void setStatusText(int status);
+ void download_dialog_traverse_xml(xmlNodePtr node, unsigned int *download_status);
+ unsigned int download_dialog_parse_response(const QByteArray& length);
+
+ explicit SubsurfaceWebServices(QWidget* parent = 0, Qt::WindowFlags f = 0);
+ Ui::SubsurfaceWebServices *ui;
+ QNetworkReply *reply;
+ QNetworkAccessManager *manager;
+ QByteArray downloadedData;
+};
+
+#endif \ No newline at end of file
diff --git a/qt-ui/subsurfacewebservices.ui b/qt-ui/subsurfacewebservices.ui
new file mode 100644
index 000000000..899eea909
--- /dev/null
+++ b/qt-ui/subsurfacewebservices.ui
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SubsurfaceWebServices</class>
+ <widget class="QDialog" name="SubsurfaceWebServices">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>399</width>
+ <height>104</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Download Location Data</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="0" colspan="3">
+ <widget class="QProgressBar" name="progressBar">
+ <property name="value">
+ <number>0</number>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" colspan="3">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Help</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QPushButton" name="download">
+ <property name="text">
+ <string>Download</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="userID">
+ <property name="placeholderText">
+ <string>Enter your ID here</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>User ID</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Status:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" colspan="2">
+ <widget class="QLabel" name="status">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>SubsurfaceWebServices</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>SubsurfaceWebServices</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>