// SPDX-License-Identifier: GPL-2.0-or-later /** @file * @brief A base class for all dialogs. * * Authors: see git history * Tavmjong Bah * * Copyright (c) 2018 Tavmjong Bah, Authors * * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ #include "dialog-base.h" #include #include #include #include #include #include #include #include "inkscape.h" #include "desktop.h" #include "ui/dialog/dialog-data.h" #include "ui/dialog/dialog-notebook.h" #include "ui/dialog-events.h" // get_latin_keyval #include "ui/tools/tool-base.h" #include "widgets/spw-utilities.h" #include "ui/widget/canvas.h" #include "ui/util.h" namespace Inkscape { namespace UI { namespace Dialog { /** * DialogBase constructor. * * @param prefs_path characteristic path to load/save dialog position. * @param dialog_type is the "type" string for the dialog. */ DialogBase::DialogBase(gchar const *prefs_path, Glib::ustring dialog_type) : Gtk::Box(Gtk::ORIENTATION_VERTICAL) , desktop(nullptr) , document(nullptr) , selection(nullptr) , _name("DialogBase") , _prefs_path(prefs_path) , _dialog_type(dialog_type) , _app(InkscapeApplication::instance()) { auto const &dialog_data = get_dialog_data(); // Derive a pretty display name for the dialog. auto it = dialog_data.find(dialog_type); if (it != dialog_data.end()) { _name = it->second.label; // Already translated // remove ellipsis and mnemonics int pos = _name.find("...", 0); if (pos >= 0 && pos < _name.length() - 2) { _name.erase(pos, 3); } pos = _name.find("…", 0); if (pos >= 0 && pos < _name.length()) { _name.erase(pos, 1); } pos = _name.find("_", 0); if (pos >= 0 && pos < _name.length()) { _name.erase(pos, 1); } } set_name(_dialog_type); // Essential for dialog functionality property_margin().set_value(1); // Essential for dialog UI ensure_size(); } DialogBase::~DialogBase() { #ifdef _WIN32 // this is bad, but it supposedly fixes some resizng problem on Windows ensure_size(); #endif unsetDesktop(); }; void DialogBase::ensure_size() { if (desktop) { resize_widget_children(desktop->getToplevel()); } } void DialogBase::on_map() { // Update asks the dialogs if they need their Gtk widgets updated. update(); // Set the desktop on_map, although we might want to be smarter about this. // Note: Inkscape::Application::instance().active_desktop() is used here, as it contains current desktop at // the time of dialog creation. Formerly used _app.get_active_view() did not at application start-up. setDesktop(Inkscape::Application::instance().active_desktop()); parent_type::on_map(); } bool DialogBase::on_key_press_event(GdkEventKey* key_event) { switch (Inkscape::UI::Tools::get_latin_keyval(key_event)) { case GDK_KEY_Escape: defocus_dialog(); return true; } return parent_type::on_key_press_event(key_event); } /** * Highlight notebook where dialog already exists. */ void DialogBase::blink() { Gtk::Notebook *notebook = dynamic_cast(get_parent()); if (notebook && notebook->get_is_drawable()) { // Switch notebook to this dialog. notebook->set_current_page(notebook->page_num(*this)); notebook->get_style_context()->add_class("blink"); // Add timer to turn off blink. sigc::slot slot = sigc::mem_fun(*this, &DialogBase::blink_off); sigc::connection connection = Glib::signal_timeout().connect(slot, 1000); // msec } } void DialogBase::focus_dialog() { if (auto window = dynamic_cast(get_toplevel())) { window->present(); } // widget that had focus, if any if (auto child = get_focus_child()) { child->grab_focus(); } else { // find first focusable widget if (auto child = sp_find_focusable_widget(this)) { child->grab_focus(); } } } void DialogBase::defocus_dialog() { if (auto wnd = dynamic_cast(get_toplevel())) { // defocus floating dialog: sp_dialog_defocus_cpp(wnd); // for docked dialogs, move focus to canvas if (auto desktop = getDesktop()) { desktop->getCanvas()->grab_focus(); } } } /** * Callback to reset the dialog highlight. */ bool DialogBase::blink_off() { Gtk::Notebook *notebook = dynamic_cast(get_parent()); if (notebook && notebook->get_is_drawable()) { notebook->get_style_context()->remove_class("blink"); } return false; } /** * Called when the desktop might have changed for this dialog. */ void DialogBase::setDesktop(SPDesktop *new_desktop) { if (desktop == new_desktop) { return; } unsetDesktop(); if (new_desktop) { desktop = new_desktop; if (desktop->selection) { selection = desktop->selection; _select_changed = selection->connectChanged(sigc::mem_fun(*this, &DialogBase::selectionChanged_impl)); _select_modified = selection->connectModified(sigc::mem_fun(*this, &DialogBase::selectionModified_impl)); } _doc_replaced = desktop->connectDocumentReplaced(sigc::hide<0>(sigc::mem_fun(*this, &DialogBase::setDocument))); _desktop_destroyed = desktop->connectDestroy(sigc::mem_fun(*this, &DialogBase::desktopDestroyed)); this->setDocument(desktop->getDocument()); if (desktop->selection) { this->selectionChanged(selection); } set_sensitive(true); } desktopReplaced(); } // void DialogBase::fix_inner_scroll(Gtk::Widget *scrollwindow) { auto scrollwin = dynamic_cast(scrollwindow); auto viewport = dynamic_cast(scrollwin->get_child()); Gtk::Widget *child = nullptr; if (viewport) { //some widgets has viewportother not child = viewport->get_child(); } else { child = scrollwin->get_child(); } if (child && scrollwin) { Glib::RefPtr adjustment = scrollwin->get_vadjustment(); child->signal_scroll_event().connect([=](GdkEventScroll* event) { auto container = dynamic_cast(this); if (container) { std::vector widgets = container->get_children(); if (widgets.size()) { auto parentscroll = dynamic_cast(widgets[0]); if (parentscroll) { if (event->delta_y > 0 && (adjustment->get_value() + adjustment->get_page_size()) == adjustment->get_upper()) { parentscroll->event((GdkEvent*)event); return true; } else if (event->delta_y < 0 && adjustment->get_value() == adjustment->get_lower()) { parentscroll->event((GdkEvent*)event); return true; } } } } return false; }); } } /** * implementation method that call to main function only when tab is showing */ void DialogBase::selectionChanged_impl(Inkscape::Selection *selection) { if (_showing) { selectionChanged(selection); } } /** * implementation method that call to main function only when tab is showing */ void DialogBase::selectionModified_impl(Inkscape::Selection *selection, guint flags) { if (_showing) { selectionModified(selection, flags); } } /** * function called from notebook dialog that performs an update of the dialog and sets the dialog showing state true */ void DialogBase::setShowing(bool showing) { _showing = showing; selectionChanged(getSelection()); } /** * Called to destruct desktops, must not call virtuals */ void DialogBase::unsetDesktop() { desktop = nullptr; document = nullptr; selection = nullptr; _desktop_destroyed.disconnect(); _doc_replaced.disconnect(); _select_changed.disconnect(); _select_modified.disconnect(); } void DialogBase::desktopDestroyed(SPDesktop* old_desktop) { if (old_desktop == desktop && desktop) { unsetDesktop(); set_sensitive(false); } } /** * Called when the document might have changed, called from setDesktop too. */ void DialogBase::setDocument(SPDocument *new_document) { if (document != new_document) { document = new_document; documentReplaced(); } } } // namespace Dialog } // 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 :