summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ReleaseNotes/ReleaseNotes.txt3
-rw-r--r--desktop-widgets/mainwindow.cpp20
-rw-r--r--desktop-widgets/printoptions.cpp89
-rw-r--r--desktop-widgets/printoptions.h1
-rw-r--r--desktop-widgets/templatelayout.cpp58
-rw-r--r--desktop-widgets/templatelayout.h3
-rw-r--r--printing_templates/One Dive.html188
7 files changed, 267 insertions, 95 deletions
diff --git a/ReleaseNotes/ReleaseNotes.txt b/ReleaseNotes/ReleaseNotes.txt
index 772dca088..10fdfa7d7 100644
--- a/ReleaseNotes/ReleaseNotes.txt
+++ b/ReleaseNotes/ReleaseNotes.txt
@@ -15,6 +15,9 @@ Some of the changes since _Subsurface_ 4.7.4
- mobile: fix black/white switch in splash screen (#531)
- UI: tag editing. Comma entry shows all tags (again) (#605)
- mobile: enable auto completion for dive site entry (#546)
+- Printing: the bundled templates are now read-only and are always overwritten
+ by the application. The first time the user runs this update, backup files
+ of the previous templates would be created. (#847).
Some of the changes since _Subsurface_ 4.7.2
diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp
index b0817b9f1..330ea7d05 100644
--- a/desktop-widgets/mainwindow.cpp
+++ b/desktop-widgets/mainwindow.cpp
@@ -256,8 +256,24 @@ MainWindow::MainWindow() : QMainWindow(),
connect(geoLookup, SIGNAL(started()),information(), SLOT(disableGeoLookupEdition()));
connect(geoLookup, SIGNAL(finished()), information(), SLOT(enableGeoLookupEdition()));
#ifndef NO_PRINTING
- // copy the bundled print templates to the user path; no overwriting occurs!
- copyPath(getPrintingTemplatePathBundle(), getPrintingTemplatePathUser());
+ // copy the bundled print templates to the user path
+ QStringList templateBackupList;
+ QString templatePathUser(getPrintingTemplatePathUser());
+ copy_bundled_templates(getPrintingTemplatePathBundle(), templatePathUser, &templateBackupList);
+ if (templateBackupList.length()) {
+ QMessageBox msgBox(this);
+ templatePathUser.replace("\\", "/");
+ templateBackupList.replaceInStrings(templatePathUser + "/", "");
+ msgBox.setWindowTitle(tr("Template backup created"));
+ msgBox.setText(tr("The following backup printing templates were created:\n\n%1\n\n"
+ "Location:\n%2\n\n"
+ "Please note that as of this version of Subsurface the default templates\n"
+ "are read-only and should not be edited directly, since the application\n"
+ "can overwrite them on startup.").arg(templateBackupList.join("\n")).arg(templatePathUser));
+ msgBox.setStandardButtons(QMessageBox::Ok);
+ msgBox.exec();
+ }
+ set_bundled_templates_as_read_only();
find_all_templates();
#endif
diff --git a/desktop-widgets/printoptions.cpp b/desktop-widgets/printoptions.cpp
index eaeb6954b..cebb073a3 100644
--- a/desktop-widgets/printoptions.cpp
+++ b/desktop-widgets/printoptions.cpp
@@ -60,11 +60,13 @@ void PrintOptions::setupTemplates()
int current_index = 0;
ui.printTemplate->clear();
Q_FOREACH(const QString& theme, currList) {
- if (theme == storedTemplate) // find the stored template in the list
+ // find the stored template in the list
+ if (theme == storedTemplate || theme == lastImportExportTemplate)
current_index = currList.indexOf(theme);
ui.printTemplate->addItem(theme.split('.')[0], theme);
}
ui.printTemplate->setCurrentIndex(current_index);
+ lastImportExportTemplate = "";
}
// print type radio buttons
@@ -121,6 +123,20 @@ void PrintOptions::on_printTemplate_currentIndexChanged(int index)
void PrintOptions::on_editButton_clicked()
{
+ QString templateName = getSelectedTemplate();
+ QString prefix = (printOptions->type == print_options::STATISTICS) ? "statistics/" : "";
+ QFile f(getPrintingTemplatePathUser() + QDir::separator() + prefix + templateName);
+ if (!f.open(QFile::ReadWrite | QFile::Text)) {
+ QMessageBox msgBox(this);
+ msgBox.setWindowTitle(tr("Read-only template!"));
+ msgBox.setText(tr("The template '%1' is read-only and connot be edited.\n"
+ "Please export this template to a different file.").arg(templateName));
+ msgBox.setStandardButtons(QMessageBox::Ok);
+ msgBox.exec();
+ return;
+ } else {
+ f.close();
+ }
TemplateEdit te(this, printOptions, templateOptions);
te.exec();
setup();
@@ -128,36 +144,93 @@ void PrintOptions::on_editButton_clicked()
void PrintOptions::on_importButton_clicked()
{
- QString filename = QFileDialog::getOpenFileName(this, tr("Import template file"), "",
+ QString pathUser = getPrintingTemplatePathUser();
+ QString filename = QFileDialog::getOpenFileName(this, tr("Import template file"), pathUser,
tr("HTML files") + " (*.html)");
if (filename.isEmpty())
return;
QFileInfo fileInfo(filename);
- QFile::copy(filename, getPrintingTemplatePathUser() + QDir::separator() + fileInfo.fileName());
+
+ const QString dest = pathUser + QDir::separator() + fileInfo.fileName();
+ QFile f(dest);
+ if (!f.open(QFile::ReadWrite | QFile::Text)) {
+ QMessageBox msgBox(this);
+ msgBox.setWindowTitle(tr("Read-only template!"));
+ msgBox.setText(tr("The destination template '%1' is read-only and cannot be overwritten.").arg(fileInfo.fileName()));
+ msgBox.setStandardButtons(QMessageBox::Ok);
+ msgBox.exec();
+ return;
+ } else {
+ f.close();
+ if (filename != dest)
+ f.remove();
+ }
+
+ QFile::copy(filename, dest);
printOptions->p_template = fileInfo.fileName();
+ lastImportExportTemplate = fileInfo.fileName();
find_all_templates();
setup();
}
void PrintOptions::on_exportButton_clicked()
{
- QString filename = QFileDialog::getSaveFileName(this, tr("Export template files as"), "",
+ QString pathUser = getPrintingTemplatePathUser();
+ QString filename = QFileDialog::getSaveFileName(this, tr("Export template files as"), pathUser,
tr("HTML files") + " (*.html)");
if (filename.isEmpty())
return;
- QFile::copy(getPrintingTemplatePathUser() + QDir::separator() + getSelectedTemplate(), filename);
+ const QString ext(".html");
+ if (filename.endsWith(".htm", Qt::CaseInsensitive))
+ filename += "l";
+ else if (!filename.endsWith(ext, Qt::CaseInsensitive))
+ filename += ext;
+ QFileInfo fileInfo(filename);
+ const QString dest = pathUser + QDir::separator() + getSelectedTemplate();
+
+ QFile f(filename);
+ if (!f.open(QFile::ReadWrite | QFile::Text)) {
+ QMessageBox msgBox(this);
+ msgBox.setWindowTitle(tr("Read-only template!"));
+ msgBox.setText(tr("The destination template '%1' is read-only and cannot be overwritten.").arg(fileInfo.fileName()));
+ msgBox.setStandardButtons(QMessageBox::Ok);
+ msgBox.exec();
+ return;
+ } else {
+ f.close();
+ if (dest != filename)
+ f.remove();
+ }
+
+ QFile::copy(dest, filename);
+ if (!f.open(QFile::ReadWrite | QFile::Text))
+ f.setPermissions(QFileDevice::ReadUser | QFileDevice::ReadOwner | QFileDevice::WriteUser | QFileDevice::WriteOwner);
+ else
+ f.close();
+ lastImportExportTemplate = fileInfo.fileName();
+ find_all_templates();
+ setup();
}
void PrintOptions::on_deleteButton_clicked()
{
QString templateName = getSelectedTemplate();
- QMessageBox msgBox;
- msgBox.setText(tr("This action cannot be undone!"));
- msgBox.setInformativeText(tr("Delete template: %1?").arg(templateName));
+ QMessageBox msgBox(this);
+ msgBox.setWindowTitle(tr("This action cannot be undone!"));
+ msgBox.setText(tr("Delete template '%1'?").arg(templateName));
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Cancel);
if (msgBox.exec() == QMessageBox::Ok) {
QFile f(getPrintingTemplatePathUser() + QDir::separator() + templateName);
+ if (!f.open(QFile::ReadWrite | QFile::Text)) {
+ msgBox.setWindowTitle(tr("Read-only template!"));
+ msgBox.setText(tr("The template '%1' is read-only and cannot be deleted.").arg(templateName));
+ msgBox.setStandardButtons(QMessageBox::Ok);
+ msgBox.exec();
+ return;
+ } else {
+ f.close();
+ }
f.remove();
find_all_templates();
setup();
diff --git a/desktop-widgets/printoptions.h b/desktop-widgets/printoptions.h
index 9aaf8de70..1db0dfe61 100644
--- a/desktop-widgets/printoptions.h
+++ b/desktop-widgets/printoptions.h
@@ -72,6 +72,7 @@ private:
struct print_options *printOptions;
struct template_options *templateOptions;
bool hasSetupSlots;
+ QString lastImportExportTemplate;
void setupTemplates();
private
diff --git a/desktop-widgets/templatelayout.cpp b/desktop-widgets/templatelayout.cpp
index 9f933e91c..f3f3c8be2 100644
--- a/desktop-widgets/templatelayout.cpp
+++ b/desktop-widgets/templatelayout.cpp
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
+#include <QFileDevice>
#include <string>
#include "templatelayout.h"
@@ -19,23 +20,74 @@ int getTotalWork(print_options *printOptions)
void find_all_templates()
{
+ const QString ext(".html");
grantlee_templates.clear();
grantlee_statistics_templates.clear();
QDir dir(getPrintingTemplatePathUser());
QStringList list = dir.entryList(QDir::Files | QDir::NoDotAndDotDot);
foreach (const QString& filename, list) {
- if (filename.at(filename.size() - 1) != '~') {
+ if (filename.at(filename.size() - 1) != '~' && filename.endsWith(ext))
grantlee_templates.append(filename);
- }
}
// find statistics templates
dir.setPath(getPrintingTemplatePathUser() + QDir::separator() + "statistics");
list = dir.entryList(QDir::Files | QDir::NoDotAndDotDot);
foreach (const QString& filename, list) {
- if (filename.at(filename.size() - 1) != '~') {
+ if (filename.at(filename.size() - 1) != '~' && filename.endsWith(ext))
grantlee_statistics_templates.append(filename);
+ }
+}
+
+/* find templates which are part of the bundle in the user path
+ * and set them as read only.
+ */
+void set_bundled_templates_as_read_only()
+{
+ QDir dir;
+ const QString stats("statistics");
+ QStringList list, listStats;
+ QString pathBundle = getPrintingTemplatePathBundle();
+ QString pathUser = getPrintingTemplatePathUser();
+
+ dir.setPath(pathBundle);
+ list = dir.entryList(QDir::Files | QDir::NoDotAndDotDot);
+ dir.setPath(pathBundle + QDir::separator() + stats);
+ listStats = dir.entryList(QDir::Files | QDir::NoDotAndDotDot);
+ for (int i = 0; i < listStats.length(); i++)
+ listStats[i] = stats + QDir::separator() + listStats.at(i);
+ list += listStats;
+
+ foreach (const QString& f, list)
+ QFile::setPermissions(pathUser + QDir::separator() + f, QFileDevice::ReadOwner | QFileDevice::ReadUser);
+}
+
+void copy_bundled_templates(QString src, QString dst, QStringList *templateBackupList)
+{
+ QDir dir(src);
+ if (!dir.exists())
+ return;
+ foreach (QString d, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
+ QString dst_path = dst + QDir::separator() + d;
+ dir.mkpath(dst_path);
+ copy_bundled_templates(src + QDir::separator() + d, dst_path, templateBackupList);
+ }
+ foreach (QString f, dir.entryList(QDir::Files)) {
+ QFile fileSrc(src + QDir::separator() + f);
+ QFile fileDest(dst + QDir::separator() + f);
+ if (fileDest.exists()) {
+ // if open() fails the file is either locked or r/o. try to remove it and then overwrite
+ if (!fileDest.open(QFile::ReadWrite | QFile::Text)) {
+ fileDest.setPermissions(QFileDevice::WriteOwner | QFileDevice::WriteUser);
+ fileDest.remove();
+ } else { // if the file is not read-only create a backup
+ fileDest.close();
+ const QString targetFile = fileDest.fileName().replace(".html", "-User.html");
+ fileDest.copy(targetFile);
+ *templateBackupList << targetFile;
+ }
}
+ fileSrc.copy(fileDest.fileName()); // in all cases copy the file
}
}
diff --git a/desktop-widgets/templatelayout.h b/desktop-widgets/templatelayout.h
index 9c24d096d..cb60cc03d 100644
--- a/desktop-widgets/templatelayout.h
+++ b/desktop-widgets/templatelayout.h
@@ -2,6 +2,7 @@
#ifndef TEMPLATELAYOUT_H
#define TEMPLATELAYOUT_H
+#include <QStringList>
#include <grantlee_templates.h>
#include "mainwindow.h"
#include "printoptions.h"
@@ -12,6 +13,8 @@
int getTotalWork(print_options *printOptions);
void find_all_templates();
+void set_bundled_templates_as_read_only();
+void copy_bundled_templates(QString src, QString dst, QStringList *templateBackupList);
extern QList<QString> grantlee_templates, grantlee_statistics_templates;
diff --git a/printing_templates/One Dive.html b/printing_templates/One Dive.html
index 7f7945396..a9ee00471 100644
--- a/printing_templates/One Dive.html
+++ b/printing_templates/One Dive.html
@@ -4,19 +4,17 @@
body {
{{ print_options.grayscale }};
padding: 0;
- margin: 0;
+ margin: 0 0 0 6%; <!-- Provide LH margin for binding the page -->
font-size: {{ template_options.font_size }}vw;
line-height: {{ template_options.line_spacing }};
font-family: {{ template_options.font }};
}
h1 {
- float: left;
font-size: {{ template_options.font_size }}vw;
}
p {
- float: left;
font-size: {{ template_options.font_size }}vw;
}
@@ -26,11 +24,17 @@
border-width: {{ template_options.borderwidth }}px;
border-style:solid;
border-color: {{ template_options.color6 }};
+ border-collapse: separate;
+ }
+
+ tr {
+ height: 4vh;
}
td {
- padding-left: 0.5vw;
- padding-right: 0.5vw;
+ padding: 0;
+ margin: 0;
+ padding-left: 1%;
}
#body_div {
@@ -38,70 +42,101 @@
}
.mainContainer {
- width: 98%;
+ width: 97%;
height: 100%;
- margin-left: 1%;
+ margin-left: 0%;
margin-right: 1%;
margin-top: 0%;
margin-bottom: 0%;
- overflow: hidden;
- border-width: 0;
+ border-width: 1px;
page-break-inside: avoid;
}
.innerContainer {
- width: 100%;
+ width: 99%;
height: 99%;
- padding-top: 1%;
- overflow: hidden;
+ padding-top: 0%;
}
.diveDetails {
width: 100%;
- height: 98%;
- float: left;
+ margin: 0.0%;
+ }
+
+ .dataSection {
+ width: 100%;
+ margin: 0.0% 0% 0% 0%;
}
.diveProfile {
- width: 99%;
- height: 40%;
- margin: 0.5%;
+ width: 99.5%;
+ height: 45%;
+ margin: 0.2% 0% 0.5% 0.5%;
}
- .dataSection {
+ .notesSection {
width: 100%;
- height: 40%;
- margin: 0%;
+ margin: 0.0%;
+ min-height: 35%;
}
.fieldTitle {
background-color: {{ template_options.color2 }};
overflow: hidden;
color: {{ template_options.color4 }};
+ width: 7%;
+ padding-left:5px;
}
.fieldData {
background-color: {{ template_options.color3 }};
color: {{ template_options.color5 }};
+ width: 13%;
+ padding: o$ 1% 0% 1%;
}
.table_class {
- float: left;
- margin: 0.5%;
- width: 49%;
+ margin: 0%;
+ width: 100%;
+ }
+
+ td.insert_column_inner {
+ border-left-style:solid;
+ border-left-color: {{ template_options.color6 }};
+ background-color: {{ template_options.color2 }};
+ color: {{ template_options.color4 }};
+ border: 5px solid black;
}
+ td.insert_column_outer {
+ background-color: {{ template_options.color2 }};
+ color: {{ template_options.color4 }};
+ }
+
.notes_table_class {
overflow: hidden;
- width: 99%;
- margin: 0.5%;
+ width: 100%;
+ margin: 0.0% 0% 0% 0%;
+ max-height: 35%;
}
+ .notes_table_class td.fieldTitle {
+ max-height: 0.15vh;
+ }
+
.textArea {
line-height: {{ template_options.line_spacing }};
color: {{ template_options.color5 }};
- max-height: 19vh;
- overflow: hidden;
+ font-size: {{ template_options.font_size }}vw;
+ padding: 1%;
+ }
+
+ td.fieldTitle b {
+ font-size: {{ template_options.font_size }}vw;
+ }
+
+ .hidden_div {
+ display: none;
}
</style>
</head>
@@ -111,80 +146,59 @@
{% for dive in dives %}
<div class="mainContainer">
<div class="innerContainer">
- <div class="diveDetails">
- <div class="diveProfile" id="dive_{{ dive.id }}">
- </div>
- <div class="dataSection">
+ <div class="dataSection">
<table class="table_class">
- <tbody><tr>
- <td class="fieldTitle">
- <h1> Dive No. </h1>
- </td>
- <td class="fieldData">
- <p> {{ dive.number }} </p>
- </td>
- </tr>
<tr>
<td class="fieldTitle">
- <h1> Date </h1>
+ <b> Date </b>
</td>
<td class="fieldData">
<p> {{ dive.date }} </p>
</td>
- </tr>
- <tr>
<td class="fieldTitle">
- <h1> Location </h1>
+ <b> Dive No. </b>
</td>
<td class="fieldData">
- <p> {{ dive.location }} </p>
+ <p> {{ dive.number }} </p>
</td>
</tr>
<tr>
<td class="fieldTitle">
- <h1> Max. depth </h1>
+ <b> Time </b>
</td>
<td class="fieldData">
- <p> {{ dive.depth }} </p>
+ <p> {{ dive.time }} </p>
</td>
- </tr>
- <tr>
<td class="fieldTitle">
- <h1> Duration </h1>
+ <b> Gases </b>
</td>
<td class="fieldData">
- <p> {{ dive.duration }} </p>
+ <p> {{ dive.gas }} </p>
</td>
</tr>
- </tbody></table>
- <table class="table_class">
- <tbody><tr>
+ <tr>
<td class="fieldTitle">
- <h1> Time. </h1>
+ <b> Location </b>
</td>
<td class="fieldData">
- <p> {{ dive.time }} </p>
+ <p> {{ dive.location }} </p>
</td>
- </tr>
- <tr>
<td class="fieldTitle">
- <h1> Air Temp. </h1>
+ <b> Water Temp. </b>
</td>
<td class="fieldData">
- <p> {{ dive.airTemp }} </p>
+ <p> {{ dive.waterTemp }} </p>
</td>
</tr>
<tr>
<td class="fieldTitle">
- <h1> Water Temp. </h1>
+ <b> Max Depth </b>
</td>
<td class="fieldData">
- <p> {{ dive.waterTemp }} </p>
+ <p> {{ dive.depth }} </p>
</td>
- </tr>
- <tr>
<td class="fieldTitle">
- <h1> Buddy </h1>
+ <b> Buddy </b>
</td>
<td class="fieldData">
<p> {{ dive.buddy }} </p>
@@ -192,34 +206,44 @@
</tr>
<tr>
<td class="fieldTitle">
- <h1> Divemaster </h1>
+ <b> Duration </b>
</td>
<td class="fieldData">
- <p> {{ dive.divemaster }} </p>
+ <p> {{ dive.duration }} </p>
</td>
- </tr>
- </tbody>
- </table>
- <table class="notes_table_class">
- <tbody>
- <tr>
<td class="fieldTitle">
- <h1> Notes </h1>
+ <b> Dive Master </b>
</td>
- </tr>
- <tr>
<td class="fieldData">
- <div class="textArea">
- <p> {{ dive.notes|safe }} </p>
- </div>
+ <p> {{ dive.divemaster }} </p>
</td>
</tr>
- </tbody>
</table>
- </div>
</div>
- </div>
- </div>
+
+ <div class="diveProfile" id="dive_{{ dive.id }}">
+ </div>
+
+ <div class="notesSection">
+ <table class="notes_table_class">
+ <tbody>
+ <tr>
+ <td class="fieldTitle">
+ <b> Notes </b>
+ </td>
+ </tr>
+ <tr>
+ <td class="fieldData">
+ <p> {{ dive.notes|safe }} </p>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div> <!-- notesSection -->
+
+ </div> <!-- innerContainer -->
+ </div> <!-- mainContainer -->
+
{% endfor %}
{% endblock %}
</div>