Huge refacoring of the settings stuff. Provides automated migration between old and new configuration file stuff. Themes are now just files in a folder instead of encoded data in the config file.

This commit is contained in:
Felix (xq) Queißner 2020-06-28 16:30:52 +02:00
parent a53e490d2e
commit 6edd9e7a12
23 changed files with 565 additions and 254 deletions

View File

@ -49,10 +49,10 @@ This document contains TODO items for planned Kristall releases as well as some
- [ ] Add an "offline mode" that only allowes cached files
- [ ] New url scheme for cached sites: kristall+cache://
- [ ] Add window that
- [ ] Folder based color scheme system
- [ ] Migrate settings-based color schemes to folder
- [x] Folder based color scheme system
- [x] Migrate settings-based color schemes to folder
- [x] Define human-readable color scheme file format
- [ ] Add per-site scheming
- [ ] Define human-readable color scheme file format
## Unspecced
- [ ] Add option: "Transient certificates survive an application reboot and are stored on disk"

View File

@ -110,7 +110,7 @@ BrowserTab::~BrowserTab()
void BrowserTab::navigateTo(const QUrl &url, PushToHistory mode)
{
if (mainWindow->protocols.isSchemeSupported(url.scheme()) != ProtocolSetup::Enabled)
if (kristall::protocols.isSchemeSupported(url.scheme()) != ProtocolSetup::Enabled)
{
QMessageBox::warning(this, "Kristall", "URI scheme not supported or disabled: " + url.scheme());
return;
@ -180,11 +180,11 @@ void BrowserTab::toggleIsFavourite(bool isFavourite)
{
if (isFavourite)
{
global_favourites.add(this->current_location);
kristall::favourites.add(this->current_location);
}
else
{
global_favourites.remove(this->current_location);
kristall::favourites.remove(this->current_location);
}
this->updateUI();
@ -425,11 +425,11 @@ void BrowserTab::on_requestComplete(const QByteArray &ref_data, const QString &m
this->outline.clear();
auto doc_style = mainWindow->current_style.derive(this->current_location);
auto doc_style = kristall::document_style.derive(this->current_location);
this->ui->text_browser->setStyleSheet(QString("QTextBrowser { background-color: %1; }").arg(doc_style.background_color.name()));
bool plaintext_only = (global_options.text_display == GenericSettings::PlainText);
bool plaintext_only = (kristall::options.text_display == GenericSettings::PlainText);
if (not plaintext_only and mime.is("text", "gemini"))
{
@ -587,7 +587,7 @@ void BrowserTab::on_redirected(const QUrl &uri, bool is_permanent)
this->network_timeout_timer.stop();
if (redirection_count >= global_options.max_redirections)
if (redirection_count >= kristall::options.max_redirections)
{
setErrorMessage(QString("Too many consecutive redirections. The last redirection would have redirected you to:\r\n%1").arg(uri.toString(QUrl::FullyEncoded)));
return;
@ -598,7 +598,7 @@ void BrowserTab::on_redirected(const QUrl &uri, bool is_permanent)
bool is_cross_host = (this->current_location.host() != uri.host());
QString question;
if(global_options.redirection_policy == GenericSettings::WarnAlways)
if(kristall::options.redirection_policy == GenericSettings::WarnAlways)
{
question = QString(
"The location you visited wants to redirect you to another location:\r\n"
@ -606,7 +606,7 @@ void BrowserTab::on_redirected(const QUrl &uri, bool is_permanent)
"Do you want to allow the redirection?"
).arg(uri.toString(QUrl::FullyEncoded));
}
else if((global_options.redirection_policy & (GenericSettings::WarnOnHostChange | GenericSettings::WarnOnSchemeChange)) and is_cross_protocol and is_cross_host)
else if((kristall::options.redirection_policy & (GenericSettings::WarnOnHostChange | GenericSettings::WarnOnSchemeChange)) and is_cross_protocol and is_cross_host)
{
question = QString(
"The location you visited wants to redirect you to another host and switch the protocol.\r\n"
@ -615,7 +615,7 @@ void BrowserTab::on_redirected(const QUrl &uri, bool is_permanent)
"Do you want to allow the redirection?"
).arg(uri.scheme()).arg(uri.host());
}
else if((global_options.redirection_policy & GenericSettings::WarnOnSchemeChange) and is_cross_protocol)
else if((kristall::options.redirection_policy & GenericSettings::WarnOnSchemeChange) and is_cross_protocol)
{
question = QString(
"The location you visited wants to switch the protocol.\r\n"
@ -623,7 +623,7 @@ void BrowserTab::on_redirected(const QUrl &uri, bool is_permanent)
"Do you want to allow the redirection?"
).arg(uri.scheme());
}
else if((global_options.redirection_policy & GenericSettings::WarnOnHostChange) and is_cross_host)
else if((kristall::options.redirection_policy & GenericSettings::WarnOnHostChange) and is_cross_host)
{
question = QString(
"The location you visited wants to redirect you to another host.\r\n"
@ -720,10 +720,10 @@ void BrowserTab::on_text_browser_anchorClicked(const QUrl &url)
}
if(this->current_location.scheme() == "gemini") {
global_gemini_trust.addTrust(this->current_location, this->current_server_certificate);
kristall::trust::gemini.addTrust(this->current_location, this->current_server_certificate);
}
else if(this->current_location.scheme() == "https") {
global_https_trust.addTrust(this->current_location, this->current_server_certificate);
kristall::trust::https.addTrust(this->current_location, this->current_server_certificate);
}
else {
assert(false and "missing protocol implementation!");
@ -745,7 +745,7 @@ void BrowserTab::on_text_browser_anchorClicked(const QUrl &url)
if (real_url.isRelative())
real_url = this->current_location.resolved(url);
auto support = mainWindow->protocols.isSchemeSupported(real_url.scheme());
auto support = kristall::protocols.isSchemeSupported(real_url.scheme());
if (support == ProtocolSetup::Enabled)
{
@ -757,7 +757,7 @@ void BrowserTab::on_text_browser_anchorClicked(const QUrl &url)
}
else
{
if (global_options.use_os_scheme_handler)
if (kristall::options.use_os_scheme_handler)
{
if (not QDesktopServices::openUrl(url))
{
@ -806,7 +806,7 @@ void BrowserTab::on_requestProgress(qint64 transferred)
emit this->fileLoaded(this->current_stats);
this->network_timeout_timer.stop();
this->network_timeout_timer.start(global_options.network_timeout);
this->network_timeout_timer.start(kristall::options.network_timeout);
}
void BrowserTab::on_back_button_clicked()
@ -830,7 +830,7 @@ void BrowserTab::updateUI()
this->ui->stop_button->setVisible(in_progress);
this->ui->fav_button->setEnabled(this->successfully_loaded);
this->ui->fav_button->setChecked(global_favourites.contains(this->current_location));
this->ui->fav_button->setChecked(kristall::favourites.contains(this->current_location));
}
bool BrowserTab::trySetClientCertificate(const QString &query)
@ -939,7 +939,7 @@ bool BrowserTab::startRequest(const QUrl &url, ProtocolHandler::RequestOptions o
}
}
else if(not this->current_identity.isValid()) {
for(auto ident_ptr : global_identities.allIdentities())
for(auto ident_ptr : kristall::identities.allIdentities())
{
if(ident_ptr->isAutomaticallyEnabledOn(url)) {
@ -969,7 +969,7 @@ bool BrowserTab::startRequest(const QUrl &url, ProtocolHandler::RequestOptions o
this->current_location = url;
this->ui->url_bar->setText(url.toString(QUrl::FormattingOptions(QUrl::FullyEncoded)));
this->network_timeout_timer.start(global_options.network_timeout);
this->network_timeout_timer.start(kristall::options.network_timeout);
return this->current_handler->startRequest(url, options);
}
@ -1016,7 +1016,7 @@ void BrowserTab::on_text_browser_customContextMenuRequested(const QPoint &pos)
});
connect(menu.addAction("Copy link"), &QAction::triggered, [real_url]() {
global_clipboard->setText(real_url.toString(QUrl::FullyEncoded));
kristall::clipboard->setText(real_url.toString(QUrl::FullyEncoded));
});
menu.addSeparator();

View File

@ -17,7 +17,7 @@ CertificateManagementDialog::CertificateManagementDialog(QWidget *parent) :
{
ui->setupUi(this);
this->ui->certificates->setModel(&global_identities);
this->ui->certificates->setModel(&identity_set);
this->ui->certificates->expandAll();
connect(
@ -34,11 +34,23 @@ CertificateManagementDialog::~CertificateManagementDialog()
delete ui;
}
IdentityCollection CertificateManagementDialog::identitySet() const
{
return this->identity_set;
}
void CertificateManagementDialog::setIdentitySet(const IdentityCollection &src)
{
this->identity_set = src;
this->ui->certificates->expandAll();
}
void CertificateManagementDialog::on_certificates_selected(QModelIndex const& index, QModelIndex const & previous)
{
Q_UNUSED(previous);
selected_identity = global_identities.getMutableIdentity(index);
selected_identity = identity_set.getMutableIdentity(index);
this->ui->export_cert_button->setEnabled(selected_identity != nullptr);
@ -70,8 +82,8 @@ void CertificateManagementDialog::on_certificates_selected(QModelIndex const& in
this->ui->cert_host_filter->setText("");
this->ui->cert_auto_enable->setChecked(false);
if(auto group_name = global_identities.group(index); not group_name.isEmpty()) {
this->ui->delete_cert_button->setEnabled(global_identities.canDeleteGroup(group_name));
if(auto group_name = identity_set.group(index); not group_name.isEmpty()) {
this->ui->delete_cert_button->setEnabled(identity_set.canDeleteGroup(group_name));
} else {
this->ui->delete_cert_button->setEnabled(false);
}
@ -97,7 +109,7 @@ void CertificateManagementDialog::on_delete_cert_button_clicked()
{
auto index = this->ui->certificates->currentIndex();
if(global_identities.getMutableIdentity(index) != nullptr)
if(identity_set.getMutableIdentity(index) != nullptr)
{
auto answer = QMessageBox::question(
this,
@ -108,11 +120,11 @@ void CertificateManagementDialog::on_delete_cert_button_clicked()
);
if(answer != QMessageBox::Yes)
return;
if(not global_identities.destroyIdentity(index)) {
if(not identity_set.destroyIdentity(index)) {
QMessageBox::warning(this, "Kristall", "Could not destroy identity!");
}
}
else if(auto group_name = global_identities.group(index); not group_name.isEmpty()) {
else if(auto group_name = identity_set.group(index); not group_name.isEmpty()) {
auto answer = QMessageBox::question(
this,
@ -122,7 +134,7 @@ void CertificateManagementDialog::on_delete_cert_button_clicked()
if(answer != QMessageBox::Yes)
return;
if(not global_identities.deleteGroup(group_name)) {
if(not identity_set.deleteGroup(group_name)) {
QMessageBox::warning(this, "Kristall", "Could not delete group!");
}
}
@ -261,7 +273,7 @@ void CertificateManagementDialog::on_import_cert_button_clicked()
return;
}
if(not global_identities.addCertificate(tr("Imported Certificates"), ident)) {
if(not identity_set.addCertificate(tr("Imported Certificates"), ident)) {
QMessageBox::warning(
this,
"Kristall",
@ -274,7 +286,7 @@ void CertificateManagementDialog::on_create_cert_button_clicked()
{
NewIdentitiyDialog dialog { this };
dialog.setGroupName(global_identities.group(this->ui->certificates->currentIndex()));
dialog.setGroupName(identity_set.group(this->ui->certificates->currentIndex()));
if(dialog.exec() != QDialog::Accepted)
return;
@ -284,7 +296,7 @@ void CertificateManagementDialog::on_create_cert_button_clicked()
return;
id.is_persistent = true;
global_identities.addCertificate(
identity_set.addCertificate(
dialog.groupName(),
id);
}

View File

@ -4,6 +4,7 @@
#include <QDialog>
#include "cryptoidentity.hpp"
#include "identitycollection.hpp"
namespace Ui {
class CertificateManagementDialog;
@ -17,6 +18,9 @@ public:
explicit CertificateManagementDialog(QWidget *parent = nullptr);
~CertificateManagementDialog();
IdentityCollection identitySet() const;
void setIdentitySet(IdentityCollection const & src);
private slots:
void on_cert_notes_textChanged();
@ -39,6 +43,8 @@ private:
private:
Ui::CertificateManagementDialog *ui;
IdentityCollection identity_set;
CryptoIdentity * selected_identity;
};

View File

@ -255,7 +255,7 @@
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>

View File

@ -16,7 +16,7 @@ CertificateSelectionDialog::CertificateSelectionDialog(QWidget *parent) :
ui->setupUi(this);
this->ui->server_request->setVisible(false);
this->ui->certificates->setModel(&global_identities);
this->ui->certificates->setModel(&kristall::identities);
this->ui->certificates->expandAll();
connect(this->ui->certificates->selectionModel(), &QItemSelectionModel::currentChanged, this, &CertificateSelectionDialog::on_currentChanged);
@ -86,7 +86,7 @@ void CertificateSelectionDialog::on_currentChanged(const QModelIndex &current, c
{
Q_UNUSED(current)
Q_UNUSED(previous)
auto id = global_identities.getIdentity(current);
auto id = kristall::identities.getIdentity(current);
this->ui->use_selected_cert->setEnabled(id.isValid());
}
@ -103,7 +103,7 @@ void CertificateSelectionDialog::on_create_new_cert_clicked()
return;
id.is_persistent = true;
global_identities.addCertificate(
kristall::identities.addCertificate(
dialog.groupName(),
id);
}
@ -111,7 +111,7 @@ void CertificateSelectionDialog::on_create_new_cert_clicked()
void CertificateSelectionDialog::on_use_selected_cert_clicked()
{
auto sel = this->ui->certificates->selectionModel()->currentIndex();
this->cryto_identity = global_identities.getIdentity(sel);
this->cryto_identity = kristall::identities.getIdentity(sel);
if(this->cryto_identity.isValid()) {
this->accept();
} else {
@ -121,7 +121,7 @@ void CertificateSelectionDialog::on_use_selected_cert_clicked()
void CertificateSelectionDialog::on_certificates_doubleClicked(const QModelIndex &index)
{
this->cryto_identity = global_identities.getIdentity(index);
this->cryto_identity = kristall::identities.getIdentity(index);
if(this->cryto_identity.isValid()) {
this->accept();
} else {

View File

@ -19,7 +19,7 @@ NewIdentitiyDialog::NewIdentitiyDialog(QWidget *parent) :
ui->expiration_date->setTime(QTime(12, 00));
ui->group->clear();
for(auto group_name : global_identities.groups())
for(auto group_name : kristall::identities.groups())
{
ui->group->addItem(group_name);
}

View File

@ -36,30 +36,29 @@ SettingsDialog::SettingsDialog(QWidget *parent) :
setGeminiStyle(DocumentStyle { });
int items = global_settings.beginReadArray("Themes");
this->predefined_styles.clear();
for(int i = 0; i < items; i++)
for(auto const & fileName : kristall::dirs::styles.entryList())
{
global_settings.setArrayIndex(i);
QSettings style_sheet {
kristall::dirs::styles.absoluteFilePath(fileName),
QSettings::IniFormat
};
QString name = global_settings.value("name").toString();
QString name = style_sheet.value("name").toString();
DocumentStyle style;
style.load(global_settings);
style.load(style_sheet);
this->predefined_styles.insert(name, style);
}
global_settings.endArray();
this->ui->presets->clear();
for(auto const & style_name : this->predefined_styles.keys())
{
this->ui->presets->addItem(style_name);
}
if(items > 0) {
if(this->predefined_styles.size() > 0) {
on_presets_currentIndexChanged(0);
} else {
this->on_presets_currentIndexChanged(-1);
@ -461,19 +460,28 @@ void SettingsDialog::on_preset_load_clicked()
void SettingsDialog::on_SettingsDialog_accepted()
{
global_settings.beginWriteArray("Themes", this->predefined_styles.size());
QStringList files = kristall::dirs::styles.entryList();
int index = 0;
for(auto const & style_name : this->predefined_styles.keys())
{
global_settings.setArrayIndex(index);
QString fileName = DocumentStyle::createFileNameFromName(style_name, 0);
files.removeAll(fileName);
global_settings.setValue("name", style_name);
this->predefined_styles.value(style_name).save(global_settings);
QSettings style_sheet {
kristall::dirs::styles.absoluteFilePath(fileName),
QSettings::IniFormat
};
index += 1;
style_sheet.setValue("name", style_name);
this->predefined_styles.value(style_name).save(style_sheet);
style_sheet.sync();
}
for(auto const & fileName : files)
{
kristall::dirs::styles.remove(fileName);
}
global_settings.endArray();
}
void SettingsDialog::on_preset_import_clicked()

View File

@ -7,6 +7,7 @@
#include <QCryptographicHash>
#include <QDebug>
#include <cctype>
#include <array>
#include <cmath>
@ -115,24 +116,24 @@ static QString encodeCssFont (const QFont& refFont)
}
DocumentStyle::DocumentStyle() : theme(Fixed),
standard_font(),
h1_font(),
h2_font(),
h3_font(),
preformatted_font(),
background_color("#edefff"),
standard_color(0x00, 0x00, 0x00),
preformatted_color(0x00, 0x00, 0x00),
h1_color("#022f90"),
h2_color("#022f90"),
h3_color("#022f90"),
blockquote_color("#FFFFFF"),
internal_link_color("#0e8fff"),
external_link_color("#0e8fff"),
cross_scheme_link_color("#0960a7"),
internal_link_prefix(""),
external_link_prefix(""),
margin(55.0)
standard_font(),
h1_font(),
h2_font(),
h3_font(),
preformatted_font(),
background_color("#edefff"),
standard_color(0x00, 0x00, 0x00),
preformatted_color(0x00, 0x00, 0x00),
h1_color("#022f90"),
h2_color("#022f90"),
h3_color("#022f90"),
blockquote_color("#FFFFFF"),
internal_link_color("#0e8fff"),
external_link_color("#0e8fff"),
cross_scheme_link_color("#0960a7"),
internal_link_prefix(""),
external_link_prefix(""),
margin(55.0)
{
preformatted_font.setFamily("monospace");
preformatted_font.setPointSizeF(10.0);
@ -153,61 +154,174 @@ DocumentStyle::DocumentStyle() : theme(Fixed),
h3_font.setPointSizeF(12.0);
}
QString DocumentStyle::createFileNameFromName(const QString &src, int index)
{
QString result;
result.reserve(src.size() + 5);
for(int i = 0; i < src.size(); i++)
{
QChar c = src.at(i);
if(c.isLetterOrNumber()) {
result.append(c.toLower());
}
else if(c.isSpace()) {
result.append('-');
}
else {
result.append(QString("").arg(c.unicode()));
}
}
if(index > 0) {
result.append(QString("-%1").arg(index));
}
result.append(".kthm");
return result;
}
bool DocumentStyle::save(QSettings &settings) const
{
settings.setValue("standard_font", standard_font.toString());
settings.setValue("h1_font", h1_font.toString());
settings.setValue("h2_font", h2_font.toString());
settings.setValue("h3_font", h3_font.toString());
settings.setValue("preformatted_font", preformatted_font.toString());
settings.setValue("version", 1);
settings.setValue("theme", int(theme));
settings.setValue("background_color", background_color.name());
settings.setValue("standard_color", standard_color.name());
settings.setValue("preformatted_color", preformatted_color.name());
settings.setValue("blockquote_color", blockquote_color.name());
settings.setValue("h1_color", h1_color.name());
settings.setValue("h2_color", h2_color.name());
settings.setValue("h3_color", h3_color.name());
settings.setValue("internal_link_color", internal_link_color.name());
settings.setValue("external_link_color", external_link_color.name());
settings.setValue("cross_scheme_link_color", cross_scheme_link_color.name());
settings.setValue("internal_link_prefix", internal_link_prefix);
settings.setValue("external_link_prefix", external_link_prefix);
settings.setValue("margins", margin);
settings.setValue("theme", int(theme));
{
settings.beginGroup("Standard");
settings.setValue("font", standard_font.toString());
settings.setValue("color", standard_color.name());
settings.endGroup();
}
{
settings.beginGroup("Preformatted");
settings.setValue("font", preformatted_font.toString());
settings.setValue("color", preformatted_color.name());
settings.endGroup();
}
{
settings.beginGroup("H1");
settings.setValue("font", h1_font.toString());
settings.setValue("color", h1_color.name());
settings.endGroup();
}
{
settings.beginGroup("H2");
settings.setValue("font", h2_font.toString());
settings.setValue("color", h2_color.name());
settings.endGroup();
}
{
settings.beginGroup("H3");
settings.setValue("font", h3_font.toString());
settings.setValue("color", h3_color.name());
settings.endGroup();
}
{
settings.beginGroup("Link");
settings.setValue("color_internal", internal_link_color.name());
settings.setValue("color_external", external_link_color.name());
settings.setValue("color_cross_scheme", cross_scheme_link_color.name());
settings.setValue("internal_prefix", internal_link_prefix);
settings.setValue("external_prefix", external_link_prefix);
settings.endGroup();
}
return true;
}
bool DocumentStyle::load(QSettings &settings)
{
if(settings.contains("standard_color"))
switch(settings.value("version", 0).toInt())
{
standard_font.fromString(settings.value("standard_font").toString());
h1_font.fromString(settings.value("h1_font").toString());
h2_font.fromString(settings.value("h2_font").toString());
h3_font.fromString(settings.value("h3_font").toString());
preformatted_font.fromString(settings.value("preformatted_font").toString());
case 0: {
if(settings.contains("standard_color"))
{
standard_font.fromString(settings.value("standard_font").toString());
h1_font.fromString(settings.value("h1_font").toString());
h2_font.fromString(settings.value("h2_font").toString());
h3_font.fromString(settings.value("h3_font").toString());
preformatted_font.fromString(settings.value("preformatted_font").toString());
background_color = QColor(settings.value("background_color").toString());
standard_color = QColor(settings.value("standard_color").toString());
preformatted_color = QColor(settings.value("preformatted_color").toString());
blockquote_color = QColor(settings.value("blockquote_color").toString());
h1_color = QColor(settings.value("h1_color").toString());
h2_color = QColor(settings.value("h2_color").toString());
h3_color = QColor(settings.value("h3_color").toString());
internal_link_color = QColor(settings.value("internal_link_color").toString());
external_link_color = QColor(settings.value("external_link_color").toString());
cross_scheme_link_color = QColor(settings.value("cross_scheme_link_color").toString());
background_color = QColor(settings.value("background_color").toString());
standard_color = QColor(settings.value("standard_color").toString());
preformatted_color = QColor(settings.value("preformatted_color").toString());
blockquote_color = QColor(settings.value("blockquote_color").toString());
h1_color = QColor(settings.value("h1_color").toString());
h2_color = QColor(settings.value("h2_color").toString());
h3_color = QColor(settings.value("h3_color").toString());
internal_link_color = QColor(settings.value("internal_link_color").toString());
external_link_color = QColor(settings.value("external_link_color").toString());
cross_scheme_link_color = QColor(settings.value("cross_scheme_link_color").toString());
internal_link_prefix = settings.value("internal_link_prefix").toString();
external_link_prefix = settings.value("external_link_prefix").toString();
internal_link_prefix = settings.value("internal_link_prefix").toString();
external_link_prefix = settings.value("external_link_prefix").toString();
margin = settings.value("margins").toDouble();
theme = Theme(settings.value("theme").toInt());
margin = settings.value("margins").toDouble();
theme = Theme(settings.value("theme").toInt());
}
return true;
}
return true;
case 1: {
theme = Theme(settings.value("theme", int(theme)).toInt());
background_color = QColor { settings.value("background_color", background_color.name()).toString() };
blockquote_color = QColor { settings.value("blockquote_color", blockquote_color.name()).toString() };
margin = settings.value("margins", 55).toInt();
{
settings.beginGroup("Standard");
standard_font.fromString(settings.value("font", standard_font.toString()).toString());
standard_color = QString { settings.value("color", standard_color.name()).toString() };
settings.endGroup();
}
{
settings.beginGroup("Preformatted");
preformatted_font.fromString(settings.value("font", preformatted_font.toString()).toString());
preformatted_color = QString { settings.value("color", preformatted_color.name()).toString() };
settings.endGroup();
}
{
settings.beginGroup("H1");
h1_font.fromString(settings.value("font", h1_font.toString()).toString());
h1_color = QString { settings.value("color", h1_color.name()).toString() };
settings.endGroup();
}
{
settings.beginGroup("H2");
h2_font.fromString(settings.value("font", h2_font.toString()).toString());
h2_color = QString { settings.value("color", h2_color.name()).toString() };
settings.endGroup();
}
{
settings.beginGroup("H3");
h3_font.fromString(settings.value("font", h3_font.toString()).toString());
h3_color = QString { settings.value("color", h3_color.name()).toString() };
settings.endGroup();
}
{
settings.beginGroup("Link");
internal_link_color = QString { settings.value("color_internal", internal_link_color.name()).toString() };
external_link_color = QString {settings.value("color_external", external_link_color.name()).toString() };
cross_scheme_link_color = QString {settings.value("color_cross_scheme", cross_scheme_link_color.name()).toString() };
internal_link_prefix = settings.value("internal_prefix", internal_link_prefix).toString();
external_link_prefix = settings.value("external_prefix", external_link_prefix).toString();
settings.endGroup();
}
return true;
}
}
return false;
}
DocumentStyle DocumentStyle::derive(const QUrl &url) const

View File

@ -16,6 +16,9 @@ struct DocumentStyle
DocumentStyle();
//! Calculates a filtered/legal file name with all non-allowed chars escaped
static QString createFileNameFromName(QString const & src, int index);
Theme theme;
QFont standard_font;

View File

@ -13,6 +13,69 @@ IdentityCollection::IdentityCollection(QObject *parent)
}
IdentityCollection::IdentityCollection(const IdentityCollection &other)
{
for(auto const & grp : other.root.children)
{
auto const & src_group = grp->as<GroupNode>();
auto dst_group = std::make_unique<GroupNode>();
dst_group->title = src_group.title;
for(auto const & id : src_group.children) {
auto const & src_id = id->as<IdentityNode>();
auto dst_id = std::make_unique<IdentityNode>();
dst_id->identity = src_id.identity;
dst_group->children.emplace_back(std::move(dst_id));
}
root.children.emplace_back(std::move(dst_group));
}
relayout();
}
IdentityCollection &IdentityCollection::operator=(const IdentityCollection & other)
{
beginResetModel();
root.children.clear();
for(auto const & grp : other.root.children)
{
auto const & src_group = grp->as<GroupNode>();
auto dst_group = std::make_unique<GroupNode>();
dst_group->title = src_group.title;
for(auto const & id : src_group.children) {
auto const & src_id = id->as<IdentityNode>();
auto dst_id = std::make_unique<IdentityNode>();
dst_id->identity = src_id.identity;
dst_group->children.emplace_back(std::move(dst_id));
}
root.children.emplace_back(std::move(dst_group));
}
this->relayout();
endResetModel();
return *this;
}
IdentityCollection &IdentityCollection::operator=(IdentityCollection && other)
{
beginResetModel();
this->root.children = std::move(other.root.children);
this->relayout();
endResetModel();
return *this;
}
void IdentityCollection::load(QSettings &settings)
{
this->beginResetModel();

View File

@ -46,6 +46,11 @@ class IdentityCollection : public QAbstractItemModel
public:
explicit IdentityCollection(QObject *parent = nullptr);
IdentityCollection(IdentityCollection const & other);
IdentityCollection & operator=(IdentityCollection const &);
IdentityCollection & operator=(IdentityCollection &&);
public:
void load(QSettings & settings);

View File

@ -9,6 +9,8 @@
#include "identitycollection.hpp"
#include "ssltrust.hpp"
#include "favouritecollection.hpp"
#include "protocolsetup.hpp"
#include "documentstyle.hpp"
enum class Theme : int
{
@ -52,14 +54,6 @@ struct GenericSettings
//! `:`-separated SHA256 hash
QString toFingerprintString(QSslCertificate const & certificate);
extern QSettings global_settings;
extern IdentityCollection global_identities;
extern QClipboard * global_clipboard;
extern SslTrust global_gemini_trust;
extern SslTrust global_https_trust;
extern FavouriteCollection global_favourites;
extern GenericSettings global_options;
///
/// Kristall directory structure:
/// ~/.cache/kristall/
@ -72,11 +66,36 @@ extern GenericSettings global_options;
///
namespace kristall
{
extern QDir config_root;
extern QDir cache_root;
extern QDir offline_pages;
extern QDir themes;
extern QDir styles;
extern ProtocolSetup protocols;
extern QSettings * settings;
extern IdentityCollection identities;
extern QClipboard * clipboard;
extern FavouriteCollection favourites;
extern GenericSettings options;
extern DocumentStyle document_style;
namespace trust {
extern SslTrust gemini;
extern SslTrust https;
}
namespace dirs {
extern QDir config_root;
extern QDir cache_root;
//! Contains files per host
extern QDir offline_pages;
//! Contains custom UI themes for kristall
extern QDir themes;
//! Contains custom document styles / presets
extern QDir styles;
}
void saveSettings();
}
#endif // KRISTALL_HPP

View File

@ -6,31 +6,32 @@
#include <QSettings>
#include <QCommandLineParser>
#include <QDebug>
#include <QStandardPaths>
#include <cassert>
IdentityCollection global_identities;
QSettings global_settings { "xqTechnologies", "Kristall" };
QClipboard * global_clipboard;
SslTrust global_gemini_trust;
SslTrust global_https_trust;
FavouriteCollection global_favourites;
GenericSettings global_options;
ProtocolSetup kristall::protocols;
IdentityCollection kristall::identities;
QSettings * kristall::settings;
QClipboard * kristall::clipboard;
SslTrust kristall::trust::gemini;
SslTrust kristall::trust::https;
FavouriteCollection kristall::favourites;
GenericSettings kristall::options;
DocumentStyle kristall::document_style;
namespace kristall
{
QDir config_root;
QDir cache_root;
QDir offline_pages;
QDir themes;
QDir styles;
}
QDir kristall::dirs::config_root;
QDir kristall::dirs::cache_root;
QDir kristall::dirs::offline_pages;
QDir kristall::dirs::themes;
QDir kristall::dirs::styles;
QString toFingerprintString(QSslCertificate const & certificate)
{
return QCryptographicHash::hash(certificate.toDer(), QCryptographicHash::Sha256).toHex(':');
}
static QSettings * app_settings_ptr;
#define SSTR(X) STR(X)
#define STR(X) #X
@ -53,7 +54,7 @@ int main(int argc, char *argv[])
QApplication app(argc, argv);
app.setApplicationVersion(SSTR(KRISTALL_VERSION));
global_clipboard = app.clipboard();
kristall::clipboard = app.clipboard();
QCommandLineParser cli_parser;
cli_parser.addVersionOption();
@ -62,33 +63,119 @@ int main(int argc, char *argv[])
cli_parser.process(app);
QString cache_root = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
QString config_root = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
kristall::config_root = QDir { config_root };
kristall::cache_root = QDir { cache_root };
kristall::dirs::config_root = QDir { config_root };
kristall::dirs::cache_root = QDir { cache_root };
kristall::offline_pages = derive_dir(kristall::cache_root, "offline-pages");
kristall::themes = derive_dir(kristall::config_root, "themes");
kristall::styles = derive_dir(kristall::config_root, "styles");
kristall::dirs::offline_pages = derive_dir(kristall::dirs::cache_root, "offline-pages");
kristall::dirs::themes = derive_dir(kristall::dirs::config_root, "themes");
global_options.load(global_settings);
kristall::dirs::styles = derive_dir(kristall::dirs::config_root, "styles");
kristall::dirs::styles.setNameFilters(QStringList { "*.kthm" });
kristall::dirs::styles.setFilter(QDir::Files);
global_settings.beginGroup("Client Identities");
global_identities.load(global_settings);
global_settings.endGroup();
QSettings app_settings {
kristall::dirs::config_root.absoluteFilePath("config.ini"),
QSettings::IniFormat
};
app_settings_ptr = &app_settings;
global_settings.beginGroup("Trusted Servers");
global_gemini_trust.load(global_settings);
global_settings.endGroup();
{
QSettings deprecated_settings { "xqTechnologies", "Kristall" };
if(QFile(deprecated_settings.fileName()).exists())
{
if(deprecated_settings.value("deprecated", false) == false)
{
qDebug() << "Migrating to new configuration style.";
for(auto const & key : deprecated_settings.allKeys())
{
app_settings.setValue(key, deprecated_settings.value(key));
}
global_settings.beginGroup("Trusted HTTPS Servers");
global_https_trust.load(global_settings);
global_settings.endGroup();
// Migrate themes to new model
{
int items = deprecated_settings.beginReadArray("Themes");
global_favourites.load(global_settings);
for(int i = 0; i < items; i++)
{
deprecated_settings.setArrayIndex(i);
QString name = deprecated_settings.value("name").toString();
DocumentStyle style;
style.load(deprecated_settings);
QString fileName;
int index = 0;
do {
fileName = DocumentStyle::createFileNameFromName(name, index);
index += 1;
} while(kristall::dirs::styles.exists(fileName));
QSettings style_sheet {
kristall::dirs::styles.absoluteFilePath(fileName),
QSettings::IniFormat
};
style_sheet.setValue("name", name);
style.save(style_sheet);
style_sheet.sync();
}
deprecated_settings.endArray();
}
// Remove old theming stuff
app_settings.remove("Theme");
app_settings.remove("Themes");
// Migrate "current theme" to new format
{
DocumentStyle current_style;
deprecated_settings.beginGroup("Theme");
current_style.load(deprecated_settings);
deprecated_settings.endGroup();
app_settings.beginGroup("Theme");
current_style.save(app_settings);
app_settings.endGroup();
}
deprecated_settings.setValue("deprecated", true);
}
else
{
qDebug() << "Migration complete. Please delete" << deprecated_settings.fileName();
}
}
}
kristall::settings = &app_settings;
kristall::options.load(app_settings);
app_settings.beginGroup("Protocols");
kristall::protocols.load(app_settings);
app_settings.endGroup();
app_settings.beginGroup("Client Identities");
kristall::identities.load(app_settings);
app_settings.endGroup();
app_settings.beginGroup("Trusted Servers");
kristall::trust::gemini.load(app_settings);
app_settings.endGroup();
app_settings.beginGroup("Trusted HTTPS Servers");
kristall::trust::https.load(app_settings);
app_settings.endGroup();
app_settings.beginGroup("Theme");
kristall::document_style.load(app_settings);
app_settings.endGroup();
kristall::favourites.load(app_settings);
MainWindow w(&app);
@ -106,9 +193,29 @@ int main(int argc, char *argv[])
else {
w.addEmptyTab(true, true);
}
app_settings.beginGroup("Window State");
if(app_settings.contains("geometry")) {
w.restoreGeometry(app_settings.value("geometry").toByteArray());
}
if(app_settings.contains("state")) {
w.restoreState(app_settings.value("state").toByteArray());
}
app_settings.endGroup();
w.show();
return app.exec();
int exit_code = app.exec();
app_settings.beginGroup("Window State");
app_settings.setValue("geometry", w.saveGeometry());
app_settings.setValue("state", w.saveState());
app_settings.endGroup();
kristall::saveSettings();
return exit_code;
}
void GenericSettings::load(QSettings &settings)
@ -161,3 +268,36 @@ void GenericSettings::save(QSettings &settings) const
settings.setValue("redirection_policy", int(redirection_policy));
settings.setValue("network_timeout", network_timeout);
}
void kristall::saveSettings()
{
assert(app_settings_ptr != nullptr);
QSettings & app_settings = *app_settings_ptr;
kristall::favourites.save(app_settings);
app_settings.beginGroup("Protocols");
kristall::protocols.save(app_settings);
app_settings.endGroup();
app_settings.beginGroup("Client Identities");
kristall::identities.save(app_settings);
app_settings.endGroup();
app_settings.beginGroup("Trusted Servers");
kristall::trust::gemini.save(app_settings);
app_settings.endGroup();
app_settings.beginGroup("Trusted HTTPS Servers");
kristall::trust::https.save(app_settings);
app_settings.endGroup();
app_settings.beginGroup("Theme");
kristall::document_style.save(app_settings);
app_settings.endGroup();
kristall::options.save(app_settings);
app_settings.sync();
}

View File

@ -33,13 +33,7 @@ MainWindow::MainWindow(QApplication * app, QWidget *parent) :
this->statusBar()->addPermanentWidget(this->file_size);
this->statusBar()->addPermanentWidget(this->load_time);
this->protocols.load(global_settings);
global_settings.beginGroup("Theme");
this->current_style.load(global_settings);
global_settings.endGroup();
ui->favourites_view->setModel(&global_favourites);
ui->favourites_view->setModel(&kristall::favourites);
this->ui->outline_window->setVisible(false);
this->ui->history_window->setVisible(false);
@ -58,7 +52,7 @@ MainWindow::MainWindow(QApplication * app, QWidget *parent) :
connect(this->ui->menuNavigation, &QMenu::aboutToShow, [this]() {
BrowserTab * tab = qobject_cast<BrowserTab*>(this->ui->browser_tabs->currentWidget());
if(tab != nullptr) {
ui->actionAdd_to_favourites->setChecked(global_favourites.contains(tab->current_location));
ui->actionAdd_to_favourites->setChecked(kristall::favourites.contains(tab->current_location));
}
});
@ -75,17 +69,6 @@ MainWindow::MainWindow(QApplication * app, QWidget *parent) :
connect(sc, &QShortcut::activated, this, &MainWindow::on_focus_inputbar);
}
{
global_settings.beginGroup("Window State");
if(global_settings.contains("geometry")) {
restoreGeometry(global_settings.value("geometry").toByteArray());
}
if(global_settings.contains("state")) {
restoreState(global_settings.value("state").toByteArray());
}
global_settings.endGroup();
}
this->ui->favourites_view->setContextMenuPolicy(Qt::CustomContextMenu);
this->ui->history_view->setContextMenuPolicy(Qt::CustomContextMenu);
@ -94,7 +77,6 @@ MainWindow::MainWindow(QApplication * app, QWidget *parent) :
MainWindow::~MainWindow()
{
this->saveSettings();
delete ui;
}
@ -112,7 +94,7 @@ BrowserTab * MainWindow::addEmptyTab(bool focus_new, bool load_default)
}
if(load_default) {
tab->navigateTo(QUrl(global_options.start_page), BrowserTab::PushImmediate);
tab->navigateTo(QUrl(kristall::options.start_page), BrowserTab::PushImmediate);
} else {
tab->navigateTo(QUrl("about:blank"), BrowserTab::DontPush);
}
@ -141,41 +123,6 @@ void MainWindow::setUrlPreview(const QUrl &url)
}
}
void MainWindow::saveSettings()
{
global_favourites.save(global_settings);
this->protocols.save(global_settings);
global_settings.beginGroup("Client Identities");
global_identities.save(global_settings);
global_settings.endGroup();
global_settings.beginGroup("Trusted Servers");
global_gemini_trust.save(global_settings);
global_settings.endGroup();
global_settings.beginGroup("Trusted HTTPS Servers");
global_https_trust.save(global_settings);
global_settings.endGroup();
global_settings.beginGroup("Theme");
this->current_style.save(global_settings);
global_settings.endGroup();
{
global_settings.beginGroup("Window State");
global_settings.setValue("geometry", saveGeometry());
global_settings.setValue("state", saveState());
global_settings.endGroup();
}
global_options.save(global_settings);
global_settings.sync();
}
void MainWindow::on_browser_tabs_currentChanged(int index)
{
if(index >= 0) {
@ -202,7 +149,7 @@ void MainWindow::on_browser_tabs_currentChanged(int index)
void MainWindow::on_favourites_view_doubleClicked(const QModelIndex &index)
{
if(auto url = global_favourites.get(index); url.isValid()) {
if(auto url = kristall::favourites.get(index); url.isValid()) {
this->addNewTab(true, url);
}
}
@ -256,23 +203,23 @@ void MainWindow::on_actionSettings_triggered()
{
SettingsDialog dialog;
dialog.setGeminiStyle(this->current_style);
dialog.setProtocols(this->protocols);
dialog.setOptions(global_options);
dialog.setGeminiSslTrust(global_gemini_trust);
dialog.setHttpsSslTrust(global_https_trust);
dialog.setGeminiStyle(kristall::document_style);
dialog.setProtocols(kristall::protocols);
dialog.setOptions(kristall::options);
dialog.setGeminiSslTrust(kristall::trust::gemini);
dialog.setHttpsSslTrust(kristall::trust::https);
if(dialog.exec() != QDialog::Accepted)
return;
global_gemini_trust = dialog.geminiSslTrust();
global_https_trust = dialog.httpsSslTrust();
global_options = dialog.options();
kristall::trust::gemini = dialog.geminiSslTrust();
kristall::trust::https = dialog.httpsSslTrust();
kristall::options = dialog.options();
this->protocols = dialog.protocols();
this->current_style = dialog.geminiStyle();
kristall::protocols = dialog.protocols();
kristall::document_style = dialog.geminiStyle();
this->saveSettings();
kristall::saveSettings();
this->reloadTheme();
}
@ -338,12 +285,12 @@ void MainWindow::on_actionAbout_Qt_triggered()
void MainWindow::reloadTheme()
{
if(global_options.theme == Theme::os_default)
if(kristall::options.theme == Theme::os_default)
{
application->setStyleSheet("");
QIcon::setThemeName("light");
}
if(global_options.theme == Theme::light)
if(kristall::options.theme == Theme::light)
{
QFile file(":/light.qss");
file.open(QFile::ReadOnly | QFile::Text);
@ -352,7 +299,7 @@ void MainWindow::reloadTheme()
QIcon::setThemeName("light");
}
else if(global_options.theme == Theme::dark)
else if(kristall::options.theme == Theme::dark)
{
QFile file(":/dark.qss");
file.open(QFile::ReadOnly | QFile::Text);
@ -406,7 +353,7 @@ void MainWindow::on_actionGo_to_home_triggered()
{
BrowserTab * tab = qobject_cast<BrowserTab*>(this->ui->browser_tabs->currentWidget());
if(tab != nullptr) {
tab->navigateTo(QUrl(global_options.start_page), BrowserTab::PushImmediate);
tab->navigateTo(QUrl(kristall::options.start_page), BrowserTab::PushImmediate);
}
}
@ -470,7 +417,7 @@ void MainWindow::on_history_view_customContextMenuRequested(const QPoint &pos)
void MainWindow::on_favourites_view_customContextMenuRequested(const QPoint &pos)
{
if(auto idx = this->ui->favourites_view->indexAt(pos); idx.isValid()) {
if(QUrl url = global_favourites.get(idx); url.isValid()) {
if(QUrl url = kristall::favourites.get(idx); url.isValid()) {
QMenu menu;
BrowserTab * tab = qobject_cast<BrowserTab*>(this->ui->browser_tabs->currentWidget());
@ -498,7 +445,11 @@ void MainWindow::on_actionManage_Certificates_triggered()
{
CertificateManagementDialog dialog { this };
dialog.exec();
dialog.setIdentitySet(kristall::identities);
if(dialog.exec() != QDialog::Accepted)
return;
this->saveSettings();
kristall::identities = dialog.identitySet();
kristall::saveSettings();
}

View File

@ -8,7 +8,6 @@
#include "favouritecollection.hpp"
#include "renderers/geminirenderer.hpp"
#include "protocolsetup.hpp"
#include "widgets/elidelabel.hpp"
@ -33,8 +32,6 @@ public:
void setUrlPreview(QUrl const & url);
void saveSettings();
private slots:
void on_browser_tabs_currentChanged(int index);
@ -98,9 +95,6 @@ private:
public:
QApplication * application;
DocumentStyle current_style;
ProtocolSetup protocols;
private:
Ui::MainWindow *ui;

View File

@ -28,7 +28,7 @@ bool AboutHandler::startRequest(const QUrl &url, ProtocolHandler::RequestOptions
document.append("# Favourites\n");
document.append("\n");
for (auto const &fav : global_favourites.getAll())
for (auto const &fav : kristall::favourites.getAll())
{
document.append("=> " + fav.toString().toUtf8() + "\n");
}

View File

@ -51,7 +51,7 @@ bool GeminiClient::startRequest(const QUrl &url, RequestOptions options)
QSslConfiguration ssl_config = socket.sslConfiguration();
ssl_config.setProtocol(QSsl::TlsV1_2);
if(not global_gemini_trust.enable_ca)
if(not kristall::trust::gemini.enable_ca)
ssl_config.setCaCertificates(QList<QSslCertificate> { });
else
ssl_config.setCaCertificates(QSslConfiguration::systemCaCertificates());
@ -316,7 +316,7 @@ void GeminiClient::sslErrors(QList<QSslError> const & errors)
bool ignore = false;
if(SslTrust::isTrustRelated(err.error()))
{
switch(global_gemini_trust.getTrust(target_url, socket.peerCertificate()))
switch(kristall::trust::gemini.getTrust(target_url, socket.peerCertificate()))
{
case SslTrust::Trusted:
ignore = true;

View File

@ -36,7 +36,7 @@ bool WebClient::startRequest(const QUrl &url, RequestOptions options)
auto ssl_config = request.sslConfiguration();
// ssl_config.setProtocol(QSsl::TlsV1_2);
if(global_https_trust.enable_ca)
if(kristall::trust::https.enable_ca)
ssl_config.setCaCertificates(QSslConfiguration::systemCaCertificates());
else
ssl_config.setCaCertificates(QList<QSslCertificate> { });
@ -182,7 +182,7 @@ void WebClient::on_sslErrors(const QList<QSslError> &errors)
if(SslTrust::isTrustRelated(err.error()))
{
auto cert = this->current_reply->sslConfiguration().peerCertificate();
switch(global_https_trust.getTrust(this->current_reply->url(), cert))
switch(kristall::trust::https.getTrust(this->current_reply->url(), cert))
{
case SslTrust::Trusted:
ignore = true;

View File

@ -11,20 +11,16 @@ ProtocolSetup::ProtocolSetup()
void ProtocolSetup::save(QSettings &settings) const
{
settings.beginGroup("Protocols");
#define MAC(X) settings.setValue(#X, this->X);
PROTOCOLS(MAC)
#undef MAC
settings.endGroup();
}
void ProtocolSetup::load(QSettings &settings)
{
settings.beginGroup("Protocols");
#define MAC(X) if(settings.contains(#X)) this->X = settings.value(#X).toBool();
PROTOCOLS(MAC)
#undef MAC
settings.endGroup();
}
ProtocolSetup::ProtocolSupport ProtocolSetup::isSchemeSupported(QString const & _scheme) const

View File

@ -38,7 +38,7 @@ std::unique_ptr<GeminiDocument> GeminiRenderer::render(
result->background_color = themed_style.background_color;
result->setIndentWidth(20);
bool emit_fancy_text = global_options.enable_text_decoration;
bool emit_fancy_text = kristall::options.enable_text_decoration;
QTextCursor cursor{result.get()};

View File

@ -26,7 +26,7 @@ std::unique_ptr<QTextDocument> GophermapRenderer::render(const QByteArray &input
external_link.setFont(themed_style.standard_font);
external_link.setForeground(QBrush(themed_style.external_link_color));
bool emit_text_only = (global_options.gophermap_display == GenericSettings::PlainText);
bool emit_text_only = (kristall::options.gophermap_display == GenericSettings::PlainText);
std::unique_ptr<QTextDocument> result = std::make_unique<QTextDocument>();
result->setDocumentMargin(themed_style.margin);

View File

@ -7,7 +7,7 @@
SearchBar::SearchBar(QWidget *parent) : QLineEdit(parent)
{
QCompleter *completer = new QCompleter(this);
completer->setModel(&global_favourites);
completer->setModel(&kristall::favourites);
completer->setCaseSensitivity(Qt::CaseInsensitive);
completer->setCompletionRole(Qt::DisplayRole);
this->setCompleter(completer);