diff options
| author | Felix (xq) Queißner <git@mq32.de> | 2020-05-30 19:33:47 +0200 |
|---|---|---|
| committer | Felix (xq) Queißner <git@mq32.de> | 2020-05-30 19:33:47 +0200 |
| commit | ea39cc542e17ce592dc3c4f2053d534bc458d88e (patch) | |
| tree | c3c6a369d5b6d8a6a4e0b3e3667a56ca19e93173 | |
| parent | 79ff338a3427a236ef53adf806c56616faa3426c (diff) | |
| download | kristall-ea39cc542e17ce592dc3c4f2053d534bc458d88e.tar.gz | |
More usability, survives conmans torture nearly with 100%
| -rw-r--r-- | README.md | 10 | ||||
| -rw-r--r-- | browsertab.cpp | 46 | ||||
| -rw-r--r-- | browsertab.hpp | 11 | ||||
| -rw-r--r-- | documentoutlinemodel.cpp | 131 | ||||
| -rw-r--r-- | documentoutlinemodel.hpp | 46 | ||||
| -rw-r--r-- | favouritecollection.cpp | 115 | ||||
| -rw-r--r-- | favouritecollection.hpp | 41 | ||||
| -rw-r--r-- | geminiclient.cpp | 10 | ||||
| -rw-r--r-- | geminiclient.hpp | 2 | ||||
| -rw-r--r-- | geminiwebpage.cpp | 12 | ||||
| -rw-r--r-- | geminiwebpage.hpp | 9 | ||||
| -rw-r--r-- | kristall.pro | 4 | ||||
| -rw-r--r-- | main.cpp | 2 | ||||
| -rw-r--r-- | mainwindow.cpp | 63 | ||||
| -rw-r--r-- | mainwindow.hpp | 19 | ||||
| -rw-r--r-- | mainwindow.ui | 27 |
16 files changed, 520 insertions, 28 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..c62add1 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# Kristall +A high-quality visual cross-platform gemini browser. + +## TODO +- [ ] Survive full torture suite + - [ ] Correctly parse mime parameters + - [ ] Correctly parse charset (0013, 0014) + - [ ] Correctly parse other params (0015) + - [ ] Correctly parse undefined params (0016) + - [ ]
\ No newline at end of file diff --git a/browsertab.cpp b/browsertab.cpp index 7a14c24..46603b6 100644 --- a/browsertab.cpp +++ b/browsertab.cpp @@ -10,7 +10,9 @@ BrowserTab::BrowserTab(MainWindow * mainWindow) : QWidget(nullptr), ui(new Ui::BrowserTab), - mainWindow(mainWindow) + mainWindow(mainWindow), + page(mainWindow), + outline() { ui->setupUi(this); @@ -28,6 +30,8 @@ BrowserTab::BrowserTab(MainWindow * mainWindow) : connect(&page, &GeminiWebPage::navigationRequest, this, &BrowserTab::on_navigationRequest); ui->content->setPage(&page); + + this->updateUI(); } BrowserTab::~BrowserTab() @@ -50,8 +54,11 @@ void BrowserTab::navigateTo(const QUrl &url) } this->redirection_count = 0; + this->successfully_loaded = false; gemini_client.startRequest(url); + + this->updateUI(); } void BrowserTab::on_menu_button_clicked() @@ -97,10 +104,12 @@ void BrowserTab::on_refresh_button_clicked() void BrowserTab::on_gemini_complete(const QByteArray &data, const QString &mime) { if(mime.startsWith("text/gemini")) { - this->page.setHtml(translateGeminiToHtml(data), this->current_location); + this->page.setHtml(translateGeminiToHtml(data, this->outline), this->current_location); } else { this->page.setContent(data, mime); } + this->successfully_loaded = true; + this->updateUI(); } void BrowserTab::on_protocolViolation(const QString &reason) @@ -187,11 +196,13 @@ void BrowserTab::on_permanentFailure(PermanentFailure reason, const QString &inf void BrowserTab::on_transientCertificateRequested(const QString &reason) { QMessageBox::warning(this, "Kristall", "Transient certificate requirested:\n" + reason); + this->updateUI(); } void BrowserTab::on_authorisedCertificateRequested(const QString &reason) { QMessageBox::warning(this, "Kristall", "Authorized certificate requirested:\n" + reason); + this->updateUI(); } void BrowserTab::on_certificateRejected(CertificateRejection reason, const QString &info) @@ -231,7 +242,9 @@ void BrowserTab::on_navigationRequest(const QUrl &url, bool &allow) void BrowserTab::setErrorMessage(const QString &msg) { - this->page.setContent(QString("An error happened:\n%0").arg(msg).toUtf8(), "text/plain charset=utf-8"); + // this->page.setContent(QString("An error happened:\n%0").arg(msg).toUtf8(), "text/plain charset=utf-8"); + QMessageBox::warning(this, "Kristall", msg); + this->updateUI(); } void BrowserTab::pushToHistory(const QUrl &url) @@ -240,13 +253,30 @@ void BrowserTab::pushToHistory(const QUrl &url) this->updateUI(); } +void BrowserTab::on_fav_button_clicked() +{ + if(this->ui->fav_button->isChecked()) { + this->mainWindow->favourites.add(this->current_location); + } else { + this->mainWindow->favourites.remove(this->current_location); + } + + this->updateUI(); +} + + void BrowserTab::updateUI() { this->ui->back_button->setEnabled(this->navigation_history.size() > 0); this->ui->forward_button->setEnabled(false); + + this->ui->refresh_button->setEnabled(this->successfully_loaded); + + this->ui->fav_button->setEnabled(this->successfully_loaded); + this->ui->fav_button->setChecked(this->mainWindow->favourites.contains(this->current_location)); } -QByteArray BrowserTab::translateGeminiToHtml(const QByteArray &input) +QByteArray BrowserTab::translateGeminiToHtml(const QByteArray &input, DocumentOutlineModel & outline) { QByteArray result; result.append(QString(R"html(<!doctype html> @@ -260,6 +290,8 @@ QByteArray BrowserTab::translateGeminiToHtml(const QByteArray &input) bool verbatim = false; bool listing = false; + outline.beginBuild(); + QList<QByteArray> lines = input.split('\n'); for(auto const & line : lines) { @@ -275,6 +307,7 @@ QByteArray BrowserTab::translateGeminiToHtml(const QByteArray &input) } else { result.append(line); + result.append("\n"); } } else { if(line.startsWith("*")) { @@ -296,16 +329,19 @@ QByteArray BrowserTab::translateGeminiToHtml(const QByteArray &input) if(line.startsWith("###")) { result.append("<h3>"); + outline.appendH3(line.mid(3).trimmed()); result.append(line.mid(3).trimmed()); result.append("</h3>"); } else if(line.startsWith("##")) { result.append("<h2>"); + outline.appendH2(line.mid(2).trimmed()); result.append(line.mid(2).trimmed()); result.append("</h2>"); } else if(line.startsWith("#")) { result.append("<h1>"); + outline.appendH1(line.mid(1).trimmed()); result.append(line.mid(1).trimmed()); result.append("</h1>"); } @@ -349,6 +385,8 @@ QByteArray BrowserTab::translateGeminiToHtml(const QByteArray &input) } } + outline.endBuild(); + result.append(QString(R"html( </body> </html> diff --git a/browsertab.hpp b/browsertab.hpp index 47ee473..ddaf1b4 100644 --- a/browsertab.hpp +++ b/browsertab.hpp @@ -6,6 +6,7 @@ #include "geminiclient.hpp" #include "geminiwebpage.hpp" +#include "documentoutlinemodel.hpp" namespace Ui { class BrowserTab; @@ -61,6 +62,8 @@ private slots: void on_navigationRequest(QUrl const & url, bool & allow); + void on_fav_button_clicked(); + private: void setErrorMessage(QString const & msg); @@ -68,9 +71,9 @@ private: void updateUI(); - QByteArray translateGeminiToHtml(QByteArray const & input); + static QByteArray translateGeminiToHtml(QByteArray const & input, DocumentOutlineModel & outline); -private: +public: Ui::BrowserTab *ui; MainWindow * mainWindow; QUrl current_location; @@ -81,6 +84,10 @@ private: QVector<QUrl> navigation_history; GeminiWebPage page; + + bool successfully_loaded = false; + + DocumentOutlineModel outline; }; #endif // BROWSERTAB_HPP diff --git a/documentoutlinemodel.cpp b/documentoutlinemodel.cpp new file mode 100644 index 0000000..1787d9e --- /dev/null +++ b/documentoutlinemodel.cpp @@ -0,0 +1,131 @@ +#include "documentoutlinemodel.hpp" + +#include <QModelIndex> + +DocumentOutlineModel::DocumentOutlineModel() : + QAbstractItemModel(), + root() +{ + +} + +void DocumentOutlineModel::beginBuild() +{ + beginResetModel(); + root = Node { + nullptr, + "<ROOT>", + 0, + QVector<Node> { }, +}; +} + +void DocumentOutlineModel::appendH1(const QString &title) +{ + root.children.append(Node { + &root, + title, + 1, + QVector<Node> { }, + }); +} + +void DocumentOutlineModel::appendH2(const QString &title) +{ + if(root.children.size() == 0) { + root.children.append(Node { + &root, + "<missing layer>", + 1, + QVector<Node> { }, + }); + } + auto & parent = root.children.last(); + parent.children.append(Node { + &parent, + title, + 2, + QVector<Node> { }, + }); +} + +void DocumentOutlineModel::appendH3(const QString &title) +{ + +} + +void DocumentOutlineModel::endBuild() +{ + endResetModel(); +} + +QModelIndex DocumentOutlineModel::index(int row, int column, const QModelIndex &parent) const +{ + if (not hasIndex(row, column, parent)) + return QModelIndex(); + + Node const * parentItem; + + if (!parent.isValid()) + parentItem = &this->root; + else + parentItem = static_cast<Node*>(parent.internalPointer()); + + Node const * childItem = &parentItem->children[row]; + if (childItem) + return createIndex(row, column, reinterpret_cast<quintptr>(childItem)); + return QModelIndex(); + +} + +QModelIndex DocumentOutlineModel::parent(const QModelIndex &child) const +{ + if (!child.isValid()) + return QModelIndex(); + + Node const *childItem = static_cast<Node const *>(child.internalPointer()); + Node const * parent = childItem->parent; + + if (parent == &root) + return QModelIndex(); + + return createIndex( + parent - parent->parent->children.data(), + 0, + reinterpret_cast<quintptr>(parent)); +} + +int DocumentOutlineModel::rowCount(const QModelIndex &parent) const +{ + Node const *parentItem; + if (parent.column() > 0) + return 0; + + if (!parent.isValid()) + parentItem = &root; + else + parentItem = static_cast<Node const *>(parent.internalPointer()); + + return parentItem->children.size(); +} + +int DocumentOutlineModel::columnCount(const QModelIndex &parent) const +{ + return 1; +} + +QVariant DocumentOutlineModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.column() != 0) + return QVariant(); + + if (role != Qt::DisplayRole) + return QVariant(); + + Node const *item = static_cast<Node const*>(index.internalPointer()); + + return item->title; +} diff --git a/documentoutlinemodel.hpp b/documentoutlinemodel.hpp new file mode 100644 index 0000000..802bd86 --- /dev/null +++ b/documentoutlinemodel.hpp @@ -0,0 +1,46 @@ +#ifndef DOCUMENTOUTLINEMODEL_HPP +#define DOCUMENTOUTLINEMODEL_HPP + +#include <QAbstractItemModel> + +class DocumentOutlineModel : + public QAbstractItemModel +{ + Q_OBJECT +public: + DocumentOutlineModel(); + + void beginBuild(); + + void appendH1(QString const & title); + + void appendH2(QString const & title); + + void appendH3(QString const & title); + + void endBuild(); + +public: + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; + + QModelIndex parent(const QModelIndex &child) const override; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + +private: + struct Node + { + Node * parent; + QString title; + int depth = 0; + QVector<Node> children; + }; + + Node root; +}; + +#endif // DOCUMENTOUTLINEMODEL_HPP diff --git a/favouritecollection.cpp b/favouritecollection.cpp new file mode 100644 index 0000000..79409bb --- /dev/null +++ b/favouritecollection.cpp @@ -0,0 +1,115 @@ +#include "favouritecollection.hpp" + +#include <QFile> + +FavouriteCollection::FavouriteCollection(QObject *parent) : + QAbstractListModel(parent) +{ + +} + +void FavouriteCollection::add(QUrl const & url) +{ + if(contains(url)) + return; + + beginInsertRows(QModelIndex{}, items.size(), items.size() + 1); + items.push_back(url); + endInsertRows(); +} + +void FavouriteCollection::remove(QUrl const & url) +{ + for(int i = 0; i < items.size(); i++) + { + if(items.at(i) == url) { + beginRemoveRows(QModelIndex{}, i, i + 1); + items.removeAt(i); + endRemoveRows(); + return; + } + } +} + +bool FavouriteCollection::contains(const QUrl &url) +{ + for(auto const & item : items) { + if(item == url) + return true; + } + return false; +} + +QUrl FavouriteCollection::get(const QModelIndex &index) const +{ + if(index.isValid()) { + return items.at(index.row()); + } else { + return QUrl { }; + } +} + +bool FavouriteCollection::save(const QString &fileName) const +{ + QFile file(fileName); + if(not file.open(QFile::WriteOnly)) + return false; + + for(auto const & url: items) + { + QByteArray blob = (url.toString() + "\n").toUtf8(); + + qint64 offset = 0; + while(offset < blob.size()) + { + auto len = file.write(blob.data() + offset, blob.size() - offset); + if(len <= 0) { + file.close(); + return false; + } + offset += len; + } + } + + file.close(); + return true; +} + +bool FavouriteCollection::load(const QString &fileName) +{ + QFile file(fileName); + if(not file.open(QFile::ReadOnly)) + return false; + auto data = file.readAll(); + + beginResetModel(); + + items.clear(); + for(auto line : data.split('\n')) { + if(line.size() > 0) { + items.push_back(QUrl(QString::fromUtf8(line))); + } + } + endResetModel(); + + return true; +} + +int FavouriteCollection::rowCount(const QModelIndex &parent) const +{ + return items.size(); +} + +bool FavouriteCollection::setData(const QModelIndex &index, const QVariant &value, int role) +{ + return false; +} + +QVariant FavouriteCollection::data(const QModelIndex &index, int role) const +{ + if(role != Qt::DisplayRole) { + return QVariant{}; + } + return items.at(index.row()).toString(); +} + diff --git a/favouritecollection.hpp b/favouritecollection.hpp new file mode 100644 index 0000000..dd68b93 --- /dev/null +++ b/favouritecollection.hpp @@ -0,0 +1,41 @@ +#ifndef FAVOURITECOLLECTION_HPP +#define FAVOURITECOLLECTION_HPP + +#include <QObject> +#include <QAbstractListModel> +#include <QUrl> + + +class FavouriteCollection : public QAbstractListModel +{ + Q_OBJECT +public: + explicit FavouriteCollection(QObject *parent = nullptr); + + void add(QUrl const & url); + + void remove(QUrl const & url); + + bool contains(QUrl const & url); + + QUrl get(QModelIndex const & index) const ; + + bool save(QString const & fileName) const; + + bool load(QString const & fileName); + +public: + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + +signals: + +private: + QVector<QUrl> items; + +}; + +#endif // FAVOURITECOLLECTION_HPP diff --git a/geminiclient.cpp b/geminiclient.cpp index 4a914a3..57e1679 100644 --- a/geminiclient.cpp +++ b/geminiclient.cpp @@ -7,6 +7,7 @@ GeminiClient::GeminiClient(QObject *parent) : QObject(parent) connect(&socket, &QSslSocket::encrypted, this, &GeminiClient::socketEncrypted); connect(&socket, &QSslSocket::readyRead, this, &GeminiClient::socketReadyRead); connect(&socket, &QSslSocket::disconnected, this, &GeminiClient::socketDisconnected); + connect(&socket, QOverload<const QList<QSslError> &>::of(&QSslSocket::sslErrors), this, &GeminiClient::sslErrors); } bool GeminiClient::startRequest(const QUrl &url) @@ -218,3 +219,12 @@ void GeminiClient::socketDisconnected() emit requestComplete(body, mime_type); } } + +void GeminiClient::sslErrors(const QList<QSslError> &errors) +{ + for(auto const & error : errors) { + qDebug() << error.errorString() ; + } + + socket.ignoreSslErrors(errors); +} diff --git a/geminiclient.hpp b/geminiclient.hpp index 5f0a38a..71256c0 100644 --- a/geminiclient.hpp +++ b/geminiclient.hpp @@ -69,6 +69,8 @@ private slots: void socketDisconnected(); + void sslErrors(const QList<QSslError> &errors); + private: bool is_receiving_body; diff --git a/geminiwebpage.cpp b/geminiwebpage.cpp index 3ac41aa..1cbd130 100644 --- a/geminiwebpage.cpp +++ b/geminiwebpage.cpp @@ -1,6 +1,9 @@ #include "geminiwebpage.hpp" +#include "browsertab.hpp" -GeminiWebPage::GeminiWebPage(QObject *parent) : QWebEnginePage(parent) +GeminiWebPage::GeminiWebPage(MainWindow * container) : + QWebEnginePage(), + main_window(container) { } @@ -32,3 +35,10 @@ bool GeminiWebPage::acceptNavigationRequest(const QUrl &url, QWebEnginePage::Nav return false; } } + +QWebEnginePage *GeminiWebPage::createWindow(QWebEnginePage::WebWindowType type) +{ + auto tab = main_window->addEmptyTab(true); + + return &tab->page; +} diff --git a/geminiwebpage.hpp b/geminiwebpage.hpp index 94c6a82..a5de627 100644 --- a/geminiwebpage.hpp +++ b/geminiwebpage.hpp @@ -3,20 +3,25 @@ #include <QObject> #include <QWebEnginePage> +#include "mainwindow.hpp" class GeminiWebPage : public QWebEnginePage { Q_OBJECT public: - explicit GeminiWebPage(QObject *parent = nullptr); + explicit GeminiWebPage(MainWindow * container); signals: void navigationRequest(QUrl const & url, bool & allow); protected: - bool acceptNavigationRequest(const QUrl &url, NavigationType type, bool isMainFrame); + bool acceptNavigationRequest(const QUrl &url, NavigationType type, bool isMainFrame) override; + QWebEnginePage *createWindow(QWebEnginePage::WebWindowType type) override; + +private: + MainWindow * main_window; }; #endif // GEMINIWEBPAGE_HPP diff --git a/kristall.pro b/kristall.pro index 834ee5b..20a3219 100644 --- a/kristall.pro +++ b/kristall.pro @@ -17,6 +17,8 @@ DEFINES += QT_DEPRECATED_WARNINGS SOURCES += \ browsertab.cpp \ + documentoutlinemodel.cpp \ + favouritecollection.cpp \ geminiclient.cpp \ geminiwebpage.cpp \ main.cpp \ @@ -24,6 +26,8 @@ SOURCES += \ HEADERS += \ browsertab.hpp \ + documentoutlinemodel.hpp \ + favouritecollection.hpp \ geminiclient.hpp \ geminiwebpage.hpp \ mainwindow.hpp @@ -8,7 +8,7 @@ int main(int argc, char *argv[]) QApplication a(argc, argv); MainWindow w; - w.addNewTab(QUrl("gemini://gemini.circumlunar.space/")); + w.addNewTab(true, QUrl("gemini://gemini.circumlunar.space/")); w.show(); return a.exec(); diff --git a/mainwindow.cpp b/mainwindow.cpp index 9e5c3df..75cdeba 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -12,36 +12,77 @@ MainWindow::MainWindow(QWidget *parent) ui->setupUi(this); this->statusBar()->addWidget(this->url_status); + + this->favourites.load("./favourites.db"); + + ui->favourites_view->setModel(&favourites); } MainWindow::~MainWindow() { + this->favourites.save("./favourites.db"); delete ui; } -void MainWindow::addEmptyTab() +BrowserTab * MainWindow::addEmptyTab(bool focus_new) { - auto tab = std::make_unique<BrowserTab>(this); + BrowserTab * tab = new BrowserTab(this); + int index = this->ui->browser_tabs->addTab(tab, "Page"); + if(focus_new) { + this->ui->browser_tabs->setCurrentIndex(index); + } - this->ui->browser_tabs->addTab(tab.release(), "Page"); + return tab; } -void MainWindow::addNewTab(QUrl const & url) +BrowserTab * MainWindow::addNewTab(bool focus_new, QUrl const & url) { - auto tab = std::make_unique<BrowserTab>(this); - + auto tab = addEmptyTab(focus_new); tab->navigateTo(url); - - this->ui->browser_tabs->addTab(tab.release(), "Page"); + return tab; } void MainWindow::setUrlPreview(const QUrl &url) { - if(url.isValid()) - this->url_status->setText(url.toString()); - else + if(url.isValid()) { + auto str = url.toString(); + if(str.length() > 300) { + str = str.mid(0, 300) + "..."; + } + this->url_status->setText(str); + } + else { this->url_status->setText(""); + } +} + + +void MainWindow::on_browser_tabs_currentChanged(int index) +{ + if(index >= 0) { + BrowserTab * tab = qobject_cast<BrowserTab*>(this->ui->browser_tabs->widget(index)); + + if(tab != nullptr) { + this->ui->outline_view->setModel(&tab->outline); + this->ui->outline_view->expandAll(); + } else { + this->ui->outline_view->setModel(nullptr); + } + } else { + this->ui->outline_view->setModel(nullptr); + } } +void MainWindow::on_favourites_view_doubleClicked(const QModelIndex &index) +{ + if(auto url = this->favourites.get(index); url.isValid()) { + this->addNewTab(true, url); + } +} + +void MainWindow::on_browser_tabs_tabCloseRequested(int index) +{ + delete this->ui->browser_tabs->widget(index); +} diff --git a/mainwindow.hpp b/mainwindow.hpp index b9da644..0481011 100644 --- a/mainwindow.hpp +++ b/mainwindow.hpp @@ -4,10 +4,14 @@ #include <QMainWindow> #include <QLabel> +#include "favouritecollection.hpp" + QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE +class BrowserTab; + class MainWindow : public QMainWindow { Q_OBJECT @@ -17,14 +21,25 @@ public: ~MainWindow(); - void addEmptyTab(); - void addNewTab(QUrl const & url); + BrowserTab * addEmptyTab(bool focus_new); + BrowserTab * addNewTab(bool focus_new, QUrl const & url); void setUrlPreview(QUrl const & url); +public: + FavouriteCollection favourites; + +private slots: + void on_browser_tabs_currentChanged(int index); + + void on_favourites_view_doubleClicked(const QModelIndex &index); + + void on_browser_tabs_tabCloseRequested(int index); + private: Ui::MainWindow *ui; QLabel * url_status; + }; #endif // MAINWINDOW_HPP diff --git a/mainwindow.ui b/mainwindow.ui index 5befe13..652857a 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -74,12 +74,23 @@ <number>0</number> </property> <item> - <widget class="QTreeView" name="treeView"/> + <widget class="QTreeView" name="outline_view"> + <property name="autoExpandDelay"> + <number>0</number> + </property> + <attribute name="headerVisible"> + <bool>false</bool> + </attribute> + </widget> </item> </layout> </widget> </widget> <widget class="QDockWidget" name="bookmarks_window"> + <property name="windowIcon"> + <iconset resource="icons.qrc"> + <normaloff>:/icons/heart.svg</normaloff>:/icons/heart.svg</iconset> + </property> <property name="windowTitle"> <string>Bookmarks</string> </property> @@ -101,7 +112,7 @@ <number>0</number> </property> <item> - <widget class="QTreeView" name="treeView_2"/> + <widget class="QListView" name="favourites_view"/> </item> </layout> </widget> @@ -128,7 +139,7 @@ <number>0</number> </property> <item> - <widget class="QListView" name="listView"/> + <widget class="QListView" name="history_view"/> </item> </layout> </widget> @@ -155,12 +166,18 @@ <number>0</number> </property> <item> - <widget class="QTreeView" name="treeView_3"/> + <widget class="QTreeView" name="clientcert_view"> + <property name="headerHidden"> + <bool>true</bool> + </property> + </widget> </item> </layout> </widget> </widget> </widget> - <resources/> + <resources> + <include location="icons.qrc"/> + </resources> <connections/> </ui> |
