summaryrefslogtreecommitdiffstats
path: root/src/ui/toolbar/page-toolbar.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/toolbar/page-toolbar.cpp')
-rw-r--r--src/ui/toolbar/page-toolbar.cpp351
1 files changed, 351 insertions, 0 deletions
diff --git a/src/ui/toolbar/page-toolbar.cpp b/src/ui/toolbar/page-toolbar.cpp
new file mode 100644
index 0000000..d845872
--- /dev/null
+++ b/src/ui/toolbar/page-toolbar.cpp
@@ -0,0 +1,351 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * @file
+ * Page aux toolbar: Temp until we convert all toolbars to ui files with Gio::Actions.
+ */
+/* Authors:
+ * Martin Owens <doctormo@geek-2.com>
+
+ * Copyright (C) 2021 Tavmjong Bah
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "page-toolbar.h"
+
+#include <glibmm/i18n.h>
+#include <gtkmm.h>
+#include <regex>
+
+#include "desktop.h"
+#include "document-undo.h"
+#include "document.h"
+#include "io/resource.h"
+#include "object/sp-namedview.h"
+#include "object/sp-page.h"
+#include "ui/icon-names.h"
+#include "ui/tools/pages-tool.h"
+#include "util/paper.h"
+#include "util/units.h"
+
+using Inkscape::IO::Resource::UIS;
+
+namespace Inkscape {
+namespace UI {
+namespace Toolbar {
+
+PageToolbar::PageToolbar(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder> &builder, SPDesktop *desktop)
+ : Gtk::Toolbar(cobject)
+ , _desktop(desktop)
+ , combo_page_sizes(nullptr)
+ , text_page_label(nullptr)
+{
+ builder->get_widget("page_sizes", combo_page_sizes);
+ builder->get_widget("page_label", text_page_label);
+ builder->get_widget("page_pos", label_page_pos);
+ builder->get_widget("page_backward", btn_page_backward);
+ builder->get_widget("page_foreward", btn_page_foreward);
+ builder->get_widget("page_delete", btn_page_delete);
+ builder->get_widget("page_move_objects", btn_move_toggle);
+ builder->get_widget("sep1", sep1);
+
+ if (text_page_label) {
+ text_page_label->signal_changed().connect(sigc::mem_fun(*this, &PageToolbar::labelEdited));
+ }
+
+ if (combo_page_sizes) {
+ combo_page_sizes->signal_changed().connect(sigc::mem_fun(*this, &PageToolbar::sizeChoose));
+ entry_page_sizes = dynamic_cast<Gtk::Entry *>(combo_page_sizes->get_child());
+ if (entry_page_sizes) {
+ entry_page_sizes->set_placeholder_text(_("ex.: 100x100cm"));
+ entry_page_sizes->set_tooltip_text(_("Type in width & height of a page. (ex.: 15x10cm, 10in x 100mm)\n"
+ "or choose preset from dropdown."));
+ entry_page_sizes->signal_activate().connect(sigc::mem_fun(*this, &PageToolbar::sizeChanged));
+ entry_page_sizes->signal_icon_press().connect([=](Gtk::EntryIconPosition, const GdkEventButton*){
+ _document->getPageManager().changeOrientation();
+ DocumentUndo::maybeDone(_document, "page-resize", _("Resize Page"), INKSCAPE_ICON("tool-pages"));
+ setSizeText();
+ });
+ entry_page_sizes->signal_focus_in_event().connect([=](GdkEventFocus* focus){
+ entry_page_sizes->set_text("");
+ return false;
+ });
+ }
+ auto& page_sizes = Inkscape::PaperSize::getPageSizes();
+ for (int i = 0; i < page_sizes.size(); i++) {
+ combo_page_sizes->append(std::to_string(i), page_sizes[i].getDescription(false));
+ }
+ }
+
+ // Watch for when the tool changes
+ _ec_connection = _desktop->connectEventContextChanged(sigc::mem_fun(*this, &PageToolbar::toolChanged));
+ _doc_connection = _desktop->connectDocumentReplaced([=](SPDesktop *desktop, SPDocument *doc) {
+ if (doc) {
+ toolChanged(desktop, desktop->getEventContext());
+ }
+ });
+
+ // Constructed by a builder, so we're going to protect the widget from destruction.
+ this->reference();
+ was_referenced = true;
+}
+
+void PageToolbar::on_parent_changed(Gtk::Widget *)
+{
+ if (was_referenced) {
+ // Undo the gtkbuilder protection now that we have a parent
+ this->unreference();
+ was_referenced = false;
+ }
+}
+
+PageToolbar::~PageToolbar()
+{
+ _ec_connection.disconnect();
+ _doc_connection.disconnect();
+ toolChanged(nullptr, nullptr);
+}
+
+void PageToolbar::toolChanged(SPDesktop *desktop, Inkscape::UI::Tools::ToolBase *ec)
+{
+ // Disconnect previous page changed signal
+ _page_selected.disconnect();
+ _pages_changed.disconnect();
+ _page_modified.disconnect();
+ _document = nullptr;
+
+ if (dynamic_cast<Inkscape::UI::Tools::PagesTool *>(ec)) {
+ // Save the document and page_manager for future use.
+ if ((_document = desktop->getDocument())) {
+ auto &page_manager = _document->getPageManager();
+ // Connect the page changed signal and indicate changed
+ _pages_changed = page_manager.connectPagesChanged(sigc::mem_fun(*this, &PageToolbar::pagesChanged));
+ _page_selected = page_manager.connectPageSelected(sigc::mem_fun(*this, &PageToolbar::selectionChanged));
+ // Update everything now.
+ pagesChanged();
+ }
+ }
+}
+
+void PageToolbar::labelEdited()
+{
+ auto text = text_page_label->get_text();
+ if (auto page = _document->getPageManager().getSelected()) {
+ page->setLabel(text.empty() ? nullptr : text.c_str());
+ DocumentUndo::maybeDone(_document, "page-relabel", _("Relabel Page"), INKSCAPE_ICON("tool-pages"));
+ }
+}
+
+void PageToolbar::sizeChoose()
+{
+ auto &pm = _document->getPageManager();
+ try {
+ auto p_rect = pm.getSelectedPageRect();
+ bool landscape = p_rect.width() > p_rect.height();
+
+ auto page_id = std::stoi(combo_page_sizes->get_active_id());
+ auto& page_sizes = Inkscape::PaperSize::getPageSizes();
+ if (page_id >= 0 && page_id < page_sizes.size()) {
+ auto&& ps = page_sizes[page_id];
+ // Keep page orientation while selecting size
+ auto width = ps.unit->convert(ps.size[landscape], "px");
+ auto height = ps.unit->convert(ps.size[!landscape], "px");
+ pm.resizePage(width, height);
+ setSizeText();
+ DocumentUndo::maybeDone(_document, "page-resize", _("Resize Page"), INKSCAPE_ICON("tool-pages"));
+ }
+ } catch (std::invalid_argument const &e) {
+ // Ignore because user is typing into Entry
+ }
+}
+
+double PageToolbar::_unit_to_size(std::string number, std::string unit_str, std::string backup)
+{
+ // We always support comma, even if not in that particular locale.
+ std::replace(number.begin(), number.end(), ',', '.');
+ double value = std::stod(number);
+
+ // Get the best unit, for example 50x40cm means cm for both
+ if (unit_str.empty() && !backup.empty())
+ unit_str = backup;
+ if (unit_str == "\"")
+ unit_str = "in";
+
+ // Output is always in px as it's the most useful.
+ auto px = Inkscape::Util::unit_table.getUnit("px");
+
+ // Convert from user entered unit to display unit
+ if (!unit_str.empty())
+ return Inkscape::Util::Quantity::convert(value, unit_str, px);
+
+ // Default unit is the document's display unit
+ auto unit = _document->getDisplayUnit();
+ return Inkscape::Util::Quantity::convert(value, unit, px);
+}
+
+/**
+ * A manually typed input size, parse out what we can understand from
+ * the text or ignore it if the text can't be parsed.
+ *
+ * Format: 50cm x 40mm
+ * 20',40"
+ * 30,4-40.2
+ */
+void PageToolbar::sizeChanged()
+{
+ // Parse the size out of the typed text if possible.
+ auto text = std::string(combo_page_sizes->get_active_text());
+ // This does not support negative values, because pages can not be negatively sized.
+ static std::string arg = "([0-9]+[\\.,]?[0-9]*|\\.[0-9]+) ?(px|mm|cm|in|\\\")?";
+ // We can't support × here since it's UTF8 and this doesn't match
+ static std::regex re_size("^ *" + arg + " *([ *Xx,\\-]) *" + arg + " *$");
+
+ std::smatch matches;
+ if (std::regex_match(text, matches, re_size)) {
+ double width = _unit_to_size(matches[1], matches[2], matches[5]);
+ double height = _unit_to_size(matches[4], matches[5], matches[2]);
+ if (width > 0 && height > 0) {
+ _document->getPageManager().resizePage(width, height);
+ }
+ }
+ setSizeText();
+}
+
+/**
+ * Sets the size of the current page into the entry page size.
+ */
+void PageToolbar::setSizeText(SPPage *page)
+{
+ if (!page)
+ page = _document->getPageManager().getSelected();
+
+ auto unit = _document->getDisplayUnit();
+ double width = _document->getWidth().value(unit);
+ double height = _document->getHeight().value(unit);
+ if (page) {
+ auto px = Inkscape::Util::unit_table.getUnit("px");
+ auto rect = page->getDesktopRect();
+ width = px->convert(rect.width(), unit);
+ height = px->convert(rect.height(), unit);
+ }
+ // Orientation button
+ std::string icon = width > height ? "page-landscape" : "page-portrait";
+ if (width == height) {
+ entry_page_sizes->unset_icon(Gtk::ENTRY_ICON_SECONDARY);
+ } else {
+ entry_page_sizes->set_icon_from_icon_name(INKSCAPE_ICON(icon), Gtk::ENTRY_ICON_SECONDARY);
+ }
+
+ if (auto page_size = Inkscape::PaperSize::findPaperSize(width, height, unit)) {
+ entry_page_sizes->set_text(page_size->getDescription(width > height));
+ } else {
+ entry_page_sizes->set_text(Inkscape::PaperSize::toDescription(_("Custom"), width, height, unit));
+ }
+ // Select text if box is currently in focus.
+ if (entry_page_sizes->has_focus()) {
+ entry_page_sizes->select_region(0, -1);
+ }
+}
+
+void PageToolbar::pagesChanged()
+{
+ selectionChanged(_document->getPageManager().getSelected());
+}
+
+void PageToolbar::selectionChanged(SPPage *page)
+{
+ _page_modified.disconnect();
+ auto &page_manager = _document->getPageManager();
+ text_page_label->set_tooltip_text(_("Page label"));
+
+ // Set label widget content with page label.
+ if (page) {
+ text_page_label->set_sensitive(true);
+ text_page_label->set_placeholder_text(page->getDefaultLabel());
+
+ if (auto label = page->label()) {
+ text_page_label->set_text(label);
+ } else {
+ text_page_label->set_text("");
+ }
+
+ // TRANSLATORS: "%1" is replaced with the page we are on, and "%2" is the total number of pages.
+ auto label = Glib::ustring::compose(_("%1/%2"), page->getPagePosition(), page_manager.getPageCount());
+ label_page_pos->set_label(label);
+
+ _page_modified = page->connectModified([=](SPObject *obj, unsigned int flags) {
+ if (auto page = dynamic_cast<SPPage *>(obj)) {
+ // Make sure we don't 'select' on removal of the page
+ if (flags & SP_OBJECT_MODIFIED_FLAG) {
+ selectionChanged(page);
+ }
+ }
+ });
+ } else {
+ text_page_label->set_text("");
+ text_page_label->set_sensitive(false);
+ text_page_label->set_placeholder_text(_("Single Page Document"));
+ label_page_pos->set_label(_("1/-"));
+
+ _page_modified = _document->connectModified([=](guint) {
+ selectionChanged(nullptr);
+ });
+ }
+ if (!page_manager.hasPrevPage() && !page_manager.hasNextPage() && !page) {
+ sep1->set_visible(false);
+ label_page_pos->get_parent()->set_visible(false);
+ btn_page_backward->set_visible(false);
+ btn_page_foreward->set_visible(false);
+ btn_page_delete->set_visible(false);
+ btn_move_toggle->set_sensitive(false);
+ } else {
+ // Set the forward and backward button sensitivities
+ sep1->set_visible(true);
+ label_page_pos->get_parent()->set_visible(true);
+ btn_page_backward->set_visible(true);
+ btn_page_foreward->set_visible(true);
+ btn_page_backward->set_sensitive(page_manager.hasPrevPage());
+ btn_page_foreward->set_sensitive(page_manager.hasNextPage());
+ btn_page_delete->set_visible(true);
+ btn_move_toggle->set_sensitive(true);
+ }
+ setSizeText(page);
+}
+
+GtkWidget *PageToolbar::create(SPDesktop *desktop)
+{
+ Glib::ustring page_toolbar_builder_file = get_filename(UIS, "toolbar-page.ui");
+ PageToolbar *toolbar = nullptr;
+
+ try {
+ auto builder = Gtk::Builder::create_from_file(page_toolbar_builder_file);
+ builder->get_widget_derived("page-toolbar", toolbar, desktop);
+
+ if (!toolbar) {
+ std::cerr << "InkscapeWindow: Failed to load page toolbar!" << std::endl;
+ return nullptr;
+ }
+ // Usually we should be packing this widget into a parent before the builder
+ // is destroyed, but the create method expects a blind widget so this widget
+ // contains a special keep-alive pattern which can be removed when refactoring.
+ } catch (const Glib::Error &ex) {
+ std::cerr << "PageToolbar: " << page_toolbar_builder_file << " file not read! " << ex.what().raw() << std::endl;
+ }
+ return GTK_WIDGET(toolbar->gobj());
+}
+
+
+} // namespace Toolbar
+} // namespace UI
+} // namespace Inkscape
+
+/*
+ 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:fileencoding=utf-8:textwidth=99 :