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 {
35 /* Add a "signal_response" signal to the GdlDockItem, make sure it is
36 * only done once for the class.
37 */
38 static guint response_signal = 0;
40 if (response_signal == 0) {
41 response_signal = g_signal_new ("signal_response",
42 GDL_TYPE_DOCK_ITEM,
43 G_SIGNAL_RUN_FIRST,
44 0,
45 NULL, NULL,
46 g_cclosure_marshal_VOID__INT,
47 G_TYPE_NONE, 1, G_TYPE_INT);
48 }
51 GdlDockItemBehavior gdl_dock_behavior =
52 (prefs_get_int_attribute_limited ("options.dock", "cancenterdock", 1, 0, 1) == 0 ?
53 GDL_DOCK_ITEM_BEH_CANT_DOCK_CENTER
54 : GDL_DOCK_ITEM_BEH_NORMAL);
56 if (!icon_name.empty()) {
57 Gtk::Widget *icon = sp_icon_get_icon(icon_name, Inkscape::ICON_SIZE_MENU);
58 if (icon) {
59 // check icon type (inkscape, gtk, none)
60 if ( SP_IS_ICON(icon->gobj()) ) {
61 SPIcon* sp_icon = SP_ICON(icon->gobj());
62 sp_icon_fetch_pixbuf(sp_icon);
63 _icon_pixbuf = Glib::wrap(sp_icon->pb, true);
64 } else if ( GTK_IS_IMAGE(icon->gobj()) ) {
65 _icon_pixbuf = Gtk::Invisible().render_icon(Gtk::StockID(icon_name),
66 Gtk::ICON_SIZE_MENU);
67 }
68 delete icon;
70 _gdl_dock_item =
71 gdl_dock_item_new_with_pixbuf_icon(name.c_str(), long_name.c_str(),
72 _icon_pixbuf->gobj(), gdl_dock_behavior);
73 }
74 } else {
75 _gdl_dock_item =
76 gdl_dock_item_new(name.c_str(), long_name.c_str(), gdl_dock_behavior);
77 }
79 _frame.set_shadow_type(Gtk::SHADOW_IN);
80 gtk_container_add (GTK_CONTAINER (_gdl_dock_item), GTK_WIDGET (_frame.gobj()));
81 _frame.add(_dock_item_box);
82 _dock_item_box.set_border_width(3);
84 signal_drag_begin().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onDragBegin));
85 signal_drag_end().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onDragEnd));
86 signal_hide().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onHide), false);
87 signal_show().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onShow), false);
88 signal_state_changed().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onStateChanged));
89 signal_delete_event().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onDeleteEvent));
90 signal_realize().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onRealize));
92 _dock.addItem(*this, (_prev_state == FLOATING_STATE ? FLOATING : TOP));
94 show_all();
95 }
97 DockItem::~DockItem()
98 {
99 g_free(_gdl_dock_item);
100 }
102 Gtk::Widget&
103 DockItem::getWidget()
104 {
105 return *Glib::wrap(GTK_WIDGET(_gdl_dock_item));
106 }
108 GtkWidget *
109 DockItem::gobj()
110 {
111 return _gdl_dock_item;
112 }
114 Gtk::VBox *
115 DockItem::get_vbox()
116 {
117 return &_dock_item_box;
118 }
121 void
122 DockItem::get_position(int& x, int& y)
123 {
124 if (getWindow()) {
125 getWindow()->get_position(x, y);
126 } else {
127 x = _x;
128 y = _y;
129 }
130 }
132 void
133 DockItem::get_size(int& width, int& height)
134 {
135 if (_window) {
136 _window->get_size(width, height);
137 } else {
138 width = get_vbox()->get_width();
139 height = get_vbox()->get_height();
140 }
141 }
144 void
145 DockItem::resize(int width, int height)
146 {
147 if (_window)
148 _window->resize(width, height);
149 }
152 void
153 DockItem::move(int x, int y)
154 {
155 if (_window)
156 _window->move(x, y);
157 }
159 void
160 DockItem::set_position(Gtk::WindowPosition position)
161 {
162 if (_window)
163 _window->set_position(position);
164 }
166 void
167 DockItem::set_size_request(int width, int height)
168 {
169 getWidget().set_size_request(width, height);
170 }
172 void
173 DockItem::size_request(Gtk::Requisition& requisition)
174 {
175 getWidget().size_request(requisition);
176 }
178 void
179 DockItem::set_title(Glib::ustring title)
180 {
181 g_object_set (_gdl_dock_item,
182 "long-name", title.c_str(),
183 NULL);
185 gdl_dock_item_set_tablabel(GDL_DOCK_ITEM(_gdl_dock_item),
186 gtk_label_new (title.c_str()));
187 }
189 bool
190 DockItem::isAttached() const
191 {
192 return GDL_DOCK_OBJECT_ATTACHED (_gdl_dock_item);
193 }
196 bool
197 DockItem::isFloating() const
198 {
199 gboolean floating = FALSE;
200 if (GDL_IS_DOCK (gdl_dock_object_get_parent_object (GDL_DOCK_OBJECT (_gdl_dock_item)))) {
201 GdlDock* dock = GDL_DOCK (gdl_dock_object_get_parent_object (GDL_DOCK_OBJECT (_gdl_dock_item)));
202 g_object_get (dock,
203 "floating", &floating,
204 NULL);
205 }
206 return floating;
207 }
209 bool
210 DockItem::isIconified() const
211 {
212 return GDL_DOCK_ITEM_ICONIFIED (_gdl_dock_item);
213 }
215 DockItem::State
216 DockItem::getState() const
217 {
218 return (isAttached() ? (isFloating() ? FLOATING_STATE : DOCKED_STATE) : UNATTACHED);
219 }
221 DockItem::State
222 DockItem::getPrevState() const
223 {
224 return _prev_state;
225 }
227 DockItem::Placement
228 DockItem::getPlacement() const
229 {
230 GdlDockPlacement placement = (GdlDockPlacement)NONE;
231 gdl_dock_object_child_placement(gdl_dock_object_get_parent_object (GDL_DOCK_OBJECT(_gdl_dock_item)),
232 GDL_DOCK_OBJECT(_gdl_dock_item),
233 &placement);
234 return (Placement)placement;
235 }
238 void
239 DockItem::addButton(Gtk::Button* button, int response_id)
240 {
241 // Create a button box for the response buttons if it's the first button to be added
242 if (!_dock_item_action_area) {
243 _dock_item_action_area = new Gtk::HButtonBox(Gtk::BUTTONBOX_END, 6);
244 _dock_item_box.pack_end(*_dock_item_action_area, Gtk::PACK_SHRINK, 0);
245 _dock_item_action_area->set_border_width(6);
246 }
248 _dock_item_action_area->pack_start(*button);
249 }
251 void
252 DockItem::hide()
253 {
254 gdl_dock_item_hide_item (GDL_DOCK_ITEM(_gdl_dock_item));
255 }
257 void
258 DockItem::show()
259 {
260 gdl_dock_item_show_item (GDL_DOCK_ITEM(_gdl_dock_item));
261 }
263 void
264 DockItem::show_all()
265 {
266 gtk_widget_show_all(_gdl_dock_item);
267 }
269 void
270 DockItem::present()
271 {
272 // iconified or unattached
273 if (isIconified() || !isAttached()) {
274 show();
275 }
277 // tabbed
278 else if (getPlacement() == CENTER) {
279 int i = gtk_notebook_page_num (GTK_NOTEBOOK (_gdl_dock_item->parent),
280 GTK_WIDGET (_gdl_dock_item));
281 if (i >= 0)
282 gtk_notebook_set_current_page (GTK_NOTEBOOK (_gdl_dock_item->parent), i);
283 return;
284 }
286 // always grab focus, even if we're already present
287 grab_focus();
288 }
291 void
292 DockItem::grab_focus()
293 {
294 if (GTK_WIDGET_REALIZED (_gdl_dock_item)) {
296 // make sure the window we're in is present
297 Gtk::Widget *toplevel = getWidget().get_toplevel();
298 if (Gtk::Window *window = dynamic_cast<Gtk::Window *>(toplevel)) {
299 window->present();
300 }
302 gtk_widget_grab_focus (_gdl_dock_item);
304 } else {
305 _grab_focus_on_realize = true;
306 }
307 }
310 /* Signal wrappers */
312 Glib::SignalProxy0<void>
313 DockItem::signal_show()
314 {
315 return Glib::SignalProxy0<void>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)),
316 &_signal_show_proxy);
317 }
319 Glib::SignalProxy0<void>
320 DockItem::signal_hide()
321 {
322 return Glib::SignalProxy0<void>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)),
323 &_signal_hide_proxy);
324 }
326 Glib::SignalProxy1<bool, GdkEventAny *>
327 DockItem::signal_delete_event()
328 {
329 return Glib::SignalProxy1<bool, GdkEventAny *>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)),
330 &_signal_delete_event_proxy);
331 }
333 Glib::SignalProxy1<void, int>
334 DockItem::signal_response()
335 {
336 return Glib::SignalProxy1<void, int>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)),
337 &_signal_response_proxy);
338 }
340 Glib::SignalProxy0<void>
341 DockItem::signal_drag_begin()
342 {
343 return Glib::SignalProxy0<void>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)),
344 &_signal_drag_begin_proxy);
345 }
347 Glib::SignalProxy1<void, bool>
348 DockItem::signal_drag_end()
349 {
350 return Glib::SignalProxy1<void, bool>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)),
351 &_signal_drag_end_proxy);
352 }
354 Glib::SignalProxy0<void>
355 DockItem::signal_realize()
356 {
357 return Glib::SignalProxy0<void>(Glib::wrap(GTK_WIDGET(_gdl_dock_item)),
358 &_signal_realize_proxy);
359 }
361 sigc::signal<void, DockItem::State, DockItem::State>
362 DockItem::signal_state_changed()
363 {
364 return _signal_state_changed;
365 }
367 void
368 DockItem::_onHideWindow()
369 {
370 if (_window)
371 _window->get_position(_x, _y);
372 }
374 void
375 DockItem::_onHide()
376 {
377 _signal_state_changed.emit(UNATTACHED, getState());
378 }
380 void
381 DockItem::_onShow()
382 {
383 _signal_state_changed.emit(UNATTACHED, getState());
384 }
386 void
387 DockItem::_onDragBegin()
388 {
389 _prev_state = getState();
390 if (_prev_state == FLOATING_STATE)
391 _dock.toggleDockable(getWidget().get_width(), getWidget().get_height());
392 }
394 void
395 DockItem::_onDragEnd(bool)
396 {
397 State state = getState();
399 if (state != _prev_state)
400 _signal_state_changed.emit(_prev_state, state);
402 if (state == FLOATING_STATE) {
403 if (_prev_state == FLOATING_STATE)
404 _dock.toggleDockable();
405 }
407 _prev_state = state;
408 }
410 void
411 DockItem::_onRealize()
412 {
413 if (_grab_focus_on_realize) {
414 _grab_focus_on_realize = false;
415 grab_focus();
416 }
417 }
419 bool
420 DockItem::_onKeyPress(GdkEventKey *event)
421 {
422 gboolean return_value;
423 g_signal_emit_by_name (_gdl_dock_item, "key_press_event", event, &return_value);
424 return return_value;
425 }
427 void
428 DockItem::_onStateChanged(State prev_state, State new_state)
429 {
430 _window = getWindow();
432 if (new_state == FLOATING_STATE) {
433 _window->signal_hide().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onHideWindow));
434 _signal_key_press_event_connection =
435 _window->signal_key_press_event().connect(sigc::mem_fun(*this, &Inkscape::UI::Widget::DockItem::_onKeyPress));
436 }
437 }
440 bool
441 DockItem::_onDeleteEvent(GdkEventAny *event)
442 {
443 hide();
444 return false;
445 }
448 Gtk::Window *
449 DockItem::getWindow()
450 {
451 g_return_val_if_fail(_gdl_dock_item, 0);
452 Gtk::Container *parent = getWidget().get_parent();
453 parent = (parent ? parent->get_parent() : 0);
454 return (parent ? dynamic_cast<Gtk::Window *>(parent) : 0);
455 }
457 const Glib::SignalProxyInfo
458 DockItem::_signal_show_proxy =
459 {
460 "show",
461 (GCallback) &Glib::SignalProxyNormal::slot0_void_callback,
462 (GCallback) &Glib::SignalProxyNormal::slot0_void_callback
463 };
465 const Glib::SignalProxyInfo
466 DockItem::_signal_hide_proxy =
467 {
468 "hide",
469 (GCallback) &Glib::SignalProxyNormal::slot0_void_callback,
470 (GCallback) &Glib::SignalProxyNormal::slot0_void_callback
471 };
474 const Glib::SignalProxyInfo
475 DockItem::_signal_delete_event_proxy =
476 {
477 "delete_event",
478 (GCallback) &_signal_delete_event_callback,
479 (GCallback) &_signal_delete_event_callback
480 };
483 const Glib::SignalProxyInfo
484 DockItem::_signal_response_proxy =
485 {
486 "signal_response",
487 (GCallback) &_signal_response_callback,
488 (GCallback) &_signal_response_callback
489 };
491 const Glib::SignalProxyInfo
492 DockItem::_signal_drag_begin_proxy =
493 {
494 "dock-drag-begin",
495 (GCallback) &Glib::SignalProxyNormal::slot0_void_callback,
496 (GCallback) &Glib::SignalProxyNormal::slot0_void_callback
497 };
500 const Glib::SignalProxyInfo
501 DockItem::_signal_drag_end_proxy =
502 {
503 "dock_drag_end",
504 (GCallback) &_signal_drag_end_callback,
505 (GCallback) &_signal_drag_end_callback
506 };
509 const Glib::SignalProxyInfo
510 DockItem::_signal_realize_proxy =
511 {
512 "realize",
513 (GCallback) &Glib::SignalProxyNormal::slot0_void_callback,
514 (GCallback) &Glib::SignalProxyNormal::slot0_void_callback
515 };
518 gboolean
519 DockItem::_signal_delete_event_callback(GtkWidget *self, GdkEventAny *event, void *data)
520 {
521 using namespace Gtk;
522 typedef sigc::slot<bool, GdkEventAny *> SlotType;
524 if (Glib::ObjectBase::_get_current_wrapper((GObject *) self)) {
525 try {
526 if(sigc::slot_base *const slot = Glib::SignalProxyNormal::data_to_slot(data))
527 return static_cast<int>( (*static_cast<SlotType*>(slot))(event) );
528 } catch(...) {
529 Glib::exception_handlers_invoke();
530 }
531 }
533 typedef gboolean RType;
534 return RType();
535 }
537 void
538 DockItem::_signal_response_callback(GtkWidget *self, gint response_id, void *data)
539 {
540 using namespace Gtk;
541 typedef sigc::slot<void, int> SlotType;
543 if (Glib::ObjectBase::_get_current_wrapper((GObject *) self)) {
544 try {
545 if(sigc::slot_base *const slot = Glib::SignalProxyNormal::data_to_slot(data))
546 (*static_cast<SlotType *>(slot))(response_id);
547 } catch(...) {
548 Glib::exception_handlers_invoke();
549 }
550 }
551 }
553 void
554 DockItem::_signal_drag_end_callback(GtkWidget *self, gboolean cancelled, void *data)
555 {
556 using namespace Gtk;
557 typedef sigc::slot<void, bool> SlotType;
559 if (Glib::ObjectBase::_get_current_wrapper((GObject *) self)) {
560 try {
561 if(sigc::slot_base *const slot = Glib::SignalProxyNormal::data_to_slot(data))
562 (*static_cast<SlotType *>(slot))(cancelled);
563 } catch(...) {
564 Glib::exception_handlers_invoke();
565 }
566 }
567 }
570 } // namespace Widget
571 } // namespace UI
572 } // namespace Inkscape
574 /*
575 Local Variables:
576 mode:c++
577 c-file-style:"stroustrup"
578 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
579 indent-tabs-mode:nil
580 fill-column:99
581 End:
582 */
583 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :