From 8c9480f6fc1b73b2f9ca5c1463a8ee10a579712d Mon Sep 17 00:00:00 2001 From: "Felix (xq) Queißner" Date: Mon, 8 Jun 2020 18:36:35 +0200 Subject: Improves gopher map rendering with icons and monospace font, adds status bar with more infos, fixes file dialog for older qt versions. --- src/browsertab.cpp | 38 ++++++++++-------------- src/browsertab.hpp | 3 ++ src/gophermaprenderer.cpp | 66 +++++++++++++++++++++++++++++------------- src/icons.qrc | 12 ++++++++ src/icons/gopher/binary.svg | 1 + src/icons/gopher/directory.svg | 1 + src/icons/gopher/dns.svg | 1 + src/icons/gopher/error.svg | 1 + src/icons/gopher/gif.svg | 1 + src/icons/gopher/html.svg | 1 + src/icons/gopher/image.svg | 1 + src/icons/gopher/mirror.svg | 1 + src/icons/gopher/search.svg | 1 + src/icons/gopher/sound.svg | 1 + src/icons/gopher/telnet.svg | 1 + src/icons/gopher/text.svg | 1 + src/ioutil.cpp | 20 +++++++++++++ src/ioutil.hpp | 2 ++ src/mainwindow.cpp | 52 ++++++++++++++++++++++++++------- src/mainwindow.hpp | 5 ++++ 20 files changed, 158 insertions(+), 52 deletions(-) create mode 100644 src/icons/gopher/binary.svg create mode 100644 src/icons/gopher/directory.svg create mode 100644 src/icons/gopher/dns.svg create mode 100644 src/icons/gopher/error.svg create mode 100644 src/icons/gopher/gif.svg create mode 100644 src/icons/gopher/html.svg create mode 100644 src/icons/gopher/image.svg create mode 100644 src/icons/gopher/mirror.svg create mode 100644 src/icons/gopher/search.svg create mode 100644 src/icons/gopher/sound.svg create mode 100644 src/icons/gopher/telnet.svg create mode 100644 src/icons/gopher/text.svg (limited to 'src') diff --git a/src/browsertab.cpp b/src/browsertab.cpp index 26f1ab2..412c652 100644 --- a/src/browsertab.cpp +++ b/src/browsertab.cpp @@ -4,6 +4,8 @@ #include "geminirenderer.hpp" #include "settingsdialog.hpp" #include "gophermaprenderer.hpp" +#include "ioutil.hpp" + #include #include #include @@ -67,6 +69,8 @@ void BrowserTab::navigateTo(const QUrl &url, PushToHistory mode) return; } + this->timer.start(); + this->current_location = url; this->ui->url_bar->setText(url.toString()); @@ -233,23 +237,6 @@ void BrowserTab::on_requestFailed(const QString &reason) this->setErrorMessage(QString("Request failed:\n%1").arg(reason)); } -QString size_human(int size) -{ - float num = size; - QStringList list; - list << "KB" << "MB" << "GB" << "TB"; - - QStringListIterator i(list); - QString unit("bytes"); - - while(num >= 1024.0 && i.hasNext()) - { - unit = i.next(); - num /= 1024.0; - } - return QString().setNum(num,'f',2)+" "+unit; -} - void BrowserTab::on_requestComplete(const QByteArray &data, const QString &mime) { qDebug() << "Loaded" << data.length() << "bytes of type" << mime; @@ -339,7 +326,7 @@ Use the *File* menu to save the file to your local disk or navigate somewhere el Info: MIME Type: %1 File Size: %2 -)md").arg(mime).arg(size_human(data.size()))); +)md").arg(mime).arg(IoUtil::size_human(data.size()))); } assert((document != nullptr) == (doc_type == Text)); @@ -356,6 +343,8 @@ File Size: %2 QString title = this->current_location.toString(); emit this->titleChanged(title); + emit this->fileLoaded(data.size(), mime, this->timer.elapsed()); + this->successfully_loaded = true; if(this->push_to_history_after_load) { @@ -520,10 +509,15 @@ void BrowserTab::on_text_browser_anchorClicked(const QUrl &url) void BrowserTab::on_text_browser_highlighted(const QUrl &url) { - QUrl real_url = url; - if(real_url.isRelative()) - real_url = this->current_location.resolved(url); - this->mainWindow->setUrlPreview(real_url); + if(url.isValid()) { + QUrl real_url = url; + if(real_url.isRelative()) + real_url = this->current_location.resolved(url); + this->mainWindow->setUrlPreview(real_url); + } + else { + this->mainWindow->setUrlPreview(QUrl { }); + } } void BrowserTab::on_stop_button_clicked() diff --git a/src/browsertab.hpp b/src/browsertab.hpp index 815f239..7fc39f6 100644 --- a/src/browsertab.hpp +++ b/src/browsertab.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "geminiclient.hpp" #include "documentoutlinemodel.hpp" @@ -54,6 +55,7 @@ public: signals: void titleChanged(QString const & title); void locationChanged(QUrl const & url); + void fileLoaded(qint64 fileSize, QString const & mime, int msec); private slots: void on_url_bar_returnPressed(); @@ -123,6 +125,7 @@ public: QByteArray current_buffer; QString current_mime; + QElapsedTimer timer; }; #endif // BROWSERTAB_HPP diff --git a/src/gophermaprenderer.cpp b/src/gophermaprenderer.cpp index 96e0557..52f4442 100644 --- a/src/gophermaprenderer.cpp +++ b/src/gophermaprenderer.cpp @@ -7,6 +7,7 @@ #include #include +#include //Canonical Types //0 Text File @@ -31,11 +32,11 @@ std::unique_ptr GophermapRenderer::render(const QByteArray &input, const QUrl &root_url, const DocumentStyle &themed_style) { QTextCharFormat standard; - standard.setFont(themed_style.standard_font); - standard.setForeground(themed_style.standard_color); + standard.setFont(themed_style.preformatted_font); + standard.setForeground(themed_style.preformatted_color); QTextCharFormat standard_link; - standard_link.setFont(themed_style.standard_font); + standard_link.setFont(themed_style.preformatted_font); standard_link.setForeground(QBrush(themed_style.internal_link_color)); QTextCharFormat external_link; @@ -45,21 +46,34 @@ std::unique_ptr GophermapRenderer::render(const QByteArray &input std::unique_ptr result = std::make_unique(); result->setDocumentMargin(themed_style.margin); + result->addResource(QTextDocument::ImageResource, QUrl("gopher/binary"), QVariant::fromValue(QImage(":/icons/gopher/binary.svg"))); + result->addResource(QTextDocument::ImageResource, QUrl("gopher/directory"), QVariant::fromValue(QImage(":/icons/gopher/directory.svg"))); + result->addResource(QTextDocument::ImageResource, QUrl("gopher/dns"), QVariant::fromValue(QImage(":/icons/gopher/dns.svg"))); + result->addResource(QTextDocument::ImageResource, QUrl("gopher/error"), QVariant::fromValue(QImage(":/icons/gopher/error.svg"))); + result->addResource(QTextDocument::ImageResource, QUrl("gopher/gif"), QVariant::fromValue(QImage(":/icons/gopher/gif.svg"))); + result->addResource(QTextDocument::ImageResource, QUrl("gopher/html"), QVariant::fromValue(QImage(":/icons/gopher/html.svg"))); + result->addResource(QTextDocument::ImageResource, QUrl("gopher/image"), QVariant::fromValue(QImage(":/icons/gopher/image.svg"))); + result->addResource(QTextDocument::ImageResource, QUrl("gopher/mirror"), QVariant::fromValue(QImage(":/icons/gopher/mirror.svg"))); + result->addResource(QTextDocument::ImageResource, QUrl("gopher/search"), QVariant::fromValue(QImage(":/icons/gopher/search.svg"))); + result->addResource(QTextDocument::ImageResource, QUrl("gopher/sound"), QVariant::fromValue(QImage(":/icons/gopher/sound.svg"))); + result->addResource(QTextDocument::ImageResource, QUrl("gopher/telnet"), QVariant::fromValue(QImage(":/icons/gopher/telnet.svg"))); + result->addResource(QTextDocument::ImageResource, QUrl("gopher/text"), QVariant::fromValue(QImage(":/icons/gopher/text.svg"))); + QTextCursor cursor{result.get()}; QTextBlockFormat non_list_format = cursor.blockFormat(); QList lines = input.split('\n'); - for (auto const & line : lines) + for (auto const &line : lines) { if (line.length() < 2) // skip lines without continue; - if(line[line.size() - 1] != '\r') + if (line[line.size() - 1] != '\r') continue; auto items = line.mid(1, line.length() - 2).split('\t'); - if(items.size() < 2) // invalid + if (items.size() < 2) // invalid continue; QString icon; @@ -72,22 +86,22 @@ std::unique_ptr GophermapRenderer::render(const QByteArray &input icon = "directory"; break; case '2': // CCSO Nameserver - icon = "ns"; + icon = "dns"; break; case '3': // Error code returned by a Gopher server to indicate failure icon = "error"; break; case '4': // BinHex-encoded file (primarily for Macintosh computers) - icon = "binhex"; + icon = "binary"; break; case '5': // DOS file - icon = "dos"; + icon = "binary"; break; case '6': // uuencoded file - icon = "uuencoded"; + icon = "binary"; break; case '7': // Gopher full-text search - icon = "full-text"; + icon = "search"; break; case '8': // Telnet icon = "telnet"; @@ -117,7 +131,7 @@ std::unique_ptr GophermapRenderer::render(const QByteArray &input case 's': // Sound file icon = "sound"; break; - default: // unknown + default: // unknown continue; } @@ -125,34 +139,46 @@ std::unique_ptr GophermapRenderer::render(const QByteArray &input // 1Phlog /phlog octotherp.org 70 + - if(line.at(0) == 'i') + if (line.at(0) == 'i') { cursor.insertText(title + "\n", standard); } else { QString dst_url; - switch(items.size()) + switch (items.size()) { - case 0: assert(false); - case 1: assert(false); + case 0: + assert(false); + case 1: + assert(false); case 2: dst_url = root_url.resolved(QUrl(items.at(1))).toString(); break; case 3: - dst_url = "gopher://" + items.at(2) + "/" + line.mid(0,1) + items.at(1); + dst_url = "gopher://" + items.at(2) + "/" + line.mid(0, 1) + items.at(1); break; default: - dst_url = "gopher://" + items.at(2) + ":" + items.at(3) + "/" + line.mid(0,1) + items.at(1); + dst_url = "gopher://" + items.at(2) + ":" + items.at(3) + "/" + line.mid(0, 1) + items.at(1); break; } - if(not QUrl(dst_url).isValid()) { + if (not QUrl(dst_url).isValid()) + { // invlaid URL generated qDebug() << line << dst_url; } - cursor.insertText("[" + icon + "] ", standard); + QTextImageFormat icon_fmt; + icon_fmt.setName(QString("gopher/%1").arg(icon)); + icon_fmt.setVerticalAlignment(QTextImageFormat::AlignTop); + + cursor.insertImage(icon_fmt); + + // cursor.insertImage(QImage(QString(":/icons/gopher/%1.svg").arg(icon))); + + cursor.insertText(" "); + // cursor.insertText("[" + icon + "] ", standard); QTextCharFormat fmt = standard_link; fmt.setAnchor(true); diff --git a/src/icons.qrc b/src/icons.qrc index a475b23..028e0db 100644 --- a/src/icons.qrc +++ b/src/icons.qrc @@ -21,5 +21,17 @@ icons/volume-off.svg icons/play.svg icons/pause.svg + icons/gopher/binary.svg + icons/gopher/directory.svg + icons/gopher/dns.svg + icons/gopher/error.svg + icons/gopher/gif.svg + icons/gopher/html.svg + icons/gopher/image.svg + icons/gopher/mirror.svg + icons/gopher/search.svg + icons/gopher/sound.svg + icons/gopher/telnet.svg + icons/gopher/text.svg diff --git a/src/icons/gopher/binary.svg b/src/icons/gopher/binary.svg new file mode 100644 index 0000000..e731a17 --- /dev/null +++ b/src/icons/gopher/binary.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/gopher/directory.svg b/src/icons/gopher/directory.svg new file mode 100644 index 0000000..09ce7cc --- /dev/null +++ b/src/icons/gopher/directory.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/gopher/dns.svg b/src/icons/gopher/dns.svg new file mode 100644 index 0000000..ec52048 --- /dev/null +++ b/src/icons/gopher/dns.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/gopher/error.svg b/src/icons/gopher/error.svg new file mode 100644 index 0000000..8cd654a --- /dev/null +++ b/src/icons/gopher/error.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/gopher/gif.svg b/src/icons/gopher/gif.svg new file mode 100644 index 0000000..5a53e14 --- /dev/null +++ b/src/icons/gopher/gif.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/gopher/html.svg b/src/icons/gopher/html.svg new file mode 100644 index 0000000..2c65f32 --- /dev/null +++ b/src/icons/gopher/html.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/gopher/image.svg b/src/icons/gopher/image.svg new file mode 100644 index 0000000..5c61724 --- /dev/null +++ b/src/icons/gopher/image.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/gopher/mirror.svg b/src/icons/gopher/mirror.svg new file mode 100644 index 0000000..c879c79 --- /dev/null +++ b/src/icons/gopher/mirror.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/gopher/search.svg b/src/icons/gopher/search.svg new file mode 100644 index 0000000..eb42571 --- /dev/null +++ b/src/icons/gopher/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/gopher/sound.svg b/src/icons/gopher/sound.svg new file mode 100644 index 0000000..36d700f --- /dev/null +++ b/src/icons/gopher/sound.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/gopher/telnet.svg b/src/icons/gopher/telnet.svg new file mode 100644 index 0000000..77b5d13 --- /dev/null +++ b/src/icons/gopher/telnet.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/icons/gopher/text.svg b/src/icons/gopher/text.svg new file mode 100644 index 0000000..4a23bd3 --- /dev/null +++ b/src/icons/gopher/text.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/ioutil.cpp b/src/ioutil.cpp index 047a901..00caba6 100644 --- a/src/ioutil.cpp +++ b/src/ioutil.cpp @@ -14,3 +14,23 @@ bool IoUtil::writeAll(QIODevice &dst, QByteArray const & src) return true; } + +QString IoUtil::size_human(qint64 size) +{ + if(size < 1024) + return QString("%1 B").arg(size); + + float num = size; + QStringList list; + list << "KB" << "MB" << "GB" << "TB"; + + QStringListIterator i(list); + QString unit("B"); + + while(num >= 1024.0 && i.hasNext()) + { + unit = i.next(); + num /= 1024.0; + } + return QString().setNum(num,'f',2)+" "+unit; +} diff --git a/src/ioutil.hpp b/src/ioutil.hpp index 487c66e..260a556 100644 --- a/src/ioutil.hpp +++ b/src/ioutil.hpp @@ -6,6 +6,8 @@ struct IoUtil { static bool writeAll(QIODevice & dst, QByteArray const & src); + + static QString size_human(qint64 size); }; #endif // IOUTIL_HPP diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 9171924..96ae1da 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -10,13 +10,17 @@ #include #include #include +#include "ioutil.hpp" MainWindow::MainWindow(QApplication * app, QWidget *parent) : QMainWindow(parent), application(app), settings("xqTechnologies", "Kristall"), ui(new Ui::MainWindow), - url_status(new QLabel()) + url_status(new QLabel(this)), + file_size(new QLabel(this)), + file_mime(new QLabel(this)), + load_time(new QLabel(this)) { if(not this->settings.contains("start_page")) { this->settings.setValue("start_page", "about:favourites"); @@ -25,6 +29,9 @@ MainWindow::MainWindow(QApplication * app, QWidget *parent) : ui->setupUi(this); this->statusBar()->addWidget(this->url_status); + this->statusBar()->addPermanentWidget(this->file_mime); + this->statusBar()->addPermanentWidget(this->file_size); + this->statusBar()->addPermanentWidget(this->load_time); this->favourites.load(settings); this->current_style.load(settings); @@ -87,6 +94,7 @@ BrowserTab * MainWindow::addEmptyTab(bool focus_new, bool load_default) BrowserTab * tab = new BrowserTab(this); connect(tab, &BrowserTab::titleChanged, this, &MainWindow::on_tab_titleChanged); + connect(tab, &BrowserTab::fileLoaded, this, &MainWindow::on_tab_fileLoaded); int index = this->ui->browser_tabs->addTab(tab, "Page"); @@ -322,15 +330,25 @@ void MainWindow::on_actionSave_as_triggered() { BrowserTab * tab = qobject_cast(this->ui->browser_tabs->currentWidget()); if(tab != nullptr) { -#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) - QFileDialog::saveFileContent( - tab->current_buffer, - tab->current_location.fileName() - ); -#else - // TODO: Implement without saveFileContent - QMessageBox::warning(this, "Kristall", "Not support in your Qt version."); -#endif + QFileDialog dialog { this }; + dialog.setAcceptMode(QFileDialog::AcceptSave); + dialog.selectFile(tab->current_location.fileName()); + + if(dialog.exec() !=QFileDialog::Accepted) + return; + + QString fileName = dialog.selectedFiles().at(0); + + QFile file { fileName }; + + if(file.open(QFile::WriteOnly)) + { + IoUtil::writeAll(file, tab->current_buffer); + } + else + { + QMessageBox::warning(this, "Kristall", QString("Could not save file:\r\n%1").arg(file.errorString())); + } } } @@ -349,3 +367,17 @@ void MainWindow::on_actionAdd_to_favourites_triggered() tab->toggleIsFavourite(); } } + +void MainWindow::on_tab_fileLoaded(qint64 fileSize, const QString &mime, int msec) +{ + auto * tab = qobject_cast(sender()); + if(tab != nullptr) { + int index = this->ui->browser_tabs->indexOf(tab); + assert(index >= 0); + if(index == this->ui->browser_tabs->currentIndex()) { + this->file_size->setText(IoUtil::size_human(fileSize)); + this->file_mime->setText(mime); + this->load_time->setText(QString("%1 ms").arg(msec)); + } + } +} diff --git a/src/mainwindow.hpp b/src/mainwindow.hpp index 9a460a8..557744d 100644 --- a/src/mainwindow.hpp +++ b/src/mainwindow.hpp @@ -73,6 +73,8 @@ private slots: void on_actionAdd_to_favourites_triggered(); + void on_tab_fileLoaded(qint64 fileSize, QString const & mime, int msec); + private: void reloadTheme(); @@ -86,5 +88,8 @@ private: Ui::MainWindow *ui; QLabel * url_status; + QLabel * file_size; + QLabel * file_mime; + QLabel * load_time; }; #endif // MAINWINDOW_HPP -- cgit v1.2.3