Hello,

> I only have one issue: your code leaks memory if the user clicks the "Upload
> dive data" button twice before the request finishes, as in doUpload you
> simply assign the new QNetworkReply object to this->reply.
The button becomes invisible when it's pressed, so it can't really be pressed 
twice in a row.

> Nitpicking: we don't usually use the "this->" syntax, though I do see it in
> a few places in the current source code. Unless Dirk has a strong
> preference, I'd leave it.
Removed.

> 1) make the function take a const QByteArray &data
Done, but I think QByteArray internally shares memory so it only copies on 
write.

> Your code is going back and forth between QString and QByteArray, which is
> inefficient.
Now it just uses QByteArray


One thing that makes me uneasy now is this:

reply = WebServices::manager()->put(request, json_data);
QObject::connect(reply, SIGNAL(finished()), this, SLOT(finishedSlot()));

What if the signal is emitted before the connect?

Best
>From 441da7c4e1b8fd41709919e29fb79944999d1209 Mon Sep 17 00:00:00 2001
From: Salvo 'LtWorf' Tomaselli <tipos...@tiscali.it>
Signed-off-by: Salvo 'LtWorf' Tomaselli <tipos...@tiscali.it>
Date: Sun, 21 Sep 2014 16:11:58 +0200
Subject: [PATCH] Export to DiveShare

Adds the possibility of exporting dives to DiveShare.
---
 qt-ui/divelogexportdialog.cpp   |   5 +
 qt-ui/divelogexportdialog.ui    |  10 ++
 qt-ui/diveshareexportdialog.cpp | 142 +++++++++++++++++++++
 qt-ui/diveshareexportdialog.h   |  34 +++++
 qt-ui/diveshareexportdialog.ui  | 268 ++++++++++++++++++++++++++++++++++++++++
 save-html.c                     |   3 +-
 save-html.h                     |   2 +
 subsurface.pro                  |   9 +-
 8 files changed, 469 insertions(+), 4 deletions(-)
 create mode 100644 qt-ui/diveshareexportdialog.cpp
 create mode 100644 qt-ui/diveshareexportdialog.h
 create mode 100644 qt-ui/diveshareexportdialog.ui

diff --git a/qt-ui/divelogexportdialog.cpp b/qt-ui/divelogexportdialog.cpp
index 67803ee..6c50c75 100644
--- a/qt-ui/divelogexportdialog.cpp
+++ b/qt-ui/divelogexportdialog.cpp
@@ -9,6 +9,7 @@
 
 #include "mainwindow.h"
 #include "divelogexportdialog.h"
+#include "diveshareexportdialog.h"
 #include "ui_divelogexportdialog.h"
 #include "subsurfacewebservices.h"
 #include "worldmap-save.h"
