[PATCH] GUI for CSV import

Miika Turkia miika.turkia at gmail.com
Tue Oct 15 22:13:33 UTC 2013


Signed-off-by: Miika Turkia <miika.turkia at gmail.com>
---
 dive.h                          |   3 +-
 file.c                          |  53 ++++++++-
 parse-xml.c                     |  33 ++----
 qt-ui/csvimportdialog.cpp       |  94 +++++++++++++++
 qt-ui/csvimportdialog.h         |  50 ++++++++
 qt-ui/csvimportdialog.ui        | 253 ++++++++++++++++++++++++++++++++++++++++
 qt-ui/mainwindow.cpp            |  15 +++
 qt-ui/mainwindow.h              |   2 +
 qt-ui/mainwindow.ui             |   8 ++
 qt-ui/subsurfacewebservices.cpp |   2 +-
 subsurface.pro                  |   9 +-
 11 files changed, 491 insertions(+), 31 deletions(-)
 create mode 100644 qt-ui/csvimportdialog.cpp
 create mode 100644 qt-ui/csvimportdialog.h
 create mode 100644 qt-ui/csvimportdialog.ui

diff --git a/dive.h b/dive.h
index fd9431a..b1a3614 100644
--- a/dive.h
+++ b/dive.h
@@ -609,13 +609,14 @@ extern int match_one_dc(struct divecomputer *a, struct divecomputer *b);
 
 extern double ascii_strtod(char *, char **);
 extern void parse_xml_init(void);
-extern void parse_xml_buffer(const char *url, const char *buf, int size, struct dive_table *table, char **error);
+extern void parse_xml_buffer(const char *url, const char *buf, int size, struct dive_table *table, const char **params, char **error);
 extern void parse_xml_exit(void);
 extern void set_filename(const char *filename, bool force);
 
 extern int parse_dm4_buffer(const char *url, const char *buf, int size, struct dive_table *table, char **error);
 
 extern void parse_file(const char *filename, char **error);
+extern void parse_csv_file(const char *filename, int time, int depth, int temp, char **error);
 
 extern void save_dives(const char *filename);
 extern void save_dives_logic(const char *filename, bool select_only);
diff --git a/file.c b/file.c
index a4b9012..de84dea 100644
--- a/file.c
+++ b/file.c
@@ -72,7 +72,7 @@ static void zip_read(struct zip_file *file, char **error, const char *filename)
 		mem = realloc(mem, size);
 	}
 	mem[read] = 0;
-	parse_xml_buffer(filename, mem, read, &dive_table, error);
+	parse_xml_buffer(filename, mem, read, &dive_table, NULL, error);
 	free(mem);
 }
 
@@ -286,7 +286,7 @@ static void parse_file_buffer(const char *filename, struct memblock *mem, char *
 	if (fmt && open_by_filename(filename, fmt+1, mem, error))
 		return;
 
-	parse_xml_buffer(filename, mem->buffer, mem->size, &dive_table, error);
+	parse_xml_buffer(filename, mem->buffer, mem->size, &dive_table, NULL, error);
 }
 
 void parse_file(const char *filename, char **error)
@@ -319,3 +319,52 @@ void parse_file(const char *filename, char **error)
 	parse_file_buffer(filename, &mem, error);
 	free(mem.buffer);
 }
+
+#define MAXCOLDIGITS 3
+#define MAXCOLS 100
+void parse_csv_file(const char *filename, int time, int depth, int temp, char **error)
+{
+	struct memblock mem;
+	char *params[7];
+	char timebuf[MAXCOLDIGITS];
+	char depthbuf[MAXCOLDIGITS];
+	char tempbuf[MAXCOLDIGITS];
+
+	if (time >= MAXCOLS || depth >= MAXCOLS || temp >= MAXCOLS) {
+		int len = strlen(translate("gettextFromC", "Maximum number of supported columns on CSV import is %d")) + MAXCOLDIGITS;
+		*error = malloc(len);
+		snprintf(*error, len, translate("gettextFromC", "Maximum number of supported columns on CSV import is %d")) + MAXCOLS;
+
+		return;
+	}
+	snprintf(timebuf, MAXCOLDIGITS, "%d", time);
+	snprintf(depthbuf, MAXCOLDIGITS, "%d", depth);
+	snprintf(tempbuf, MAXCOLDIGITS, "%d", temp);
+
+	params[0] = "timeField";
+	params[1] = timebuf;
+	params[2] = "depthField";
+	params[3] = depthbuf;
+	params[4] = "tempField";
+	params[5] = tempbuf;
+	params[6] = NULL;
+
+	if (filename == NULL)
+		return;
+
+	if (readfile(filename, &mem) < 0) {
+		if (error) {
+			int len = strlen(translate("gettextFromC","Failed to read '%s'")) + strlen(filename);
+			*error = malloc(len);
+			snprintf(*error, len, translate("gettextFromC","Failed to read '%s'"), filename);
+		}
+
+		return;
+	}
+
+	if (try_to_xslt_open_csv(filename, &mem, error))
+		return;
+
+	parse_xml_buffer(filename, mem.buffer, mem.size, &dive_table, (const char **)params, error);
+	free(mem.buffer);
+}
diff --git a/parse-xml.c b/parse-xml.c
index fbe805a..2b04cdb 100644
--- a/parse-xml.c
+++ b/parse-xml.c
@@ -21,7 +21,7 @@
 
 int verbose;
 
