aboutsummaryrefslogtreecommitdiff
path: root/src/mods.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/mods.cc')
-rw-r--r--src/mods.cc216
1 files changed, 216 insertions, 0 deletions
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 <iostream>
+#include <thread>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <algorithm>
+
+// 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<std::mutex> 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<std::mutex> 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<std::string, std::string> langFullToAbbrevs;
+ for(const auto& [abbrev, full] : langAbbrevsToFull) langFullToAbbrevs.emplace(full, abbrev);
+ std::vector<std::string> 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<std::string> filenames) {
+ for(auto filename : filenames) {
+ libbible::installModFromZip(filename);
+ }
+}
+
+void Mods::uninstallMods(std::vector<std::string> modnames) {
+ for(auto mod : modnames) {
+ libbible::uninstallMod(mod);
+ }
+}
+
+void Mods::updateInstallable() {
+ if(! modsAvailable.empty()) {
+ return;
+ }
+ modsAvailable = libbible::downloadModsAvailable();
+}