diff options
Diffstat (limited to 'qt-ui')
-rw-r--r-- | qt-ui/divelistview.cpp | 67 | ||||
-rw-r--r-- | qt-ui/divelistview.h | 9 | ||||
-rw-r--r-- | qt-ui/mainwindow.cpp | 14 | ||||
-rw-r--r-- | qt-ui/models.h | 2 | ||||
-rw-r--r-- | qt-ui/subsurfacewebservices.cpp | 239 | ||||
-rw-r--r-- | qt-ui/subsurfacewebservices.h | 38 | ||||
-rw-r--r-- | qt-ui/subsurfacewebservices.ui | 106 |
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> |