-static xmlDoc *test_xslt_transforms(xmlDoc *doc, char **error);
+static xmlDoc *test_xslt_transforms(xmlDoc *doc, const char **params, char **error);
 char *xslt_path;
 
 /* the dive table holds the overall dive list; target table points at
@@ -1681,7 +1681,7 @@ const char *preprocess_divelog_de(const char *buffer)
 }
 
 void parse_xml_buffer(const char *url, const char *buffer, int size,
-			struct dive_table *table, char **error)
+			struct dive_table *table, const char **params, char **error)
 {
 	xmlDoc *doc;
 	const char *res = preprocess_divelog_de(buffer);
@@ -1698,7 +1698,7 @@ void parse_xml_buffer(const char *url, const char *buffer, int size,
 	}
 	reset_all();
 	dive_start();
-	doc = test_xslt_transforms(doc, error);
+	doc = test_xslt_transforms(doc, params, error);
 	traverse(xmlDocGetRootElement(doc));
 	dive_end();
 	xmlFreeDoc(doc);
@@ -2019,14 +2019,13 @@ static struct xslt_files {
 	{ NULL, }
 };
 
-static xmlDoc *test_xslt_transforms(xmlDoc *doc, char **error)
+static xmlDoc *test_xslt_transforms(xmlDoc *doc, const char **params, char **error)
 {
 	struct xslt_files *info = xslt_files;
 	xmlDoc *transformed;
 	xsltStylesheetPtr xslt = NULL;
 	xmlNode *root_element = xmlDocGetRootElement(doc);
 	char *attribute;
-	char *params[3];
 
 	while ((info->root) && (strcasecmp(root_element->name, info->root) != 0)) {
 		info++;
@@ -2049,27 +2048,13 @@ static xmlDoc *test_xslt_transforms(xmlDoc *doc, char **error)
 			return doc;
 		}
 
-		/*
-		 * params is only used for CSV import, but it does not
-		 * hurt if we supply unused parameters for other
-		 * transforms as well.
-		 *
-		 * We should have a GUI set the parameters but currently
-		 * we just have PoC how parameters would be handled.
-		 *
-		 * (Field 9 is temperature for XP5 import, field 15
-		 * is temperature for AP Logviewer.
-		 */
-
-		params[0] = strdup("tempField");
-		params[1] = strdup("15");
-		params[2] = NULL;
-
-		transformed = xsltApplyStylesheet(xslt, doc, (const char **)params);
+		if (params == NULL)
+			transformed = xsltApplyStylesheet(xslt, doc, NULL);
+		else
+			transformed = xsltApplyStylesheet(xslt, doc, params);
 		xmlFreeDoc(doc);
 		xsltFreeStylesheet(xslt);
-		free(params[0]);
-		free(params[1]);
+
 		return transformed;
 	}
 	return doc;