@@ -70,6 +71,8 @@ void DiveLogExportDialog::showExplanation()
 		ui->description->setText(tr("Comma separated values that include the most relevant information of the dive profile."));
 	} else if (ui->exportDivelogs->isChecked()) {
 		ui->description->setText(tr("Send the dive data to divelogs.de website."));
+	} else if (ui->exportDiveshare->isChecked()) {
+		ui->description->setText(tr("Send the dive data to dive-share.appspot.com website"));
 	} else if (ui->exportWorldMap->isChecked()) {
 		ui->description->setText(tr("HTML export of the dive locations, visualized on a world map."));
 	} else if (ui->exportSubsurfaceXML->isChecked()) {
@@ -243,6 +246,8 @@ void DiveLogExportDialog::on_buttonBox_accepted()
 								tr("CSV files (*.csv *.CSV)"));
 		} else if (ui->exportDivelogs->isChecked()) {
 			DivelogsDeWebServices::instance()->prepareDivesForUpload(ui->exportSelected->isChecked());
+		} else if (ui->exportDiveshare->isChecked()) {
+			DiveShareExportDialog::instance()->prepareDivesForUpload(ui->exportSelected->isChecked());
 		} else if (ui->exportWorldMap->isChecked()) {
 			filename = QFileDialog::getSaveFileName(this, tr("Export world map"), lastDir,
 								tr("HTML files (*.html)"));
diff --git a/qt-ui/divelogexportdialog.ui b/qt-ui/divelogexportdialog.ui
index 9510b5f..8e37196 100644
--- a/qt-ui/divelogexportdialog.ui
+++ b/qt-ui/divelogexportdialog.ui
@@ -84,6 +84,16 @@
            </widget>
           </item>
           <item>
+           <widget class="QRadioButton" name="exportDiveshare">
+            <property name="text">
+             <string>DiveShare</string>
+            </property>
+            <attribute name="buttonGroup">
+             <string notr="true">exportGroup</string>
+            </attribute>
+           </widget>
+          </item>
+          <item>
            <widget class="QRadioButton" name="exportCSV">
             <property name="text">
              <string>CSV</string>
diff --git a/qt-ui/diveshareexportdialog.cpp b/qt-ui/diveshareexportdialog.cpp
new file mode 100644
index 0000000..165df5e
--- /dev/null
+++ b/qt-ui/diveshareexportdialog.cpp
@@ -0,0 +1,142 @@
+#include "diveshareexportdialog.h"
+#include "ui_diveshareexportdialog.h"
+#include "mainwindow.h"
+#include "save-html.h"
+#include "qt-ui/usersurvey.h"
+#include "qt-ui/subsurfacewebservices.h"
+
+#include <QDesktopServices>
+#include <QUrl>
+#include <QSettings>
+
+DiveShareExportDialog::DiveShareExportDialog(QWidget *parent) :
+	QDialog(parent),
+	ui(new Ui::DiveShareExportDialog),
+	reply(NULL)
+{
+	ui->setupUi(this);
+}
+
+DiveShareExportDialog::~DiveShareExportDialog()
+{
+	delete ui;
+	delete reply;
+}
+
+void DiveShareExportDialog::UIDFromBrowser()
+{
+	QDesktopServices::openUrl(QUrl(DIVESHARE_BASE_URI "/secret"));
+}
+
+DiveShareExportDialog *DiveShareExportDialog::instance()
+{
+	static DiveShareExportDialog *self = new DiveShareExportDialog(MainWindow::instance());
+	self->setAttribute(Qt::WA_QuitOnClose, false);
+	self->ui->txtResult->setHtml("");
+	self->ui->buttonBox->setStandardButtons(QDialogButtonBox::Cancel);
+	return self;
+}
+
+void DiveShareExportDialog::prepareDivesForUpload(bool selected)
+{
+	exportSelected = selected;
+	ui->frameConfigure->setVisible(true);
+	ui->frameResults->setVisible(false);
+
+	QSettings settings;
+	if (settings.contains("diveshareExport/uid"))
+		ui->txtUID->setText(settings.value("diveshareExport/uid").toString());
+
+	if (settings.contains("diveshareExport/private"))
+		ui->chkPrivate->setChecked(settings.value("diveshareExport/private").toBool());
+
+	show();
+}
+
+static QByteArray generate_html_list(const QByteArray &data)
+{
+	QList<QByteArray> dives = data.split('\n');
+	QByteArray html;
+	html.append("<html><body><table>");
+	for (int i = 0; i < dives.length(); i++ ) {
+		html.append("<tr>");
+		QList<QByteArray> dive_details = dives[i].split(',');
+		if (dive_details.length() < 3)
+			continue;
+
+		QByteArray dive_id = dive_details[0];
+		QByteArray dive_delete = dive_details[1];
+
+		html.append("<td>");
+		html.append("<a href=\"" DIVESHARE_BASE_URI "/dive/" + dive_id + "\">");
+
+		//Title gets separated too, this puts it back together
+		const char *sep = "";
+		for (int t = 2; t < dive_details.length(); t++) {
+			html.append(sep);
+			html.append(dive_details[t]);
+			sep = ",";
+		}
+
+		html.append("</a>");
+		html.append("</td>");
+		html.append("<td>");
+		html.append("<a href=\"" DIVESHARE_BASE_URI "/delete/dive/" + dive_delete + "\">Delete dive</a>");
+		html.append("</td>"  );
+
+		html.append("</tr>");
+	}
+
+	html.append("</table></body></html>");
+	return html;
+}
+
+void DiveShareExportDialog::finishedSlot()
+{
+	ui->progressBar->setVisible(false);
+	if (reply->error() != 0) {
+		ui->buttonBox->setStandardButtons(QDialogButtonBox::Cancel);
+		ui->txtResult->setText(reply->errorString());
+	} else {
+		ui->buttonBox->setStandardButtons(QDialogButtonBox::Ok);
+		ui->txtResult->setHtml(generate_html_list(reply->readAll()));
+	}
+
+	reply->deleteLater();
+}
+
+void DiveShareExportDialog::doUpload()
+{
+	//Store current settings
+	QSettings settings;
+	settings.setValue("diveshareExport/uid", ui->txtUID->text());
+	settings.setValue("diveshareExport/private", ui->chkPrivate->isChecked());
+
+	//Change UI into results mode
+	ui->frameConfigure->setVisible(false);
+	ui->frameResults->setVisible(true);
+	ui->progressBar->setVisible(true);
+	ui->progressBar->setRange(0, 0);
+
+	//generate json
+	struct membuffer buf = { 0 };
+	export_list(&buf, NULL, exportSelected, false);
+	QByteArray json_data(buf.buffer, buf.len);
+	free_buffer(&buf);
+
+	//Request to server
+	QNetworkRequest request;
+
+	if (ui->chkPrivate->isChecked())
+		request.setUrl(QUrl(DIVESHARE_BASE_URI "/upload?private=true"));
+	else
+		request.setUrl(QUrl(DIVESHARE_BASE_URI "/upload"));
+
+	request.setRawHeader("User-Agent", UserSurvey::getUserAgent().toAscii());
+	if (ui->txtUID->text().length() != 0)
+		request.setRawHeader("X-UID", ui->txtUID->text().toAscii());
+
+	reply = WebServices::manager()->put(request, json_data);
+
+	QObject::connect(reply, SIGNAL(finished()), this, SLOT(finishedSlot()));
+}
diff --git a/qt-ui/diveshareexportdialog.h b/qt-ui/diveshareexportdialog.h
new file mode 100644
index 0000000..85dadf5
--- /dev/null
+++ b/qt-ui/diveshareexportdialog.h
@@ -0,0 +1,34 @@
+#ifndef DIVESHAREEXPORTDIALOG_H
+#define DIVESHAREEXPORTDIALOG_H
+
+#include <QDialog>
+#include <QNetworkReply>
+#include <QNetworkAccessManager>
+
+#define DIVESHARE_WEBSITE "dive-share.appspot.com"
+#define DIVESHARE_BASE_URI "http://"; DIVESHARE_WEBSITE
+
+namespace Ui {
+class DiveShareExportDialog;
+}
+
+class DiveShareExportDialog : public QDialog
+{
+    Q_OBJECT
+public:
+    explicit DiveShareExportDialog(QWidget *parent = 0);
+    ~DiveShareExportDialog();
+    static DiveShareExportDialog *instance();
+    void prepareDivesForUpload(bool);
+private:
+    Ui::DiveShareExportDialog *ui;
+    bool exportSelected;
+    QNetworkReply *reply;
+private
+slots:
+    void UIDFromBrowser();
+    void doUpload();
+    void finishedSlot();
+};
+
+#endif // DIVESHAREEXPORTDIALOG_H
diff --git a/qt-ui/diveshareexportdialog.ui b/qt-ui/diveshareexportdialog.ui
new file mode 100644
index 0000000..051f4f3
--- /dev/null
+++ b/qt-ui/diveshareexportdialog.ui
@@ -0,0 +1,268 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>DiveShareExportDialog</class>
+ <widget class="QDialog" name="DiveShareExportDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>320</width>
+    <height>309</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <widget class="QFrame" name="frameConfigure">
+     <property name="frameShape">
+      <enum>QFrame::NoFrame</enum>
+     </property>
+     <property name="frameShadow">
+      <enum>QFrame::Plain</enum>
+     </property>
+     <property name="lineWidth">
+      <number>0</number>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout">
+        <item>
+         <widget class="QLabel" name="label">
+          <property name="text">
+           <string>User ID</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="txtUID">
+          <property name="toolTip">
+           <string/>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="cmdClear">
+          <property name="text">
+           <string>⌫</string>
+          </property>
+          <property name="flat">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="getUIDbutton">
+          <property name="text">
+           <string>Get UserID</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <widget class="QLabel" name="label_2">
+        <property name="text">
+         <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:20pt; font-weight:600; color:#ff8000;&quot;&gt;⚠&lt;/span&gt; Not using a UserID means that you will need to manually keep bookmarks to your dives, to find them again.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+        </property>
+        <property name="textFormat">
+         <enum>Qt::AutoText</enum>
+        </property>
+        <property name="wordWrap">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QCheckBox" name="chkPrivate">
+        <property name="toolTip">
+         <string>Private dives will not appear in &quot;related dives&quot; lists, and will only be accessible if their URL is known.</string>
+        </property>
+        <property name="text">
+         <string>Keep dives private</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_2">
+        <item>
+         <spacer name="horizontalSpacer">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item>
+         <widget class="QPushButton" name="doUploadButton">
+          <property name="text">
+           <string>Upload dive data</string>
+          </property>
+          <property name="autoDefault">
+           <bool>false</bool>
+          </property>
+          <property name="default">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QFrame" name="frameResults">
+     <property name="frameShape">
+      <enum>QFrame::NoFrame</enum>
+     </property>
+     <property name="frameShadow">
+      <enum>QFrame::Raised</enum>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_3">
+      <item>
+       <widget class="QTextBrowser" name="txtResult">
+        <property name="readOnly">
+         <bool>true</bool>
+        </property>
+        <property name="html">
+         <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;a href=&quot;http://www.google.com&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;asd&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+        </property>
+        <property name="openExternalLinks">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QProgressBar" name="progressBar">
+        <property name="value">
+         <number>24</number>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>68</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>DiveShareExportDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>97</x>
+     <y>299</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>252</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>getUIDbutton</sender>
+   <signal>clicked()</signal>
+   <receiver>DiveShareExportDialog</receiver>
+   <slot>UIDFromBrowser()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>223</x>
+     <y>29</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>159</x>
+     <y>215</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>doUploadButton</sender>
+   <signal>clicked()</signal>
+   <receiver>DiveShareExportDialog</receiver>
+   <slot>doUpload()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>223</x>
+     <y>120</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>159</x>
+     <y>215</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>cmdClear</sender>
+   <signal>clicked()</signal>
+   <receiver>txtUID</receiver>
+   <slot>clear()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>148</x>
+     <y>36</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>105</x>
+     <y>27</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>DiveShareExportDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>159</x>
+     <y>288</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>159</x>
+     <y>154</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+ <slots>
+  <slot>getUID()</slot>
+  <slot>doUpload()</slot>
+ </slots>
+</ui>
diff --git a/save-html.c b/save-html.c
index b517b19..2befb41 100644
--- a/save-html.c
+++ b/save-html.c
@@ -290,7 +290,8 @@ void write_one_dive(struct membuffer *b, struct dive *dive, const char *photos_d
 		put_HTML_samples(b, dive);
 		put_HTML_bookmarks(b, dive);
 		write_dive_status(b, dive);
-		save_photos(b, photos_dir, dive);
+		if (photos_dir)
+			save_photos(b, photos_dir, dive);
 		write_divecomputers(b, dive);
 	}
 	put_HTML_notes(b, dive, "\"notes\":\"", "\"");
diff --git a/save-html.h b/save-html.h
index f47ae3b..fddbf26 100644
--- a/save-html.h
+++ b/save-html.h
@@ -16,6 +16,8 @@ void put_HTML_notes(struct membuffer *b, struct dive *dive, const char *pre, con
 void put_HTML_quoted(struct membuffer *b, const char *text);
 
 void export_HTML(const char *file_name, const char *photos_dir, const bool selected_only, const bool list_only);
+void export_list(struct membuffer *b, const char *photos_dir, bool selected_only, const bool list_only);
+
 void export_translation(const char *file_name);
 
 extern void copy_image_and_overwrite(const char *cfileName, const char *cnewName);
diff --git a/subsurface.pro b/subsurface.pro
index 1e85b11..b6bb36e 100644
--- a/subsurface.pro
+++ b/subsurface.pro
@@ -99,7 +99,8 @@ HEADERS = \
 	qt-ui/statistics/monthstatistics.h \
 	qt-ui/statistics/statisticswidget.h \
 	qt-ui/statistics/statisticsbar.h \
-	qt-ui/statistics/yearstatistics.h
+	qt-ui/statistics/yearstatistics.h \
+	qt-ui/diveshareexportdialog.h
 
 android: HEADERS -= \
 	qt-ui/usermanual.h \
@@ -188,7 +189,8 @@ SOURCES =  \
 	qt-ui/statistics/statisticswidget.cpp \
 	qt-ui/statistics/yearstatistics.cpp \
 	qt-ui/statistics/statisticsbar.cpp \
-	qt-ui/statistics/monthstatistics.cpp
+	qt-ui/statistics/monthstatistics.cpp \
+	qt-ui/diveshareexportdialog.cpp
 
 android: SOURCES += android.cpp
 else: linux*: SOURCES += linux.c
@@ -222,7 +224,8 @@ FORMS = \
 	qt-ui/usersurvey.ui \
 	qt-ui/divecomponentselection.ui \
 	qt-ui/configuredivecomputerdialog.ui \
-	qt-ui/tagfilter.ui
+	qt-ui/tagfilter.ui \
+	qt-ui/diveshareexportdialog.ui
 
 # Nether usermanual or printing is supported on android right now
 android: FORMS -= qt-ui/printoptions.ui
-- 
2.1.1

_______________________________________________
subsurface mailing list
subsurface@subsurface-divelog.org
http://lists.subsurface-divelog.org/cgi-bin/mailman/listinfo/subsurface

Reply via email to