diff options
author | Dominick Allen <djallen@librehumanitas.org> | 2024-09-29 09:28:28 -0500 |
---|---|---|
committer | Dominick Allen <djallen@librehumanitas.org> | 2024-09-29 09:28:28 -0500 |
commit | 0e648c7c62944cd81aa57f10ac01cdabe2f2b7e0 (patch) | |
tree | a2a0912e33e7a18f6c9e32e5e4b5fb0a4ba622bc /src | |
parent | fa4b4097d3283e1d6e6376c70910e245f0b1f6ec (diff) |
Remember various settings.
Diffstat (limited to 'src')
-rw-r--r-- | src/archive.cpp | 85 | ||||
-rw-r--r-- | src/archive.hpp | 13 | ||||
-rw-r--r-- | src/config.hpp | 18 | ||||
-rw-r--r-- | src/fud_mem.cpp | 19 | ||||
-rw-r--r-- | src/getsuyomi.cpp | 8 | ||||
-rw-r--r-- | src/getsuyomi.hpp | 5 | ||||
-rw-r--r-- | src/main.cpp | 4 | ||||
-rw-r--r-- | src/main_window.cpp | 159 | ||||
-rw-r--r-- | src/main_window.hpp | 39 |
9 files changed, 310 insertions, 40 deletions
diff --git a/src/archive.cpp b/src/archive.cpp index 0339d85..1ce79b4 100644 --- a/src/archive.cpp +++ b/src/archive.cpp @@ -6,26 +6,81 @@ namespace getsuyomi { +using fud::FudStatus; + ZipArchive::ZipArchive(const QString& filename) { auto filenameUtf8 = filename.toUtf8(); qDebug("Open file %s", filenameUtf8.data()); int err; m_archive = zip_open(filenameUtf8.data(), 0, &err); + if (m_archive == nullptr) { zip_error_t error; zip_error_init_with_code(&error, err); qCritical("%s: cannot open zip archive '%s': %s", AppName, filenameUtf8.data(), zip_error_strerror(&error)); zip_error_fini(&error); - throw std::runtime_error("Bad zip file"); + return; } - populate(); + auto status = populate(); + if (status != FudStatus::Success) { + cleanup(); + return; + } +} + +ZipArchive::ZipArchive(ZipArchive&& rhs) + :m_archive{rhs.m_archive}, + m_sortedIndices{std::move(rhs.m_sortedIndices)}, + m_filenames{std::move(rhs.m_filenames)}, + m_fileSizes{std::move(rhs.m_fileSizes)}, + m_pages{std::move(rhs.m_pages)} +{ + rhs.m_archive = nullptr; +} + +ZipArchive::~ZipArchive() +{ + cleanup(); } -void ZipArchive::populate() +ZipArchive& ZipArchive::operator=(ZipArchive&& rhs) { - auto numEntries = zip_get_num_entries(m_archive, ZIP_FL_UNCHANGED); + m_archive = rhs.m_archive; + m_sortedIndices = std::move(rhs.m_sortedIndices); + m_filenames = std::move(rhs.m_filenames); + m_fileSizes = std::move(rhs.m_fileSizes); + m_pages = std::move(rhs.m_pages); + + rhs.m_archive = nullptr; + + return *this; +} + +bool ZipArchive::valid() const { + return m_archive != nullptr; +} + +void ZipArchive::cleanup() { + if (m_archive != nullptr) { + zip_discard(m_archive); + m_archive = nullptr; + } +} + +fud::FudStatus ZipArchive::populate() +{ + if (!valid()) { + qCritical("Invalid zipArchive"); + return FudStatus::ObjectInvalid; + } + + auto numEntriesResult = zip_get_num_entries(m_archive, ZIP_FL_UNCHANGED); + if (numEntriesResult < 0) { + return FudStatus::Failure; + } + size_t numEntries = static_cast<size_t>(numEntriesResult); qDebug("%zu pages", numEntries); struct NameIndex { @@ -42,6 +97,9 @@ void ZipArchive::populate() struct zip_stat stats; auto status = zip_stat_index(m_archive, idx, 0, &stats); + if (status < 0) { + return FudStatus::Failure; + } static_assert(ZIP_STAT_NAME != 0); static_assert(ZIP_STAT_SIZE != 0); static_assert(ZIP_STAT_INDEX != 0); @@ -62,6 +120,11 @@ void ZipArchive::populate() m_pages.emplace_back(std::nullopt); } + + if (m_pages.empty()) { + return FudStatus::Empty; + } + std::sort(nameIndices.begin(), nameIndices.end(), [](const auto& lhs, const auto& rhs) { return std::lexicographical_compare(lhs.name.begin(), lhs.name.end(), rhs.name.begin(), rhs.name.end()); }); @@ -75,14 +138,8 @@ void ZipArchive::populate() m_filenames.push_back(nameIndex.name); m_fileSizes.push_back(nameIndex.filesize); } -} -ZipArchive::~ZipArchive() -{ - if (m_archive != nullptr) { - zip_discard(m_archive); - m_archive = nullptr; - } + return FudStatus::Success; } ArchiveResult ZipArchive::getPage(size_t page) @@ -105,11 +162,11 @@ ArchiveResult ZipArchive::getPage(size_t page) } QByteArray data; - data.resize(m_fileSizes[page]); - auto index = m_sortedIndices[page]; + data.resize(static_cast<qsizetype>(m_fileSizes[page])); + // auto index = m_sortedIndices[page]; qDebug("Reading in page data"); - auto bytesRead = zip_fread(file, data.data(), data.size()); + auto bytesRead = zip_fread(file, data.data(), static_cast<size_t>(data.size())); zip_fclose(file); file = nullptr; diff --git a/src/archive.hpp b/src/archive.hpp index d8464e5..e803bd2 100644 --- a/src/archive.hpp +++ b/src/archive.hpp @@ -9,6 +9,8 @@ #include <vector> #include <zip.h> +#include <fud_status.hpp> + namespace getsuyomi { enum class ArchiveError @@ -26,17 +28,26 @@ class Archive { virtual ~Archive() = default; virtual ArchiveResult getPage(size_t page) = 0; virtual size_t numPages() const = 0; + virtual bool valid() const = 0; }; class ZipArchive : public Archive { public: explicit ZipArchive(const QString& filename); + ZipArchive(const ZipArchive&) = delete; + ZipArchive(ZipArchive&&); virtual ~ZipArchive() override; + ZipArchive& operator=(const ZipArchive&) = delete; + ZipArchive& operator=(ZipArchive&& rhs); + virtual ArchiveResult getPage(size_t page) override; virtual size_t numPages() const override; + bool valid() const override; + private: - void populate(); + void cleanup(); + fud::FudStatus populate(); zip_t* m_archive{nullptr}; diff --git a/src/config.hpp b/src/config.hpp new file mode 100644 index 0000000..cb6a74a --- /dev/null +++ b/src/config.hpp @@ -0,0 +1,18 @@ +#ifndef GETSUYOMI_CONFIG_HPP +#define GETSUYOMI_CONFIG_HPP + +#include <string> + +namespace getsuyomi { + +struct GetsuyomiConfig { + std::string home{}; + std::string dataHome{}; + std::string configHome{}; + std::string stateHome{}; +}; + +} // namespace getsuyomi + + +#endif diff --git a/src/fud_mem.cpp b/src/fud_mem.cpp new file mode 100644 index 0000000..d8f6673 --- /dev/null +++ b/src/fud_mem.cpp @@ -0,0 +1,19 @@ +#include <fud_memory.hpp> + +#include <cstdlib> + +namespace fud { + +void* fudAlloc(size_t size) { + return malloc(size); +} + +void* fudRealloc(void* ptr, size_t size) { + return realloc(ptr, size); +} + +void fudFree(void* ptr) { + return free(ptr); +} + +} // namespace fud diff --git a/src/getsuyomi.cpp b/src/getsuyomi.cpp index 11f273b..5593758 100644 --- a/src/getsuyomi.cpp +++ b/src/getsuyomi.cpp @@ -13,6 +13,12 @@ Getsuyomi::Getsuyomi() setLayout(m_layout); } +Getsuyomi::~Getsuyomi() { + if (m_archive != nullptr) { + delete m_archive; + } +} + void Getsuyomi::setArchive(Archive* archive) { if (archive == m_archive) { return; @@ -38,7 +44,7 @@ void Getsuyomi::next() { } void Getsuyomi::back() { - auto numPages = m_archive->numPages(); + // auto numPages = m_archive->numPages(); size_t decrement = 1; if (m_pageLayout != PageLayout::Single) { decrement = 2; diff --git a/src/getsuyomi.hpp b/src/getsuyomi.hpp index 3d3c092..3f4da25 100644 --- a/src/getsuyomi.hpp +++ b/src/getsuyomi.hpp @@ -21,6 +21,11 @@ class Getsuyomi : public QWidget { public: friend class GetsuyomiApp; Getsuyomi(); + Getsuyomi(const Getsuyomi&) = delete; + Getsuyomi(Getsuyomi&&) = delete; + ~Getsuyomi(); + Getsuyomi& operator=(const Getsuyomi&) = delete; + Getsuyomi& operator=(Getsuyomi&&) = delete; public slots: void setArchive(Archive* archive); diff --git a/src/main.cpp b/src/main.cpp index bac3ff2..56c4563 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,6 +4,10 @@ int main(int argc, char* argv[]) { QApplication app{argc, argv}; getsuyomi::GetsuyomiApp getsuyomi{}; + auto status = getsuyomi.setup(); + if (status != fud::FudStatus::Success) { + return static_cast<int>(status); + } QCommandLineParser parser; parser.setApplicationDescription(QApplication::translate("main", "A comic book and manga reader")); diff --git a/src/main_window.cpp b/src/main_window.cpp index b05507c..5c0c401 100644 --- a/src/main_window.cpp +++ b/src/main_window.cpp @@ -1,22 +1,43 @@ #include "main_window.hpp" +#include "config.hpp" + #include <QString> +#include <filesystem> +#include <fud_result.hpp> +#include <fud_status.hpp> +#include <string> +#include <vector> #include <zip.h> namespace getsuyomi { -GetsuyomiApp::GetsuyomiApp() : m_getsuyomi{new Getsuyomi()} +using fud::FudStatus; +using GetEnvResult = fud::Result<GetsuyomiConfig, FudStatus>; + +constexpr const char* HOME{"HOME"}; + +std::optional<std::string> getEnvVar(const char* envVar); +void getEnvVar(const std::string envVar, std::string& envValue, const char* backup); +GetEnvResult getEnvironment(); + +GetsuyomiApp::GetsuyomiApp() : m_getsuyomi{new Getsuyomi()}, m_qtSettings{new QSettings(AppVendor, AppName)} { - setCentralWidget(m_getsuyomi); - setup(); } -void GetsuyomiApp::setup() +FudStatus GetsuyomiApp::setup() { + setCentralWidget(m_getsuyomi); + QCoreApplication::setApplicationName(AppName); QCoreApplication::setApplicationVersion(AppVersionString); setWindowTitle(AppName); + auto envResult = getEnvironment(); + if (envResult.isError()) { + return envResult.getError(); + } + createActions(); createMenus(); createToolBar(); @@ -25,7 +46,11 @@ void GetsuyomiApp::setup() constexpr int minimumHeight = 480; setMinimumSize(minimumWidth, minimumHeight); + readSettings(); + show(); + + return FudStatus::Success; } void GetsuyomiApp::createActions() @@ -81,18 +106,35 @@ void GetsuyomiApp::createToolBar() void GetsuyomiApp::openFile() { - auto filename = QFileDialog::getOpenFileName( + auto dialog = QFileDialog( this, tr("Open Archive"), - QDir::homePath(), + m_lastOpenedDirectory, tr("Archive types (*.zip *.cbz *.cbr *.gz)")); + dialog.setFileMode(QFileDialog::ExistingFile); + QString filename; + if (dialog.exec()) { + auto filenames = dialog.selectedFiles(); + if (filenames.length() == 0) { + qWarning("No files selected."); + return; + } else if (filenames.length() > 1) { + qWarning("Too many files selected %llu.", filenames.length()); + return; + } + filename = filenames[0]; + m_lastOpenedDirectory = dialog.directory().absolutePath(); + } else { + qWarning("File dialog did not execute"); + return; + } if (filename.endsWith(".zip")) { - try { - auto* archive = new ZipArchive(filename); - m_getsuyomi->setArchive(archive); - } catch (std::runtime_error& exc) { + auto* archive = new ZipArchive(filename); + if (!archive->valid()) { qCritical("Failed to change archive"); + } else { + m_getsuyomi->setArchive(archive); } } else { qCritical("Unsupported file extension"); @@ -101,12 +143,28 @@ void GetsuyomiApp::openFile() void GetsuyomiApp::openDirectory() { - QString directory = QFileDialog::getExistingDirectory( + auto dialog = QFileDialog( this, tr("Open Directory"), - QDir::homePath(), - QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); - qDebug("Open directory %s", qPrintable(directory)); + m_lastOpenedDirectory); + dialog.setFileMode(QFileDialog::Directory); + QString directoryName; + if (dialog.exec()) { + auto filenames = dialog.selectedFiles(); + if (filenames.length() == 0) { + qWarning("No files selected."); + return; + } else if (filenames.length() > 1) { + qWarning("Too many files selected %llu.", filenames.length()); + return; + } + directoryName = filenames[0]; + m_lastOpenedDirectory = dialog.directory().absolutePath(); + } else { + qWarning("File dialog did not execute"); + return; + } + qDebug("Opened directory %s", qPrintable(directoryName)); } void GetsuyomiApp::quit() @@ -126,4 +184,77 @@ void GetsuyomiApp::back() m_getsuyomi->back(); } +void GetsuyomiApp::closeEvent(QCloseEvent* event) +{ + QMainWindow::closeEvent(event); +} + +void GetsuyomiApp::readSettings() +{ + restoreGeometry(m_qtSettings->value("geometry").toByteArray()); + restoreState(m_qtSettings->value("windowState").toByteArray()); + m_lastOpenedDirectory = m_qtSettings->value("lastOpenedDirectory", QDir::homePath()).toString(); + qDebug("ReadSettings - last directory is %s", qPrintable(m_lastOpenedDirectory)); +} + +void GetsuyomiApp::writeSettings() +{ + m_qtSettings->setValue("geometry", saveGeometry()); + m_qtSettings->setValue("windowState", saveState()); + m_qtSettings->setValue("lastOpenedDirectory", m_lastOpenedDirectory); +} + +std::optional<std::string> getEnvVar(const char* envVar) +{ + const QByteArray defaultArray{}; + if (envVar == nullptr) { + return std::nullopt; + } + QByteArray varArray = qgetenv(envVar); + if (varArray == defaultArray) { + return std::nullopt; + } + return varArray.toStdString(); +} + +void getEnvVar(const std::string envVar, std::string& envValue, const char* backup) +{ + auto envValueOpt = getEnvVar(envVar.c_str()); + if (envValueOpt == std::nullopt || envValueOpt->length() == 0) { + std::filesystem::path envValuePath{HOME}; + envValuePath.append(backup); + envValue = envValuePath; + } else { + envValue = *envValueOpt; + } + qDebug("%s is %s", envVar.c_str(), envValue.c_str()); +} + +GetEnvResult getEnvironment() +{ + GetsuyomiConfig config{}; + + auto homeOpt = getEnvVar(HOME); + if (homeOpt == std::nullopt || homeOpt->length() == 0) { + qCritical("Error getting home"); + return GetEnvResult::error(FudStatus::Failure); + } + config.home = *homeOpt; + qDebug("Home is %s", config.home.c_str()); + + /* If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used. */ + const std::string XdgDataHome{"XDG_DATA_HOME"}; + getEnvVar(XdgDataHome, config.dataHome, ".local/share"); + + /* If $XDG_CONFIG_HOME is either not set or empty, a default equal to $HOME/.config should be used. */ + const std::string XdgConfigHome{"XDG_CONFIG_HOME"}; + getEnvVar(XdgConfigHome, config.configHome, ".config"); + + /* If $XDG_STATE_HOME is either not set or empty, a default equal to $HOME/.local/state should be used. */ + const std::string XdgStateHome{"XDG_STATE_HOME"}; + getEnvVar(XdgStateHome, config.stateHome, ".local/state"); + + return GetEnvResult::okay(config); +} + } // namespace getsuyomi diff --git a/src/main_window.hpp b/src/main_window.hpp index bc23b01..de1cb28 100644 --- a/src/main_window.hpp +++ b/src/main_window.hpp @@ -5,10 +5,14 @@ #include "getsuyomi.hpp" #include <QtWidgets> +#include <fud_status.hpp> namespace getsuyomi { -constexpr const char* AppName = "GetsuYomi"; +constexpr const char* AppVendor = "libfud"; + +constexpr const char* AppName = "getsuyomi"; + constexpr const char* AppVersionString = "1.0.0"; class GetsuyomiApp : public QMainWindow { @@ -16,29 +20,44 @@ class GetsuyomiApp : public QMainWindow { public: GetsuyomiApp(); + ~GetsuyomiApp() = default; + GetsuyomiApp(const GetsuyomiApp&) = delete; + GetsuyomiApp(GetsuyomiApp&&) = delete; + GetsuyomiApp& operator=(const GetsuyomiApp&) = delete; + GetsuyomiApp& operator=(GetsuyomiApp&&) = delete; + + fud::FudStatus setup(); private: - void setup(); + /* Private methods */ void createActions(); void createMenus(); void createToolBar(); - Getsuyomi* m_getsuyomi; + /* Private data */ + Getsuyomi* m_getsuyomi{nullptr}; - QAction* m_openFile; - QAction* m_openDirectory; - QAction* m_quitAction; - QAction* m_nextAction; - QAction* m_backAction; + QAction* m_openFile{nullptr}; + QAction* m_openDirectory{nullptr}; + QAction* m_quitAction{nullptr}; + QAction* m_nextAction{nullptr}; + QAction* m_backAction{nullptr}; - QMenu* m_menuBar; - QToolBar* m_toolBar; + QMenu* m_menuBar{nullptr}; + QToolBar* m_toolBar{nullptr}; + + QSettings* m_qtSettings{nullptr}; + QString m_lastOpenedDirectory{}; private slots: void openFile(); void openDirectory(); void quit(); + void readSettings(); + void writeSettings(); + void closeEvent(QCloseEvent* event) override; + void next(); void back(); }; |