diff --git a/qt-ui/csvimportdialog.cpp b/qt-ui/csvimportdialog.cpp
new file mode 100644
index 0000000..8d0e6ce
--- /dev/null
+++ b/qt-ui/csvimportdialog.cpp
@@ -0,0 +1,94 @@
+#include <QtDebug>
+#include <QFileDialog>
+#include "csvimportdialog.h"
+#include "mainwindow.h"
+#include "ui_csvimportdialog.h"
+
+CSVImportDialog::CSVImportDialog(QWidget *parent) :
+	QDialog(parent),
+	ui(new Ui::CSVImportDialog)
+{
+	ui->setupUi(this);
+
+	for (int i = 0; !CSVApps[i].name.isNull(); ++i)
+		ui->knownImports->addItem(CSVApps[i].name);
+
+	ui->CSVSeparator->addItem("Tab");
+	ui->knownImports->setCurrentIndex(1);
+	ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
+}
+
+CSVImportDialog::~CSVImportDialog()
+{
+	delete ui;
+}
+
+void CSVImportDialog::on_buttonBox_accepted()
+{
+	char *error = NULL;
+
+	parse_csv_file(ui->CSVFile->text().toUtf8().data(), ui->CSVTime->value(), ui->CSVDepth->value(), ui->CSVTemperature->value(), &error);
+	if (error != NULL) {
+		
+		mainWindow()->showError(error);
+		free(error);
+		error = NULL;
+	}
+	process_dives(TRUE, FALSE);
+
+	mainWindow()->refreshDisplay();
+}
+
+void CSVImportDialog::on_CSVFileSelector_clicked()
+{
+	QString filename = QFileDialog::getOpenFileName(this, tr("Open CSV Log File"), ".", tr("CSV Files (*.csv)"));
+	ui->CSVFile->setText(filename);
+	if (filename.isEmpty())
+		ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
+	else
+		ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
+}
+
+void CSVImportDialog::on_knownImports_currentIndexChanged(int index)
+{
+	if (index == 0)
+		return;
+
+	ui->CSVTime->blockSignals(true);
+	ui->CSVDepth->blockSignals(true);
+	ui->CSVTemperature->blockSignals(true);
+	ui->CSVTime->setValue(CSVApps[index].time);
+	ui->CSVDepth->setValue(CSVApps[index].depth);
+	ui->CSVTemperature->setValue(CSVApps[index].temperature);
+	ui->CSVTime->blockSignals(false);
+	ui->CSVDepth->blockSignals(false);
+	ui->CSVTemperature->blockSignals(false);
+}
+
+void CSVImportDialog::on_CSVTime_valueChanged(int arg1)
+{
+	unknownImports();
+}
+
+void CSVImportDialog::on_CSVDepth_valueChanged(int arg1)
+{
+	unknownImports();
+}
+
+void CSVImportDialog::on_CSVTemperature_valueChanged(int arg1)
+{
+	unknownImports();
+}
+
+void CSVImportDialog::unknownImports()
+{
+	ui->knownImports->setCurrentIndex(0);
+}
+
+void CSVImportDialog::on_CSVFile_textEdited()
+{
+	if (ui->CSVFile->text().isEmpty())
+		ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
+	else
+		ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
+}
diff --git a/qt-ui/csvimportdialog.h b/qt-ui/csvimportdialog.h
new file mode 100644
index 0000000..f08ac73
--- /dev/null
+++ b/qt-ui/csvimportdialog.h
@@ -0,0 +1,50 @@
+#ifndef CSVIMPORTDIALOG_H
+#define CSVIMPORTDIALOG_H
+
+#include <QDialog>
+#include <QModelIndex>
+#include "../dive.h"
+#include "../divelist.h"
+
+namespace Ui {
+class CSVImportDialog;
+}
+
+class CSVImportDialog : public QDialog
+{
+	Q_OBJECT
+
+public:
+	explicit CSVImportDialog(QWidget *parent = 0);
+	~CSVImportDialog();
+
+private slots:
+	void on_buttonBox_accepted();
+	void on_CSVFileSelector_clicked();
+	void on_knownImports_currentIndexChanged(int index);
+	void on_CSVTime_valueChanged(int arg1);
+	void on_CSVDepth_valueChanged(int arg1);
+	void on_CSVTemperature_valueChanged(int arg1);
+	void on_CSVFile_textEdited();
+
+private:
+	void unknownImports();
+
+	bool selector = TRUE;
+	Ui::CSVImportDialog *ui;
+
+	struct CSVAppConfig {
+		QString name;
+		int time;
+		int depth;
+		int temperature;
+		QString separator;
+	} CSVApps[4] = {
+		{"", },
+		{"APD Log Viewer", 0, 1, 15, "Tab"},
+		{"XP5", 0, 1, 9, "Tab"},
+		{NULL,}
+	};
+};
+
+#endif // CSVIMPORTDIALOG_H
diff --git a/qt-ui/csvimportdialog.ui b/qt-ui/csvimportdialog.ui
new file mode 100644
index 0000000..ec5002b
--- /dev/null
+++ b/qt-ui/csvimportdialog.ui
@@ -0,0 +1,253 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>CSVImportDialog</class>
+ <widget class="QDialog" name="CSVImportDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <widget class="QDialogButtonBox" name="buttonBox">
+   <property name="geometry">
+    <rect>
+     <x>30</x>
+     <y>240</y>
+     <width>341</width>
+     <height>32</height>
+    </rect>
+   </property>
+   <property name="orientation">
+    <enum>Qt::Horizontal</enum>
+   </property>
+   <property name="standardButtons">
+    <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+   </property>
+  </widget>
+  <widget class="QGroupBox" name="groupBox">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>10</y>
+     <width>331</width>
+     <height>71</height>
+    </rect>
+   </property>
+   <property name="title">
+    <string>Import File (CSV)</string>
+   </property>
+   <widget class="QLineEdit" name="CSVFile">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>30</y>
+      <width>291</width>
+      <height>29</height>
+     </rect>
+    </property>
+   </widget>
+   <widget class="QToolButton" name="CSVFileSelector">
+    <property name="geometry">
+     <rect>
+      <x>300</x>
+      <y>30</y>
+      <width>25</width>
+      <height>27</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>...</string>
+    </property>
+   </widget>
+  </widget>
+  <widget class="QGroupBox" name="groupBox_2">
+   <property name="geometry">
+    <rect>
+     <x>200</x>
+     <y>80</y>
+     <width>121</width>
+     <height>61</height>
+    </rect>
+   </property>
+   <property name="title">
+    <string>Field Separator</string>
+   </property>
+   <widget class="QComboBox" name="CSVSeparator">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>30</y>
+      <width>111</width>
+      <height>29</height>
+     </rect>
+    </property>
+   </widget>
+  </widget>
+  <widget class="QGroupBox" name="groupBox_3">
+   <property name="geometry">
+    <rect>
+     <x>40</x>
+     <y>80</y>
+     <width>151</width>
+     <height>151</height>
+    </rect>
+   </property>
+   <property name="title">
+    <string>Field Configuration</string>
+   </property>
+   <widget class="QSpinBox" name="CSVTime">
+    <property name="geometry">
+     <rect>
+      <x>60</x>
+      <y>30</y>
+      <width>56</width>
+      <height>29</height>
+     </rect>
+    </property>
+    <property name="minimum">
+     <number>0</number>
+    </property>
+    <property name="value">
+     <number>0</number>
+    </property>
+   </widget>
+   <widget class="QSpinBox" name="CSVDepth">
+    <property name="geometry">
+     <rect>
+      <x>60</x>
+      <y>70</y>
+      <width>56</width>
+      <height>29</height>
+     </rect>
+    </property>
+    <property name="minimum">
+     <number>0</number>
+    </property>
+    <property name="value">
+     <number>1</number>
+    </property>
+   </widget>
+   <widget class="QSpinBox" name="CSVTemperature">
+    <property name="geometry">
+     <rect>
+      <x>60</x>
+      <y>110</y>
+      <width>56</width>
+      <height>29</height>
+     </rect>
+    </property>
+    <property name="minimum">
+     <number>0</number>
+    </property>
+    <property name="value">
+     <number>15</number>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>30</y>
+      <width>41</width>
+      <height>19</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Time</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_2">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>70</y>
+      <width>51</width>
+      <height>19</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Depth</string>
+    </property>
+   </widget>
+   <widget class="QLabel" name="label_3">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>110</y>
+      <width>41</width>
+      <height>19</height>
+     </rect>
+    </property>
+    <property name="text">
+     <string>Temp</string>
+    </property>
+   </widget>
+  </widget>
+  <widget class="QGroupBox" name="groupBox_4">
+   <property name="geometry">
+    <rect>
+     <x>200</x>
+     <y>159</y>
+     <width>181</width>
+     <height>61</height>
+    </rect>
+   </property>
+   <property name="title">
+    <string>Pre-configured imports</string>
+   </property>
+   <widget class="QComboBox" name="knownImports">
+    <property name="geometry">
+     <rect>
+      <x>0</x>
+      <y>30</y>
+      <width>161</width>
+      <height>29</height>
+     </rect>
+    </property>
+    <property name="currentIndex">
+     <number>-1</number>
+    </property>
+   </widget>
+  </widget>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>CSVImportDialog</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>CSVImportDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp
index 247cfdf..3b56134 100644
--- a/qt-ui/mainwindow.cpp
+++ b/qt-ui/mainwindow.cpp
@@ -34,6 +34,7 @@
 #include "diveplanner.h"
 #include "about.h"
 #include "printdialog.h"
