From 4f26d55e01f9424ba5660cb27076eafba4c88b21 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 19 Feb 2022 10:54:35 -0500 Subject: Reorganized sources --- Makefile | 38 ++++---- biblereader-symbolic.svg | 8 -- biblereader.desktop | 9 -- biblereader.png | Bin 3399 -> 0 bytes biblereader_128.png | Bin 3399 -> 0 bytes biblereader_64.png | Bin 2414 -> 0 bytes files/biblereader-symbolic.svg | 8 ++ files/biblereader.desktop | 9 ++ files/biblereader.png | Bin 0 -> 3399 bytes files/biblereader_128.png | Bin 0 -> 3399 bytes files/biblereader_64.png | Bin 0 -> 2414 bytes header.cc | 210 --------------------------------------- header.h | 31 ------ main.cc | 45 --------- mods.cc | 216 ----------------------------------------- mods.h | 53 ---------- readerview.cc | 98 ------------------- readerview.h | 32 ------ src/header.cc | 210 +++++++++++++++++++++++++++++++++++++++ src/header.h | 31 ++++++ src/main.cc | 45 +++++++++ src/mods.cc | 216 +++++++++++++++++++++++++++++++++++++++++ src/mods.h | 53 ++++++++++ src/readerview.cc | 98 +++++++++++++++++++ src/readerview.h | 32 ++++++ src/sword.cc | 168 ++++++++++++++++++++++++++++++++ src/sword.h | 29 ++++++ sword.cc | 168 -------------------------------- sword.h | 29 ------ 29 files changed, 917 insertions(+), 919 deletions(-) delete mode 100644 biblereader-symbolic.svg delete mode 100644 biblereader.desktop delete mode 100644 biblereader.png delete mode 100644 biblereader_128.png delete mode 100644 biblereader_64.png create mode 100644 files/biblereader-symbolic.svg create mode 100644 files/biblereader.desktop create mode 100644 files/biblereader.png create mode 100644 files/biblereader_128.png create mode 100644 files/biblereader_64.png delete mode 100644 header.cc delete mode 100644 header.h delete mode 100644 main.cc delete mode 100644 mods.cc delete mode 100644 mods.h delete mode 100644 readerview.cc delete mode 100644 readerview.h create mode 100644 src/header.cc create mode 100644 src/header.h create mode 100644 src/main.cc create mode 100644 src/mods.cc create mode 100644 src/mods.h create mode 100644 src/readerview.cc create mode 100644 src/readerview.h create mode 100644 src/sword.cc create mode 100644 src/sword.h delete mode 100644 sword.cc delete mode 100644 sword.h diff --git a/Makefile b/Makefile index 496aac6..9c9eb08 100644 --- a/Makefile +++ b/Makefile @@ -1,31 +1,29 @@ -CC=g++ LIBS=gtkmm-3.0 -CFLAGS=-c -Wall -LDLIBS=-pthread -lbible -SOURCES=header.cc main.cc mods.cc readerview.cc sword.cc +override CXXFLAGS += -MMD -Wall `pkg-config $(LIBS) --cflags` +override LDFLAGS += -pthread -lbible `pkg-config $(LIBS) --libs` +SOURCES=$(wildcard src/*.cc) OBJECTS=$(SOURCES:.cc=.o) +DEPS = $(OBJECTS:.o=.d) EXECUTABLE=biblereader -ifeq ($(PREFIX),) - PREFIX := /usr -endif +PREFIX ?= /usr +EXEC_PREFIX ?= $(PREFIX) +BINDIR ?= $(EXEC_PREFIX)/bin -all: $(SOURCES) $(EXECUTABLE) +$(EXECUTABLE): $(OBJECTS) + $(CXX) $(OBJECTS) -o $@ $(LDFLAGS) + +-include $(DEPS) install: $(EXECUTABLE) - install -d $(DESTDIR)$(PREFIX)/bin/ - install -m 755 $(EXECUTABLE) $(DESTDIR)$(PREFIX)/bin/ + install -d $(DESTDIR)$(BINDIR) + install -m 755 $(EXECUTABLE) $(DESTDIR)$(BINDIR) install -d $(DESTDIR)$(PREFIX)/share/applications/ - install -m 644 biblereader.desktop $(DESTDIR)$(PREFIX)/share/applications/ + install -m 644 files/biblereader.desktop $(DESTDIR)$(PREFIX)/share/applications/ install -d $(DESTDIR)$(PREFIX)/share/icons/hicolor/128x128/apps/ - install -m 644 biblereader_128.png $(DESTDIR)$(PREFIX)/share/icons/hicolor/128x128/apps/biblereader.png + install -m 644 files/biblereader_128.png $(DESTDIR)$(PREFIX)/share/icons/hicolor/128x128/apps/biblereader.png install -d $(DESTDIR)$(PREFIX)/share/icons/hicolor/64x64/apps/ - install -m 644 biblereader_64.png $(DESTDIR)$(PREFIX)/share/icons/hicolor/64x64/apps/biblereader.png - -$(EXECUTABLE): $(OBJECTS) - $(CC) $(OBJECTS) -o $@ $(LDFLAGS) `pkg-config $(LIBS) --libs` $(LDLIBS) - -.cc.o: - $(CC) $(CFLAGS) $< -o $@ `pkg-config $(LIBS) --cflags` + install -m 644 files/biblereader_64.png $(DESTDIR)$(PREFIX)/share/icons/hicolor/64x64/apps/biblereader.png +.PHONY: clean clean: - rm -rf *.o $(EXECUTABLE) + $(RM) $(OBJECTS) $(DEPS) $(EXECUTABLE) diff --git a/biblereader-symbolic.svg b/biblereader-symbolic.svg deleted file mode 100644 index 7854114..0000000 --- a/biblereader-symbolic.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/biblereader.desktop b/biblereader.desktop deleted file mode 100644 index e0f2518..0000000 --- a/biblereader.desktop +++ /dev/null @@ -1,9 +0,0 @@ -[Desktop Entry] -Name=Biblereader -Comment=Read the Word -Exec=biblereader -Icon=biblereader -Terminal=false -Type=Application -Categories=Education -X-Purism-FormFactor=Workstation;Mobile; diff --git a/biblereader.png b/biblereader.png deleted file mode 100644 index 5e12481..0000000 Binary files a/biblereader.png and /dev/null differ diff --git a/biblereader_128.png b/biblereader_128.png deleted file mode 100644 index fc666a3..0000000 Binary files a/biblereader_128.png and /dev/null differ diff --git a/biblereader_64.png b/biblereader_64.png deleted file mode 100644 index b90e219..0000000 Binary files a/biblereader_64.png and /dev/null differ diff --git a/files/biblereader-symbolic.svg b/files/biblereader-symbolic.svg new file mode 100644 index 0000000..7854114 --- /dev/null +++ b/files/biblereader-symbolic.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/files/biblereader.desktop b/files/biblereader.desktop new file mode 100644 index 0000000..e0f2518 --- /dev/null +++ b/files/biblereader.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Name=Biblereader +Comment=Read the Word +Exec=biblereader +Icon=biblereader +Terminal=false +Type=Application +Categories=Education +X-Purism-FormFactor=Workstation;Mobile; diff --git a/files/biblereader.png b/files/biblereader.png new file mode 100644 index 0000000..5e12481 Binary files /dev/null and b/files/biblereader.png differ diff --git a/files/biblereader_128.png b/files/biblereader_128.png new file mode 100644 index 0000000..fc666a3 Binary files /dev/null and b/files/biblereader_128.png differ diff --git a/files/biblereader_64.png b/files/biblereader_64.png new file mode 100644 index 0000000..b90e219 Binary files /dev/null and b/files/biblereader_64.png differ diff --git a/header.cc b/header.cc deleted file mode 100644 index c40e44e..0000000 --- a/header.cc +++ /dev/null @@ -1,210 +0,0 @@ -#include "header.h" -#include "readerview.h" -#include "mods.h" -#include - -Header::Header(ReaderView *reader, Gtk::Window *window) -: back(), forward(), book(), menu(), bookMenu(), menuMenu() -{ - this->mods = new Mods(this, window); - this->reader = reader; - this->window = window; - set_show_close_button(true); - set_title ("Bible Reader"); - set_has_subtitle(false); - //this->override_background_color(*(new Gdk::RGBA("#1d1f21"))); - //this->override_color(*(new Gdk::RGBA("#c5c8c6"))); - - back.set_image_from_icon_name("go-previous-symbolic"); - forward.set_image_from_icon_name("go-next-symbolic"); - menu.set_image_from_icon_name("open-menu-symbolic"); - - back.signal_clicked().connect([reader, this]() { - reader->setChapter(reader->getChapter() - 1); - updateButtons(); - }); - - forward.signal_clicked().connect([reader, this]() { - reader->setChapter(reader->getChapter() + 1); - updateButtons(); - }); - - pack_start(back); - pack_start(book); - pack_start(forward); - pack_end(menu); - - book.set_popover(bookMenu); - menu.set_popover(menuMenu); - - updateButtons(); - updateMenus(); -} - -Header::~Header() {} - -void Header::updateButtons() { - back.set_sensitive(reader->getChapter() > 1); - forward.set_sensitive(reader->getChapter() < reader->getChapterMax()); - if(reader->getBook() == "") { - book.set_label(""); - book.set_sensitive(false); - } else { - book.set_label(reader->getBook() + " " + std::to_string(reader->getChapter())); - book.set_sensitive(true); - } -} - -void Header::updateMenus() { - // Wipe them out and start over - if(bookMenu.get_children().size() != 0) { - delete bookMenu.get_children()[0]; - } - if(menuMenu.get_children().size() != 0) { - delete menuMenu.get_children()[0]; - } - - // Populate bookMenu - Gtk::HBox *outer = Gtk::manage(new Gtk::HBox); - bookMenu.add(*outer); - - //TODO: Disgusting code duplication. Fix with strategy pattern? Or loop? - - Gtk::ScrolledWindow *swl = Gtk::manage(new Gtk::ScrolledWindow); - Gtk::VBox *boxl = Gtk::manage(new Gtk::VBox); - outer->add(*swl); - swl->add(*boxl); - swl->set_propagate_natural_width(true); - swl->set_min_content_height(300); - for(std::string bookName : reader->getAllBooks()) { - Gtk::Button *item = Gtk::manage(new Gtk::Button(bookName)); - item->set_relief(Gtk::ReliefStyle::RELIEF_NONE); - item->signal_clicked().connect([bookName, this]() { - bookMenu.popdown(); - reader->setChapter(1); - reader->setBook(bookName); - updateButtons(); - updateMenus(); - showText(); - }); - boxl->add(*item); - } - - Gtk::ScrolledWindow *swr = Gtk::manage(new Gtk::ScrolledWindow); - Gtk::VBox *boxr = Gtk::manage(new Gtk::VBox); - outer->add(*swr); - swr->add(*boxr); - swr->set_propagate_natural_width(true); - swr->set_min_content_height(300); - for(int chapter = 1; chapter <= reader->getChapterMax(); chapter++) { - Gtk::Button *item = Gtk::manage(new Gtk::Button(std::to_string(chapter))); - item->set_relief(Gtk::ReliefStyle::RELIEF_NONE); - item->signal_clicked().connect([chapter, this]() { - bookMenu.popdown(); - reader->setChapter(chapter); - updateButtons(); - showText(); - }); - boxr->add(*item); - } - - bool *hasAllocated = new bool(false); - outer->signal_size_allocate().connect([this, swl, swr, hasAllocated](Gdk::Rectangle rec) { - if(! *hasAllocated) { - // First figure out the books - auto allBooks = reader->getAllBooks(); - int position = std::find(allBooks.begin(), allBooks.end(), reader->getBookFull()) - allBooks.begin(); - swl->get_vadjustment()->set_value(swl->get_vadjustment()->get_upper() * position / allBooks.size()); - swr->get_vadjustment()->set_value(swr->get_vadjustment()->get_upper() * (reader->getChapter() - 1) / reader->getChapterMax()); - } - *hasAllocated = true; - }); - - bookMenu.show_all_children(); - book.signal_pressed().connect([this, hasAllocated, swl, swr]() { - *hasAllocated = false; - }); - - // Populate menuMenu - Gtk::VBox *outerBox = Gtk::manage(new Gtk::VBox); - menuMenu.add(*outerBox); - - Gtk::VBox *versionBox = Gtk::manage(new Gtk::VBox); - Gtk::Label *scaleLabel = Gtk::manage(new Gtk::Label); - scaleLabel->set_text("Text Size"); - versionBox->add(*scaleLabel); - Gtk::Scale *scale = Gtk::manage(new Gtk::Scale); - scale->set_range(8000, 20000); - scale->set_value(libbible::settingsReadInt("fontsize")); - scale->set_draw_value(false); - scale->signal_value_changed().connect([scale, this]() { - libbible::settingsWriteInt("fontsize", (int) scale->get_value()); - this->reader->refresh(); - }); - versionBox->add(*scale); - - vector versions = reader->getAllVersions(); - if(versions.size() > 8) { - Gtk::ScrolledWindow *sw = Gtk::manage(new Gtk::ScrolledWindow); - sw->set_propagate_natural_width(true); - sw->set_min_content_height(300); - sw->add(*versionBox); - outerBox->add(*sw); - } else { - outerBox->add(*versionBox); - } - for(string version : versions) { - Gtk::Button *item = Gtk::manage(new Gtk::Button(version)); - item->set_relief(Gtk::ReliefStyle::RELIEF_NONE); - item->signal_clicked().connect([version, this]() { - menuMenu.popdown(); - this->reader->setVersion(version); - updateButtons(); - updateMenus(); - showText(); - }); - Gtk::Button *delVersion = Gtk::manage(new Gtk::Button); - delVersion->set_image_from_icon_name("list-remove-symbolic"); - delVersion->set_relief(Gtk::ReliefStyle::RELIEF_NONE); - delVersion->signal_clicked().connect([version, this]() { - menuMenu.popdown(); - std::vector toDel {version}; - this->mods->uninstallMods(toDel); - reader->modsUpdated(); - updateButtons(); - updateMenus(); - }); - item->set_halign(Gtk::ALIGN_FILL); - delVersion->set_halign(Gtk::ALIGN_END); - Gtk::HBox *hbox = Gtk::manage(new Gtk::HBox); - hbox->add(*item); - hbox->add(*delVersion); - versionBox->add(*hbox); - item->set_sensitive(version != reader->getVersion()); - } - Gtk::Button *add = Gtk::manage(new Gtk::Button); - add->set_image_from_icon_name("list-add-symbolic"); - add->signal_clicked().connect([this]() { - menuMenu.popdown(); - showMods(); - updateMenus(); - updateButtons(); - }); - versionBox->add(*add); - - menuMenu.show_all_children(); -} - -void Header::showMods() { - window->remove(); - window->add(*this->mods); - //this->mods->displayMain(); - window->show_all_children(); -} - -void Header::showText() { - window->remove(); - window->add(*this->reader); - //this->reader->setChapter(1); - window->show_all_children(); -} diff --git a/header.h b/header.h deleted file mode 100644 index fd88fe5..0000000 --- a/header.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include - -class Mods; -class ReaderView; - -class Header : public Gtk::HeaderBar -{ -public: - Header(ReaderView *reader, Gtk::Window *window); - virtual ~Header(); - - void updateButtons(void); - void updateMenus(void); - ReaderView *reader; - void showMods(void); - void showText(void); - -protected: - //Buttons - Gtk::Button back; - Gtk::Button forward; - Gtk::MenuButton book; - Gtk::MenuButton menu; - //Menus - Gtk::PopoverMenu bookMenu; - Gtk::PopoverMenu menuMenu; - - Mods *mods; - Gtk::Window *window; -}; diff --git a/main.cc b/main.cc deleted file mode 100644 index ad62814..0000000 --- a/main.cc +++ /dev/null @@ -1,45 +0,0 @@ -/* main.cpp - * - * Copyright (C) 2018 Nathan Vance - * - * 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 3 of the License, or - * (at your option) any later version. - * - * 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 . - */ - -#include "readerview.h" -#include "header.h" -#include - -int main(int argc, - char *argv[]) -{ - auto app = Gtk::Application::create(argc, argv, "org.homelinuxserver.vance.biblereader"); - - Gtk::Window window; - window.set_default_size(550, 400); - //auto theme = Gtk::IconTheme::get_default(); - //theme->append_search_path(std::string(DATADIR) + "/icons/hicolor/scalable/apps"); - // No clue what the size *should* be. The 100 is in magical units :/ - //window.set_icon(theme->load_icon("org.homelinuxserver.vance.biblereader-symbolic", 100)); - - ReaderView reader; - - window.add(reader); - - Header *header = new Header(&reader, &window); - window.set_titlebar(*header); - - window.show_all_children(); - - return app->run(window); -} diff --git a/mods.cc b/mods.cc deleted file mode 100644 index 405b316..0000000 --- a/mods.cc +++ /dev/null @@ -1,216 +0,0 @@ -#include "mods.h" -#include "header.h" -#include "readerview.h" -#include -#include -#include -#include -#include - -// We want: https://developer.gnome.org/gtkmm-tutorial/stable/sec-multithread-example.html.en -void Mods::update(unsigned long totalBytes, unsigned long completedBytes, std::string mess) -{ - std::lock_guard lock(progressMutex); - if (!totalBytes) { - //printf("Received a 0 totalBytes\n"); - dispatcher.emit(); - return; - } - fracDone = (completedBytes / totalBytes); - message = mess; - //printf("Progress: %f\n", fracDone); - dispatcher.emit(); -} - -void Mods::getStatus(double *fractionDone, std::string *mess, bool *isComplete) const { - std::lock_guard lock(progressMutex); - *fractionDone = fracDone; - *mess = message; - *isComplete = complete; -} - -void Mods::onNotify() { - double fractionDone; - std::string mess; - bool isComplete; - getStatus(&fractionDone, &mess, &isComplete); - if(isComplete) { - if(worker && worker->joinable()) { - worker->join(); - header->reader->modsUpdated(); - header->updateButtons(); - header->updateMenus(); - endProgress(); - } - delete worker; - worker = nullptr; - } else { - showProgress(mess); - if(fractionDone >= 0 && fractionDone <= 1) { - progressBar.set_fraction(fractionDone); - } else { - progressBar.pulse(); - } - } -} - -void Mods::showProgress(std::string message) { - progressDialog.set_message(message); - progressDialog.show(); - progressDialog.show_all_children(); -} - -void Mods::endProgress() { - progressDialog.hide(); - return; -} - -Mods::Mods(Header *header, Gtk::Window *window) : modsAvailable(), progressDialog("", false, Gtk::MessageType::MESSAGE_INFO, Gtk::ButtonsType::BUTTONS_CANCEL), progressBar(), dispatcher(), progressMutex(), worker(nullptr) { - libbible::setStatusReporter(*this); - this->header = header; - this->window = window; - dispatcher.connect(sigc::mem_fun(*this, &Mods::onNotify)); - progressDialog.signal_response().connect([this](int response) { - libbible::terminateDownload(); - }); - progressDialog.get_message_area()->add(progressBar); - progressBar.show(); - displayMain(); -} - -Mods::~Mods() {} - -void Mods::displayMain() { - remove(); - auto *scroll = Gtk::manage(new Gtk::ScrolledWindow); - add(*scroll); - auto *vbox = Gtk::manage(new Gtk::VBox); - scroll->add(*vbox); - auto *label = Gtk::manage(new Gtk::Label); - label->set_text("Install new mods"); - vbox->pack_start(*label, false, false); - auto *hbox = Gtk::manage(new Gtk::HBox); - vbox->pack_start(*hbox, false, false); - auto *network = Gtk::manage(new Gtk::VBox); - hbox->add(*network); - label = Gtk::manage(new Gtk::Label); - label->set_text("Download over the network"); - network->pack_start(*label, false, false); - auto *button = Gtk::manage(new Gtk::Button("Download")); - network->pack_start(*button, false, false); - button->signal_clicked().connect([this]() { - updateInstallable(); - displayDownload(); - }); - auto *local = Gtk::manage(new Gtk::VBox); - hbox->add(*local); - label = Gtk::manage(new Gtk::Label); - label->set_text("Install local .zip file"); - local->pack_start(*label, false, false); - Gtk::FileChooserButton *add = Gtk::manage(new Gtk::FileChooserButton("Install SWORD modules", Gtk::FILE_CHOOSER_ACTION_OPEN)); - local->pack_start(*add, false, false); - auto filter = Gtk::FileFilter::create(); - filter->add_mime_type("application/zip"); - add->set_filter(filter); - add->signal_file_set().connect([this, add]() { - installMods(add->get_filenames()); - auto newMods = this->header->reader->modsUpdated(); - if(! newMods.empty()) { - this->header->reader->setVersion(newMods[0]); - } - this->header->updateMenus(); - this->header->updateButtons(); - this->header->showText(); - }); - window->show_all_children(); -} - -void Mods::displayDownload() { - remove(); - auto *scroll = Gtk::manage(new Gtk::ScrolledWindow); - add(*scroll); - auto *vbox = Gtk::manage(new Gtk::VBox); - //vbox->set_homogeneous(false); - //vbox->set_hexpand(false); - scroll->add(*vbox); - auto *button = Gtk::manage(new Gtk::Button("Back")); - vbox->pack_start(*button, false, false); - button->signal_clicked().connect([this]() { - displayMain(); - }); - auto *label = Gtk::manage(new Gtk::Label); - label->set_text("Language Selection:"); - vbox->pack_start(*label, false, false); - auto *langMenuButton = Gtk::manage(new Gtk::MenuButton); - // Perhaps default to English? Or if translations for the app are added, default to the global language setting? - langMenuButton->set_label("Select Language"); - vbox->pack_start(*langMenuButton, false, false); - label = Gtk::manage(new Gtk::Label); - label->set_text("Mods Available:"); - vbox->pack_start(*label, false, false); - auto *modsSelection = Gtk::manage(new Gtk::VBox); - modsSelection->set_vexpand(true); - vbox->pack_end(*modsSelection); - auto *langMenu = Gtk::manage(new Gtk::PopoverMenu); - auto *sw = Gtk::manage(new Gtk::ScrolledWindow); - langMenu->add(*sw); - auto *langBox = Gtk::manage(new Gtk::VBox); - sw->add(*langBox); - sw->set_propagate_natural_width(true); - sw->set_min_content_height(300); - auto langAbbrevsToFull = libbible::getLanguageNames(); - std::map langFullToAbbrevs; - for(const auto& [abbrev, full] : langAbbrevsToFull) langFullToAbbrevs.emplace(full, abbrev); - std::vector langsSorted; - for(auto const& pair : modsAvailable) langsSorted.push_back(langAbbrevsToFull[pair.first]); - std::sort(langsSorted.begin(), langsSorted.end()); - for(string fullLanguage : langsSorted) { - string language = langFullToAbbrevs[fullLanguage]; - auto *langButton = Gtk::manage(new Gtk::Button(fullLanguage)); - langButton->set_relief(Gtk::ReliefStyle::RELIEF_NONE); - langButton->signal_clicked().connect([language, fullLanguage, langMenuButton, langMenu, modsSelection, this]() { - langMenu->popdown(); - langMenuButton->set_label(fullLanguage); - for(auto child : modsSelection->get_children()) { - modsSelection->remove(*child); - } - for(string name : modsAvailable[language]) { - auto *installButton = Gtk::manage(new Gtk::Button(name)); - installButton->signal_clicked().connect([language, name, this]() { - worker = new std::thread([language, name, this] { - complete = false; - libbible::installModFromInternet(language, name); - complete = true; - dispatcher.emit(); - header->showText(); - }); - }); - modsSelection->pack_start(*installButton, false, false); - } - modsSelection->show_all_children(); - }); - langBox->add(*langButton); - } - langMenuButton->set_popover(*langMenu); - langMenu->show_all_children(); - window->show_all_children(); -} - -void Mods::installMods(std::vector filenames) { - for(auto filename : filenames) { - libbible::installModFromZip(filename); - } -} - -void Mods::uninstallMods(std::vector modnames) { - for(auto mod : modnames) { - libbible::uninstallMod(mod); - } -} - -void Mods::updateInstallable() { - if(! modsAvailable.empty()) { - return; - } - modsAvailable = libbible::downloadModsAvailable(); -} diff --git a/mods.h b/mods.h deleted file mode 100644 index 7373961..0000000 --- a/mods.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include - -using namespace std; - -class Header; - -/* - * Credit goes to the Xiphos project for this part of the code: - * https://github.com/crosswire/xiphos/ - */ - -class Mods : public Gtk::Frame, public libbible::Status -{ -public: - Mods(Header *header, Gtk::Window *window); - virtual ~Mods(); - - void installMods(std::vector filenames); - void uninstallMods(std::vector modnames); - void updateInstallable(); - void displayMain(); - void displayDownload(); - - // This is for Status. Huzzah for multiple inheritance! - virtual void update(unsigned long totalBytes, unsigned long completedBytes, std::string message); - -protected: - Header *header; - Gtk::Window *window; - std::map> modsAvailable; - - Gtk::MessageDialog progressDialog; - Gtk::ProgressBar progressBar; - Glib::Dispatcher dispatcher; - mutable std::mutex progressMutex; - double fracDone; - std::string message; - std::thread *worker; - bool complete; - void getStatus(double *fractionDone, std::string *mess, bool *isComplete) const; - void onNotify(); - - void showProgress(std::string message); - void endProgress(); - -}; diff --git a/readerview.cc b/readerview.cc deleted file mode 100644 index aef003d..0000000 --- a/readerview.cc +++ /dev/null @@ -1,98 +0,0 @@ -#include "readerview.h" -#include "sword.h" -#include -#include - -ReaderView::ReaderView() -: text() -{ - this->sword = new Sword(); - this->conf = new struct config; - text.set_editable(false); - text.set_cursor_visible(false); - text.set_wrap_mode(Gtk::WrapMode::WRAP_WORD_CHAR); - //text.override_background_color(*(new Gdk::RGBA("#1d1f21"))); - //text.override_color(*(new Gdk::RGBA("#c5c8c6"))); - auto scroll = new Gtk::ScrolledWindow(); - scroll->add(text); - add(*scroll); - // Open the passage we had last time - string book = libbible::settingsRead("book"); - int chapter = libbible::settingsReadInt("chapter"); - if(book.empty() || chapter == 0) { - book = "Genesis"; - chapter = 1; - } - sword->getConfig(book, chapter, conf); - refresh(); -} - -ReaderView::~ReaderView() {} - -// Returns all new mods -std::vector ReaderView::modsUpdated() { - auto oldMods = getAllVersions(); - std::sort(oldMods.begin(), oldMods.end()); - sword = new Sword(); - auto newMods = getAllVersions(); - std::sort(newMods.begin(), newMods.end()); - std::vector justNew; - std::set_difference(newMods.begin(), newMods.end(), oldMods.begin(), oldMods.end(), std::back_inserter(justNew)); - refresh(); - return justNew; -} - -void ReaderView::refresh() { - auto textBuffer = text.get_buffer(); - libbible::settingsWrite("book", conf->book); - libbible::settingsWriteInt("chapter", conf->chapter); - sword->getConfig(conf->book, conf->chapter, conf); - // Get passage back - string passage = conf->book + " " + std::to_string(conf->chapter); - textBuffer->set_text(""); // Clear contents - //auto iter = textBuffer->get_iter_at_offset(0); - sword->fillBuffer(passage, textBuffer); -} - -void ReaderView::setChapter(int chapter) { - conf->chapter = chapter; - refresh(); -} - -int ReaderView::getChapter() { - return conf->chapter; -} - -int ReaderView::getChapterMax() { - return conf->maxChapter; -} - -void ReaderView::setBook(std::string book) { - conf->book = book; - refresh(); -} - -std::string ReaderView::getBook() { - return conf->book; -} - -std::string ReaderView::getBookFull() { - return conf->bookFull; -} - -std::vector ReaderView::getAllBooks() { - return sword->getBooks(); -} - -void ReaderView::setVersion(std::string version) { - sword->setModule(version); - refresh(); -} - -std::string ReaderView::getVersion() { - return conf->version; -} - -std::vector ReaderView::getAllVersions() { - return sword->getModules(); -} diff --git a/readerview.h b/readerview.h deleted file mode 100644 index 0ea631e..0000000 --- a/readerview.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once -#include - -class Sword; - -class ReaderView : public Gtk::Frame -{ -public: - ReaderView(); - virtual ~ReaderView(); - - void setChapter(int chapter); - int getChapter(void); - int getChapterMax(void); - void setBook(std::string book); - std::string getBook(void); - std::string getBookFull(void); - std::vector getAllBooks(void); - - void setVersion(std::string version); - std::string getVersion(void); - std::vector getAllVersions(void); - - void refresh(void); - - std::vector modsUpdated(void); - -protected: - Sword *sword; - Gtk::TextView text; - struct config *conf; -}; diff --git a/src/header.cc b/src/header.cc new file mode 100644 index 0000000..c40e44e --- /dev/null +++ b/src/header.cc @@ -0,0 +1,210 @@ +#include "header.h" +#include "readerview.h" +#include "mods.h" +#include + +Header::Header(ReaderView *reader, Gtk::Window *window) +: back(), forward(), book(), menu(), bookMenu(), menuMenu() +{ + this->mods = new Mods(this, window); + this->reader = reader; + this->window = window; + set_show_close_button(true); + set_title ("Bible Reader"); + set_has_subtitle(false); + //this->override_background_color(*(new Gdk::RGBA("#1d1f21"))); + //this->override_color(*(new Gdk::RGBA("#c5c8c6"))); + + back.set_image_from_icon_name("go-previous-symbolic"); + forward.set_image_from_icon_name("go-next-symbolic"); + menu.set_image_from_icon_name("open-menu-symbolic"); + + back.signal_clicked().connect([reader, this]() { + reader->setChapter(reader->getChapter() - 1); + updateButtons(); + }); + + forward.signal_clicked().connect([reader, this]() { + reader->setChapter(reader->getChapter() + 1); + updateButtons(); + }); + + pack_start(back); + pack_start(book); + pack_start(forward); + pack_end(menu); + + book.set_popover(bookMenu); + menu.set_popover(menuMenu); + + updateButtons(); + updateMenus(); +} + +Header::~Header() {} + +void Header::updateButtons() { + back.set_sensitive(reader->getChapter() > 1); + forward.set_sensitive(reader->getChapter() < reader->getChapterMax()); + if(reader->getBook() == "") { + book.set_label(""); + book.set_sensitive(false); + } else { + book.set_label(reader->getBook() + " " + std::to_string(reader->getChapter())); + book.set_sensitive(true); + } +} + +void Header::updateMenus() { + // Wipe them out and start over + if(bookMenu.get_children().size() != 0) { + delete bookMenu.get_children()[0]; + } + if(menuMenu.get_children().size() != 0) { + delete menuMenu.get_children()[0]; + } + + // Populate bookMenu + Gtk::HBox *outer = Gtk::manage(new Gtk::HBox); + bookMenu.add(*outer); + + //TODO: Disgusting code duplication. Fix with strategy pattern? Or loop? + + Gtk::ScrolledWindow *swl = Gtk::manage(new Gtk::ScrolledWindow); + Gtk::VBox *boxl = Gtk::manage(new Gtk::VBox); + outer->add(*swl); + swl->add(*boxl); + swl->set_propagate_natural_width(true); + swl->set_min_content_height(300); + for(std::string bookName : reader->getAllBooks()) { + Gtk::Button *item = Gtk::manage(new Gtk::Button(bookName)); + item->set_relief(Gtk::ReliefStyle::RELIEF_NONE); + item->signal_clicked().connect([bookName, this]() { + bookMenu.popdown(); + reader->setChapter(1); + reader->setBook(bookName); + updateButtons(); + updateMenus(); + showText(); + }); + boxl->add(*item); + } + + Gtk::ScrolledWindow *swr = Gtk::manage(new Gtk::ScrolledWindow); + Gtk::VBox *boxr = Gtk::manage(new Gtk::VBox); + outer->add(*swr); + swr->add(*boxr); + swr->set_propagate_natural_width(true); + swr->set_min_content_height(300); + for(int chapter = 1; chapter <= reader->getChapterMax(); chapter++) { + Gtk::Button *item = Gtk::manage(new Gtk::Button(std::to_string(chapter))); + item->set_relief(Gtk::ReliefStyle::RELIEF_NONE); + item->signal_clicked().connect([chapter, this]() { + bookMenu.popdown(); + reader->setChapter(chapter); + updateButtons(); + showText(); + }); + boxr->add(*item); + } + + bool *hasAllocated = new bool(false); + outer->signal_size_allocate().connect([this, swl, swr, hasAllocated](Gdk::Rectangle rec) { + if(! *hasAllocated) { + // First figure out the books + auto allBooks = reader->getAllBooks(); + int position = std::find(allBooks.begin(), allBooks.end(), reader->getBookFull()) - allBooks.begin(); + swl->get_vadjustment()->set_value(swl->get_vadjustment()->get_upper() * position / allBooks.size()); + swr->get_vadjustment()->set_value(swr->get_vadjustment()->get_upper() * (reader->getChapter() - 1) / reader->getChapterMax()); + } + *hasAllocated = true; + }); + + bookMenu.show_all_children(); + book.signal_pressed().connect([this, hasAllocated, swl, swr]() { + *hasAllocated = false; + }); + + // Populate menuMenu + Gtk::VBox *outerBox = Gtk::manage(new Gtk::VBox); + menuMenu.add(*outerBox); + + Gtk::VBox *versionBox = Gtk::manage(new Gtk::VBox); + Gtk::Label *scaleLabel = Gtk::manage(new Gtk::Label); + scaleLabel->set_text("Text Size"); + versionBox->add(*scaleLabel); + Gtk::Scale *scale = Gtk::manage(new Gtk::Scale); + scale->set_range(8000, 20000); + scale->set_value(libbible::settingsReadInt("fontsize")); + scale->set_draw_value(false); + scale->signal_value_changed().connect([scale, this]() { + libbible::settingsWriteInt("fontsize", (int) scale->get_value()); + this->reader->refresh(); + }); + versionBox->add(*scale); + + vector versions = reader->getAllVersions(); + if(versions.size() > 8) { + Gtk::ScrolledWindow *sw = Gtk::manage(new Gtk::ScrolledWindow); + sw->set_propagate_natural_width(true); + sw->set_min_content_height(300); + sw->add(*versionBox); + outerBox->add(*sw); + } else { + outerBox->add(*versionBox); + } + for(string version : versions) { + Gtk::Button *item = Gtk::manage(new Gtk::Button(version)); + item->set_relief(Gtk::ReliefStyle::RELIEF_NONE); + item->signal_clicked().connect([version, this]() { + menuMenu.popdown(); + this->reader->setVersion(version); + updateButtons(); + updateMenus(); + showText(); + }); + Gtk::Button *delVersion = Gtk::manage(new Gtk::Button); + delVersion->set_image_from_icon_name("list-remove-symbolic"); + delVersion->set_relief(Gtk::ReliefStyle::RELIEF_NONE); + delVersion->signal_clicked().connect([version, this]() { + menuMenu.popdown(); + std::vector toDel {version}; + this->mods->uninstallMods(toDel); + reader->modsUpdated(); + updateButtons(); + updateMenus(); + }); + item->set_halign(Gtk::ALIGN_FILL); + delVersion->set_halign(Gtk::ALIGN_END); + Gtk::HBox *hbox = Gtk::manage(new Gtk::HBox); + hbox->add(*item); + hbox->add(*delVersion); + versionBox->add(*hbox); + item->set_sensitive(version != reader->getVersion()); + } + Gtk::Button *add = Gtk::manage(new Gtk::Button); + add->set_image_from_icon_name("list-add-symbolic"); + add->signal_clicked().connect([this]() { + menuMenu.popdown(); + showMods(); + updateMenus(); + updateButtons(); + }); + versionBox->add(*add); + + menuMenu.show_all_children(); +} + +void Header::showMods() { + window->remove(); + window->add(*this->mods); + //this->mods->displayMain(); + window->show_all_children(); +} + +void Header::showText() { + window->remove(); + window->add(*this->reader); + //this->reader->setChapter(1); + window->show_all_children(); +} diff --git a/src/header.h b/src/header.h new file mode 100644 index 0000000..fd88fe5 --- /dev/null +++ b/src/header.h @@ -0,0 +1,31 @@ +#pragma once +#include + +class Mods; +class ReaderView; + +class Header : public Gtk::HeaderBar +{ +public: + Header(ReaderView *reader, Gtk::Window *window); + virtual ~Header(); + + void updateButtons(void); + void updateMenus(void); + ReaderView *reader; + void showMods(void); + void showText(void); + +protected: + //Buttons + Gtk::Button back; + Gtk::Button forward; + Gtk::MenuButton book; + Gtk::MenuButton menu; + //Menus + Gtk::PopoverMenu bookMenu; + Gtk::PopoverMenu menuMenu; + + Mods *mods; + Gtk::Window *window; +}; diff --git a/src/main.cc b/src/main.cc new file mode 100644 index 0000000..ad62814 --- /dev/null +++ b/src/main.cc @@ -0,0 +1,45 @@ +/* main.cpp + * + * Copyright (C) 2018 Nathan Vance + * + * 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 3 of the License, or + * (at your option) any later version. + * + * 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 . + */ + +#include "readerview.h" +#include "header.h" +#include + +int main(int argc, + char *argv[]) +{ + auto app = Gtk::Application::create(argc, argv, "org.homelinuxserver.vance.biblereader"); + + Gtk::Window window; + window.set_default_size(550, 400); + //auto theme = Gtk::IconTheme::get_default(); + //theme->append_search_path(std::string(DATADIR) + "/icons/hicolor/scalable/apps"); + // No clue what the size *should* be. The 100 is in magical units :/ + //window.set_icon(theme->load_icon("org.homelinuxserver.vance.biblereader-symbolic", 100)); + + ReaderView reader; + + window.add(reader); + + Header *header = new Header(&reader, &window); + window.set_titlebar(*header); + + window.show_all_children(); + + return app->run(window); +} diff --git a/src/mods.cc b/src/mods.cc new file mode 100644 index 0000000..405b316 --- /dev/null +++ b/src/mods.cc @@ -0,0 +1,216 @@ +#include "mods.h" +#include "header.h" +#include "readerview.h" +#include +#include +#include +#include +#include + +// We want: https://developer.gnome.org/gtkmm-tutorial/stable/sec-multithread-example.html.en +void Mods::update(unsigned long totalBytes, unsigned long completedBytes, std::string mess) +{ + std::lock_guard lock(progressMutex); + if (!totalBytes) { + //printf("Received a 0 totalBytes\n"); + dispatcher.emit(); + return; + } + fracDone = (completedBytes / totalBytes); + message = mess; + //printf("Progress: %f\n", fracDone); + dispatcher.emit(); +} + +void Mods::getStatus(double *fractionDone, std::string *mess, bool *isComplete) const { + std::lock_guard lock(progressMutex); + *fractionDone = fracDone; + *mess = message; + *isComplete = complete; +} + +void Mods::onNotify() { + double fractionDone; + std::string mess; + bool isComplete; + getStatus(&fractionDone, &mess, &isComplete); + if(isComplete) { + if(worker && worker->joinable()) { + worker->join(); + header->reader->modsUpdated(); + header->updateButtons(); + header->updateMenus(); + endProgress(); + } + delete worker; + worker = nullptr; + } else { + showProgress(mess); + if(fractionDone >= 0 && fractionDone <= 1) { + progressBar.set_fraction(fractionDone); + } else { + progressBar.pulse(); + } + } +} + +void Mods::showProgress(std::string message) { + progressDialog.set_message(message); + progressDialog.show(); + progressDialog.show_all_children(); +} + +void Mods::endProgress() { + progressDialog.hide(); + return; +} + +Mods::Mods(Header *header, Gtk::Window *window) : modsAvailable(), progressDialog("", false, Gtk::MessageType::MESSAGE_INFO, Gtk::ButtonsType::BUTTONS_CANCEL), progressBar(), dispatcher(), progressMutex(), worker(nullptr) { + libbible::setStatusReporter(*this); + this->header = header; + this->window = window; + dispatcher.connect(sigc::mem_fun(*this, &Mods::onNotify)); + progressDialog.signal_response().connect([this](int response) { + libbible::terminateDownload(); + }); + progressDialog.get_message_area()->add(progressBar); + progressBar.show(); + displayMain(); +} + +Mods::~Mods() {} + +void Mods::displayMain() { + remove(); + auto *scroll = Gtk::manage(new Gtk::ScrolledWindow); + add(*scroll); + auto *vbox = Gtk::manage(new Gtk::VBox); + scroll->add(*vbox); + auto *label = Gtk::manage(new Gtk::Label); + label->set_text("Install new mods"); + vbox->pack_start(*label, false, false); + auto *hbox = Gtk::manage(new Gtk::HBox); + vbox->pack_start(*hbox, false, false); + auto *network = Gtk::manage(new Gtk::VBox); + hbox->add(*network); + label = Gtk::manage(new Gtk::Label); + label->set_text("Download over the network"); + network->pack_start(*label, false, false); + auto *button = Gtk::manage(new Gtk::Button("Download")); + network->pack_start(*button, false, false); + button->signal_clicked().connect([this]() { + updateInstallable(); + displayDownload(); + }); + auto *local = Gtk::manage(new Gtk::VBox); + hbox->add(*local); + label = Gtk::manage(new Gtk::Label); + label->set_text("Install local .zip file"); + local->pack_start(*label, false, false); + Gtk::FileChooserButton *add = Gtk::manage(new Gtk::FileChooserButton("Install SWORD modules", Gtk::FILE_CHOOSER_ACTION_OPEN)); + local->pack_start(*add, false, false); + auto filter = Gtk::FileFilter::create(); + filter->add_mime_type("application/zip"); + add->set_filter(filter); + add->signal_file_set().connect([this, add]() { + installMods(add->get_filenames()); + auto newMods = this->header->reader->modsUpdated(); + if(! newMods.empty()) { + this->header->reader->setVersion(newMods[0]); + } + this->header->updateMenus(); + this->header->updateButtons(); + this->header->showText(); + }); + window->show_all_children(); +} + +void Mods::displayDownload() { + remove(); + auto *scroll = Gtk::manage(new Gtk::ScrolledWindow); + add(*scroll); + auto *vbox = Gtk::manage(new Gtk::VBox); + //vbox->set_homogeneous(false); + //vbox->set_hexpand(false); + scroll->add(*vbox); + auto *button = Gtk::manage(new Gtk::Button("Back")); + vbox->pack_start(*button, false, false); + button->signal_clicked().connect([this]() { + displayMain(); + }); + auto *label = Gtk::manage(new Gtk::Label); + label->set_text("Language Selection:"); + vbox->pack_start(*label, false, false); + auto *langMenuButton = Gtk::manage(new Gtk::MenuButton); + // Perhaps default to English? Or if translations for the app are added, default to the global language setting? + langMenuButton->set_label("Select Language"); + vbox->pack_start(*langMenuButton, false, false); + label = Gtk::manage(new Gtk::Label); + label->set_text("Mods Available:"); + vbox->pack_start(*label, false, false); + auto *modsSelection = Gtk::manage(new Gtk::VBox); + modsSelection->set_vexpand(true); + vbox->pack_end(*modsSelection); + auto *langMenu = Gtk::manage(new Gtk::PopoverMenu); + auto *sw = Gtk::manage(new Gtk::ScrolledWindow); + langMenu->add(*sw); + auto *langBox = Gtk::manage(new Gtk::VBox); + sw->add(*langBox); + sw->set_propagate_natural_width(true); + sw->set_min_content_height(300); + auto langAbbrevsToFull = libbible::getLanguageNames(); + std::map langFullToAbbrevs; + for(const auto& [abbrev, full] : langAbbrevsToFull) langFullToAbbrevs.emplace(full, abbrev); + std::vector langsSorted; + for(auto const& pair : modsAvailable) langsSorted.push_back(langAbbrevsToFull[pair.first]); + std::sort(langsSorted.begin(), langsSorted.end()); + for(string fullLanguage : langsSorted) { + string language = langFullToAbbrevs[fullLanguage]; + auto *langButton = Gtk::manage(new Gtk::Button(fullLanguage)); + langButton->set_relief(Gtk::ReliefStyle::RELIEF_NONE); + langButton->signal_clicked().connect([language, fullLanguage, langMenuButton, langMenu, modsSelection, this]() { + langMenu->popdown(); + langMenuButton->set_label(fullLanguage); + for(auto child : modsSelection->get_children()) { + modsSelection->remove(*child); + } + for(string name : modsAvailable[language]) { + auto *installButton = Gtk::manage(new Gtk::Button(name)); + installButton->signal_clicked().connect([language, name, this]() { + worker = new std::thread([language, name, this] { + complete = false; + libbible::installModFromInternet(language, name); + complete = true; + dispatcher.emit(); + header->showText(); + }); + }); + modsSelection->pack_start(*installButton, false, false); + } + modsSelection->show_all_children(); + }); + langBox->add(*langButton); + } + langMenuButton->set_popover(*langMenu); + langMenu->show_all_children(); + window->show_all_children(); +} + +void Mods::installMods(std::vector filenames) { + for(auto filename : filenames) { + libbible::installModFromZip(filename); + } +} + +void Mods::uninstallMods(std::vector modnames) { + for(auto mod : modnames) { + libbible::uninstallMod(mod); + } +} + +void Mods::updateInstallable() { + if(! modsAvailable.empty()) { + return; + } + modsAvailable = libbible::downloadModsAvailable(); +} diff --git a/src/mods.h b/src/mods.h new file mode 100644 index 0000000..7373961 --- /dev/null +++ b/src/mods.h @@ -0,0 +1,53 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +class Header; + +/* + * Credit goes to the Xiphos project for this part of the code: + * https://github.com/crosswire/xiphos/ + */ + +class Mods : public Gtk::Frame, public libbible::Status +{ +public: + Mods(Header *header, Gtk::Window *window); + virtual ~Mods(); + + void installMods(std::vector filenames); + void uninstallMods(std::vector modnames); + void updateInstallable(); + void displayMain(); + void displayDownload(); + + // This is for Status. Huzzah for multiple inheritance! + virtual void update(unsigned long totalBytes, unsigned long completedBytes, std::string message); + +protected: + Header *header; + Gtk::Window *window; + std::map> modsAvailable; + + Gtk::MessageDialog progressDialog; + Gtk::ProgressBar progressBar; + Glib::Dispatcher dispatcher; + mutable std::mutex progressMutex; + double fracDone; + std::string message; + std::thread *worker; + bool complete; + void getStatus(double *fractionDone, std::string *mess, bool *isComplete) const; + void onNotify(); + + void showProgress(std::string message); + void endProgress(); + +}; diff --git a/src/readerview.cc b/src/readerview.cc new file mode 100644 index 0000000..aef003d --- /dev/null +++ b/src/readerview.cc @@ -0,0 +1,98 @@ +#include "readerview.h" +#include "sword.h" +#include +#include + +ReaderView::ReaderView() +: text() +{ + this->sword = new Sword(); + this->conf = new struct config; + text.set_editable(false); + text.set_cursor_visible(false); + text.set_wrap_mode(Gtk::WrapMode::WRAP_WORD_CHAR); + //text.override_background_color(*(new Gdk::RGBA("#1d1f21"))); + //text.override_color(*(new Gdk::RGBA("#c5c8c6"))); + auto scroll = new Gtk::ScrolledWindow(); + scroll->add(text); + add(*scroll); + // Open the passage we had last time + string book = libbible::settingsRead("book"); + int chapter = libbible::settingsReadInt("chapter"); + if(book.empty() || chapter == 0) { + book = "Genesis"; + chapter = 1; + } + sword->getConfig(book, chapter, conf); + refresh(); +} + +ReaderView::~ReaderView() {} + +// Returns all new mods +std::vector ReaderView::modsUpdated() { + auto oldMods = getAllVersions(); + std::sort(oldMods.begin(), oldMods.end()); + sword = new Sword(); + auto newMods = getAllVersions(); + std::sort(newMods.begin(), newMods.end()); + std::vector justNew; + std::set_difference(newMods.begin(), newMods.end(), oldMods.begin(), oldMods.end(), std::back_inserter(justNew)); + refresh(); + return justNew; +} + +void ReaderView::refresh() { + auto textBuffer = text.get_buffer(); + libbible::settingsWrite("book", conf->book); + libbible::settingsWriteInt("chapter", conf->chapter); + sword->getConfig(conf->book, conf->chapter, conf); + // Get passage back + string passage = conf->book + " " + std::to_string(conf->chapter); + textBuffer->set_text(""); // Clear contents + //auto iter = textBuffer->get_iter_at_offset(0); + sword->fillBuffer(passage, textBuffer); +} + +void ReaderView::setChapter(int chapter) { + conf->chapter = chapter; + refresh(); +} + +int ReaderView::getChapter() { + return conf->chapter; +} + +int ReaderView::getChapterMax() { + return conf->maxChapter; +} + +void ReaderView::setBook(std::string book) { + conf->book = book; + refresh(); +} + +std::string ReaderView::getBook() { + return conf->book; +} + +std::string ReaderView::getBookFull() { + return conf->bookFull; +} + +std::vector ReaderView::getAllBooks() { + return sword->getBooks(); +} + +void ReaderView::setVersion(std::string version) { + sword->setModule(version); + refresh(); +} + +std::string ReaderView::getVersion() { + return conf->version; +} + +std::vector ReaderView::getAllVersions() { + return sword->getModules(); +} diff --git a/src/readerview.h b/src/readerview.h new file mode 100644 index 0000000..0ea631e --- /dev/null +++ b/src/readerview.h @@ -0,0 +1,32 @@ +#pragma once +#include + +class Sword; + +class ReaderView : public Gtk::Frame +{ +public: + ReaderView(); + virtual ~ReaderView(); + + void setChapter(int chapter); + int getChapter(void); + int getChapterMax(void); + void setBook(std::string book); + std::string getBook(void); + std::string getBookFull(void); + std::vector getAllBooks(void); + + void setVersion(std::string version); + std::string getVersion(void); + std::vector getAllVersions(void); + + void refresh(void); + + std::vector modsUpdated(void); + +protected: + Sword *sword; + Gtk::TextView text; + struct config *conf; +}; diff --git a/src/sword.cc b/src/sword.cc new file mode 100644 index 0000000..a693085 --- /dev/null +++ b/src/sword.cc @@ -0,0 +1,168 @@ +#include "sword.h" +#include +#include +#include + +using namespace std; + +bool isNoMods; + +Sword::Sword() { + auto mods = libbible::getModules(); + isNoMods = mods.empty(); + string defaultMod = libbible::settingsRead("module"); + currMod = libbible::settingsRead("biblereader::currMod"); + if(mods.find(currMod) == mods.end()) { + currMod = defaultMod; + } + if(mods.find(currMod) == mods.end()) { + currMod = string(); + if(! mods.empty()) { + // New default mod (previous was deleted) + defaultMod = mods.begin()->first; + libbible::settingsWrite("module", defaultMod); + currMod = defaultMod; + } + } +} + +Sword::~Sword() {} + +vector Sword::getModules() { + vector mods; + auto modsInstalled = libbible::getModules(); + for(auto pair : modsInstalled) { + mods.push_back(pair.first); + } + return mods; +} + +void Sword::setModule(string version) { + currMod = version; + libbible::settingsWrite("biblereader::currMod", currMod); +} + +void Sword::fillBuffer(string ref, Glib::RefPtr buf) { + buf->set_text(""); // Clear contents + + if(isNoMods) { + auto iter = buf->get_iter_at_offset(0); + iter = buf->insert_markup(iter, "No modules installed.\n"); + iter = buf->insert_markup(iter, "Please download some modules at:\n"); + iter = buf->insert_markup(iter, "\thttp://crosswire.org/sword/modules/ModDisp.jsp?modType=Bibles\n"); + iter = buf->insert_markup(iter, "Then install them using the menu in the upper right corner, or use the built-in installer to download and install modules."); + return; + } + + auto texts = libbible::getText(libbible::getPassage(currMod, ref)); + + auto iter = buf->get_iter_at_offset(0); + + auto verseSize = buf->create_tag(); + verseSize->property_size() = libbible::settingsReadInt("fontsize"); + if(verseSize->property_size() == 0) { + verseSize->property_size() = 12000; + } + + auto verseScale = buf->create_tag(); + verseScale->property_scale() = 0.8; + auto titleScale = buf->create_tag(); + titleScale->property_scale() = 1.5; + titleScale->property_weight() = 600; + auto verseOffset = buf->create_tag(); + verseOffset->property_rise() = 3000; + auto indent = buf->create_tag(); + indent->property_left_margin() = 40; + auto redletter = buf->create_tag(); + redletter->property_foreground_gdk() = Gdk::Color("red"); + + int verse = 0; + string indentString = " "; + bool isNewline = true; + for(auto tex : texts) { + std::vector> tags = {verseSize}; + bool isParagraph = false; + bool isIndent = false; + bool isDivineName = false; + bool isPreverse = false; + for(string modifier : tex.modifiers) { + if(modifier == "paragraph") { + isParagraph = true; + } else if (modifier == "line indent0") { + isIndent = true; + } else if (modifier == "divineName") { + isDivineName = true; + } else if (modifier == "wordsOfJesus") { + tags.push_back(redletter); + } else if (modifier == "title") { + tags.push_back(titleScale); + // Ensure newline + if(tex.text.back() != '\n') { + tex.text.push_back('\n'); + } + } else if (modifier == "preverse") { + isPreverse = true; + } else if (modifier == "parallel") { + // We don't support this (yet) + tex.text = ""; + } + } + if(isIndent) { + isParagraph = false; + if(isNewline) { + tags.push_back(indent); + } + } + if(isParagraph) { + iter = buf->insert_with_tags(iter, indentString, tags); + } + if(tex.verse != verse && ! isPreverse) { + std::vector> verseTags(tags.begin(), tags.end()); + verseTags.push_back(verseScale); + verseTags.push_back(verseOffset); + iter = buf->insert_with_tags(iter, " " + std::to_string(tex.verse), verseTags); + verse = tex.verse; + } + if(isDivineName) { + // There's no small caps support. Sigh. We do fake small caps instead. + // Because i lazy, first letter is normal caps and rest small caps, always. + transform(tex.text.begin(), tex.text.end(), tex.text.begin(), ::toupper); + iter = buf->insert_with_tags(iter, tex.text.substr(0, 1), tags); + auto tag = buf->create_tag(); + tag->property_scale() = 0.75; + tags.push_back(tag); + iter = buf->insert_with_tags(iter, tex.text.substr(1), tags); + } else { + iter = buf->insert_with_tags(iter, tex.text, tags); + } + if(tex.text.back() == '\n') { + isNewline = true; + } else { + isNewline = false; + } + } +} + +void Sword::getConfig(string book, int chapter, struct config *conf) { + if(isNoMods) { + conf->chapter = 0; + conf->book = ""; + conf->bookFull = ""; + conf->maxChapter = 0; + conf->version = ""; + } else { + auto passages = libbible::getPassages(currMod, book); + conf->chapter = chapter; + conf->book = passages[0].bookShort; + conf->bookFull = passages[0].book; + conf->maxChapter = passages.back().chapterStart; + conf->version = currMod; + } +} + +vector Sword::getBooks() { + if(isNoMods) { + return vector(); + } + return libbible::getModules()[currMod]; +} diff --git a/src/sword.h b/src/sword.h new file mode 100644 index 0000000..1cd7f61 --- /dev/null +++ b/src/sword.h @@ -0,0 +1,29 @@ +#pragma once +#include +#include + +using namespace::std; + +class Sword { +public: + Sword(); + virtual ~Sword(); + + std::vector getModules(void); + std::vector getBooks(void); + void setModule(std::string modName); + void fillBuffer(std::string ref, Glib::RefPtr buf); + void getConfig(std::string book, int chapter, struct config *conf); + +protected: + std::string currMod; + +}; + +struct config { + int chapter; + std::string book; + std::string bookFull; + int maxChapter; + std::string version; +}; diff --git a/sword.cc b/sword.cc deleted file mode 100644 index a693085..0000000 --- a/sword.cc +++ /dev/null @@ -1,168 +0,0 @@ -#include "sword.h" -#include -#include -#include - -using namespace std; - -bool isNoMods; - -Sword::Sword() { - auto mods = libbible::getModules(); - isNoMods = mods.empty(); - string defaultMod = libbible::settingsRead("module"); - currMod = libbible::settingsRead("biblereader::currMod"); - if(mods.find(currMod) == mods.end()) { - currMod = defaultMod; - } - if(mods.find(currMod) == mods.end()) { - currMod = string(); - if(! mods.empty()) { - // New default mod (previous was deleted) - defaultMod = mods.begin()->first; - libbible::settingsWrite("module", defaultMod); - currMod = defaultMod; - } - } -} - -Sword::~Sword() {} - -vector Sword::getModules() { - vector mods; - auto modsInstalled = libbible::getModules(); - for(auto pair : modsInstalled) { - mods.push_back(pair.first); - } - return mods; -} - -void Sword::setModule(string version) { - currMod = version; - libbible::settingsWrite("biblereader::currMod", currMod); -} - -void Sword::fillBuffer(string ref, Glib::RefPtr buf) { - buf->set_text(""); // Clear contents - - if(isNoMods) { - auto iter = buf->get_iter_at_offset(0); - iter = buf->insert_markup(iter, "No modules installed.\n"); - iter = buf->insert_markup(iter, "Please download some modules at:\n"); - iter = buf->insert_markup(iter, "\thttp://crosswire.org/sword/modules/ModDisp.jsp?modType=Bibles\n"); - iter = buf->insert_markup(iter, "Then install them using the menu in the upper right corner, or use the built-in installer to download and install modules."); - return; - } - - auto texts = libbible::getText(libbible::getPassage(currMod, ref)); - - auto iter = buf->get_iter_at_offset(0); - - auto verseSize = buf->create_tag(); - verseSize->property_size() = libbible::settingsReadInt("fontsize"); - if(verseSize->property_size() == 0) { - verseSize->property_size() = 12000; - } - - auto verseScale = buf->create_tag(); - verseScale->property_scale() = 0.8; - auto titleScale = buf->create_tag(); - titleScale->property_scale() = 1.5; - titleScale->property_weight() = 600; - auto verseOffset = buf->create_tag(); - verseOffset->property_rise() = 3000; - auto indent = buf->create_tag(); - indent->property_left_margin() = 40; - auto redletter = buf->create_tag(); - redletter->property_foreground_gdk() = Gdk::Color("red"); - - int verse = 0; - string indentString = " "; - bool isNewline = true; - for(auto tex : texts) { - std::vector> tags = {verseSize}; - bool isParagraph = false; - bool isIndent = false; - bool isDivineName = false; - bool isPreverse = false; - for(string modifier : tex.modifiers) { - if(modifier == "paragraph") { - isParagraph = true; - } else if (modifier == "line indent0") { - isIndent = true; - } else if (modifier == "divineName") { - isDivineName = true; - } else if (modifier == "wordsOfJesus") { - tags.push_back(redletter); - } else if (modifier == "title") { - tags.push_back(titleScale); - // Ensure newline - if(tex.text.back() != '\n') { - tex.text.push_back('\n'); - } - } else if (modifier == "preverse") { - isPreverse = true; - } else if (modifier == "parallel") { - // We don't support this (yet) - tex.text = ""; - } - } - if(isIndent) { - isParagraph = false; - if(isNewline) { - tags.push_back(indent); - } - } - if(isParagraph) { - iter = buf->insert_with_tags(iter, indentString, tags); - } - if(tex.verse != verse && ! isPreverse) { - std::vector> verseTags(tags.begin(), tags.end()); - verseTags.push_back(verseScale); - verseTags.push_back(verseOffset); - iter = buf->insert_with_tags(iter, " " + std::to_string(tex.verse), verseTags); - verse = tex.verse; - } - if(isDivineName) { - // There's no small caps support. Sigh. We do fake small caps instead. - // Because i lazy, first letter is normal caps and rest small caps, always. - transform(tex.text.begin(), tex.text.end(), tex.text.begin(), ::toupper); - iter = buf->insert_with_tags(iter, tex.text.substr(0, 1), tags); - auto tag = buf->create_tag(); - tag->property_scale() = 0.75; - tags.push_back(tag); - iter = buf->insert_with_tags(iter, tex.text.substr(1), tags); - } else { - iter = buf->insert_with_tags(iter, tex.text, tags); - } - if(tex.text.back() == '\n') { - isNewline = true; - } else { - isNewline = false; - } - } -} - -void Sword::getConfig(string book, int chapter, struct config *conf) { - if(isNoMods) { - conf->chapter = 0; - conf->book = ""; - conf->bookFull = ""; - conf->maxChapter = 0; - conf->version = ""; - } else { - auto passages = libbible::getPassages(currMod, book); - conf->chapter = chapter; - conf->book = passages[0].bookShort; - conf->bookFull = passages[0].book; - conf->maxChapter = passages.back().chapterStart; - conf->version = currMod; - } -} - -vector Sword::getBooks() { - if(isNoMods) { - return vector(); - } - return libbible::getModules()[currMod]; -} diff --git a/sword.h b/sword.h deleted file mode 100644 index 1cd7f61..0000000 --- a/sword.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include -#include - -using namespace::std; - -class Sword { -public: - Sword(); - virtual ~Sword(); - - std::vector getModules(void); - std::vector getBooks(void); - void setModule(std::string modName); - void fillBuffer(std::string ref, Glib::RefPtr buf); - void getConfig(std::string book, int chapter, struct config *conf); - -protected: - std::string currMod; - -}; - -struct config { - int chapter; - std::string book; - std::string bookFull; - int maxChapter; - std::string version; -}; -- cgit v1.2.3