diff options
Diffstat (limited to 'src/ui/dialog/dialog-window.cpp')
-rw-r--r-- | src/ui/dialog/dialog-window.cpp | 308 |
1 files changed, 308 insertions, 0 deletions
diff --git a/src/ui/dialog/dialog-window.cpp b/src/ui/dialog/dialog-window.cpp new file mode 100644 index 0000000..f91f399 --- /dev/null +++ b/src/ui/dialog/dialog-window.cpp @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/** @file + * @brief A window for floating 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 "ui/dialog/dialog-window.h" + +#include <glibmm/i18n.h> +#include <gtkmm/application.h> +#include <gtkmm/box.h> +#include <gtkmm/label.h> +#include <iostream> + +#include "enums.h" +#include "inkscape-application.h" +#include "inkscape-window.h" +#include "inkscape.h" +#include "preferences.h" +#include "ui/dialog/dialog-base.h" +#include "ui/dialog/dialog-container.h" +#include "ui/dialog/dialog-manager.h" +#include "ui/dialog/dialog-multipaned.h" +#include "ui/dialog/dialog-notebook.h" +#include "ui/shortcuts.h" +#include "ui/util.h" + +// Sizing constants +const int MINIMUM_WINDOW_WIDTH = 210; +const int MINIMUM_WINDOW_HEIGHT = 320; +const int INITIAL_WINDOW_WIDTH = 360; +const int INITIAL_WINDOW_HEIGHT = 520; +const int WINDOW_DROPZONE_SIZE = 10; +const int WINDOW_DROPZONE_SIZE_LARGE = 16; +const int NOTEBOOK_TAB_HEIGHT = 36; + +namespace Inkscape { +namespace UI { +namespace Dialog { + +class DialogNotebook; +class DialogContainer; + +DialogWindow::~DialogWindow() {} + +// Create a dialog window and move page from old notebook. +DialogWindow::DialogWindow(InkscapeWindow *inkscape_window, Gtk::Widget *page) + : Gtk::Window() + , _app(InkscapeApplication::instance()) + , _inkscape_window(inkscape_window) + , _title(_("Dialog Window")) +{ + g_assert(_app != nullptr); + g_assert(_inkscape_window != nullptr); + + // ============ Initialization =============== + // Setting the window type + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + bool window_above = true; + if (prefs) { + window_above = + prefs->getInt("/options/transientpolicy/value", PREFS_DIALOGS_WINDOWS_NORMAL) != PREFS_DIALOGS_WINDOWS_NONE; + } + + set_type_hint(Gdk::WINDOW_TYPE_HINT_DIALOG); + set_transient_for(*inkscape_window); + + // Add the dialog window to our app + _app->gtk_app()->add_window(*this); + + this->signal_delete_event().connect([=](GdkEventAny *) { + DialogManager::singleton().store_state(*this); + delete this; + return true; + }); + + auto win_action_group = dynamic_cast<Gio::ActionGroup *>(inkscape_window); + if (win_action_group) { + // Must use C API as C++ API takes a RefPtr which we can't get (easily). + gtk_widget_insert_action_group(GTK_WIDGET(this->gobj()), "win", win_action_group->gobj()); + } else { + std::cerr << "DialogWindow::DialogWindow: Can't find InkscapeWindow Gio:ActionGroup!" << std::endl; + } + + insert_action_group("doc", inkscape_window->get_document()->getActionGroup()); + + // ============ Theming: icons ============== + + + // Set the style and icon theme of the new menu based on the desktop + if (auto desktop = SP_ACTIVE_DESKTOP) { + if (Gtk::Window *window = desktop->getToplevel()) { + if (window->get_style_context()->has_class("dark")) { + get_style_context()->add_class("dark"); + get_style_context()->remove_class("bright"); + } else { + get_style_context()->add_class("bright"); + get_style_context()->remove_class("dark"); + } + if (prefs->getBool("/theme/symbolicIcons", false)) { + get_style_context()->add_class("symbolic"); + get_style_context()->remove_class("regular"); + } else { + get_style_context()->add_class("regular"); + get_style_context()->remove_class("symbolic"); + } + } + } + + // ================ Window ================== + set_title(_title); + set_name(_title); + int window_width = INITIAL_WINDOW_WIDTH; + int window_height = INITIAL_WINDOW_HEIGHT; + + // =============== Outer Box ================ + Gtk::Box *box_outer = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL)); + add(*box_outer); + + // =============== Container ================ + _container = Gtk::manage(new DialogContainer(inkscape_window)); + DialogMultipaned *columns = _container->get_columns(); + auto drop_size = Inkscape::Preferences::get()->getBool("/options/dockingzone/value", true) ? WINDOW_DROPZONE_SIZE / 2 : WINDOW_DROPZONE_SIZE; + columns->set_dropzone_sizes(drop_size, drop_size); + box_outer->pack_end(*_container); + + // If there is no page, create an empty Dialogwindow to be populated later + if (page) { + // ============= Initial Column ============= + DialogMultipaned *column = _container->create_column(); + columns->append(column); + + // ============== New Notebook ============== + DialogNotebook *dialog_notebook = Gtk::manage(new DialogNotebook(_container)); + column->append(dialog_notebook); + column->set_dropzone_sizes(drop_size, drop_size); + dialog_notebook->move_page(*page); + + // Set window title + DialogBase *dialog = dynamic_cast<DialogBase *>(page); + if (dialog) { + _title = dialog->get_name(); + set_title(_title); + } + + // Set window size considering what the dialog needs + Gtk::Requisition minimum_size, natural_size; + dialog->get_preferred_size(minimum_size, natural_size); + int overhead = 2 * (drop_size + dialog->property_margin().get_value()); + int width = natural_size.width + overhead; + int height = natural_size.height + overhead + NOTEBOOK_TAB_HEIGHT; + window_width = std::max(width, window_width); + window_height = std::max(height, window_height); + } + + // Set window sizing + set_size_request(MINIMUM_WINDOW_WIDTH, MINIMUM_WINDOW_HEIGHT); + set_default_size(window_width, window_height); + + if (page) { + update_dialogs(); + } + + // window is created hidden; don't show it now, its size needs to be restored +} + +/** + * Change InkscapeWindow that DialogWindow is linked to. + */ +void DialogWindow::set_inkscape_window(InkscapeWindow* inkscape_window) +{ + if (!inkscape_window) { + std::cerr << "DialogWindow::set_inkscape_window: no inkscape_window!" << std::endl; + return; + } + + _inkscape_window = inkscape_window; + update_dialogs(); +} + +/** + * Update all dialogs that are owned by the DialogWindow's _container. + */ +void DialogWindow::update_dialogs() +{ + g_assert(_app != nullptr); + g_assert(_container != nullptr); + g_assert(_inkscape_window != nullptr); + + _container->set_inkscape_window(_inkscape_window); + _container->update_dialogs(); // Updating dialogs is not using the _app reference here. + + // Set window title. + const std::multimap<Glib::ustring, DialogBase *> *dialogs = _container->get_dialogs(); + if (dialogs->size() > 1) { + _title = "Multiple dialogs"; + } else if (dialogs->size() == 1) { + _title = dialogs->begin()->second->get_name(); + } else { + // Should not happen... but does on closing a window! + // std::cerr << "DialogWindow::update_dialogs(): No dialogs!" << std::endl; + _title = ""; + } + + auto document_name = _inkscape_window->get_document()->getDocumentName(); + if (document_name) { + set_title(_title + " - " + Glib::ustring(document_name)); + } +} + +/** + * Update window width and height in order to fit all dialogs inisde its container. + * + * The intended use of this function is at initialization. + */ +void DialogWindow::update_window_size_to_fit_children() +{ + // Declare variables + int pos_x = 0, pos_y = 0; + int width = 0, height = 0; + int overhead = 0, baseline; + Gtk::Allocation allocation; + Gtk::Requisition minimum_size, natural_size; + + // Read needed data + get_position(pos_x, pos_y); + get_allocated_size(allocation, baseline); + const std::multimap<Glib::ustring, DialogBase *> *dialogs = _container->get_dialogs(); + + // Get largest sizes for dialogs + for (auto dialog : *dialogs) { + dialog.second->get_preferred_size(minimum_size, natural_size); + width = std::max(natural_size.width, width); + height = std::max(natural_size.height, height); + overhead = std::max(overhead, dialog.second->property_margin().get_value()); + } + + // Compute sizes including overhead + overhead = 2 * (WINDOW_DROPZONE_SIZE_LARGE + overhead); + width = width + overhead; + height = height + overhead + NOTEBOOK_TAB_HEIGHT; + + // If sizes are lower then current, don't change them + if (allocation.get_width() >= width && allocation.get_height() >= height) { + return; + } + + // Compute largest sizes on both axis + width = std::max(width, allocation.get_width()); + height = std::max(height, allocation.get_height()); + + // Compute new positions to keep window centered + pos_x = pos_x - (width - allocation.get_width()) / 2; + pos_y = pos_y - (height - allocation.get_height()) / 2; + + // Keep window inside the screen + pos_x = std::max(pos_x, 0); + pos_y = std::max(pos_y, 0); + + // Resize window + move(pos_x, pos_y); + resize(width, height); +} + +// mimic InkscapeWindow handling of shortcuts to make them work with active floating dialog window +bool DialogWindow::on_key_press_event(GdkEventKey *key_event) +{ + auto focus = get_focus(); + if (focus) { + if (focus->event(reinterpret_cast<GdkEvent *>(key_event))) { + return true; + } + } + + // Pass key event to this window or to app (via this window). + if (Gtk::Window::on_key_press_event(key_event)) { + return true; + } + + // Pass key event to active InkscapeWindow to handle win (and app) level shortcuts. + if (auto win = _app->get_active_window(); win && win->on_key_press_event(key_event)) { + return true; + } + + return false; +} + +} // 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 : |