+#include "csvimportdialog.h"
 
 static MainWindow* instance = 0;
 
@@ -801,3 +802,17 @@ void MainWindow::loadFiles(const QStringList fileNames)
 	WSInfoModel *wsim = WSInfoModel::instance();
 	wsim->updateInfo();
 }
+
+void MainWindow::on_actionImportCSV_triggered()
+{
+	CSVImportDialog *csvImport = new(CSVImportDialog);
+	csvImport->show();
+	process_dives(TRUE, FALSE);
+
+	ui.InfoWidget->reload();
+	ui.globe->reload();
+	ui.ListWidget->reload(DiveTripModel::TREE);
+	ui.ListWidget->setFocus();
+	WSInfoModel *wsim = WSInfoModel::instance();
+	wsim->updateInfo();
+}
diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h
index 0319c7b..5dfae3e 100644
--- a/qt-ui/mainwindow.h
+++ b/qt-ui/mainwindow.h
@@ -99,6 +99,8 @@ private slots:
 	void current_dive_changed(int divenr);
 	void initialUiSetup();
 
+	void on_actionImportCSV_triggered();
+
 protected:
 	void closeEvent(QCloseEvent *);
 
diff --git a/qt-ui/mainwindow.ui b/qt-ui/mainwindow.ui
index 63dded7..8a10811 100644
--- a/qt-ui/mainwindow.ui
+++ b/qt-ui/mainwindow.ui
@@ -183,6 +183,7 @@
     <addaction name="actionClose"/>
     <addaction name="separator"/>
     <addaction name="actionImport"/>
