diff --git a/CHANGELOG.md b/CHANGELOG.md
index b9e9ba1cb..5b803bc9c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,4 @@
+- Desktop: Add export option for dive sites
- Import: Initial support for importing Mares log software
- Export option for profile data
- Desktop: Splitting of individual dive computers into distinct dives
diff --git a/core/dive.h b/core/dive.h
index 6e4940452..7a6cf4099 100644
--- a/core/dive.h
+++ b/core/dive.h
@@ -481,6 +481,8 @@ extern int save_dives_logic(const char *filename, bool select_only, bool anonymi
extern int save_dive(FILE *f, struct dive *dive, bool anonymize);
extern int export_dives_xslt(const char *filename, const bool selected, const int units, const char *export_xslt, bool anonymize);
+extern int save_dive_sites_logic(const char *filename, bool select_only, bool anonymize);
+
struct membuffer;
extern void save_one_dive_to_mb(struct membuffer *b, struct dive *dive, bool anonymize);
diff --git a/core/save-xml.c b/core/save-xml.c
index 50a490c80..02cb2bf87 100644
--- a/core/save-xml.c
+++ b/core/save-xml.c
@@ -21,6 +21,7 @@
#include "strndup.h"
#include "git-access.h"
#include "qthelper.h"
+#include "gettext.h"
/*
* We're outputting utf8 in xml.
@@ -590,15 +591,16 @@ void save_dives_buffer(struct membuffer *b, const bool select_only, bool anonymi
put_format(b, " \n");
put_format(b, "\n");
- /* save the dive sites - to make the output consistent let's sort the table, first */
- sort_dive_site_table(&dive_site_table);
- purge_empty_dive_sites(&dive_site_table);
+ /* save the dive sites */
put_format(b, "\n");
for (i = 0; i < dive_site_table.nr; i++) {
struct dive_site *ds = get_dive_site(i, &dive_site_table);
+ /* Don't export empty dive sites */
+ if (dive_site_is_empty(ds))
+ continue;
/* Only write used dive sites when exporting selected dives */
if (select_only && !is_dive_site_used(ds, true))
- continue;
+ continue;
put_format(b, "uuid);
show_utf8_blanked(b, ds->name, " name='", "'", 1, anonymize);
@@ -734,7 +736,7 @@ int save_dives_logic(const char *filename, const bool select_only, bool anonymiz
error = fclose(f);
}
if (error)
- report_error("Save failed (%s)", strerror(errno));
+ report_error(translate("gettextFromC", "Failed to save dives to %s (%s)"), filename, strerror(errno));
free_buffer(&buf);
return error;
@@ -798,3 +800,65 @@ int export_dives_xslt(const char *filename, const bool selected, const int units
return res;
}
+
+void save_dive_sites_buffer(struct membuffer *b, const bool select_only, bool anonymize)
+{
+ int i;
+ put_format(b, "\n", DATAFORMAT_VERSION);
+
+ /* save the dive sites */
+ for (i = 0; i < dive_site_table.nr; i++) {
+ struct dive_site *ds = get_dive_site(i, &dive_site_table);
+ /* Don't export empty dive sites */
+ if (dive_site_is_empty(ds))
+ continue;
+ /* Only write used dive sites when exporting selected dives */
+ if (select_only && !is_dive_site_used(ds, true))
+ continue;
+
+ put_format(b, "uuid);
+ show_utf8_blanked(b, ds->name, " name='", "'", 1, anonymize);
+ put_location(b, &ds->location, " gps='", "'");
+ show_utf8_blanked(b, ds->description, " description='", "'", 1, anonymize);
+ put_format(b, ">\n");
+ show_utf8_blanked(b, ds->notes, " ", " \n", 0, anonymize);
+ if (ds->taxonomy.nr) {
+ for (int j = 0; j < ds->taxonomy.nr; j++) {
+ struct taxonomy *t = &ds->taxonomy.category[j];
+ if (t->category != TC_NONE && t->value) {
+ put_format(b, " category);
+ put_format(b, " origin='%d'", t->origin);
+ show_utf8_blanked(b, t->value, " value='", "'/>\n", 1, anonymize);
+ }
+ }
+ }
+ put_format(b, "\n");
+ }
+ put_format(b, "\n");
+}
+
+int save_dive_sites_logic(const char *filename, const bool select_only, bool anonymize)
+{
+ struct membuffer buf = { 0 };
+ FILE *f;
+ int error = 0;
+
+ save_dive_sites_buffer(&buf, select_only, anonymize);
+
+ if (same_string(filename, "-")) {
+ f = stdout;
+ } else {
+ try_to_backup(filename);
+ error = -1;
+ f = subsurface_fopen(filename, "w");
+ }
+ if (f) {
+ flush_buffer(&buf, f);
+ error = fclose(f);
+ }
+ if (error)
+ report_error(translate("gettextFromC", "Failed to save divesites to %s (%s)"), filename, strerror(errno));
+
+ free_buffer(&buf);
+ return error;
+}
diff --git a/desktop-widgets/divelogexportdialog.cpp b/desktop-widgets/divelogexportdialog.cpp
index 87f2e5597..4fef543a0 100644
--- a/desktop-widgets/divelogexportdialog.cpp
+++ b/desktop-widgets/divelogexportdialog.cpp
@@ -91,6 +91,8 @@ void DiveLogExportDialog::showExplanation()
ui->description->setText(tr("HTML export of the dive locations, visualized on a world map."));
} else if (ui->exportSubsurfaceXML->isChecked()) {
ui->description->setText(tr("Subsurface native XML format."));
+ } else if (ui->exportSubsurfaceSitesXML->isChecked()) {
+ ui->description->setText(tr("Subsurface dive sites native XML format."));
} else if (ui->exportImageDepths->isChecked()) {
ui->description->setText(tr("Write depths of images to file."));
} else if (ui->exportTeX->isChecked()) {
@@ -166,6 +168,15 @@ void DiveLogExportDialog::on_buttonBox_accepted()
QByteArray bt = QFile::encodeName(filename);
save_dives_logic(bt.data(), ui->exportSelected->isChecked(), ui->anonymize->isChecked());
}
+ } else if (ui->exportSubsurfaceSitesXML->isChecked()) {
+ filename = QFileDialog::getSaveFileName(this, tr("Export Subsurface dive sites XML"), lastDir,
+ tr("Subsurface files") + " (*.xml)");
+ if (!filename.isNull() && !filename.isEmpty()) {
+ if (!filename.contains('.'))
+ filename.append(".xml");
+ QByteArray bt = QFile::encodeName(filename);
+ save_dive_sites_logic(bt.data(), ui->exportSelected->isChecked(), ui->anonymize->isChecked());
+ }
} else if (ui->exportImageDepths->isChecked()) {
filename = QFileDialog::getSaveFileName(this, tr("Save image depths"), lastDir);
if (!filename.isNull() && !filename.isEmpty())
diff --git a/desktop-widgets/divelogexportdialog.ui b/desktop-widgets/divelogexportdialog.ui
index 07b06378b..3b15f348e 100644
--- a/desktop-widgets/divelogexportdialog.ui
+++ b/desktop-widgets/divelogexportdialog.ui
@@ -56,34 +56,6 @@
0
- -
-
-
-
- 0
- 0
-
-
-
-
- 0
- 50
-
-
-
-
- 16777215
- 50
-
-
-
-
-
-
- true
-
-
-
-
@@ -97,6 +69,117 @@
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 100
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Selection
+
+
+
-
+
+
+ true
+
+
+ Selected dives
+
+
+ true
+
+
+
+ -
+
+
+ All dives
+
+
+
+
+
+
+ -
+
+
+ true
+
+
+ Options
+
+
+
+ false
+
+
+
+ 10
+ 30
+ 102
+ 27
+
+
+
-
+
+ Metric
+
+
+ -
+
+ Imperial
+
+
+
+
+
+
+ 10
+ 70
+ 111
+ 20
+
+
+
+ Anonymize
+
+
+
+
+
+
+
-
@@ -122,6 +205,16 @@
+ -
+
+
+ Subsurface dive sites XML
+
+
+ exportGroup
+
+
+
-
@@ -244,115 +337,32 @@
- -
-
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 0
- 100
-
-
-
-
- 16777215
- 16777215
-
-
-
- Selection
-
-
-
-
-
-
- true
-
-
- Selected dives
-
-
- true
-
-
-
- -
-
-
- All dives
-
-
-
-
-
-
- -
-
-
- true
-
-
- Options
-
-
-
- false
-
-
-
- 10
- 30
- 102
- 27
-
-
-
-
-
- Metric
-
-
- -
-
- Imperial
-
-
-
-
-
-
- 10
- 70
- 111
- 20
-
-
-
- Anonymize
-
-
-
-
-
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 50
+
+
+
+
+ 16777215
+ 50
+
+
+
+
+
+
+ true
+