// SPDX-License-Identifier: GPL-2.0-or-later /* * Author: * Gustav Broberg * * Copyright (C) 2007 Authors * * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ #include "ui/widget/dock.h" #include "desktop.h" #include "inkscape.h" #include "ui/icon-loader.h" #include "ui/icon-names.h" #include #include namespace Inkscape { namespace UI { namespace Widget { DockItem::DockItem(Dock& dock, const Glib::ustring& name, const Glib::ustring& long_name, const Glib::ustring& icon_name, State state, GdlDockPlacement placement) : _dock(dock), _prev_state(state), _prev_position(0), _window(nullptr), _x(0), _y(0), _grab_focus_on_realize(false), _gdl_dock_item(nullptr) { Inkscape::Preferences *prefs = Inkscape::Preferences::get(); GdlDockItemBehavior gdl_dock_behavior = (prefs->getBool("/options/dock/cancenterdock", true) ? GDL_DOCK_ITEM_BEH_NORMAL : GDL_DOCK_ITEM_BEH_CANT_DOCK_CENTER); if (!icon_name.empty()) { _icon_pixbuf = sp_get_icon_pixbuf(icon_name, "/toolbox/secondary"); } if ( _icon_pixbuf ) { _gdl_dock_item = gdl_dock_item_new_with_pixbuf_icon( name.c_str(), long_name.c_str(), _icon_pixbuf->gobj(), gdl_dock_behavior ); } else { _gdl_dock_item = gdl_dock_item_new(name.c_str(), long_name.c_str(), gdl_dock_behavior); } _frame.set_shadow_type(Gtk::SHADOW_IN); gtk_container_add (GTK_CONTAINER (_gdl_dock_item), GTK_WIDGET (_frame.gobj())); _frame.add(_dock_item_box); _dock_item_box.set_border_width(3); signal_drag_begin().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onDragBegin)); signal_drag_end().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onDragEnd)); signal_hide().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onHide), false); signal_show().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onShow), false); signal_state_changed().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onStateChanged)); signal_delete_event().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onDeleteEvent)); signal_realize().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onRealize)); _dock.addItem(*this, ( _prev_state == FLOATING_STATE || _prev_state == ICONIFIED_FLOATING_STATE ) ? GDL_DOCK_FLOATING : placement); if (_prev_state == ICONIFIED_FLOATING_STATE || _prev_state == ICONIFIED_DOCKED_STATE) { iconify(); } show_all(); } DockItem::~DockItem() { g_free(_gdl_dock_item); } Gtk::Widget& DockItem::getWidget() { return *Glib::wrap(GTK_WIDGET(_gdl_dock_item)); } GtkWidget * DockItem::gobj() { return _gdl_dock_item; } Gtk::VBox * DockItem::get_vbox() { return &_dock_item_box; } void DockItem::get_position(int& x, int& y) { if (getWindow()) { getWindow()->get_position(x, y); } else { x = _x; y = _y; } } void DockItem::get_size(int& width, int& height) { if (getWindow()) { getWindow()->get_size(width, height); } else { width = get_vbox()->get_width(); height = get_vbox()->get_height(); } } void DockItem::resize(int width, int height) { if (_window) _window->resize(width, height); } void DockItem::move(int x, int y) { if (_window) _window->move(x, y); } void DockItem::set_position(Gtk::WindowPosition position) { if (_window) _window->set_position(position); } void DockItem::set_size_request(int width, int height) { getWidget().set_size_request(width, height); } void DockItem::size_request(Gtk::Requisition& requisition) { Gtk::Requisition req_natural; getWidget().get_preferred_size(req_natural, requisition); } void DockItem::set_title(Glib::ustring title) { g_object_set (_gdl_dock_item, "long-name", title.c_str(), NULL); gdl_dock_item_set_tablabel(GDL_DOCK_ITEM(_gdl_dock_item), gtk_label_new (title.c_str())); } bool DockItem::isAttached() const { return GDL_DOCK_OBJECT_ATTACHED (_gdl_dock_item); } bool DockItem::isFloating() const { return (GTK_WIDGET(gdl_dock_object_get_toplevel(GDL_DOCK_OBJECT (_gdl_dock_item))) != _dock.getGdlWidget()); } bool DockItem::isIconified() const { return GDL_DOCK_ITEM_ICONIFIED (_gdl_dock_item); } DockItem::State DockItem::getState() const { if (isIconified() && _prev_state == FLOATING_STATE) { return ICONIFIED_FLOATING_STATE; } else if (isIconified()) { return ICONIFIED_DOCKED_STATE; } else if (isFloating() && isAttached()) { return FLOATING_STATE; } else if (isAttached()) { return DOCKED_STATE; } return UNATTACHED; } DockItem::State DockItem::getPrevState() const { return _prev_state; } GdlDockPlacement DockItem::getPlacement() const { GdlDockPlacement placement = GDL_DOCK_TOP; GdlDockObject *parent = gdl_dock_object_get_parent_object (GDL_DOCK_OBJECT(_gdl_dock_item)); if (parent) { gdl_dock_object_child_placement(parent, GDL_DOCK_OBJECT(_gdl_dock_item), &placement); } return placement; } void DockItem::hide() { gdl_dock_item_hide_item (GDL_DOCK_ITEM(_gdl_dock_item)); } void DockItem::show() { gdl_dock_item_show_item (GDL_DOCK_ITEM(_gdl_dock_item)); } void DockItem::iconify() { gdl_dock_item_iconify_item (GDL_DOCK_ITEM(_gdl_dock_item)); } void DockItem::show_all() { gtk_widget_show_all(_gdl_dock_item); } void DockItem::present() { gdl_dock_object_present(GDL_DOCK_OBJECT(_gdl_dock_item), nullptr); // always grab focus, even if we're already present grab_focus(); if (!isFloating() && getWidget().get_realized()) _dock.scrollToItem(*this); } void DockItem::grab_focus() { if (gtk_widget_get_realized (_gdl_dock_item)) { // make sure the window we're in is present Gtk::Widget *toplevel = getWidget().get_toplevel(); if (Gtk::Window *window = dynamic_cast(toplevel)) { window->present(); } gtk_widget_grab_focus (_gdl_dock_item); } else { _grab_focus_on_realize = true; } } /* Signal wrappers */ Glib::SignalProxy0 DockItem::signal_show() { return Glib::SignalProxy0(Glib::wrap(GTK_WIDGET(_gdl_dock_item)), &_signal_show_proxy); } Glib::SignalProxy0 DockItem::signal_hide() { return Glib::SignalProxy0(Glib::wrap(GTK_WIDGET(_gdl_dock_item)), &_signal_hide_proxy); } Glib::SignalProxy1 DockItem::signal_delete_event() { return Glib::SignalProxy1(Glib::wrap(GTK_WIDGET(_gdl_dock_item)), &_signal_delete_event_proxy); } Glib::SignalProxy0 DockItem::signal_drag_begin() { return Glib::SignalProxy0(Glib::wrap(GTK_WIDGET(_gdl_dock_item)), &_signal_drag_begin_proxy); } Glib::SignalProxy1 DockItem::signal_drag_end() { return Glib::SignalProxy1(Glib::wrap(GTK_WIDGET(_gdl_dock_item)), &_signal_drag_end_proxy); } Glib::SignalProxy0 DockItem::signal_realize() { return Glib::SignalProxy0(Glib::wrap(GTK_WIDGET(_gdl_dock_item)), &_signal_realize_proxy); } sigc::signal DockItem::signal_state_changed() { return _signal_state_changed; } void DockItem::_onHideWindow() { if (_window) _window->get_position(_x, _y); } void DockItem::_onHide() { if (_prev_state == ICONIFIED_DOCKED_STATE) _prev_state = DOCKED_STATE; else if (_prev_state == ICONIFIED_FLOATING_STATE) _prev_state = FLOATING_STATE; _signal_state_changed.emit(UNATTACHED, getState()); } void DockItem::_onShow() { _signal_state_changed.emit(UNATTACHED, getState()); } void DockItem::_onDragBegin() { _prev_state = getState(); if (_prev_state == FLOATING_STATE) _dock.toggleDockable(getWidget().get_width(), getWidget().get_height()); } void DockItem::_onDragEnd(bool) { State state = getState(); if (state != _prev_state) _signal_state_changed.emit(_prev_state, state); if (state == FLOATING_STATE) { if (_prev_state == FLOATING_STATE) _dock.toggleDockable(); } _prev_state = state; } void DockItem::_onRealize() { if (_grab_focus_on_realize) { _grab_focus_on_realize = false; grab_focus(); } } bool DockItem::_onKeyPress(GdkEventKey *event) { gboolean return_value; g_signal_emit_by_name (_gdl_dock_item, "key_press_event", event, &return_value); return return_value; } void DockItem::_onStateChanged(State /*prev_state*/, State new_state) { _window = getWindow(); if(_window) _window->set_type_hint(Gdk::WINDOW_TYPE_HINT_NORMAL); if (new_state == FLOATING_STATE && _window) { _window->signal_hide().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onHideWindow)); _signal_key_press_event_connection = _window->signal_key_press_event().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onKeyPress)); } } bool DockItem::_onDeleteEvent(GdkEventAny */*event*/) { hide(); return false; } Gtk::Window * DockItem::getWindow() { g_return_val_if_fail(_gdl_dock_item, 0); Gtk::Container *parent = getWidget().get_parent(); parent = (parent ? parent->get_parent() : nullptr); return (parent ? dynamic_cast(parent) : nullptr); } const Glib::SignalProxyInfo DockItem::_signal_show_proxy = { "show", (GCallback) &Glib::SignalProxyNormal::slot0_void_callback, (GCallback) &Glib::SignalProxyNormal::slot0_void_callback }; const Glib::SignalProxyInfo DockItem::_signal_hide_proxy = { "hide", (GCallback) &Glib::SignalProxyNormal::slot0_void_callback, (GCallback) &Glib::SignalProxyNormal::slot0_void_callback }; const Glib::SignalProxyInfo DockItem::_signal_delete_event_proxy = { "delete_event", (GCallback) &_signal_delete_event_callback, (GCallback) &_signal_delete_event_callback }; const Glib::SignalProxyInfo DockItem::_signal_drag_begin_proxy = { "dock-drag-begin", (GCallback) &Glib::SignalProxyNormal::slot0_void_callback, (GCallback) &Glib::SignalProxyNormal::slot0_void_callback }; const Glib::SignalProxyInfo DockItem::_signal_drag_end_proxy = { "dock_drag_end", (GCallback) &_signal_drag_end_callback, (GCallback) &_signal_drag_end_callback }; const Glib::SignalProxyInfo DockItem::_signal_realize_proxy = { "realize", (GCallback) &Glib::SignalProxyNormal::slot0_void_callback, (GCallback) &Glib::SignalProxyNormal::slot0_void_callback }; gboolean DockItem::_signal_delete_event_callback(GtkWidget *self, GdkEventAny *event, void *data) { using namespace Gtk; typedef sigc::slot SlotType; if (Glib::ObjectBase::_get_current_wrapper((GObject *) self)) { try { if(sigc::slot_base *const slot = Glib::SignalProxyNormal::data_to_slot(data)) return static_cast( (*static_cast(slot))(event) ); } catch(...) { Glib::exception_handlers_invoke(); } } typedef gboolean RType; return RType(); } void DockItem::_signal_drag_end_callback(GtkWidget *self, gboolean cancelled, void *data) { using namespace Gtk; typedef sigc::slot SlotType; if (Glib::ObjectBase::_get_current_wrapper((GObject *) self)) { try { if(sigc::slot_base *const slot = Glib::SignalProxyNormal::data_to_slot(data)) (*static_cast(slot))(cancelled); } catch(...) { Glib::exception_handlers_invoke(); } } } } // namespace Widget } // 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 :