+    <addaction name="actionImportCSV"/>
     <addaction name="actionExportUDDF"/>
     <addaction name="separator"/>
     <addaction name="actionPrint"/>
@@ -441,6 +442,13 @@
     <string>Dive Planner</string>
    </property>
   </action>
+  <action name="actionImportCSV">
+    <property name="text">
+      <string>Import CSV</string>
+    </property>
+      <property name="toolTip">
+    </property>
+  </action>
  </widget>
  <customwidgets>
   <customwidget>
diff --git a/qt-ui/subsurfacewebservices.cpp b/qt-ui/subsurfacewebservices.cpp
index 2a560ef..f2b1b88 100644
--- a/qt-ui/subsurfacewebservices.cpp
+++ b/qt-ui/subsurfacewebservices.cpp
@@ -47,7 +47,7 @@ void SubsurfaceWebServices::buttonClicked(QAbstractButton* 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);
+			parse_xml_buffer(url.data(), downloadedData.data(), downloadedData.length(), &gps_location_table, NULL, NULL);
 
 			/* now merge the data in the gps_location table into the dive_table */
 			if (merge_locations_into_dives()) {
diff --git a/subsurface.pro b/subsurface.pro
index a2fd973..d0c06cd 100644
--- a/subsurface.pro
+++ b/subsurface.pro
@@ -55,7 +55,8 @@ HEADERS = \
 	subsurface-icon.h \
 	subsurfacestartup.h \
 	uemis.h \
-	webservice.h 
+	webservice.h \ 
+	qt-ui/csvimportdialog.h
 
 SOURCES =  \
 	deco.c \
@@ -101,7 +102,8 @@ SOURCES =  \
 	subsurfacestartup.c \
 	time.c \
 	uemis.c \
-	uemis-downloader.c
+	uemis-downloader.c \
+	qt-ui/csvimportdialog.cpp
 
 linux*: SOURCES += linux.c
 mac: SOURCES += macos.c
@@ -118,7 +120,8 @@ FORMS = \
 	qt-ui/printoptions.ui \
 	qt-ui/renumber.ui \
 	qt-ui/subsurfacewebservices.ui \
-	qt-ui/tableview.ui
+	qt-ui/tableview.ui \
+    qt-ui/csvimportdialog.ui
 
 RESOURCES = subsurface.qrc
 
-- 
1.8.1.2



More information about the subsurface mailing list