1 /**
2 * \brief A custom Inkscape wrapper around gdl_dock_item
3 *
4 * Author:
5 * Gustav Broberg <broberg@kth.se>
6 *
7 * Copyright (C) 2007 Authors
8 *
9 * Released under GNU GPL. Read the file 'COPYING' for more information.
10 */
12 #include "dock-item.h"
13 #include "desktop.h"
14 #include "inkscape.h"
15 #include "prefs-utils.h"
16 #include "ui/widget/dock.h"
17 #include "widgets/icon.h"
19 #include <gtk/gtk.h>
21 #include <gtkmm/invisible.h>
22 #include <gtkmm/stock.h>
24 namespace Inkscape {
25 namespace UI {
26 namespace Widget {
28 DockItem::DockItem(Dock& dock, const Glib::ustring& name, const Glib::ustring& long_name,
29 const Glib::ustring& icon_name, State state) :
30 _dock (dock),
31 _prev_state (state),
32 _window (NULL),
33 _dock_item_action_area (NULL)
34 {
36 GdlDockItemBehavior gdl_dock_behavior =
37 (prefs_get_int_attribute_limited ("options.dock", "cancenterdock", 1, 0, 1) == 0 ?
38 GDL_DOCK_ITEM_BEH_CANT_DOCK_CENTER
39 : GDL_DOCK_ITEM_BEH_NORMAL);
41 if (!icon_name.empty()) {
42 Gtk::Widget *icon = sp_icon_get_icon(icon_name, Inkscape::ICON_SIZE_MENU);
43 if (icon) {
44 // check icon type (inkscape, gtk, none)
45 if ( SP_IS_ICON(icon->gobj()) ) {
46 SPIcon* sp_icon = SP_ICON(icon->gobj());
47 sp_icon_fetch_pixbuf(sp_icon);
48 _icon_pixbuf = Glib::wrap(sp_icon->pb, true);
49 } else if ( GTK_IS_IMAGE(icon->gobj()) ) {
50 _icon_pixbuf = Gtk::Invisible().render_icon(Gtk::StockID(icon_name),
51 Gtk::ICON_SIZE_MENU);
52 }
53 delete icon;
55 _gdl_dock_item =
56 gdl_dock_item_new_with_pixbuf_icon(name.c_str(), long_name.c_str(),
57 _icon_pixbuf->gobj(), gdl_dock_behavior);
58 }
59 } else {
60 _gdl_dock_item =
61 gdl_dock_item_new(name.c_str(), long_name.c_str(), gdl_dock_behavior);
62 }
64 _frame.set_shadow_type(Gtk::SHADOW_IN);
65 gtk_container_add (GTK_CONTAINER (_gdl_dock_item), GTK_WIDGET (_frame.gobj()));
66 _frame.add(_dock_item_box);
67 _dock_item_box.set_border_width(3);
69 signal_drag_begin().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onDragBegin));
70 signal_drag_end().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onDragEnd));
71 signal_hide().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onHide), false);
72 signal_show().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onShow), false);
73 signal_state_changed().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onStateChanged));
74 signal_delete_event().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onDeleteEvent));
75 signal_realize().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onRealize));
77 _dock.addItem(*this, (_prev_state == FLOATING_STATE ? FLOATING : TOP));
79 show_all();
80 }
82 DockItem::~DockItem()
83 {
84 g_free(_gdl_dock_item);
85 }
87 Gtk::Widget&
88 DockItem::getWidget()
89 {
90 return *Glib::wrap(GTK_WIDGET(_gdl_dock_item));
91 }
93 GtkWidget *
94 DockItem::gobj()
95 {
96 return _gdl_dock_item;
97 }
99 Gtk::VBox *
100 DockItem::get_vbox()
101 {
102 return &_dock_item_box;
103 }
106 void
107 DockItem::get_position(int& x, int& y)
108 {
109 if (getWindow()) {
110 getWindow()->get_position(x, y);
111 } else {
112 x = _x;
113 y = _y;
114 }
115 }
117 void
118 DockItem::get_size(int& width, int& height)
119 {
120 if (_window) {
121 _window->get_size(width, height);
122 } else {
123 width = get_vbox()->get_width();
124 height = get_vbox()->get_height();
125 }
126 }
129 void
130 DockItem::resize(int width, int height)
131 {
132 if (_window)
133 _window->resize(width, height);
134 }
137 void
138 DockItem::move(int x, int y)
139 {
140 if (_window)
141 _window->move(x, y);
142 }
144 void
145 DockItem::set_position(Gtk::WindowPosition position)
146 {
147 if (_window)
148 _window->set_position(position);
149 }
151 void
152 DockItem::set_size_request(int width, int height)
153 {
154 getWidget().set_size_request(width, height);
155 }
157 void
158 DockItem::size_request(Gtk::Requisition& requisition)
159 {
160 getWidget().size_request(requisition);
161 }
163 void
164 DockItem::set_title(Glib::ustring title)
165 {
166 g_object_set (_gdl_dock_item,
167 "long-name", title.c_str(),
168 NULL);
170 gdl_dock_item_set_tablabel(GDL_DOCK_ITEM(_gdl_dock_item),
171 gtk_label_new (title.c_str()));
172 }
174 bool
175 DockItem::isAttached() const
176 {
177 return GDL_DOCK_OBJECT_ATTACHED (_gdl_dock_item);
178 }
181 bool
182 DockItem::isFloating() const
183 {
184 return (GTK_WIDGET(gdl_dock_object_get_toplevel(GDL_DOCK_OBJECT (_gdl_dock_item))) !=
185 _dock.getGdlWidget());
186 }
188 bool
189 DockItem::isIconified() const
190 {
191 return GDL_DOCK_ITEM_ICONIFIED (_gdl_dock_item);
192 }
194 DockItem::State
195 DockItem::getState() const
196 {
197 return (isAttached() ? (isFloating() ? FLOATING_STATE : DOCKED_STATE) : UNATTACHED);
198 }
200 DockItem::State
201 DockItem::getPrevState() const
202 {
203 return _prev_state;
204 }
206 DockItem::Placement
207 DockItem::getPlacement() const
208 {
209 GdlDockPlacement placement = (GdlDockPlacement)NONE;
210 gdl_dock_object_child_placement(gdl_dock_object_get_parent_object (GDL_DOCK_OBJECT(_gdl_dock_item)),
211 GDL_DOCK_OBJECT(_gdl_dock_item),
212 &placement);
213 return (Placement)placement;
214 }
216 void
217 DockItem::hide()
218 {
219 gdl_dock_item_hide_item (GDL_DOCK_ITEM(_gdl_dock_item));
220 }
222 void
223 DockItem::show()
224 {
225 gdl_dock_item_show_item (GDL_DOCK_ITEM(_gdl_dock_item));
226 }
228 void
229 DockItem::show_all()
230 {
231 gtk_widget_show_all(_gdl_dock_item);
232 }
234 void
235 DockItem::present()
236 {
238 if (isIconified() || !isAttached()) {
239 show();
240 }
242 // tabbed
243 else if (getPlacement() == CENTER) {
244 int i = gtk_notebook_page_num (GTK_NOTEBOOK (_gdl_dock_item->parent),
245 GTK_WIDGET (_gdl_dock_item));
246 if (i >= 0)
247 gtk_notebook_set_current_page (GTK_NOTEBOOK (_gdl_dock_item->parent), i);
248 }
250 // always grab focus, even if we're already present
251 grab_focus();
253 if (!isFloating() && getWidget().is_realized())
254 _dock.scrollToItem(*this);
255 }
258 void
259 DockItem::grab_focus()
260 {
261 if (GTK_WIDGET_REALIZED (_gdl_dock_item)) {
263 // make sure the window we're in is present
264 Gtk::Widget *toplevel = getWidget().get_toplevel();
265 if (Gtk::Window *window = dynamic_cast<Gtk::Window *>(toplevel)) {
266 window->present();
267 }
269 gtk_widget_grab_focus (_gdl_dock_item);
271 } else {
272 _grab_focus_on_realize = true;
273 }
274 }
277 /* Signal wrappers */
279 Glib::SignalProxy0<void>
280 DockItem::signal_show()
281 {
282 return Glib::SignalProxy0<void>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)),
283 &_signal_show_proxy);
284 }
286 Glib::SignalProxy0<void>
287 DockItem::signal_hide()
288 {
289 return Glib::SignalProxy0<void>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)),
290 &_signal_hide_proxy);
291 }
293 Glib::SignalProxy1<bool, GdkEventAny *>
294 DockItem::signal_delete_event()
295 {
296 return Glib::SignalProxy1<bool, GdkEventAny *>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)),
297 &_signal_delete_event_proxy);
298 }
300 Glib::SignalProxy0<void>
301 DockItem::signal_drag_begin()
302 {
303 return Glib::SignalProxy0<void>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)),
304 &_signal_drag_begin_proxy);
305 }
307 Glib::SignalProxy1<void, bool>
308 DockItem::signal_drag_end()
309 {
310 return Glib::SignalProxy1<void, bool>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)),
311 &_signal_drag_end_proxy);
312 }
314 Glib::SignalProxy0<void>
315 DockItem::signal_realize()
316 {
317 return Glib::SignalProxy0<void>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)),
318 &_signal_realize_proxy);
319 }
321 sigc::signal<void, DockItem::State, DockItem::State>
322 DockItem::signal_state_changed()
323 {
324 return _signal_state_changed;
325 }
327 void
328 DockItem::_onHideWindow()
329 {
330 if (_window)
331 _window->get_position(_x, _y);
332 }
334 void
335 DockItem::_onHide()
336 {
337 _signal_state_changed.emit(UNATTACHED, getState());
338 }
340 void
341 DockItem::_onShow()
342 {
343 _signal_state_changed.emit(UNATTACHED, getState());
344 }
346 void
347 DockItem::_onDragBegin()
348 {
349 _prev_state = getState();
350 if (_prev_state == FLOATING_STATE)
351 _dock.toggleDockable(getWidget().get_width(), getWidget().get_height());
352 }
354 void
355 DockItem::_onDragEnd(bool)
356 {
357 State state = getState();
359 if (state != _prev_state)
360 _signal_state_changed.emit(_prev_state, state);
362 if (state == FLOATING_STATE) {
363 if (_prev_state == FLOATING_STATE)
364 _dock.toggleDockable();
365 }
367 _prev_state = state;
368 }
370 void
371 DockItem::_onRealize()
372 {
373 if (_grab_focus_on_realize) {
374 _grab_focus_on_realize = false;
375 grab_focus();
376 }
377 }
379 bool
380 DockItem::_onKeyPress(GdkEventKey *event)
381 {
382 gboolean return_value;
383 g_signal_emit_by_name (_gdl_dock_item, "key_press_event", event, &return_value);
384 return return_value;
385 }
387 void
388 DockItem::_onStateChanged(State /*prev_state*/, State new_state)
389 {
390 _window = getWindow();
392 if (new_state == FLOATING_STATE && _window) {
393 _window->signal_hide().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onHideWindow));
394 _signal_key_press_event_connection =
395 _window->signal_key_press_event().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onKeyPress));
396 }
397 }
400 bool
401 DockItem::_onDeleteEvent(GdkEventAny */*event*/)
402 {
403 hide();
404 return false;
405 }
408 Gtk::Window *
409 DockItem::getWindow()
410 {
411 g_return_val_if_fail(_gdl_dock_item, 0);
412 Gtk::Container *parent = getWidget().get_parent();
413 parent = (parent ? parent->get_parent() : 0);
414 return (parent ? dynamic_cast<Gtk::Window *>(parent) : 0);
415 }
417 const Glib::SignalProxyInfo
418 DockItem::_signal_show_proxy =
419 {
420 "show",
421 (GCallback) &Glib::SignalProxyNormal::slot0_void_callback,
422 (GCallback) &Glib::SignalProxyNormal::slot0_void_callback
423 };
425 const Glib::SignalProxyInfo
426 DockItem::_signal_hide_proxy =
427 {
428 "hide",
429 (GCallback) &Glib::SignalProxyNormal::slot0_void_callback,
430 (GCallback) &Glib::SignalProxyNormal::slot0_void_callback
431 };
434 const Glib::SignalProxyInfo
435 DockItem::_signal_delete_event_proxy =
436 {
437 "delete_event",
438 (GCallback) &_signal_delete_event_callback,
439 (GCallback) &_signal_delete_event_callback
440 };
443 const Glib::SignalProxyInfo
444 DockItem::_signal_drag_begin_proxy =
445 {
446 "dock-drag-begin",
447 (GCallback) &Glib::SignalProxyNormal::slot0_void_callback,
448 (GCallback) &Glib::SignalProxyNormal::slot0_void_callback
449 };
452 const Glib::SignalProxyInfo
453 DockItem::_signal_drag_end_proxy =
454 {
455 "dock_drag_end",
456 (GCallback) &_signal_drag_end_callback,
457 (GCallback) &_signal_drag_end_callback
458 };
461 const Glib::SignalProxyInfo
462 DockItem::_signal_realize_proxy =
463 {
464 "realize",
465 (GCallback) &Glib::SignalProxyNormal::slot0_void_callback,
466 (GCallback) &Glib::SignalProxyNormal::slot0_void_callback
467 };
470 gboolean
471 DockItem::_signal_delete_event_callback(GtkWidget *self, GdkEventAny *event, void *data)
472 {
473 using namespace Gtk;
474 typedef sigc::slot<bool, GdkEventAny *> SlotType;
476 if (Glib::ObjectBase::_get_current_wrapper((GObject *) self)) {
477 try {
478 if(sigc::slot_base *const slot = Glib::SignalProxyNormal::data_to_slot(data))
479 return static_cast<int>( (*static_cast<SlotType*>(slot))(event) );
480 } catch(...) {
481 Glib::exception_handlers_invoke();
482 }
483 }
485 typedef gboolean RType;
486 return RType();
487 }
489 void
490 DockItem::_signal_drag_end_callback(GtkWidget *self, gboolean cancelled, void *data)
491 {
492 using namespace Gtk;
493 typedef sigc::slot<void, bool> SlotType;
495 if (Glib::ObjectBase::_get_current_wrapper((GObject *) self)) {
496 try {
497 if(sigc::slot_base *const slot = Glib::SignalProxyNormal::data_to_slot(data))
498 (*static_cast<SlotType *>(slot))(cancelled);
499 } catch(...) {
500 Glib::exception_handlers_invoke();
501 }
502 }
503 }
506 } // namespace Widget
507 } // namespace UI
508 } // namespace Inkscape
510 /*
511 Local Variables:
512 mode:c++
513 c-file-style:"stroustrup"
514 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
515 indent-tabs-mode:nil
516 fill-column:99
517 End:
518 */
519 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :