This commit is contained in:
Dirk Hohndel 2017-11-25 08:30:24 -08:00
commit 357dea0bcb
7 changed files with 280 additions and 108 deletions

View file

@ -15,6 +15,9 @@ Some of the changes since _Subsurface_ 4.7.4
- mobile: fix black/white switch in splash screen (#531) - mobile: fix black/white switch in splash screen (#531)
- UI: tag editing. Comma entry shows all tags (again) (#605) - UI: tag editing. Comma entry shows all tags (again) (#605)
- mobile: enable auto completion for dive site entry (#546) - 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 Some of the changes since _Subsurface_ 4.7.2

View file

@ -256,8 +256,24 @@ MainWindow::MainWindow() : QMainWindow(),
connect(geoLookup, SIGNAL(started()),information(), SLOT(disableGeoLookupEdition())); connect(geoLookup, SIGNAL(started()),information(), SLOT(disableGeoLookupEdition()));
connect(geoLookup, SIGNAL(finished()), information(), SLOT(enableGeoLookupEdition())); connect(geoLookup, SIGNAL(finished()), information(), SLOT(enableGeoLookupEdition()));
#ifndef NO_PRINTING #ifndef NO_PRINTING
// copy the bundled print templates to the user path; no overwriting occurs! // copy the bundled print templates to the user path
copyPath(getPrintingTemplatePathBundle(), getPrintingTemplatePathUser()); 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(); find_all_templates();
#endif #endif

View file

@ -60,11 +60,13 @@ void PrintOptions::setupTemplates()
int current_index = 0; int current_index = 0;
ui.printTemplate->clear(); ui.printTemplate->clear();
Q_FOREACH(const QString& theme, currList) { 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); current_index = currList.indexOf(theme);
ui.printTemplate->addItem(theme.split('.')[0], theme); ui.printTemplate->addItem(theme.split('.')[0], theme);
} }
ui.printTemplate->setCurrentIndex(current_index); ui.printTemplate->setCurrentIndex(current_index);
lastImportExportTemplate = "";
} }
// print type radio buttons // print type radio buttons
@ -121,6 +123,20 @@ void PrintOptions::on_printTemplate_currentIndexChanged(int index)
void PrintOptions::on_editButton_clicked() 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); TemplateEdit te(this, printOptions, templateOptions);
te.exec(); te.exec();
setup(); setup();
@ -128,36 +144,93 @@ void PrintOptions::on_editButton_clicked()
void PrintOptions::on_importButton_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)"); tr("HTML files") + " (*.html)");
if (filename.isEmpty()) if (filename.isEmpty())
return; return;
QFileInfo fileInfo(filename); 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(); printOptions->p_template = fileInfo.fileName();
lastImportExportTemplate = fileInfo.fileName();
find_all_templates(); find_all_templates();
setup(); setup();
} }
void PrintOptions::on_exportButton_clicked() 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)"); tr("HTML files") + " (*.html)");
if (filename.isEmpty()) if (filename.isEmpty())
return; 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() void PrintOptions::on_deleteButton_clicked()
{ {
QString templateName = getSelectedTemplate(); QString templateName = getSelectedTemplate();
QMessageBox msgBox; QMessageBox msgBox(this);
msgBox.setText(tr("This action cannot be undone!")); msgBox.setWindowTitle(tr("This action cannot be undone!"));
msgBox.setInformativeText(tr("Delete template: %1?").arg(templateName)); msgBox.setText(tr("Delete template '%1'?").arg(templateName));
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Cancel);
if (msgBox.exec() == QMessageBox::Ok) { if (msgBox.exec() == QMessageBox::Ok) {
QFile f(getPrintingTemplatePathUser() + QDir::separator() + templateName); 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(); f.remove();
find_all_templates(); find_all_templates();
setup(); setup();

View file

@ -72,6 +72,7 @@ private:
struct print_options *printOptions; struct print_options *printOptions;
struct template_options *templateOptions; struct template_options *templateOptions;
bool hasSetupSlots; bool hasSetupSlots;
QString lastImportExportTemplate;
void setupTemplates(); void setupTemplates();
private private

View file

@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
#include <QFileDevice>
#include <string> #include <string>
#include "templatelayout.h" #include "templatelayout.h"
@ -19,23 +20,74 @@ int getTotalWork(print_options *printOptions)
void find_all_templates() void find_all_templates()
{ {
const QString ext(".html");
grantlee_templates.clear(); grantlee_templates.clear();
grantlee_statistics_templates.clear(); grantlee_statistics_templates.clear();
QDir dir(getPrintingTemplatePathUser()); QDir dir(getPrintingTemplatePathUser());
QStringList list = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); QStringList list = dir.entryList(QDir::Files | QDir::NoDotAndDotDot);
foreach (const QString& filename, list) { foreach (const QString& filename, list) {
if (filename.at(filename.size() - 1) != '~') { if (filename.at(filename.size() - 1) != '~' && filename.endsWith(ext))
grantlee_templates.append(filename); grantlee_templates.append(filename);
}
} }
// find statistics templates // find statistics templates
dir.setPath(getPrintingTemplatePathUser() + QDir::separator() + "statistics"); dir.setPath(getPrintingTemplatePathUser() + QDir::separator() + "statistics");
list = dir.entryList(QDir::Files | QDir::NoDotAndDotDot); list = dir.entryList(QDir::Files | QDir::NoDotAndDotDot);
foreach (const QString& filename, list) { 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); 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
} }
} }

View file

@ -2,6 +2,7 @@
#ifndef TEMPLATELAYOUT_H #ifndef TEMPLATELAYOUT_H
#define TEMPLATELAYOUT_H #define TEMPLATELAYOUT_H
#include <QStringList>
#include <grantlee_templates.h> #include <grantlee_templates.h>
#include "mainwindow.h" #include "mainwindow.h"
#include "printoptions.h" #include "printoptions.h"
@ -12,6 +13,8 @@
int getTotalWork(print_options *printOptions); int getTotalWork(print_options *printOptions);
void find_all_templates(); 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; extern QList<QString> grantlee_templates, grantlee_statistics_templates;

View file

@ -4,19 +4,17 @@
body { body {
{{ print_options.grayscale }}; {{ print_options.grayscale }};
padding: 0; padding: 0;
margin: 0; margin: 0 0 0 6%; <!-- Provide LH margin for binding the page -->
font-size: {{ template_options.font_size }}vw; font-size: {{ template_options.font_size }}vw;
line-height: {{ template_options.line_spacing }}; line-height: {{ template_options.line_spacing }};
font-family: {{ template_options.font }}; font-family: {{ template_options.font }};
} }
h1 { h1 {
float: left;
font-size: {{ template_options.font_size }}vw; font-size: {{ template_options.font_size }}vw;
} }
p { p {
float: left;
font-size: {{ template_options.font_size }}vw; font-size: {{ template_options.font_size }}vw;
} }
@ -26,11 +24,17 @@
border-width: {{ template_options.borderwidth }}px; border-width: {{ template_options.borderwidth }}px;
border-style:solid; border-style:solid;
border-color: {{ template_options.color6 }}; border-color: {{ template_options.color6 }};
border-collapse: separate;
}
tr {
height: 4vh;
} }
td { td {
padding-left: 0.5vw; padding: 0;
padding-right: 0.5vw; margin: 0;
padding-left: 1%;
} }
#body_div { #body_div {
@ -38,70 +42,101 @@
} }
.mainContainer { .mainContainer {
width: 98%; width: 97%;
height: 100%; height: 100%;
margin-left: 1%; margin-left: 0%;
margin-right: 1%; margin-right: 1%;
margin-top: 0%; margin-top: 0%;
margin-bottom: 0%; margin-bottom: 0%;
overflow: hidden; border-width: 1px;
border-width: 0;
page-break-inside: avoid; page-break-inside: avoid;
} }
.innerContainer { .innerContainer {
width: 100%; width: 99%;
height: 99%; height: 99%;
padding-top: 1%; padding-top: 0%;
overflow: hidden;
} }
.diveDetails { .diveDetails {
width: 100%; width: 100%;
height: 98%; margin: 0.0%;
float: left;
}
.diveProfile {
width: 99%;
height: 40%;
margin: 0.5%;
} }
.dataSection { .dataSection {
width: 100%; width: 100%;
height: 40%; margin: 0.0% 0% 0% 0%;
margin: 0%; }
.diveProfile {
width: 99.5%;
height: 45%;
margin: 0.2% 0% 0.5% 0.5%;
}
.notesSection {
width: 100%;
margin: 0.0%;
min-height: 35%;
} }
.fieldTitle { .fieldTitle {
background-color: {{ template_options.color2 }}; background-color: {{ template_options.color2 }};
overflow: hidden; overflow: hidden;
color: {{ template_options.color4 }}; color: {{ template_options.color4 }};
width: 7%;
padding-left:5px;
} }
.fieldData { .fieldData {
background-color: {{ template_options.color3 }}; background-color: {{ template_options.color3 }};
color: {{ template_options.color5 }}; color: {{ template_options.color5 }};
width: 13%;
padding: o$ 1% 0% 1%;
} }
.table_class { .table_class {
float: left; margin: 0%;
margin: 0.5%; width: 100%;
width: 49%; }
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 { .notes_table_class {
overflow: hidden; overflow: hidden;
width: 99%; width: 100%;
margin: 0.5%; margin: 0.0% 0% 0% 0%;
max-height: 35%;
}
.notes_table_class td.fieldTitle {
max-height: 0.15vh;
} }
.textArea { .textArea {
line-height: {{ template_options.line_spacing }}; line-height: {{ template_options.line_spacing }};
color: {{ template_options.color5 }}; color: {{ template_options.color5 }};
max-height: 19vh; font-size: {{ template_options.font_size }}vw;
overflow: hidden; padding: 1%;
}
td.fieldTitle b {
font-size: {{ template_options.font_size }}vw;
}
.hidden_div {
display: none;
} }
</style> </style>
</head> </head>
@ -111,14 +146,17 @@
{% for dive in dives %} {% for dive in dives %}
<div class="mainContainer"> <div class="mainContainer">
<div class="innerContainer"> <div class="innerContainer">
<div class="diveDetails"> <div class="dataSection">
<div class="diveProfile" id="dive_{{ dive.id }}">
</div>
<div class="dataSection">
<table class="table_class"> <table class="table_class">
<tbody><tr> <tr>
<td class="fieldTitle"> <td class="fieldTitle">
<h1> Dive No. </h1> <b> Date </b>
</td>
<td class="fieldData">
<p> {{ dive.date }} </p>
</td>
<td class="fieldTitle">
<b> Dive No. </b>
</td> </td>
<td class="fieldData"> <td class="fieldData">
<p> {{ dive.number }} </p> <p> {{ dive.number }} </p>
@ -126,57 +164,27 @@
</tr> </tr>
<tr> <tr>
<td class="fieldTitle"> <td class="fieldTitle">
<h1> Date </h1> <b> Time </b>
</td>
<td class="fieldData">
<p> {{ dive.date }} </p>
</td>
</tr>
<tr>
<td class="fieldTitle">
<h1> Location </h1>
</td>
<td class="fieldData">
<p> {{ dive.location }} </p>
</td>
</tr>
<tr>
<td class="fieldTitle">
<h1> Max. depth </h1>
</td>
<td class="fieldData">
<p> {{ dive.depth }} </p>
</td>
</tr>
<tr>
<td class="fieldTitle">
<h1> Duration </h1>
</td>
<td class="fieldData">
<p> {{ dive.duration }} </p>
</td>
</tr>
</tbody></table>
<table class="table_class">
<tbody><tr>
<td class="fieldTitle">
<h1> Time. </h1>
</td> </td>
<td class="fieldData"> <td class="fieldData">
<p> {{ dive.time }} </p> <p> {{ dive.time }} </p>
</td> </td>
</tr>
<tr>
<td class="fieldTitle"> <td class="fieldTitle">
<h1> Air Temp. </h1> <b> Gases </b>
</td> </td>
<td class="fieldData"> <td class="fieldData">
<p> {{ dive.airTemp }} </p> <p> {{ dive.gas }} </p>
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="fieldTitle"> <td class="fieldTitle">
<h1> Water Temp. </h1> <b> Location </b>
</td>
<td class="fieldData">
<p> {{ dive.location }} </p>
</td>
<td class="fieldTitle">
<b> Water Temp. </b>
</td> </td>
<td class="fieldData"> <td class="fieldData">
<p> {{ dive.waterTemp }} </p> <p> {{ dive.waterTemp }} </p>
@ -184,7 +192,13 @@
</tr> </tr>
<tr> <tr>
<td class="fieldTitle"> <td class="fieldTitle">
<h1> Buddy </h1> <b> Max Depth </b>
</td>
<td class="fieldData">
<p> {{ dive.depth }} </p>
</td>
<td class="fieldTitle">
<b> Buddy </b>
</td> </td>
<td class="fieldData"> <td class="fieldData">
<p> {{ dive.buddy }} </p> <p> {{ dive.buddy }} </p>
@ -192,34 +206,44 @@
</tr> </tr>
<tr> <tr>
<td class="fieldTitle"> <td class="fieldTitle">
<h1> Divemaster </h1> <b> Duration </b>
</td>
<td class="fieldData">
<p> {{ dive.duration }} </p>
</td>
<td class="fieldTitle">
<b> Dive Master </b>
</td> </td>
<td class="fieldData"> <td class="fieldData">
<p> {{ dive.divemaster }} </p> <p> {{ dive.divemaster }} </p>
</td> </td>
</tr> </tr>
</tbody>
</table> </table>
<table class="notes_table_class">
<tbody>
<tr>
<td class="fieldTitle">
<h1> Notes </h1>
</td>
</tr>
<tr>
<td class="fieldData">
<div class="textArea">
<p> {{ dive.notes|safe }} </p>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</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 %} {% endfor %}
{% endblock %} {% endblock %}
</div> </div>