mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-12-24 06:49:46 -05:00
Add sorting of HTML export
- Closes #6164 - Implement sorting support in HtmlExporter - Add ExportDialog class and UI, which allows to configure export options.
This commit is contained in:
parent
d3b28f8651
commit
296cbf0df7
@ -2217,14 +2217,6 @@ This is definitely a bug, please report it to the developers.</translation>
|
||||
<source>Failed to open %1. It either does not exist or is not accessible.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export database to HTML file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>HTML file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Writing the HTML file failed.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
@ -3836,6 +3828,47 @@ Would you like to overwrite the existing attachment?</source>
|
||||
<translation>Reset to defaults</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ExportDialog</name>
|
||||
<message>
|
||||
<source>Export options</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>You are about to export your database to an unencrypted file.
|
||||
This will leave your passwords and sensitive information vulnerable!
|
||||
</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export database to HTML file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>HTML file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>database order</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>name (ascending)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>name (descending)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sort entries by...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>unknown</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FdoSecrets::DBusMgr</name>
|
||||
<message>
|
||||
|
@ -145,6 +145,7 @@ set(keepassx_SOURCES
|
||||
gui/entry/EntryHistoryModel.cpp
|
||||
gui/entry/EntryModel.cpp
|
||||
gui/entry/EntryView.cpp
|
||||
gui/export/ExportDialog.cpp
|
||||
gui/group/EditGroupWidget.cpp
|
||||
gui/group/GroupModel.cpp
|
||||
gui/group/GroupView.cpp
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "gui/FileDialog.h"
|
||||
#include "gui/HtmlExporter.h"
|
||||
#include "gui/MessageBox.h"
|
||||
#include "gui/export/ExportDialog.h"
|
||||
#ifdef Q_OS_MACOS
|
||||
#include "gui/osutils/macutils/MacUtils.h"
|
||||
#endif
|
||||
@ -440,6 +441,11 @@ void DatabaseTabWidget::exportToCsv()
|
||||
}
|
||||
}
|
||||
|
||||
void DatabaseTabWidget::handleExportError(const QString& reason)
|
||||
{
|
||||
emit messageGlobal(tr("Writing the HTML file failed.").append("\n").append(reason), MessageWidget::Error);
|
||||
}
|
||||
|
||||
void DatabaseTabWidget::exportToHtml()
|
||||
{
|
||||
auto db = databaseWidgetFromIndex(currentIndex())->database();
|
||||
@ -448,23 +454,9 @@ void DatabaseTabWidget::exportToHtml()
|
||||
return;
|
||||
}
|
||||
|
||||
if (!warnOnExport()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QString fileName = fileDialog()->getSaveFileName(
|
||||
this, tr("Export database to HTML file"), FileDialog::getLastDir("html"), tr("HTML file").append(" (*.html)"));
|
||||
if (fileName.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
FileDialog::saveLastDir("html", fileName, true);
|
||||
|
||||
HtmlExporter htmlExporter;
|
||||
if (!htmlExporter.exportDatabase(fileName, db)) {
|
||||
emit messageGlobal(tr("Writing the HTML file failed.").append("\n").append(htmlExporter.errorString()),
|
||||
MessageWidget::Error);
|
||||
}
|
||||
auto exportDialog = new ExportDialog(db, this);
|
||||
connect(exportDialog, SIGNAL(exportFailed(QString)), SLOT(handleExportError(const QString&)));
|
||||
exportDialog->exec();
|
||||
}
|
||||
|
||||
bool DatabaseTabWidget::warnOnExport()
|
||||
|
@ -100,6 +100,7 @@ private slots:
|
||||
void emitActiveDatabaseChanged();
|
||||
void emitDatabaseLockChanged();
|
||||
void handleDatabaseUnlockDialogFinished(bool accepted, DatabaseWidget* dbWidget);
|
||||
void handleExportError(const QString& reason);
|
||||
|
||||
private:
|
||||
QSharedPointer<Database> execNewDatabaseWizard();
|
||||
|
@ -39,82 +39,87 @@ namespace
|
||||
return QString("<img src=\"data:image/png;base64,") + a.toBase64() + "\"/>";
|
||||
}
|
||||
|
||||
QString formatHTML(const QString& value)
|
||||
{
|
||||
return value.toHtmlEscaped().replace(" ", " ").replace('\n', "<br>");
|
||||
}
|
||||
|
||||
QString formatAttribute(const QString& key,
|
||||
const QString& value,
|
||||
const QString& classname,
|
||||
const QString& templt = QString("<tr><th>%1</th><td class=\"%2\">%3</td></tr>"))
|
||||
{
|
||||
const auto& formatted_attribute = templt;
|
||||
if (!value.isEmpty()) {
|
||||
// Format key as well -> Translations into other languages may have non-standard chars
|
||||
return formatted_attribute.arg(formatHTML(key), classname, formatHTML(value));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QString formatAttribute(const Entry& entry,
|
||||
const QString& key,
|
||||
const QString& value,
|
||||
const QString& classname,
|
||||
const QString& templt = QString("<tr><th>%1</th><td class=\"%2\">%3</td></tr>"))
|
||||
{
|
||||
if (value.isEmpty())
|
||||
return {};
|
||||
return formatAttribute(key, entry.resolveMultiplePlaceholders(value), classname, templt);
|
||||
}
|
||||
|
||||
QString formatEntry(const Entry& entry)
|
||||
{
|
||||
// Here we collect the table rows with this entry's data fields
|
||||
QString item;
|
||||
|
||||
// Output the fixed fields
|
||||
item.append(formatAttribute(entry, QObject::tr("User name"), entry.username(), "username"));
|
||||
|
||||
item.append(formatAttribute(entry, QObject::tr("Password"), entry.password(), "password"));
|
||||
|
||||
if (!entry.url().isEmpty()) {
|
||||
constexpr auto maxlen = 100;
|
||||
QString displayedURL(formatHTML(entry.url()).mid(0, maxlen));
|
||||
|
||||
if (displayedURL.size() == maxlen) {
|
||||
displayedURL.append("…");
|
||||
}
|
||||
|
||||
item.append(formatAttribute(entry,
|
||||
QObject::tr("URL"),
|
||||
entry.url(),
|
||||
"url",
|
||||
R"(<tr><th>%1</th><td class="%2"><a href="%3">%4</a></td></tr>)")
|
||||
.arg(entry.resolveMultiplePlaceholders(displayedURL)));
|
||||
const auto& u = entry.username();
|
||||
if (!u.isEmpty()) {
|
||||
item.append("<tr><th>");
|
||||
item.append(QObject::tr("User name"));
|
||||
item.append("</th><td class=\"username\">");
|
||||
item.append(entry.username().toHtmlEscaped());
|
||||
item.append("</td></tr>");
|
||||
}
|
||||
|
||||
item.append(formatAttribute(entry, QObject::tr("Notes"), entry.notes(), "notes"));
|
||||
const auto& p = entry.password();
|
||||
if (!p.isEmpty()) {
|
||||
item.append("<tr><th>");
|
||||
item.append(QObject::tr("Password"));
|
||||
item.append("</th><td class=\"password\">");
|
||||
item.append(entry.password().toHtmlEscaped());
|
||||
item.append("</td></tr>");
|
||||
}
|
||||
|
||||
const auto& r = entry.url();
|
||||
if (!r.isEmpty()) {
|
||||
item.append("<tr><th>");
|
||||
item.append(QObject::tr("URL"));
|
||||
item.append("</th><td class=\"url\"><a href=\"");
|
||||
item.append(r.toHtmlEscaped());
|
||||
item.append("\">");
|
||||
|
||||
// Restrict the length of what we display of the URL -
|
||||
// even from a paper backup, nobody will every type in
|
||||
// more than 100 characters of a URL
|
||||
constexpr auto maxlen = 100;
|
||||
if (r.size() <= maxlen) {
|
||||
item.append(r.toHtmlEscaped());
|
||||
} else {
|
||||
item.append(r.mid(0, maxlen).toHtmlEscaped());
|
||||
item.append("…");
|
||||
}
|
||||
|
||||
item.append("</a></td></tr>");
|
||||
}
|
||||
|
||||
const auto& n = entry.notes();
|
||||
if (!n.isEmpty()) {
|
||||
item.append("<tr><th>");
|
||||
item.append(QObject::tr("Notes"));
|
||||
item.append("</th><td class=\"notes\">");
|
||||
item.append(entry.notes().toHtmlEscaped().replace("\n", "<br>"));
|
||||
item.append("</td></tr>");
|
||||
}
|
||||
|
||||
// Now add the attributes (if there are any)
|
||||
const auto* const attr = entry.attributes();
|
||||
if (attr && !attr->customKeys().isEmpty()) {
|
||||
for (const auto& key : attr->customKeys()) {
|
||||
item.append(formatAttribute(entry, key, attr->value(key), "attr"));
|
||||
item.append("<tr><th>");
|
||||
item.append(key.toHtmlEscaped());
|
||||
item.append("</th><td class=\"attr\">");
|
||||
item.append(attr->value(key).toHtmlEscaped().replace(" ", " ").replace("\n", "<br>"));
|
||||
item.append("</td></tr>");
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool HtmlExporter::exportDatabase(const QString& filename, const QSharedPointer<const Database>& db)
|
||||
bool HtmlExporter::exportDatabase(const QString& filename,
|
||||
const QSharedPointer<const Database>& db,
|
||||
bool sorted,
|
||||
bool ascending)
|
||||
{
|
||||
QFile file(filename);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
m_error = file.errorString();
|
||||
return false;
|
||||
}
|
||||
return exportDatabase(&file, db);
|
||||
return exportDatabase(&file, db, sorted, ascending);
|
||||
}
|
||||
|
||||
QString HtmlExporter::errorString() const
|
||||
@ -122,7 +127,10 @@ QString HtmlExporter::errorString() const
|
||||
return m_error;
|
||||
}
|
||||
|
||||
bool HtmlExporter::exportDatabase(QIODevice* device, const QSharedPointer<const Database>& db)
|
||||
bool HtmlExporter::exportDatabase(QIODevice* device,
|
||||
const QSharedPointer<const Database>& db,
|
||||
bool sorted,
|
||||
bool ascending)
|
||||
{
|
||||
const auto meta = db->metadata();
|
||||
if (!meta) {
|
||||
@ -171,7 +179,7 @@ bool HtmlExporter::exportDatabase(QIODevice* device, const QSharedPointer<const
|
||||
}
|
||||
|
||||
if (db->rootGroup()) {
|
||||
if (!writeGroup(*device, *db->rootGroup())) {
|
||||
if (!writeGroup(*device, *db->rootGroup(), QString(), sorted, ascending)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -184,7 +192,7 @@ bool HtmlExporter::exportDatabase(QIODevice* device, const QSharedPointer<const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HtmlExporter::writeGroup(QIODevice& device, const Group& group, QString path)
|
||||
bool HtmlExporter::writeGroup(QIODevice& device, const Group& group, QString path, bool sorted, bool ascending)
|
||||
{
|
||||
// Don't output the recycle bin
|
||||
if (&group == group.database()->metadata()->recycleBin()) {
|
||||
@ -199,10 +207,8 @@ bool HtmlExporter::writeGroup(QIODevice& device, const Group& group, QString pat
|
||||
// Output the header for this group (but only if there are
|
||||
// any notes or entries in this group, otherwise we'd get
|
||||
// a header with nothing after it, which looks stupid)
|
||||
const auto& entries = group.entries();
|
||||
const auto notes = group.notes();
|
||||
if (!entries.empty() || !notes.isEmpty()) {
|
||||
|
||||
if (!group.entries().empty() || !notes.isEmpty()) {
|
||||
// Header line
|
||||
auto header = QString("<hr><h2>");
|
||||
header.append(PixmapToHTML(Icons::groupIconPixmap(&group, IconSize::Medium)));
|
||||
@ -227,8 +233,16 @@ bool HtmlExporter::writeGroup(QIODevice& device, const Group& group, QString pat
|
||||
// Begin the table for the entries in this group
|
||||
auto table = QString("<table width=\"100%\">");
|
||||
|
||||
auto entries = group.entries();
|
||||
if (sorted) {
|
||||
std::sort(entries.begin(), entries.end(), [&](Entry* lhs, Entry* rhs) {
|
||||
int cmp = lhs->title().compare(rhs->title(), Qt::CaseInsensitive);
|
||||
return ascending ? cmp < 0 : cmp > 0;
|
||||
});
|
||||
}
|
||||
|
||||
// Output the entries in this group
|
||||
for (const auto entry : entries) {
|
||||
for (const auto* entry : entries) {
|
||||
auto formatted_entry = formatEntry(*entry);
|
||||
|
||||
if (formatted_entry.isEmpty())
|
||||
@ -252,10 +266,17 @@ bool HtmlExporter::writeGroup(QIODevice& device, const Group& group, QString pat
|
||||
return false;
|
||||
}
|
||||
|
||||
auto children = group.children();
|
||||
if (sorted) {
|
||||
std::sort(children.begin(), children.end(), [&](Group* lhs, Group* rhs) {
|
||||
int cmp = lhs->name().compare(rhs->name(), Qt::CaseInsensitive);
|
||||
return ascending ? cmp < 0 : cmp > 0;
|
||||
});
|
||||
}
|
||||
|
||||
// Recursively output the child groups
|
||||
const auto& children = group.children();
|
||||
for (const auto child : children) {
|
||||
if (child && !writeGroup(device, *child, path)) {
|
||||
for (const auto* child : children) {
|
||||
if (child && !writeGroup(device, *child, path, sorted, ascending)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -28,12 +28,22 @@ class QIODevice;
|
||||
class HtmlExporter
|
||||
{
|
||||
public:
|
||||
bool exportDatabase(const QString& filename, const QSharedPointer<const Database>& db);
|
||||
bool exportDatabase(const QString& filename,
|
||||
const QSharedPointer<const Database>& db,
|
||||
bool sorted = true,
|
||||
bool ascending = true);
|
||||
QString errorString() const;
|
||||
|
||||
private:
|
||||
bool exportDatabase(QIODevice* device, const QSharedPointer<const Database>& db);
|
||||
bool writeGroup(QIODevice& device, const Group& group, QString path = QString());
|
||||
bool exportDatabase(QIODevice* device,
|
||||
const QSharedPointer<const Database>& db,
|
||||
bool sorted = true,
|
||||
bool ascending = true);
|
||||
bool writeGroup(QIODevice& device,
|
||||
const Group& group,
|
||||
QString path = QString(),
|
||||
bool sorted = true,
|
||||
bool ascending = true);
|
||||
|
||||
QString m_error;
|
||||
};
|
||||
|
85
src/gui/export/ExportDialog.cpp
Normal file
85
src/gui/export/ExportDialog.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ExportDialog.h"
|
||||
#include "ui_ExportDialog.h"
|
||||
|
||||
#include "gui/FileDialog.h"
|
||||
#include "gui/HtmlExporter.h"
|
||||
|
||||
ExportDialog::ExportDialog(QSharedPointer<const Database> db, DatabaseTabWidget* parent)
|
||||
: QDialog(parent)
|
||||
, m_ui(new Ui::ExportDialog())
|
||||
, m_db(std::move(db))
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
connect(m_ui->buttonBox, SIGNAL(rejected()), SLOT(close()));
|
||||
connect(m_ui->buttonBox, SIGNAL(accepted()), SLOT(exportDatabase()));
|
||||
|
||||
m_ui->sortingStrategy->addItem(getStrategyName(BY_NAME_ASC), BY_NAME_ASC);
|
||||
m_ui->sortingStrategy->addItem(getStrategyName(BY_NAME_DESC), BY_NAME_DESC);
|
||||
m_ui->sortingStrategy->addItem(getStrategyName(BY_DATABASE_ORDER), BY_DATABASE_ORDER);
|
||||
|
||||
m_ui->messageWidget->setCloseButtonVisible(false);
|
||||
m_ui->messageWidget->setAutoHideTimeout(-1);
|
||||
m_ui->messageWidget->showMessage(tr("You are about to export your database to an unencrypted file.\n"
|
||||
"This will leave your passwords and sensitive information vulnerable!\n"),
|
||||
MessageWidget::Warning);
|
||||
}
|
||||
|
||||
ExportDialog::~ExportDialog()
|
||||
{
|
||||
}
|
||||
|
||||
QString ExportDialog::getStrategyName(ExportSortingStrategy strategy)
|
||||
{
|
||||
switch (strategy) {
|
||||
case ExportSortingStrategy::BY_DATABASE_ORDER:
|
||||
return tr("database order");
|
||||
case ExportSortingStrategy::BY_NAME_ASC:
|
||||
return tr("name (ascending)");
|
||||
case ExportSortingStrategy::BY_NAME_DESC:
|
||||
return tr("name (descending)");
|
||||
}
|
||||
return tr("unknown");
|
||||
}
|
||||
|
||||
void ExportDialog::exportDatabase()
|
||||
{
|
||||
auto sortBy = m_ui->sortingStrategy->currentData().toInt();
|
||||
bool ascendingOrder = sortBy == ExportSortingStrategy::BY_NAME_ASC;
|
||||
|
||||
const QString fileName = fileDialog()->getSaveFileName(
|
||||
this, tr("Export database to HTML file"), FileDialog::getLastDir("html"), tr("HTML file").append(" (*.html)"));
|
||||
if (fileName.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
FileDialog::saveLastDir("html", fileName, true);
|
||||
|
||||
HtmlExporter htmlExporter;
|
||||
if (!htmlExporter.exportDatabase(
|
||||
fileName, m_db, sortBy != ExportSortingStrategy::BY_DATABASE_ORDER, ascendingOrder)) {
|
||||
emit exportFailed(htmlExporter.errorString());
|
||||
reject();
|
||||
}
|
||||
|
||||
accept();
|
||||
}
|
58
src/gui/export/ExportDialog.h
Normal file
58
src/gui/export/ExportDialog.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSXC_EXPORTDIALOG_H
|
||||
#define KEEPASSXC_EXPORTDIALOG_H
|
||||
|
||||
#include "core/Database.h"
|
||||
#include "gui/DatabaseTabWidget.h"
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class ExportDialog;
|
||||
}
|
||||
|
||||
class ExportDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ExportDialog(QSharedPointer<const Database> db, DatabaseTabWidget* parent = nullptr);
|
||||
~ExportDialog() override;
|
||||
|
||||
enum ExportSortingStrategy
|
||||
{
|
||||
BY_DATABASE_ORDER = 0,
|
||||
BY_NAME_ASC = 1,
|
||||
BY_NAME_DESC = 2
|
||||
};
|
||||
|
||||
signals:
|
||||
void exportFailed(QString reason);
|
||||
|
||||
private slots:
|
||||
void exportDatabase();
|
||||
|
||||
private:
|
||||
QString getStrategyName(ExportSortingStrategy strategy);
|
||||
|
||||
QScopedPointer<Ui::ExportDialog> m_ui;
|
||||
QSharedPointer<const Database> m_db;
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_EXPORTDIALOG_H
|
79
src/gui/export/ExportDialog.ui
Normal file
79
src/gui/export/ExportDialog.ui
Normal file
@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ExportDialog</class>
|
||||
<widget class="QDialog" name="ExportDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>186</width>
|
||||
<height>164</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Export options</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="MessageWidget" name="messageWidget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="sortingStrategyLabel">
|
||||
<property name="text">
|
||||
<string>Sort entries by...</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>sortingStrategy</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="sortingStrategy">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>MessageWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/MessageWidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
Loading…
Reference in New Issue
Block a user