#include "mods.h" #include "header.h" #include "readerview.h" #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); for(auto item : modsAvailable) { string language = item.first; auto *langButton = Gtk::manage(new Gtk::Button(language)); langButton->set_relief(Gtk::ReliefStyle::RELIEF_NONE); langButton->signal_clicked().connect([language, langMenuButton, langMenu, modsSelection, this]() { langMenu->popdown(); langMenuButton->set_label(language); 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(); }