diff options
Diffstat (limited to '')
-rw-r--r-- | src/inkview-window.cpp | 452 |
1 files changed, 452 insertions, 0 deletions
diff --git a/src/inkview-window.cpp b/src/inkview-window.cpp new file mode 100644 index 0000000..b299a64 --- /dev/null +++ b/src/inkview-window.cpp @@ -0,0 +1,452 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * @file + * Inkview - An SVG file viewer. + */ +/* + * Authors: + * Tavmjong Bah + * + * Copyright (C) 2018 Authors + * + * The contents of this file may be used under the GNU General Public License Version 2 or later. + * Read the file 'COPYING' for more information. + * + */ + + +#include "inkview-window.h" + +#include <iostream> + +#include "document.h" + +#include "ui/monitor.h" +#include "ui/view/svg-view-widget.h" + +#include "util/units.h" + +InkviewWindow::InkviewWindow(const Gio::Application::type_vec_files files, + bool fullscreen, + bool recursive, + int timer, + double scale, + bool preload + ) + : _files(files) + , _fullscreen(fullscreen) + , _recursive(recursive) + , _timer(timer) + , _scale(scale) + , _preload(preload) + , _index(-1) + , _view(nullptr) + , _controlwindow(nullptr) +{ + _files = create_file_list(_files); + + if (_preload) { + preload_documents(); + } + + if (_files.empty()) { + throw NoValidFilesException(); + } + + _documents.resize( _files.size(), nullptr); // We keep _documents and _files in sync. + + // Callbacks + signal_key_press_event().connect(sigc::mem_fun(*this, &InkviewWindow::key_press), false); + + if (_timer) { + Glib::signal_timeout().connect_seconds(sigc::mem_fun(*this, &InkviewWindow::on_timer), _timer); + } + + // Actions + add_action( "show_first", sigc::mem_fun(*this, &InkviewWindow::show_first) ); + add_action( "show_prev", sigc::mem_fun(*this, &InkviewWindow::show_prev) ); + add_action( "show_next", sigc::mem_fun(*this, &InkviewWindow::show_next) ); + add_action( "show_last", sigc::mem_fun(*this, &InkviewWindow::show_last) ); + + // ToDo: Add Pause, Resume. + + if (_fullscreen) { + Gtk::Window::fullscreen(); + } + + // Show first file + activate_action( "show_first" ); +} + +std::vector<Glib::RefPtr<Gio::File> > +InkviewWindow::create_file_list(const std::vector<Glib::RefPtr<Gio::File > >& files) +{ + std::vector<Glib::RefPtr<Gio::File> > valid_files; + + static bool first = true; + + for (auto file : files) { + Gio::FileType type = file->query_file_type(); + switch (type) { + case Gio::FILE_TYPE_NOT_KNOWN: + std::cerr << "InkviewWindow: File or directory does not exist: " + << file->get_basename() << std::endl; + break; + + case Gio::FILE_TYPE_REGULAR: + { + // Only look at SVG and SVGZ files. + std::string basename = file->get_basename(); + std::string extension = basename.substr(basename.find_last_of(".") + 1); + if (extension == "svg" || extension == "svgz") { + valid_files.push_back(file); + } + break; + } + + case Gio::FILE_TYPE_DIRECTORY: + { + if (_recursive || first) { + // No easy way to get children of directory! + Glib::RefPtr<Gio::FileEnumerator> children = file->enumerate_children(); + Glib::RefPtr<Gio::FileInfo> info; + std::vector<Glib::RefPtr<Gio::File> > input; + while ((info = children->next_file())) { + input.push_back(children->get_child(info)); + } + auto new_files = create_file_list(input); + valid_files.insert(valid_files.end(), new_files.begin(), new_files.end()); + } + break; + } + default: + std::cerr << "InkviewWindow: Unknown file type: " << type << std::endl; + } + first = false; + } + + return valid_files; +} + +void +InkviewWindow::update_title() +{ + Glib::ustring title(_documents[_index]->getDocumentName()); + + if (_documents.size() > 1) { + title += Glib::ustring::compose(" (%1/%2)", _index+1, _documents.size()); + } + + set_title(title); +} + +// Returns true if successfully shows document. +bool +InkviewWindow::show_document(SPDocument* document) +{ + document->ensureUpToDate(); // Crashes on some documents if this isn't called! + + // Resize window: (Might be better to use get_monitor_geometry_at_window(this)) + Gdk::Rectangle monitor_geometry = Inkscape::UI::get_monitor_geometry_primary(); + int width = MIN((int)document->getWidth().value("px") * _scale, monitor_geometry.get_width()); + int height = MIN((int)document->getHeight().value("px") * _scale, monitor_geometry.get_height()); + resize (width, height); + + if (_view) { + _view->setDocument(document); + } else { + _view = Gtk::manage(new Inkscape::UI::View::SVGViewWidget(document)); + add (*_view); + } + + update_title(); + + return true; +} + + +// Load document, if fail, remove entry from lists. +SPDocument* +InkviewWindow::load_document() +{ + SPDocument* document = _documents[_index]; + + if (!document) { + // We need to load document. ToDo: Pass Gio::File. Is get_base_name() better? + document = SPDocument::createNewDoc (_files[_index]->get_parse_name().c_str(), true, false); + if (document) { + // We've successfully loaded it! + _documents[_index] = document; + } + } + + if (!document) { + // Failed to load document, remove from vectors. + _documents.erase(std::next(_documents.begin(), _index)); + _files.erase( std::next( _files.begin(), _index)); + } + + return document; +} + + + +void +InkviewWindow::preload_documents() +{ + for (auto it =_files.begin(); it != _files.end(); ) { + + SPDocument* document = + SPDocument::createNewDoc ((*it)->get_parse_name().c_str(), true, false); + if (document) { + _documents.push_back(document); + ++it; + } else { + it = _files.erase(it); + } + } +} + +static std::string window_markup = R"( +<interface> + <object class="GtkWindow" id="ControlWindow"> + <child> + <object class="GtkButtonBox"> + <child> + <object class="GtkButton" id="show-first"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="icon_name">go-first</property> + </object> + </child> + </object> + </child> + <child> + <object class="GtkButton" id="show-prev"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="icon_name">go-previous</property> + </object> + </child> + </object> + </child> + <child> + <object class="GtkButton" id="show-next"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="icon_name">go-next</property> + </object> + </child> + </object> + </child> + <child> + <object class="GtkButton" id="show-last"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="icon_name">go-last</property> + </object> + </child> + </object> + </child> + </object> + </child> + </object> +</interface> +)"; + +void +InkviewWindow::show_control() +{ + if (!_controlwindow) { + + auto builder = Gtk::Builder::create(); + try + { + builder->add_from_string(window_markup); + } + catch (const Glib::Error& err) + { + std::cerr << "InkviewWindow::show_control: builder failed: " << err.what().raw() << std::endl; + return; + } + + + builder->get_widget("ControlWindow", _controlwindow); + if (!_controlwindow) { + std::cerr << "InkviewWindow::show_control: Control Window not found!" << std::endl; + return; + } + + // Need to give control window access to viewer window's actions. + Glib::RefPtr<Gio::ActionGroup> viewer = get_action_group("win"); + if (viewer) { + _controlwindow->insert_action_group("viewer", viewer); + } + + // Gtk::Button not derived from Gtk::Actionable due to ABI issues. Must use Gtk. + // Fixed in Gtk4. In Gtk4 this can be replaced by setting the action in the interface. + Gtk::Button* button; + builder->get_widget("show-first", button); + gtk_actionable_set_action_name( GTK_ACTIONABLE(button->gobj()), "viewer.show_first"); + builder->get_widget("show-prev", button); + gtk_actionable_set_action_name( GTK_ACTIONABLE(button->gobj()), "viewer.show_prev"); + builder->get_widget("show-next", button); + gtk_actionable_set_action_name( GTK_ACTIONABLE(button->gobj()), "viewer.show_next"); + builder->get_widget("show-last", button); + gtk_actionable_set_action_name( GTK_ACTIONABLE(button->gobj()), "viewer.show_last"); + + _controlwindow->set_resizable(false); + _controlwindow->set_transient_for(*this); + _controlwindow->show_all(); + + } else { + _controlwindow->present(); + } +} + +// Next document +void +InkviewWindow::show_next() +{ + ++_index; + + SPDocument* document = nullptr; + + while (_index < _documents.size() && !document) { + document = load_document(); + } + + if (document) { + // Show new document + show_document(document); + } else { + // Failed to load new document, keep current. + --_index; + } +} + +// Previous document +void +InkviewWindow::show_prev() +{ + SPDocument* document = nullptr; + int old_index = _index; + + while (_index > 0 && !document) { + --_index; + document = load_document(); + } + + if (document) { + // Show new document + show_document(document); + } else { + // Failed to load new document, keep current. + _index = old_index; + } +} + +// Show first document +void +InkviewWindow::show_first() +{ + _index = -1; + show_next(); +} + +// Show last document +void +InkviewWindow::show_last() +{ + _index = _documents.size(); + show_prev(); +} + +bool +InkviewWindow::key_press(GdkEventKey* event) +{ + switch (event->keyval) { + case GDK_KEY_Up: + case GDK_KEY_Home: + show_first(); + break; + + case GDK_KEY_Down: + case GDK_KEY_End: + show_last(); + break; + + case GDK_KEY_F11: + if (_fullscreen) { + unfullscreen(); + _fullscreen = false; + } else { + fullscreen(); + _fullscreen = true; + } + break; + + case GDK_KEY_Return: + show_control(); + break; + + case GDK_KEY_KP_Page_Down: + case GDK_KEY_Page_Down: + case GDK_KEY_Right: + case GDK_KEY_space: + show_next(); + break; + + case GDK_KEY_KP_Page_Up: + case GDK_KEY_Page_Up: + case GDK_KEY_Left: + case GDK_KEY_BackSpace: + show_prev(); + break; + + case GDK_KEY_Escape: + case GDK_KEY_q: + case GDK_KEY_Q: + close(); + break; + + default: + break; + } + return false; +} + +bool +InkviewWindow::on_timer() +{ + show_next(); + + // Stop if at end. + if (_index >= _documents.size() - 1) { + return false; + } + return true; +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : |