aboutsummaryrefslogtreecommitdiff
path: root/src/dialogs
diff options
context:
space:
mode:
authorFelix (xq) Queißner <git@mq32.de>2020-06-22 21:10:04 +0200
committerFelix (xq) Queißner <git@mq32.de>2020-06-22 21:10:04 +0200
commit75ec461eeaa851cb5c53f4cfffc434e3e529ed1d (patch)
tree3944737340718ca3675381aa06636045d397e780 /src/dialogs
parent8dbfb0890560fd1cd698d06fa05ac868c4db8576 (diff)
downloadkristall-75ec461eeaa851cb5c53f4cfffc434e3e529ed1d.tar.gz
Restructures the project source and cleans up a bit
Diffstat (limited to 'src/dialogs')
-rw-r--r--src/dialogs/certificateiodialog.cpp126
-rw-r--r--src/dialogs/certificateiodialog.hpp50
-rw-r--r--src/dialogs/certificateiodialog.ui127
-rw-r--r--src/dialogs/certificatemanagementdialog.cpp308
-rw-r--r--src/dialogs/certificatemanagementdialog.hpp45
-rw-r--r--src/dialogs/certificatemanagementdialog.ui294
-rw-r--r--src/dialogs/certificateselectiondialog.cpp130
-rw-r--r--src/dialogs/certificateselectiondialog.hpp54
-rw-r--r--src/dialogs/certificateselectiondialog.ui172
-rw-r--r--src/dialogs/newidentitiydialog.cpp81
-rw-r--r--src/dialogs/newidentitiydialog.hpp41
-rw-r--r--src/dialogs/newidentitiydialog.ui118
-rw-r--r--src/dialogs/settingsdialog.cpp613
-rw-r--r--src/dialogs/settingsdialog.hpp143
-rw-r--r--src/dialogs/settingsdialog.ui940
15 files changed, 3242 insertions, 0 deletions
diff --git a/src/dialogs/certificateiodialog.cpp b/src/dialogs/certificateiodialog.cpp
new file mode 100644
index 0000000..8c9fcd5
--- /dev/null
+++ b/src/dialogs/certificateiodialog.cpp
@@ -0,0 +1,126 @@
+#include "certificateiodialog.hpp"
+#include "ui_certificateiodialog.h"
+
+#include <QFileDialog>
+#include <QPushButton>
+#include <QSsl>
+
+CertificateIoDialog::CertificateIoDialog(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::CertificateIoDialog)
+{
+ ui->setupUi(this);
+
+ this->ui->key_type->clear();
+ this->ui->key_type->addItem("RSA", QVariant::fromValue<int>(QSsl::Rsa));
+ this->ui->key_type->addItem("ECDSA", QVariant::fromValue<int>(QSsl::Ec));
+
+ this->updateUI();
+}
+
+CertificateIoDialog::~CertificateIoDialog()
+{
+ delete ui;
+}
+
+void CertificateIoDialog::setIoMode(CertificateIoDialog::IoMode mode)
+{
+ this->current_mode = mode;
+ if(mode == Export) {
+ this->setWindowTitle(tr("Export Certificate"));
+ } else {
+ this->setWindowTitle(tr("Import Certificate"));
+ }
+ this->ui->key_type->setEnabled(mode == Import);
+ this->updateUI();
+}
+
+QSsl::KeyAlgorithm CertificateIoDialog::keyAlgorithm() const
+{
+ return QSsl::KeyAlgorithm(this->ui->key_type->currentData().toInt());
+}
+
+void CertificateIoDialog::setKeyAlgorithm(QSsl::KeyAlgorithm alg)
+{
+ this->ui->key_type->setCurrentIndex(-1);
+ for(int i = 0; i< this->ui->key_type->count(); i++) {
+ if(this->ui->key_type->itemData(i).toInt() == int(alg)) {
+ this->ui->key_type->setCurrentIndex(i);
+ break;
+ }
+ }
+}
+
+QString CertificateIoDialog::keyFileName() const
+{
+ return this->ui->key_file_name->text();
+}
+
+QString CertificateIoDialog::certificateFileName() const
+{
+ return this->ui->certificate_file_name->text();
+}
+
+void CertificateIoDialog::on_select_certificate_file_button_clicked()
+{
+ QFileDialog dialog { this };
+
+ dialog.setNameFilter("Certificate File(*.pem *.der)");
+ dialog.setAcceptMode((this->current_mode == Export) ? QFileDialog::AcceptSave : QFileDialog::AcceptOpen);
+ dialog.selectFile(this->ui->certificate_file_name->text());
+
+ if(dialog.exec() != QDialog::Accepted)
+ return;
+
+ this->ui->certificate_file_name->setText(dialog.selectedFiles().first());
+
+ this->updateUI();
+}
+
+void CertificateIoDialog::on_select_key_file_button_clicked()
+{
+ QFileDialog dialog { this };
+
+ dialog.setNameFilter("Certificate File(*.pem *.der)");
+ dialog.setAcceptMode((this->current_mode == Export) ? QFileDialog::AcceptSave : QFileDialog::AcceptOpen);
+ dialog.selectFile(this->ui->key_file_name->text());
+
+ if(dialog.exec() != QDialog::Accepted)
+ return;
+
+ this->ui->key_file_name->setText(dialog.selectedFiles().first());
+
+ this->updateUI();
+}
+
+void CertificateIoDialog::on_certificate_file_name_textChanged(const QString &arg1)
+{
+ Q_UNUSED(arg1)
+ this->updateUI();
+}
+
+void CertificateIoDialog::on_key_file_name_textChanged(const QString &arg1)
+{
+ Q_UNUSED(arg1)
+ this->updateUI();
+}
+
+void CertificateIoDialog::updateUI()
+{
+ QString cert_file_name = certificateFileName();
+ QString key_file_name = keyFileName();
+
+ bool ok = true;
+
+ ok &= (cert_file_name.endsWith(".pem") or cert_file_name.endsWith(".der"));
+ ok &= (key_file_name.endsWith(".pem") or key_file_name.endsWith(".der"));
+
+ ok &= (this->ui->key_type->currentIndex() >= 0);
+
+ if(current_mode == Import) {
+ ok &= QFile(cert_file_name).exists();
+ ok &= QFile(key_file_name).exists();
+ }
+
+ this->ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ok);
+}
diff --git a/src/dialogs/certificateiodialog.hpp b/src/dialogs/certificateiodialog.hpp
new file mode 100644
index 0000000..590b282
--- /dev/null
+++ b/src/dialogs/certificateiodialog.hpp
@@ -0,0 +1,50 @@
+#ifndef CERTIFICATEIODIALOG_HPP
+#define CERTIFICATEIODIALOG_HPP
+
+#include <QDialog>
+#include <QSsl>
+
+namespace Ui {
+class CertificateIoDialog;
+}
+
+class CertificateIoDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ enum IoMode {
+ Import,
+ Export
+ };
+public:
+ explicit CertificateIoDialog(QWidget *parent = nullptr);
+ ~CertificateIoDialog();
+
+ IoMode mode() const { return this->current_mode; }
+ void setIoMode(IoMode mode);
+
+ QSsl::KeyAlgorithm keyAlgorithm() const;
+ void setKeyAlgorithm(QSsl::KeyAlgorithm alg);
+
+ QString keyFileName() const;
+ QString certificateFileName() const;
+
+private slots:
+ void on_select_certificate_file_button_clicked();
+
+ void on_select_key_file_button_clicked();
+
+ void on_certificate_file_name_textChanged(const QString &arg1);
+
+ void on_key_file_name_textChanged(const QString &arg1);
+
+private:
+ void updateUI();
+
+private:
+ Ui::CertificateIoDialog *ui;
+
+ IoMode current_mode;
+};
+
+#endif // CERTIFICATEIODIALOG_HPP
diff --git a/src/dialogs/certificateiodialog.ui b/src/dialogs/certificateiodialog.ui
new file mode 100644
index 0000000..074df28
--- /dev/null
+++ b/src/dialogs/certificateiodialog.ui
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>CertificateIoDialog</class>
+ <widget class="QDialog" name="CertificateIoDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>411</width>
+ <height>160</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Key Type</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="key_type">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="editable">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Key File</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLineEdit" name="key_file_name"/>
+ </item>
+ <item>
+ <widget class="QToolButton" name="select_key_file_button">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Certificate File</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLineEdit" name="certificate_file_name"/>
+ </item>
+ <item>
+ <widget class="QToolButton" name="select_certificate_file_button">
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>CertificateIoDialog</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>CertificateIoDialog</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/src/dialogs/certificatemanagementdialog.cpp b/src/dialogs/certificatemanagementdialog.cpp
new file mode 100644
index 0000000..5141b30
--- /dev/null
+++ b/src/dialogs/certificatemanagementdialog.cpp
@@ -0,0 +1,308 @@
+#include "certificatemanagementdialog.hpp"
+#include "ui_certificatemanagementdialog.h"
+
+#include "kristall.hpp"
+
+#include "newidentitiydialog.hpp"
+#include "certificateiodialog.hpp"
+#include "ioutil.hpp"
+
+#include <QCryptographicHash>
+#include <QMessageBox>
+
+CertificateManagementDialog::CertificateManagementDialog(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::CertificateManagementDialog),
+ selected_identity { nullptr }
+{
+ ui->setupUi(this);
+
+ this->ui->certificates->setModel(&global_identities);
+ this->ui->certificates->expandAll();
+
+ connect(
+ this->ui->certificates->selectionModel(),
+ &QItemSelectionModel::currentChanged,
+ this,
+ &CertificateManagementDialog::on_certificates_selected
+ );
+ on_certificates_selected(QModelIndex { }, QModelIndex { });
+}
+
+CertificateManagementDialog::~CertificateManagementDialog()
+{
+ delete ui;
+}
+
+void CertificateManagementDialog::on_certificates_selected(QModelIndex const& index, QModelIndex const & previous)
+{
+ Q_UNUSED(previous);
+
+ selected_identity = global_identities.getMutableIdentity(index);
+
+ this->ui->export_cert_button->setEnabled(selected_identity != nullptr);
+
+ if(selected_identity != nullptr)
+ {
+ auto & cert = *selected_identity;
+ this->ui->groupBox->setEnabled(true);
+ this->ui->cert_display_name->setText(cert.display_name);
+ this->ui->cert_common_name->setText(cert.certificate.subjectInfo(QSslCertificate::CommonName).join(", "));
+ this->ui->cert_expiration_date->setDateTime(cert.certificate.expiryDate());
+ this->ui->cert_livetime->setText(QString("%1 days").arg(QDateTime::currentDateTime().daysTo(cert.certificate.expiryDate())));
+ this->ui->cert_fingerprint->setPlainText(toFingerprintString(cert.certificate));
+ this->ui->cert_notes->setPlainText(cert.user_notes);
+
+ this->ui->cert_host_filter->setText(cert.host_filter);
+ this->ui->cert_auto_enable->setEnabled(not cert.host_filter.isEmpty());
+ this->ui->cert_auto_enable->setChecked(cert.auto_enable);
+
+ this->ui->delete_cert_button->setEnabled(true);
+ }
+ else
+ {
+ this->ui->groupBox->setEnabled(false);
+ this->ui->cert_display_name->setText("");
+ this->ui->cert_common_name->setText("");
+ this->ui->cert_expiration_date->setDateTime(QDateTime { });
+ this->ui->cert_livetime->setText("");
+ this->ui->cert_fingerprint->setPlainText("");
+ 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));
+ } else {
+ this->ui->delete_cert_button->setEnabled(false);
+ }
+ }
+}
+
+void CertificateManagementDialog::on_cert_notes_textChanged()
+{
+ if(this->selected_identity != nullptr) {
+ this->selected_identity->user_notes = this->ui->cert_notes->toPlainText();
+ }
+}
+
+void CertificateManagementDialog::on_cert_display_name_textChanged(const QString &arg1)
+{
+ Q_UNUSED(arg1)
+ if(this->selected_identity != nullptr) {
+ this->selected_identity->display_name = this->ui->cert_display_name->text();
+ }
+}
+
+void CertificateManagementDialog::on_delete_cert_button_clicked()
+{
+ auto index = this->ui->certificates->currentIndex();
+
+ if(global_identities.getMutableIdentity(index) != nullptr)
+ {
+ auto answer = QMessageBox::question(
+ this,
+ "Kristall",
+ "Do you really want to delete this certificate?\r\n\r\nYou will not be able to restore the identity after this!",
+ QMessageBox::Yes | QMessageBox::No,
+ QMessageBox::No
+ );
+ if(answer != QMessageBox::Yes)
+ return;
+ if(not global_identities.destroyIdentity(index)) {
+ QMessageBox::warning(this, "Kristall", "Could not destroy identity!");
+ }
+ }
+ else if(auto group_name = global_identities.group(index); not group_name.isEmpty()) {
+
+ auto answer = QMessageBox::question(
+ this,
+ "Kristall",
+ QString("Do you want to delete the group '%1'").arg(group_name)
+ );
+ if(answer != QMessageBox::Yes)
+ return;
+
+ if(not global_identities.deleteGroup(group_name)) {
+ QMessageBox::warning(this, "Kristall", "Could not delete group!");
+ }
+ }
+}
+
+void CertificateManagementDialog::on_export_cert_button_clicked()
+{
+ if(this->selected_identity == nullptr)
+ return;
+ CertificateIoDialog dialog { this };
+
+ dialog.setKeyAlgorithm(this->selected_identity->private_key.algorithm());
+ dialog.setIoMode(CertificateIoDialog::Export);
+
+ if(dialog.exec() != QDialog::Accepted)
+ return;
+
+ {
+ QFile cert_file { dialog.certificateFileName() };
+ if(not cert_file.open(QFile::WriteOnly)) {
+ QMessageBox::warning(
+ this,
+ "Kristall",
+ tr("The file %1 could not be found!").arg(dialog.certificateFileName())
+ );
+ return;
+ }
+
+ QByteArray cert_blob;
+ if(dialog.certificateFileName().endsWith(".der")) {
+ cert_blob = this->selected_identity->certificate.toDer();
+ } else {
+ cert_blob = this->selected_identity->certificate.toPem();
+ }
+
+ if(not IoUtil::writeAll(cert_file, cert_blob)) {
+ QMessageBox::warning(
+ this,
+ "Kristall",
+ tr("The file %1 could not be created found!").arg(dialog.certificateFileName())
+ );
+ return;
+ }
+ }
+
+ {
+ QFile key_file { dialog.keyFileName() };
+ if(not key_file.open(QFile::WriteOnly)) {
+ QMessageBox::warning(
+ this,
+ "Kristall",
+ tr("The file %1 could not be found!").arg(dialog.keyFileName())
+ );
+ return;
+ }
+
+ QByteArray key_blob;
+ if(dialog.keyFileName().endsWith(".der")) {
+ key_blob = this->selected_identity->private_key.toDer();
+ } else {
+ key_blob = this->selected_identity->private_key.toPem();
+ }
+
+ if(not IoUtil::writeAll(key_file, key_blob)) {
+ QMessageBox::warning(
+ this,
+ "Kristall",
+ tr("The file %1 could not be created found!").arg(dialog.keyFileName())
+ );
+ return;
+ }
+ }
+}
+
+void CertificateManagementDialog::on_import_cert_button_clicked()
+{
+ CertificateIoDialog dialog { this };
+
+ dialog.setIoMode(CertificateIoDialog::Import);
+
+ if(dialog.exec() != QDialog::Accepted)
+ return;
+
+ QFile cert_file { dialog.certificateFileName() };
+ if(not cert_file.open(QFile::ReadOnly)) {
+ QMessageBox::warning(
+ this,
+ "Kristall",
+ tr("The file %1 could not be found!").arg(dialog.certificateFileName())
+ );
+ return;
+ }
+
+ QFile key_file { dialog.keyFileName() };
+ if(not key_file.open(QFile::ReadOnly)) {
+ QMessageBox::warning(
+ this,
+ "Kristall",
+ tr("The file %1 could not be found!").arg(dialog.keyFileName())
+ );
+ return;
+ }
+
+ CryptoIdentity ident;
+ ident.private_key = QSslKey {
+ &key_file,
+ dialog.keyAlgorithm(),
+ dialog.keyFileName().endsWith(".der") ? QSsl::Der : QSsl::Pem,
+ QSsl::PrivateKey
+ };
+ ident.certificate = QSslCertificate {
+ &cert_file,
+ dialog.keyFileName().endsWith(".der") ? QSsl::Der : QSsl::Pem,
+ };
+ ident.user_notes = tr("Imported from:\r\nkey: %1\r\n:cert: %2").arg(dialog.keyFileName()).arg(dialog.certificateFileName());
+ ident.display_name = "Imported Certificate";
+ ident.auto_enable = false;
+ ident.host_filter = "";
+ ident.is_persistent = true;
+
+ if(ident.private_key.isNull()) {
+ QMessageBox::warning(
+ this,
+ "Kristall",
+ tr("The key file %1 could not be loaded. Please verify your key file.").arg(dialog.keyFileName())
+ );
+ return;
+ }
+
+ if(ident.certificate.isNull()) {
+ QMessageBox::warning(
+ this,
+ "Kristall",
+ tr("The certificate file %1 could not be loaded. Please verify your certificate.").arg(dialog.keyFileName())
+ );
+ return;
+ }
+
+ if(not global_identities.addCertificate(tr("Imported Certificates"), ident)) {
+ QMessageBox::warning(
+ this,
+ "Kristall",
+ tr("Failed to import the certificate.")
+ );
+ }
+}
+
+void CertificateManagementDialog::on_create_cert_button_clicked()
+{
+ NewIdentitiyDialog dialog { this };
+
+ dialog.setGroupName(global_identities.group(this->ui->certificates->currentIndex()));
+
+ if(dialog.exec() != QDialog::Accepted)
+ return;
+
+ auto id = dialog.createIdentity();
+ if(not id.isValid())
+ return;
+ id.is_persistent = true;
+
+ global_identities.addCertificate(
+ dialog.groupName(),
+ id);
+}
+
+void CertificateManagementDialog::on_cert_host_filter_textChanged(const QString &host_filter)
+{
+ if(this->selected_identity != nullptr) {
+ this->ui->cert_auto_enable->setEnabled(not host_filter.isEmpty());
+ this->selected_identity->host_filter = host_filter;
+ } else {
+ this->ui->cert_auto_enable->setEnabled(false);
+ }
+
+}
+
+void CertificateManagementDialog::on_cert_auto_enable_clicked(bool checked)
+{
+ if(this->selected_identity != nullptr) {
+ this->selected_identity->auto_enable = checked;
+ }
+}
diff --git a/src/dialogs/certificatemanagementdialog.hpp b/src/dialogs/certificatemanagementdialog.hpp
new file mode 100644
index 0000000..0d7178a
--- /dev/null
+++ b/src/dialogs/certificatemanagementdialog.hpp
@@ -0,0 +1,45 @@
+#ifndef CERTIFICATEMANAGEMENTDIALOG_HPP
+#define CERTIFICATEMANAGEMENTDIALOG_HPP
+
+#include <QDialog>
+
+#include "cryptoidentity.hpp"
+
+namespace Ui {
+class CertificateManagementDialog;
+}
+
+class CertificateManagementDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit CertificateManagementDialog(QWidget *parent = nullptr);
+ ~CertificateManagementDialog();
+
+private slots:
+ void on_cert_notes_textChanged();
+
+ void on_cert_display_name_textChanged(const QString &arg1);
+
+ void on_delete_cert_button_clicked();
+
+ void on_export_cert_button_clicked();
+
+ void on_import_cert_button_clicked();
+
+ void on_create_cert_button_clicked();
+
+ void on_cert_host_filter_textChanged(const QString &arg1);
+
+ void on_cert_auto_enable_clicked(bool checked);
+
+private:
+ void on_certificates_selected(const QModelIndex &index, QModelIndex const & previous);
+private:
+ Ui::CertificateManagementDialog *ui;
+
+ CryptoIdentity * selected_identity;
+};
+
+#endif // CERTIFICATEMANAGEMENTDIALOG_HPP
diff --git a/src/dialogs/certificatemanagementdialog.ui b/src/dialogs/certificatemanagementdialog.ui
new file mode 100644
index 0000000..82dd788
--- /dev/null
+++ b/src/dialogs/certificatemanagementdialog.ui
@@ -0,0 +1,294 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>CertificateManagementDialog</class>
+ <widget class="QDialog" name="CertificateManagementDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>731</width>
+ <height>480</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Certificate Manager</string>
+ </property>
+ <property name="windowIcon">
+ <iconset theme="certificate"/>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QTreeView" name="certificates">
+ <property name="acceptDrops">
+ <bool>true</bool>
+ </property>
+ <property name="dragEnabled">
+ <bool>true</bool>
+ </property>
+ <property name="dragDropMode">
+ <enum>QAbstractItemView::DragDrop</enum>
+ </property>
+ <property name="defaultDropAction">
+ <enum>Qt::MoveAction</enum>
+ </property>
+ <attribute name="headerVisible">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QToolButton" name="create_cert_button">
+ <property name="toolTip">
+ <string>Craete new certificate</string>
+ </property>
+ <property name="text">
+ <string>Create...</string>
+ </property>
+ <property name="icon">
+ <iconset theme="create-new"/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="import_cert_button">
+ <property name="toolTip">
+ <string>Import certificate</string>
+ </property>
+ <property name="text">
+ <string>Import...</string>
+ </property>
+ <property name="icon">
+ <iconset theme="save-import"/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QToolButton" name="export_cert_button">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string>Export certificate</string>
+ </property>
+ <property name="text">
+ <string>Export...</string>
+ </property>
+ <property name="icon">
+ <iconset theme="save-export"/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="delete_cert_button">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string>Delete certificate</string>
+ </property>
+ <property name="text">
+ <string>Delete</string>
+ </property>
+ <property name="icon">
+ <iconset theme="delete-alert"/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>Certificate</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Display Name</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="cert_display_name"/>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Common Name</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="cert_common_name">
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Expiration Date</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QDateTimeEdit" name="cert_expiration_date">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Expires in</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLabel" name="cert_livetime">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>??? days</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>Fingerprint</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>Notes</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1">
+ <widget class="QPlainTextEdit" name="cert_fingerprint">
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="1">
+ <widget class="QPlainTextEdit" name="cert_notes"/>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Host Filter</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLineEdit" name="cert_host_filter">
+ <property name="placeholderText">
+ <string>gemini://*</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QCheckBox" name="cert_auto_enable">
+ <property name="toolTip">
+ <string>If this is checked, Kristall will automatically enable this certificate when visiting a URL matching the host filter</string>
+ </property>
+ <property name="text">
+ <string>Auto-Enable Certificate</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>CertificateManagementDialog</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>CertificateManagementDialog</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/src/dialogs/certificateselectiondialog.cpp b/src/dialogs/certificateselectiondialog.cpp
new file mode 100644
index 0000000..f4dab38
--- /dev/null
+++ b/src/dialogs/certificateselectiondialog.cpp
@@ -0,0 +1,130 @@
+#include "certificateselectiondialog.hpp"
+#include "ui_certificateselectiondialog.h"
+
+#include "certificatehelper.hpp"
+#include "kristall.hpp"
+#include "newidentitiydialog.hpp"
+
+#include <random>
+#include <QDebug>
+#include <QItemSelectionModel>
+
+CertificateSelectionDialog::CertificateSelectionDialog(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::CertificateSelectionDialog)
+{
+ ui->setupUi(this);
+ this->ui->server_request->setVisible(false);
+
+ this->ui->certificates->setModel(&global_identities);
+ this->ui->certificates->expandAll();
+
+ connect(this->ui->certificates->selectionModel(), &QItemSelectionModel::currentChanged, this, &CertificateSelectionDialog::on_currentChanged);
+}
+
+CertificateSelectionDialog::~CertificateSelectionDialog()
+{
+ delete ui;
+}
+
+void CertificateSelectionDialog::setServerQuery(const QString &query)
+{
+ this->ui->server_request->setText(query);
+ this->ui->server_request->setVisible(not query.isEmpty());
+}
+
+CryptoIdentity CertificateSelectionDialog::identity() const
+{
+ return cryto_identity;
+}
+
+void CertificateSelectionDialog::on_use_temp_cert_30m_clicked()
+{
+ acceptTemporaryWithTimeout(QDateTime::currentDateTime().addSecs(1800 * 12));
+}
+
+void CertificateSelectionDialog::on_use_temp_cert_1h_clicked()
+{
+ acceptTemporaryWithTimeout(QDateTime::currentDateTime().addSecs(3600));
+}
+
+void CertificateSelectionDialog::on_use_temp_cert_12h_clicked()
+{
+ acceptTemporaryWithTimeout(QDateTime::currentDateTime().addSecs(3600 * 12));
+}
+
+void CertificateSelectionDialog::on_use_temp_cert_24h_clicked()
+{
+ acceptTemporaryWithTimeout(QDateTime::currentDateTime().addDays(1));
+}
+
+void CertificateSelectionDialog::on_use_temp_cert_48h_clicked()
+{
+ acceptTemporaryWithTimeout(QDateTime::currentDateTime().addDays(2));
+}
+
+void CertificateSelectionDialog::acceptTemporaryWithTimeout(QDateTime timeout)
+{
+ std::default_random_engine rng;
+ rng.seed(QDateTime::currentDateTime().toMSecsSinceEpoch());
+
+ std::uniform_int_distribution<int> distr(0, 255);
+
+ char items[8];
+ for(auto & c : items) {
+ c = distr(rng);
+ }
+
+ this->cryto_identity = CertificateHelper::createNewIdentity(
+ QByteArray(items, sizeof items).toBase64(QByteArray::OmitTrailingEquals),
+ timeout);
+
+ this->accept();
+}
+
+void CertificateSelectionDialog::on_currentChanged(const QModelIndex &current, const QModelIndex &previous)
+{
+ Q_UNUSED(current)
+ Q_UNUSED(previous)
+ auto id = global_identities.getIdentity(current);
+
+ this->ui->use_selected_cert->setEnabled(id.isValid());
+}
+
+void CertificateSelectionDialog::on_create_new_cert_clicked()
+{
+ NewIdentitiyDialog dialog { this };
+
+ if(dialog.exec() != QDialog::Accepted)
+ return;
+
+ auto id = dialog.createIdentity();
+ if(not id.isValid())
+ return;
+ id.is_persistent = true;
+
+ global_identities.addCertificate(
+ dialog.groupName(),
+ id);
+}
+
+void CertificateSelectionDialog::on_use_selected_cert_clicked()
+{
+ auto sel = this->ui->certificates->selectionModel()->currentIndex();
+ this->cryto_identity = global_identities.getIdentity(sel);
+ if(this->cryto_identity.isValid()) {
+ this->accept();
+ } else {
+ qDebug() << "Tried to use an invalid identity when the button should not be enabled. This is a bug!";
+ }
+}
+
+void CertificateSelectionDialog::on_certificates_doubleClicked(const QModelIndex &index)
+{
+ this->cryto_identity = global_identities.getIdentity(index);
+ if(this->cryto_identity.isValid()) {
+ this->accept();
+ } else {
+ qDebug() << "Tried to use an invalid identity when the button should not be enabled. This is a bug!";
+ }
+}
diff --git a/src/dialogs/certificateselectiondialog.hpp b/src/dialogs/certificateselectiondialog.hpp
new file mode 100644
index 0000000..e628f5c
--- /dev/null
+++ b/src/dialogs/certificateselectiondialog.hpp
@@ -0,0 +1,54 @@
+#ifndef CERTIFICATESELECTIONDIALOG_HPP
+#define CERTIFICATESELECTIONDIALOG_HPP
+
+#include <QDialog>
+
+#include "cryptoidentity.hpp"
+
+namespace Ui {
+class CertificateSelectionDialog;
+}
+
+class CertificateSelectionDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit CertificateSelectionDialog(QWidget *parent = nullptr);
+ ~CertificateSelectionDialog();
+
+ void setServerQuery(QString const & query);
+
+ CryptoIdentity identity() const;
+
+private slots:
+ void on_use_temp_cert_30m_clicked();
+
+ void on_use_temp_cert_1h_clicked();
+
+ void on_use_temp_cert_12h_clicked();
+
+ void on_use_temp_cert_24h_clicked();
+
+ void on_use_temp_cert_48h_clicked();
+
+ void on_create_new_cert_clicked();
+
+ void on_use_selected_cert_clicked();
+
+ void on_certificates_doubleClicked(const QModelIndex &index);
+
+private:
+ //! Creates an anonymous identity with a randomly chosen name that
+ //! will time out on `timeout`, then accepts the dialog.
+ void acceptTemporaryWithTimeout(QDateTime timeout);
+
+
+ void on_currentChanged(const QModelIndex &current, const QModelIndex &previous);
+private:
+ Ui::CertificateSelectionDialog *ui;
+
+ CryptoIdentity cryto_identity;
+};
+
+#endif // CERTIFICATESELECTIONDIALOG_HPP
diff --git a/src/dialogs/certificateselectiondialog.ui b/src/dialogs/certificateselectiondialog.ui
new file mode 100644
index 0000000..d6128f9
--- /dev/null
+++ b/src/dialogs/certificateselectiondialog.ui
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>CertificateSelectionDialog</class>
+ <widget class="QDialog" name="CertificateSelectionDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>474</width>
+ <height>355</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Select client certificate</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="icons.qrc">
+ <normaloff>:/icons/certificate.svg</normaloff>:/icons/certificate.svg</iconset>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Select existing certificate:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="server_request">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QTreeView" name="certificates">
+ <property name="headerHidden">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QPushButton" name="create_new_cert">
+ <property name="text">
+ <string>Create new identity</string>
+ </property>
+ <property name="icon">
+ <iconset resource="icons.qrc">
+ <normaloff>:/icons/plus.svg</normaloff>:/icons/plus.svg</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="use_selected_cert">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Use</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Create transient session certificate:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="use_temp_cert_30m">
+ <property name="text">
+ <string>30 Minutes</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="use_temp_cert_1h">
+ <property name="text">
+ <string>1 Hour</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="use_temp_cert_12h">
+ <property name="text">
+ <string>12 Hours</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="use_temp_cert_24h">
+ <property name="text">
+ <string>24 Hours</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="use_temp_cert_48h">
+ <property name="text">
+ <string>48 Hours</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="icons.qrc"/>
+ </resources>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>CertificateSelectionDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>236</x>
+ <y>333</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>236</x>
+ <y>177</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/dialogs/newidentitiydialog.cpp b/src/dialogs/newidentitiydialog.cpp
new file mode 100644
index 0000000..af2a97e
--- /dev/null
+++ b/src/dialogs/newidentitiydialog.cpp
@@ -0,0 +1,81 @@
+#include "newidentitiydialog.hpp"
+#include "ui_newidentitiydialog.h"
+
+#include "certificatehelper.hpp"
+#include "kristall.hpp"
+
+#include <QPushButton>
+#include <QDebug>
+
+NewIdentitiyDialog::NewIdentitiyDialog(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::NewIdentitiyDialog)
+{
+ ui->setupUi(this);
+
+ ui->display_name->setText("Unnamed");
+ ui->common_name->setText("Unnamed");
+ ui->expiration_date->setDate(QDate::currentDate().addYears(1));
+ ui->expiration_date->setTime(QTime(12, 00));
+
+ ui->group->clear();
+ for(auto group_name : global_identities.groups())
+ {
+ ui->group->addItem(group_name);
+ }
+}
+
+NewIdentitiyDialog::~NewIdentitiyDialog()
+{
+ delete ui;
+}
+
+CryptoIdentity NewIdentitiyDialog::createIdentity() const
+{
+ auto id = CertificateHelper::createNewIdentity(
+ this->ui->common_name->text(),
+ this->ui->expiration_date->dateTime()
+ );
+ id.display_name = this->ui->display_name->text();
+ return id;
+}
+
+QString NewIdentitiyDialog::groupName() const
+{
+ return this->ui->group->currentText();
+}
+
+void NewIdentitiyDialog::setGroupName(const QString &name)
+{
+ this->ui->group->setCurrentText(name);
+}
+
+void NewIdentitiyDialog::updateUI()
+{
+ bool is_ok = true;
+
+ is_ok &= (not this->ui->group->currentText().isEmpty());
+ is_ok &= (not this->ui->common_name->text().isEmpty());
+ is_ok &= (not this->ui->display_name->text().isEmpty());
+ is_ok &= (this->ui->expiration_date->dateTime() > QDateTime::currentDateTime());
+
+ this->ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(is_ok);
+}
+
+void NewIdentitiyDialog::on_group_editTextChanged(const QString &arg1)
+{
+ Q_UNUSED(arg1);
+ this->updateUI();
+}
+
+void NewIdentitiyDialog::on_display_name_textChanged(const QString &arg1)
+{
+ Q_UNUSED(arg1);
+ this->updateUI();
+}
+
+void NewIdentitiyDialog::on_common_name_textChanged(const QString &arg1)
+{
+ Q_UNUSED(arg1);
+ this->updateUI();
+}
diff --git a/src/dialogs/newidentitiydialog.hpp b/src/dialogs/newidentitiydialog.hpp
new file mode 100644
index 0000000..83d740b
--- /dev/null
+++ b/src/dialogs/newidentitiydialog.hpp
@@ -0,0 +1,41 @@
+#ifndef NEWIDENTITIYDIALOG_HPP
+#define NEWIDENTITIYDIALOG_HPP
+
+#include <QDialog>
+
+#include "cryptoidentity.hpp"
+
+namespace Ui {
+class NewIdentitiyDialog;
+}
+
+class NewIdentitiyDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit NewIdentitiyDialog(QWidget *parent = nullptr);
+ ~NewIdentitiyDialog();
+
+ //! Creates a new identity from the currently set
+ //! user settings.
+ CryptoIdentity createIdentity() const;
+
+ QString groupName() const;
+ void setGroupName(QString const & name);
+
+private slots:
+ void on_group_editTextChanged(const QString &arg1);
+
+ void on_display_name_textChanged(const QString &arg1);
+
+ void on_common_name_textChanged(const QString &arg1);
+
+private:
+ void updateUI();
+
+private:
+ Ui::NewIdentitiyDialog *ui;
+};
+
+#endif // NEWIDENTITIYDIALOG_HPP
diff --git a/src/dialogs/newidentitiydialog.ui b/src/dialogs/newidentitiydialog.ui
new file mode 100644
index 0000000..556ac93
--- /dev/null
+++ b/src/dialogs/newidentitiydialog.ui
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>NewIdentitiyDialog</class>
+ <widget class="QDialog" name="NewIdentitiyDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>328</width>
+ <height>191</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Create new certificate</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="icons.qrc">
+ <normaloff>:/icons/certificate.svg</normaloff>:/icons/certificate.svg</iconset>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="1" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Display Name</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Expiration Date</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="display_name"/>
+ </item>
+ <item row="3" column="1">
+ <widget class="QDateTimeEdit" name="expiration_date"/>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLineEdit" name="common_name"/>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Common Name</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Group</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="group">
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="icons.qrc"/>
+ </resources>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>NewIdentitiyDialog</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>NewIdentitiyDialog</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/src/dialogs/settingsdialog.cpp b/src/dialogs/settingsdialog.cpp
new file mode 100644
index 0000000..ebd1664
--- /dev/null
+++ b/src/dialogs/settingsdialog.cpp
@@ -0,0 +1,613 @@
+#include "settingsdialog.hpp"
+#include "ui_settingsdialog.h"
+#include <QFontDialog>
+#include <QColorDialog>
+#include <QStyle>
+#include <QSettings>
+#include <QInputDialog>
+#include <QFileDialog>
+#include <QMessageBox>
+#include <QDebug>
+
+#include <cassert>
+
+#include "kristall.hpp"
+
+SettingsDialog::SettingsDialog(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::SettingsDialog),
+ current_style()
+{
+ ui->setupUi(this);
+
+ static_assert(DocumentStyle::Fixed == 0);
+ static_assert(DocumentStyle::AutoDarkTheme == 1);
+ static_assert(DocumentStyle::AutoLightTheme == 2);
+
+ this->ui->auto_theme->clear();
+ this->ui->auto_theme->addItem(tr("Disabled"), QVariant::fromValue<int>(DocumentStyle::Fixed));
+ this->ui->auto_theme->addItem(tr("Dark Theme"), QVariant::fromValue<int>(DocumentStyle::AutoDarkTheme));
+ this->ui->auto_theme->addItem(tr("Light Theme"), QVariant::fromValue<int>(DocumentStyle::AutoLightTheme));
+
+ this->ui->ui_theme->clear();
+ this->ui->ui_theme->addItem(tr("OS Default"), QVariant::fromValue<int>(int(Theme::os_default)));
+ this->ui->ui_theme->addItem(tr("Light"), QVariant::fromValue<int>(int(Theme::light)));
+ this->ui->ui_theme->addItem(tr("Dark"), QVariant::fromValue<int>(int(Theme::dark)));
+
+ setGeminiStyle(DocumentStyle { });
+
+ int items = global_settings.beginReadArray("Themes");
+
+ this->predefined_styles.clear();
+ for(int i = 0; i < items; i++)
+ {
+ global_settings.setArrayIndex(i);
+
+ QString name = global_settings.value("name").toString();
+
+ DocumentStyle style;
+ style.load(global_settings);
+
+ 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) {
+ on_presets_currentIndexChanged(0);
+ } else {
+ this->on_presets_currentIndexChanged(-1);
+ }
+
+ this->ui->redirection_mode->clear();
+ this->ui->redirection_mode->addItem("Ask for cross-scheme or cross-host redirection", int(GenericSettings::WarnOnHostChange | GenericSettings::WarnOnSchemeChange));
+ this->ui->redirection_mode->addItem("Ask for cross-scheme redirection", int(GenericSettings::WarnOnSchemeChange));
+ this->ui->redirection_mode->addItem("Ask for cross-host redirection", int(GenericSettings::WarnOnHostChange));
+ this->ui->redirection_mode->addItem("Ask for all redirection", int(GenericSettings::WarnAlways));
+ this->ui->redirection_mode->addItem("Silently redirect everything", int(GenericSettings::WarnNever));
+}
+
+SettingsDialog::~SettingsDialog()
+{
+ delete ui;
+}
+
+static QString formatFont(QFont const & font)
+{
+ QString style;
+ if(font.italic() and font.bold())
+ style = "bold, italic";
+ else if(font.italic())
+ style = "italic";
+ else if(font.bold())
+ style = "bold";
+ else
+ style = "regular";
+
+ return QString("%1 (%2pt, %3)")
+ .arg(font.family())
+ .arg(font.pointSizeF())
+ .arg(style);
+}
+
+void SettingsDialog::setGeminiStyle(DocumentStyle const &style)
+{
+ static const QString COLOR_STYLE("border: 1px solid black; padding: 4px; background-color : %1; color : %2;");
+
+ this->current_style = style;
+
+ this->ui->auto_theme->setCurrentIndex(this->current_style.theme);
+
+ this->ui->page_margin->setValue(this->current_style.margin);
+
+ auto setFontAndColor = [this](QLabel * label, QFont font, QColor color)
+ {
+ label->setText(formatFont(font));
+ label->setStyleSheet(COLOR_STYLE
+ .arg(this->current_style.background_color.name())
+ .arg(color.name()));
+ };
+
+ ui->bg_preview->setStyleSheet(COLOR_STYLE
+ .arg(this->current_style.background_color.name())
+ .arg("#FF00FF"));
+
+ ui->quote_preview->setStyleSheet(COLOR_STYLE
+ .arg(this->current_style.blockquote_color.name())
+ .arg("#FF00FF"));
+
+ ui->link_local_preview->setStyleSheet(COLOR_STYLE
+ .arg(this->current_style.background_color.name())
+ .arg(this->current_style.internal_link_color.name()));
+
+ ui->link_foreign_preview->setStyleSheet(COLOR_STYLE
+ .arg(this->current_style.background_color.name())
+ .arg(this->current_style.external_link_color.name()));
+
+ ui->link_cross_preview->setStyleSheet(COLOR_STYLE
+ .arg(this->current_style.background_color.name())
+ .arg(this->current_style.cross_scheme_link_color.name()));
+
+ setFontAndColor(this->ui->std_preview, this->current_style.standard_font, this->current_style.standard_color);
+ setFontAndColor(this->ui->pre_preview, this->current_style.preformatted_font, this->current_style.preformatted_color);
+ setFontAndColor(this->ui->h1_preview, this->current_style.h1_font, this->current_style.h1_color);
+ setFontAndColor(this->ui->h2_preview, this->current_style.h2_font, this->current_style.h2_color);
+ setFontAndColor(this->ui->h3_preview, this->current_style.h3_font, this->current_style.h3_color);
+
+ this->reloadStylePreview();
+}
+
+ProtocolSetup SettingsDialog::protocols() const
+{
+ ProtocolSetup protocols;
+#define M(X) \
+ protocols.X = this->ui->enable_##X->isChecked();
+ PROTOCOLS(M)
+#undef M
+ return protocols;
+}
+
+void SettingsDialog::setProtocols(ProtocolSetup const & protocols)
+{
+#define M(X) \
+ this->ui->enable_##X->setChecked(protocols.X);
+ PROTOCOLS(M)
+ #undef M
+}
+
+SslTrust SettingsDialog::geminiSslTrust() const
+{
+ return this->ui->gemini_trust_editor->trust();
+}
+
+void SettingsDialog::setGeminiSslTrust(const SslTrust &trust)
+{
+ return this->ui->gemini_trust_editor->setTrust(trust);
+}
+
+SslTrust SettingsDialog::httpsSslTrust() const
+{
+ return this->ui->https_trust_editor->trust();
+}
+
+void SettingsDialog::setHttpsSslTrust(const SslTrust &trust)
+{
+ this->ui->https_trust_editor->setTrust(trust);
+}
+
+void SettingsDialog::setOptions(const GenericSettings &options)
+{
+ this->current_options = options;
+
+ this->ui->ui_theme->setCurrentIndex(0);
+ for(int i = 0; i < this->ui->ui_theme->count(); i++) {
+ if(this->ui->ui_theme->itemData(i).toInt() == int(options.theme)) {
+ this->ui->ui_theme->setCurrentIndex(i);
+ break;
+ }
+ }
+
+ this->ui->start_page->setText(this->current_options.start_page);
+
+ if(this->current_options.gophermap_display == GenericSettings::PlainText) {
+ this->ui->gophermap_text->setChecked(true);
+ } else {
+ this->ui->gophermap_icon->setChecked(true);
+ }
+
+ if(this->current_options.text_display == GenericSettings::PlainText) {
+ this->ui->fancypants_off->setChecked(true);
+ } else {
+ this->ui->fancypants_on->setChecked(true);
+ }
+
+ if(this->current_options.enable_text_decoration) {
+ this->ui->texthl_on->setChecked(true);
+ } else {
+ this->ui->texthl_off->setChecked(true);
+ }
+
+ if(this->current_options.use_os_scheme_handler) {
+ this->ui->scheme_os_default->setChecked(true);
+ } else {
+ this->ui->scheme_error->setChecked(true);
+ }
+
+ this->ui->max_redirects->setValue(this->current_options.max_redirections);
+
+ this->ui->redirection_mode->setCurrentIndex(0);
+ for(int i = 0; i < this->ui->redirection_mode->count(); i++)
+ {
+ if(this->ui->redirection_mode->itemData(i).toInt() == this->current_options.redirection_policy) {
+ this->ui->redirection_mode->setCurrentIndex(i);
+ break;
+ }
+ }
+
+ this->ui->network_timeout->setValue(this->current_options.network_timeout);
+}
+
+GenericSettings SettingsDialog::options() const
+{
+ return this->current_options;
+}
+
+void SettingsDialog::reloadStylePreview()
+{
+ QFile document_src { ":/about/style-preview.gemini" };
+ bool ok = document_src.open(QFile::ReadOnly);
+ assert(ok and "failed to find style-preview.gemini!");
+
+ auto const document = document_src.readAll();
+
+ QString host = this->ui->preview_url->text();
+ if(host.length() == 0)
+ host = "preview";
+
+ QUrl url { QUrl(QString("about://%1/foobar").arg(host)) };
+
+ DocumentOutlineModel outline;
+ auto doc = GeminiRenderer::render(
+ document,
+ url,
+ current_style.derive(url),
+ outline
+ );
+
+ ui->style_preview->setStyleSheet(QString("QTextBrowser { background-color: %1; }")
+ .arg(doc->background_color.name()));
+ ui->style_preview->setDocument(doc.get());
+ preview_document = std::move(doc);
+}
+
+void SettingsDialog::updateFont(QFont & input)
+{
+ QFontDialog dialog { this };
+
+ dialog.setCurrentFont(input);
+
+ if(dialog.exec() == QDialog::Accepted) {
+ input = dialog.currentFont();
+ setGeminiStyle(current_style);
+ }
+}
+
+void SettingsDialog::on_std_change_font_clicked()
+{
+ updateFont(current_style.standard_font);
+}
+
+void SettingsDialog::on_pre_change_font_clicked()
+{
+ updateFont(current_style.preformatted_font);
+}
+
+void SettingsDialog::on_h1_change_font_clicked()
+{
+ updateFont(current_style.h1_font);
+}
+
+void SettingsDialog::on_h2_change_font_clicked()
+{
+ updateFont(current_style.h2_font);
+}
+
+void SettingsDialog::on_h3_change_font_clicked()
+{
+ updateFont(current_style.h3_font);
+}
+
+void SettingsDialog::updateColor(QColor &input)
+{
+ QColorDialog dialog { this };
+
+ dialog.setCurrentColor(input);
+
+ if(dialog.exec() == QDialog::Accepted) {
+ input = dialog.currentColor();
+ setGeminiStyle(current_style);
+ }
+}
+
+void SettingsDialog::on_std_change_color_clicked()
+{
+ updateColor(current_style.standard_color);
+}
+
+void SettingsDialog::on_pre_change_color_clicked()
+{
+ updateColor(current_style.preformatted_color);
+}
+
+void SettingsDialog::on_h1_change_color_clicked()
+{
+ updateColor(current_style.h1_color);
+}
+
+void SettingsDialog::on_h2_change_color_clicked()
+{
+ updateColor(current_style.h2_color);
+}
+
+void SettingsDialog::on_h3_change_color_clicked()
+{
+ updateColor(current_style.h3_color);
+}
+
+void SettingsDialog::on_bg_change_color_clicked()
+{
+ updateColor(current_style.background_color);
+}
+
+void SettingsDialog::on_link_local_change_color_clicked()
+{
+ updateColor(current_style.internal_link_color);
+}
+
+void SettingsDialog::on_link_foreign_change_color_clicked()
+{
+ updateColor(current_style.external_link_color);
+}
+
+void SettingsDialog::on_link_cross_change_color_clicked()
+{
+ updateColor(current_style.cross_scheme_link_color);
+}
+void SettingsDialog::on_quote_change_color_clicked()
+{
+ updateColor(current_style.blockquote_color);
+}
+
+void SettingsDialog::on_link_local_prefix_textChanged(const QString &text)
+{
+ current_style.internal_link_prefix = text;
+ reloadStylePreview();
+}
+
+void SettingsDialog::on_link_foreign_prefix_textChanged(const QString &text)
+{
+ current_style.external_link_prefix = text;
+ reloadStylePreview();
+}
+
+void SettingsDialog::on_auto_theme_currentIndexChanged(int index)
+{
+ if(index >= 0) {
+ current_style.theme = DocumentStyle::Theme(index);
+ reloadStylePreview();
+ }
+}
+
+void SettingsDialog::on_preview_url_textChanged(const QString &)
+{
+ this->reloadStylePreview();
+}
+
+void SettingsDialog::on_page_margin_valueChanged(double value)
+{
+ this->current_style.margin = value;
+ this->reloadStylePreview();
+}
+
+void SettingsDialog::on_presets_currentIndexChanged(int index)
+{
+ this->ui->preset_load->setEnabled(index >= 0);
+ this->ui->preset_save->setEnabled(index >= 0);
+ this->ui->preset_export->setEnabled(index >= 0);
+}
+
+void SettingsDialog::on_preset_new_clicked()
+{
+ QInputDialog dlg { this };
+ dlg.setInputMode(QInputDialog::TextInput);
+ dlg.setOkButtonText("Save");
+ dlg.setCancelButtonText("Cancel");
+ dlg.setLabelText("Enter the name of your new preset:");
+
+ if(dlg.exec() != QInputDialog::Accepted)
+ return;
+ QString name = dlg.textValue();
+
+ bool override = false;
+ if(this->predefined_styles.contains(name))
+ {
+ auto response = QMessageBox::question(this, "Kristall", QString("A style with the name '%1' already exists! Replace?").arg(name));
+ if(response != QMessageBox::Yes)
+ return;
+ override = true;
+ }
+
+ this->predefined_styles.insert(name, this->current_style);
+
+ if(not override)
+ {
+ this->ui->presets->addItem(name);
+ }
+}
+
+void SettingsDialog::on_preset_save_clicked()
+{
+ QString name = this->ui->presets->currentText();
+ if(name.isEmpty())
+ return;
+
+ auto response = QMessageBox::question(this, "Kristall", QString("Do you want to override the style '%1'?").arg(name));
+ if(response != QMessageBox::Yes)
+ return;
+
+ this->predefined_styles.insert(name, this->current_style);
+}
+
+
+void SettingsDialog::on_preset_load_clicked()
+{
+ QString name = this->ui->presets->currentText();
+ if(name.isEmpty())
+ return;
+
+ auto response = QMessageBox::question(this, "Kristall", QString("Do you want to load the style '%1'?\r\nThis will discard all currently set up values!").arg(name));
+ if(response != QMessageBox::Yes)
+ return;
+
+ this->setGeminiStyle(this->predefined_styles.value(name));
+}
+
+
+void SettingsDialog::on_SettingsDialog_accepted()
+{
+ global_settings.beginWriteArray("Themes", this->predefined_styles.size());
+
+ int index = 0;
+ for(auto const & style_name : this->predefined_styles.keys())
+ {
+ global_settings.setArrayIndex(index);
+
+ global_settings.setValue("name", style_name);
+ this->predefined_styles.value(style_name).save(global_settings);
+
+ index += 1;
+ }
+ global_settings.endArray();
+}
+
+void SettingsDialog::on_preset_import_clicked()
+{
+ QFileDialog dialog { this };
+ dialog.setAcceptMode(QFileDialog::AcceptOpen);
+ dialog.selectNameFilter("Kristall Theme (*.kthm)");
+
+ if(dialog.exec() !=QFileDialog::Accepted)
+ return;
+
+ QString fileName = dialog.selectedFiles().at(0);
+
+ QSettings import_settings { fileName, QSettings::IniFormat };
+
+ QString name;
+
+ name = import_settings.value("name").toString();
+
+ while(name.isEmpty())
+ {
+ QInputDialog dlg { this };
+ dlg.setInputMode(QInputDialog::TextInput);
+ dlg.setOkButtonText("Save");
+ dlg.setCancelButtonText("Cancel");
+ dlg.setLabelText("Imported preset has no name.\r\nPlease enter a name for the preset:");
+ if(dlg.exec() != QDialog::Accepted)
+ return;
+ name = dlg.textValue();
+ }
+
+ bool override = false;
+ if(this->predefined_styles.contains(name))
+ {
+ auto response = QMessageBox::question(this, "Kristall", QString("Do you want to override the style '%1'?").arg(name));
+ if(response != QMessageBox::Yes)
+ return;
+ override = true;
+ }
+
+ DocumentStyle style;
+ style.load(import_settings);
+
+ this->predefined_styles.insert(name, style);
+
+ if(not override)
+ {
+ this->ui->presets->addItem(name);
+ }
+}
+
+void SettingsDialog::on_preset_export_clicked()
+{
+ QString name = this->ui->presets->currentText();
+ if(name.isEmpty())
+ return;
+
+ QFileDialog dialog { this };
+ dialog.setAcceptMode(QFileDialog::AcceptSave);
+ dialog.selectNameFilter("Kristall Theme (*.kthm)");
+ dialog.selectFile(QString("%1.kthm").arg(name));
+
+ if(dialog.exec() !=QFileDialog::Accepted)
+ return;
+
+ QString fileName = dialog.selectedFiles().at(0);
+
+ QSettings export_settings { fileName, QSettings::IniFormat };
+ export_settings.setValue("name", name);
+ this->predefined_styles.value(name).save(export_settings);
+ export_settings.sync();
+}
+
+void SettingsDialog::on_start_page_textChanged(const QString &start_page)
+{
+ this->current_options.start_page = start_page;
+}
+
+void SettingsDialog::on_ui_theme_currentIndexChanged(int index)
+{
+ this->current_options.theme = Theme(this->ui->ui_theme->itemData(index).toInt());
+}
+
+void SettingsDialog::on_fancypants_on_clicked()
+{
+ this->current_options.text_display = GenericSettings::FormattedText;
+}
+
+void SettingsDialog::on_fancypants_off_clicked()
+{
+ this->current_options.text_display = GenericSettings::PlainText;
+}
+
+void SettingsDialog::on_texthl_on_clicked()
+{
+ this->current_options.enable_text_decoration = true;
+}
+
+void SettingsDialog::on_texthl_off_clicked()
+{
+ this->current_options.enable_text_decoration = false;
+}
+
+void SettingsDialog::on_gophermap_icon_clicked()
+{
+ this->current_options.gophermap_display = GenericSettings::FormattedText;
+}
+
+void SettingsDialog::on_gophermap_text_clicked()
+{
+ this->current_options.gophermap_display = GenericSettings::PlainText;
+}
+
+void SettingsDialog::on_scheme_os_default_clicked()
+{
+ this->current_options.use_os_scheme_handler = true;
+}
+
+void SettingsDialog::on_scheme_error_clicked()
+{
+ this->current_options.use_os_scheme_handler = false;
+}
+
+void SettingsDialog::on_redirection_mode_currentIndexChanged(int index)
+{
+ this->current_options.redirection_policy = GenericSettings::RedirectionWarning(this->ui->redirection_mode->itemData(index).toInt());
+}
+
+void SettingsDialog::on_max_redirects_valueChanged(int max_redirections)
+{
+ this->current_options.max_redirections = max_redirections;
+}
+
+void SettingsDialog::on_network_timeout_valueChanged(int timeout)
+{
+ this->current_options.network_timeout = timeout;
+}
diff --git a/src/dialogs/settingsdialog.hpp b/src/dialogs/settingsdialog.hpp
new file mode 100644
index 0000000..045b861
--- /dev/null
+++ b/src/dialogs/settingsdialog.hpp
@@ -0,0 +1,143 @@
+#ifndef SETTINGSDIALOG_HPP
+#define SETTINGSDIALOG_HPP
+
+#include <QDialog>
+
+#include "renderers/geminirenderer.hpp"
+#include "protocolsetup.hpp"
+#include "documentstyle.hpp"
+#include "ssltrust.hpp"
+#include "kristall.hpp"
+
+namespace Ui {
+class SettingsDialog;
+}
+
+class SettingsDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit SettingsDialog(QWidget *parent = nullptr);
+ ~SettingsDialog();
+
+ void setGeminiStyle(DocumentStyle const & style);
+
+ DocumentStyle geminiStyle() const {
+ return current_style;
+ }
+
+ ProtocolSetup protocols() const;
+ void setProtocols(ProtocolSetup const & proto);
+
+ SslTrust geminiSslTrust() const;
+ void setGeminiSslTrust(SslTrust const & trust);
+
+ SslTrust httpsSslTrust() const;
+ void setHttpsSslTrust(SslTrust const & trust);
+
+ GenericSettings options() const;
+ void setOptions(GenericSettings const & options);
+
+private slots:
+ void on_std_change_font_clicked();
+
+ void on_pre_change_font_clicked();
+
+ void on_h1_change_font_clicked();
+
+ void on_h2_change_font_clicked();
+
+ void on_h3_change_font_clicked();
+
+ void on_std_change_color_clicked();
+
+ void on_pre_change_color_clicked();
+
+ void on_h1_change_color_clicked();
+
+ void on_h2_change_color_clicked();
+
+ void on_h3_change_color_clicked();
+
+ void on_bg_change_color_clicked();
+
+ void on_link_local_change_color_clicked();
+
+ void on_link_foreign_change_color_clicked();
+
+ void on_link_cross_change_color_clicked();
+
+ void on_link_local_prefix_textChanged(const QString &arg1);
+
+ void on_link_foreign_prefix_textChanged(const QString &arg1);
+
+ void on_auto_theme_currentIndexChanged(int index);
+
+ void on_preview_url_textChanged(const QString &arg1);
+
+ void on_page_margin_valueChanged(double arg1);
+
+ void on_presets_currentIndexChanged(int index);
+
+ void on_preset_new_clicked();
+
+ void on_SettingsDialog_accepted();
+
+ void on_quote_change_color_clicked();
+
+ void on_preset_save_clicked();
+
+ void on_preset_load_clicked();
+
+ void on_preset_import_clicked();
+
+ void on_preset_export_clicked();
+
+ void on_start_page_textChanged(const QString &arg1);
+
+ void on_ui_theme_currentIndexChanged(int index);
+
+ void on_fancypants_on_clicked();
+
+ void on_fancypants_off_clicked();
+
+ void on_texthl_on_clicked();
+
+ void on_texthl_off_clicked();
+
+ void on_gophermap_icon_clicked();
+
+ void on_gophermap_text_clicked();
+
+ void on_scheme_os_default_clicked();
+
+ void on_scheme_error_clicked();
+
+ void on_redirection_mode_currentIndexChanged(int index);
+
+ void on_max_redirects_valueChanged(int arg1);
+
+ void on_network_timeout_valueChanged(int arg1);
+
+private:
+ void reloadStylePreview();
+
+ void updateFont(QFont & input);
+
+ void updateColor(QColor & input);
+
+private:
+ Ui::SettingsDialog *ui;
+
+ DocumentStyle current_style;
+ std::unique_ptr<QTextDocument> preview_document;
+
+ QMap<QString, DocumentStyle> predefined_styles;
+
+ SslTrust current_trust;
+
+ GenericSettings current_options;
+};
+
+#endif // SETTINGSDIALOG_HPP
diff --git a/src/dialogs/settingsdialog.ui b/src/dialogs/settingsdialog.ui
new file mode 100644
index 0000000..9acbca6
--- /dev/null
+++ b/src/dialogs/settingsdialog.ui
@@ -0,0 +1,940 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SettingsDialog</class>
+ <widget class="QDialog" name="SettingsDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>850</width>
+ <height>650</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Settings</string>
+ </property>
+ <property name="windowIcon">
+ <iconset theme="settings">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="currentIndex">
+ <number>2</number>
+ </property>
+ <widget class="QWidget" name="generic">
+ <attribute name="icon">
+ <iconset theme="settings">
+ <normaloff>.</normaloff>.</iconset>
+ </attribute>
+ <attribute name="title">
+ <string>Generic</string>
+ </attribute>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_15">
+ <property name="text">
+ <string>UI Theme</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="ui_theme"/>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string>Start Page:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="start_page">
+ <property name="placeholderText">
+ <string>about://blank</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_16">
+ <property name="text">
+ <string>Enabled Protocols</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QCheckBox" name="enable_gemini">
+ <property name="text">
+ <string>Gemini</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="enable_gopher">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Gopher</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="enable_finger">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Finger</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="enable_http">
+ <property name="text">
+ <string>HTTP</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="enable_https">
+ <property name="text">
+ <string>HTTPS</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_19">
+ <property name="text">
+ <string>Text Rendering</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_5">
+ <item>
+ <widget class="QRadioButton" name="fancypants_on">
+ <property name="text">
+ <string>Fancy</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">textRenderingBtnGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="fancypants_off">
+ <property name="text">
+ <string>Always plain text</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">textRenderingBtnGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_18">
+ <property name="text">
+ <string>Enable text highlights</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QRadioButton" name="texthl_on">
+ <property name="text">
+ <string>On (Experimental)</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">textHighlightsBtnGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="texthl_off">
+ <property name="text">
+ <string>Off</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">textHighlightsBtnGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="label_20">
+ <property name="text">
+ <string>Gopher Map</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <widget class="QRadioButton" name="gophermap_icon">
+ <property name="text">
+ <string>Use icons</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">gophermapBtnGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="gophermap_text">
+ <property name="text">
+ <string>Use text only</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">gophermapBtnGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="label_22">
+ <property name="text">
+ <string>Unknown Scheme</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_7">
+ <item>
+ <widget class="QRadioButton" name="scheme_os_default">
+ <property name="text">
+ <string>Use OS default handler</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">buttonGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="scheme_error">
+ <property name="text">
+ <string>Display error message</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">buttonGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="7" column="0">
+ <widget class="QLabel" name="label_26">
+ <property name="text">
+ <string>Max. Number of Redirections</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="1">
+ <widget class="QSpinBox" name="max_redirects">
+ <property name="value">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="0">
+ <widget class="QLabel" name="label_27">
+ <property name="text">
+ <string>Redirection Handling</string>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="1">
+ <widget class="QComboBox" name="redirection_mode"/>
+ </item>
+ <item row="9" column="0">
+ <widget class="QLabel" name="label_28">
+ <property name="text">
+ <string>Network Timeout</string>
+ </property>
+ </widget>
+ </item>
+ <item row="9" column="1">
+ <widget class="QSpinBox" name="network_timeout">
+ <property name="suffix">
+ <string> ms</string>
+ </property>
+ <property name="minimum">
+ <number>100</number>
+ </property>
+ <property name="maximum">
+ <number>90000</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="style_tab">
+ <attribute name="icon">
+ <iconset theme="palette">
+ <normaloff>.</normaloff>.</iconset>
+ </attribute>
+ <attribute name="title">
+ <string>Style</string>
+ </attribute>
+ <layout class="QHBoxLayout" name="horizontalLayout_23">
+ <item>
+ <layout class="QFormLayout" name="formLayout_3">
+ <property name="leftMargin">
+ <number>5</number>
+ </property>
+ <item row="0" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_13">
+ <item>
+ <widget class="QLabel" name="bg_preview">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="bg_change_color">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="palette">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Background Color</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Standard Font</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_14">
+ <item>
+ <widget class="QLabel" name="std_preview">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="text">
+ <string>This text will be displayed for normal text.</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="std_change_font">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="font">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="std_change_color">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="palette">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Preformatted Font</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_15">
+ <item>
+ <widget class="QLabel" name="pre_preview">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="text">
+ <string>This text will be displayed for preformatted text.</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="pre_change_font">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="font">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="pre_change_color">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="palette">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>H1 Font</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_16">
+ <item>
+ <widget class="QLabel" name="h1_preview">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="text">
+ <string>This text will be displayed for a level 1 heading.</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="h1_change_font">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="font">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="h1_change_color">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="palette">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>H2 Font</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_18">
+ <item>
+ <widget class="QLabel" name="h2_preview">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="text">
+ <string>This text will be displayed for a level 2 heading.</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="h2_change_font">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="font">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="h2_change_color">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="palette">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>H3 Font</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_19">
+ <item>
+ <widget class="QLabel" name="h3_preview">
+ <property name="styleSheet">
+ <string notr="true">border: 1px solid black;</string>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="text">
+ <string>This text will be displayed for a level 3 heading.</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="h3_change_font">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="font">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="h3_change_color">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="palette">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>Local Link Color</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>Foreign Link Color</string>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="0">
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>Cross-Scheme-Color</string>
+ </property>
+ </widget>
+ </item>
+ <item row="9" column="0">
+ <widget class="QLabel" name="label_10">
+ <property name="text">
+ <string>Local Link Prefix</string>
+ </property>
+ </widget>
+ </item>
+ <item row="10" column="0">
+ <widget class="QLabel" name="label_11">
+ <property name="text">
+ <string>Extern Link Prefix</string>
+ </property>
+ </widget>
+ </item>
+ <item row="10" column="1">
+ <widget class="QLineEdit" name="link_foreign_prefix">
+ <property name="text">
+ <string>⇒ </string>
+ </property>
+ </widget>
+ </item>
+ <item row="9" column="1">
+ <widget class="QLineEdit" name="link_local_prefix">
+ <property name="text">
+ <string>→ </string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_20">
+ <item>
+ <widget class="QLabel" name="link_local_preview">
+ <property name="text">
+ <string>This is a local reference</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="link_local_change_color">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="palette">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="7" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_21">
+ <item>
+ <widget class="QLabel" name="link_foreign_preview">
+ <property name="text">
+ <string>This is a foreign reference</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="link_foreign_change_color">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="palette">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="8" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_22">
+ <item>
+ <widget class="QLabel" name="link_cross_preview">
+ <property name="text">
+ <string>This reference is cross-scheme</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="link_cross_change_color">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="palette">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="12" column="0">
+ <widget class="QLabel" name="label_12">
+ <property name="text">
+ <string>Auto-Theme Generation</string>
+ </property>
+ </widget>
+ </item>
+ <item row="12" column="1">
+ <widget class="QComboBox" name="auto_theme"/>
+ </item>
+ <item row="13" column="0">
+ <widget class="QLabel" name="label_13">
+ <property name="text">
+ <string>Page Margin</string>
+ </property>
+ </widget>
+ </item>
+ <item row="13" column="1">
+ <widget class="QDoubleSpinBox" name="page_margin">
+ <property name="suffix">
+ <string> px</string>
+ </property>
+ <property name="decimals">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <double>350.000000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item row="14" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QComboBox" name="presets"/>
+ </item>
+ <item>
+ <widget class="QToolButton" name="preset_new">
+ <property name="toolTip">
+ <string>Save as new preset</string>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset theme="create-new">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="preset_save">
+ <property name="toolTip">
+ <string>Override current preset</string>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset theme="save">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="preset_load">
+ <property name="toolTip">
+ <string>Load preset</string>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset theme="folder">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="preset_import">
+ <property name="toolTip">
+ <string>Imports preset…</string>
+ </property>
+ <property name="toolTipDuration">
+ <number>-1</number>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset theme="save-import">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="preset_export">
+ <property name="toolTip">
+ <string>Export preset…</string>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset theme="save-export">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="14" column="0">
+ <widget class="QLabel" name="label_17">
+ <property name="text">
+ <string>Presets</string>
+ </property>
+ </widget>
+ </item>
+ <item row="11" column="0">
+ <widget class="QLabel" name="label_21">
+ <property name="text">
+ <string>Block Quote Background</string>
+ </property>
+ </widget>
+ </item>
+ <item row="11" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_6">
+ <item>
+ <widget class="QLabel" name="quote_preview">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="quote_change_color">
+ <property name="text">
+ <string>...</string>
+ </property>
+ <property name="icon">
+ <iconset theme="palette">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QTextBrowser" name="style_preview">
+ <property name="openLinks">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="preview_url">
+ <property name="placeholderText">
+ <string>host.name</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="gem_trust_page">
+ <attribute name="icon">
+ <iconset theme="certificate">
+ <normaloff>.</normaloff>.</iconset>
+ </attribute>
+ <attribute name="title">
+ <string>Gemini TLS</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="SslTrustEditor" name="gemini_trust_editor" native="true"/>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="https_trust">
+ <attribute name="icon">
+ <iconset theme="certificate">
+ <normaloff>.</normaloff>.</iconset>
+ </attribute>
+ <attribute name="title">
+ <string>HTTPS TLS</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="SslTrustEditor" name="https_trust_editor" native="true"/>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>SslTrustEditor</class>
+ <extends>QWidget</extends>
+ <header>widgets/ssltrusteditor.hpp</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>SettingsDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>325</x>
+ <y>470</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>SettingsDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>257</x>
+ <y>470</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+ <buttongroups>
+ <buttongroup name="buttonGroup"/>
+ <buttongroup name="textHighlightsBtnGroup"/>
+ <buttongroup name="gophermapBtnGroup"/>
+ <buttongroup name="textRenderingBtnGroup"/>
+ </buttongroups>
+</ui>