4faaec2979c42919457310b17aa474caa26d7096
1 /*
2 * A simple panel for layers
3 *
4 * Authors:
5 * Jon A. Cruz
6 *
7 * Copyright (C) 2006 Jon A. Cruz
8 *
9 * Released under GNU GPL, read the file 'COPYING' for more information
10 */
11 #ifdef HAVE_CONFIG_H
12 # include <config.h>
13 #endif
15 #include <glibmm/i18n.h>
17 #include <gtk/gtkstock.h>
18 #include <gtk/gtkmain.h>
20 #include "inkscape.h"
22 #include "layers-panel.h"
24 #include "layer-manager.h"
25 #include "layer-fns.h"
27 #include "verbs.h"
28 #include "helper/action.h"
30 #include "document.h"
31 #include "desktop.h"
32 #include "sp-object.h"
33 #include "sp-item.h"
34 #include "widgets/icon.h"
35 #include <gtkmm/widget.h>
36 #include <gtkmm/spinbutton.h>
37 #include "prefs-utils.h"
38 #include "xml/repr.h"
39 #include "svg/css-ostringstream.h"
40 #include "desktop-style.h"
42 //#define DUMP_LAYERS 1
44 namespace Inkscape {
45 namespace UI {
46 namespace Dialogs {
48 LayersPanel* LayersPanel::instance = 0;
50 LayersPanel& LayersPanel::getInstance()
51 {
52 if ( !instance ) {
53 instance = new LayersPanel();
54 }
56 return *instance;
57 }
59 enum {
60 COL_VISIBLE = 1,
61 COL_LOCKED
62 };
64 enum {
65 BUTTON_NEW = 0,
66 BUTTON_RENAME,
67 BUTTON_TOP,
68 BUTTON_BOTTOM,
69 BUTTON_UP,
70 BUTTON_DOWN,
71 // BUTTON_DUPLICATE,
72 BUTTON_DELETE
73 };
75 class ImageToggler : public Gtk::CellRendererPixbuf {
76 public:
77 ImageToggler( char const* on, char const* off) :
78 Glib::ObjectBase(typeid(ImageToggler)),
79 Gtk::CellRendererPixbuf(),
80 _pixOnName(on),
81 _pixOffName(off),
82 _property_active(*this, "active", false),
83 _property_activatable(*this, "activatable", true),
84 _property_pixbuf_on(*this, "pixbuf_on", Glib::RefPtr<Gdk::Pixbuf>(0)),
85 _property_pixbuf_off(*this, "pixbuf_off", Glib::RefPtr<Gdk::Pixbuf>(0))
86 {
87 property_mode() = Gtk::CELL_RENDERER_MODE_ACTIVATABLE;
89 Gtk::Widget* thingie = sp_icon_get_icon(_pixOnName.c_str(), Inkscape::ICON_SIZE_DECORATION);
90 if ( thingie ) {
91 if ( SP_IS_ICON(thingie->gobj()) ) {
92 SPIcon* icon = SP_ICON(thingie->gobj());
93 sp_icon_fetch_pixbuf( icon );
94 _property_pixbuf_on = Glib::wrap( icon->pb, true );
95 }
96 delete thingie;
97 }
98 thingie = sp_icon_get_icon(_pixOffName.c_str(), Inkscape::ICON_SIZE_DECORATION);
99 if ( thingie ) {
100 if ( SP_IS_ICON(thingie->gobj()) ) {
101 SPIcon* icon = SP_ICON(thingie->gobj());
102 sp_icon_fetch_pixbuf( icon );
103 _property_pixbuf_off = Glib::wrap( icon->pb, true );
104 }
105 delete thingie;
106 }
107 property_pixbuf() = _property_pixbuf_off.get_value();
108 }
110 sigc::signal<void, const Glib::ustring&> signal_toggled()
111 {
112 return _signal_toggled;
113 }
115 sigc::signal<void, GdkEvent const *> signal_pre_toggle()
116 {
117 return _signal_pre_toggle;
118 }
120 Glib::PropertyProxy<bool> property_active() { return _property_active.get_proxy(); }
121 Glib::PropertyProxy<bool> property_activatable() { return _property_activatable.get_proxy(); }
122 Glib::PropertyProxy< Glib::RefPtr<Gdk::Pixbuf> > property_pixbuf_on();
123 Glib::PropertyProxy< Glib::RefPtr<Gdk::Pixbuf> > property_pixbuf_off();
124 // virtual Glib::PropertyProxy_Base _property_renderable(); //override
126 protected:
128 virtual void get_size_vfunc( Gtk::Widget& widget,
129 const Gdk::Rectangle* cell_area,
130 int* x_offset,
131 int* y_offset,
132 int* width,
133 int* height ) const
134 {
135 Gtk::CellRendererPixbuf::get_size_vfunc( widget, cell_area, x_offset, y_offset, width, height );
137 if ( width ) {
138 *width += (*width) >> 1;
139 }
140 if ( height ) {
141 *height += (*height) >> 1;
142 }
143 }
146 virtual void render_vfunc( const Glib::RefPtr<Gdk::Drawable>& window,
147 Gtk::Widget& widget,
148 const Gdk::Rectangle& background_area,
149 const Gdk::Rectangle& cell_area,
150 const Gdk::Rectangle& expose_area,
151 Gtk::CellRendererState flags )
152 {
153 property_pixbuf() = _property_active.get_value() ? _property_pixbuf_on : _property_pixbuf_off;
154 Gtk::CellRendererPixbuf::render_vfunc( window, widget, background_area, cell_area, expose_area, flags );
155 }
157 virtual bool activate_vfunc(GdkEvent* event,
158 Gtk::Widget& widget,
159 const Glib::ustring& path,
160 const Gdk::Rectangle& background_area,
161 const Gdk::Rectangle& cell_area,
162 Gtk::CellRendererState flags)
163 {
164 _signal_pre_toggle.emit(event);
165 _signal_toggled.emit(path);
167 return false;
168 }
171 private:
172 Glib::ustring _pixOnName;
173 Glib::ustring _pixOffName;
175 Glib::Property<bool> _property_active;
176 Glib::Property<bool> _property_activatable;
177 Glib::Property< Glib::RefPtr<Gdk::Pixbuf> > _property_pixbuf_on;
178 Glib::Property< Glib::RefPtr<Gdk::Pixbuf> > _property_pixbuf_off;
180 sigc::signal<void, const Glib::ustring&> _signal_toggled;
181 sigc::signal<void, GdkEvent const *> _signal_pre_toggle;
182 };
184 class LayersPanel::InternalUIBounce
185 {
186 public:
187 int _actionCode;
188 SPObject* _target;
189 };
191 static gboolean layers_panel_activated( GtkObject *object, GdkEvent * /*event*/, gpointer data )
192 {
193 if ( data )
194 {
195 LayersPanel* panel = reinterpret_cast<LayersPanel*>(data);
196 panel->setDesktop( SP_ACTIVE_DESKTOP );
197 }
199 return FALSE;
200 }
203 void LayersPanel::_styleButton( Gtk::Button& btn, SPDesktop *desktop, unsigned int code, char const* iconName, char const* fallback )
204 {
205 bool set = false;
207 if ( iconName ) {
208 GtkWidget *child = sp_icon_new( Inkscape::ICON_SIZE_SMALL_TOOLBAR, iconName );
209 gtk_widget_show( child );
210 btn.add( *manage(Glib::wrap(child)) );
211 set = true;
212 }
214 if ( desktop ) {
215 Verb *verb = Verb::get( code );
216 if ( verb ) {
217 SPAction *action = verb->get_action(desktop);
218 if ( !set && action && action->image ) {
219 GtkWidget *child = sp_icon_new( Inkscape::ICON_SIZE_SMALL_TOOLBAR, action->image );
220 gtk_widget_show( child );
221 btn.add( *manage(Glib::wrap(child)) );
222 set = true;
223 }
225 if ( action && action->tip ) {
226 _tips.set_tip( btn, action->tip );
227 }
228 }
229 }
231 if ( !set && fallback ) {
232 btn.set_label( fallback );
233 }
234 }
237 Gtk::MenuItem& LayersPanel::_addPopupItem( SPDesktop *desktop, unsigned int code, char const* iconName, char const* fallback, int id )
238 {
239 GtkWidget* iconWidget = 0;
240 const char* label = 0;
242 if ( iconName ) {
243 iconWidget = sp_icon_new( Inkscape::ICON_SIZE_MENU, iconName );
244 }
246 if ( desktop ) {
247 Verb *verb = Verb::get( code );
248 if ( verb ) {
249 SPAction *action = verb->get_action(desktop);
250 if ( !iconWidget && action && action->image ) {
251 iconWidget = sp_icon_new( Inkscape::ICON_SIZE_MENU, action->image );
252 }
254 if ( action ) {
255 label = action->name;
256 }
257 }
258 }
260 if ( !label && fallback ) {
261 label = fallback;
262 }
264 Gtk::Widget* wrapped = 0;
265 if ( iconWidget ) {
266 wrapped = manage(Glib::wrap(iconWidget));
267 wrapped->show();
268 }
272 Gtk::Menu::MenuList& menulist = _popupMenu.items();
274 if ( wrapped ) {
275 menulist.push_back( Gtk::Menu_Helpers::ImageMenuElem( label, *wrapped, sigc::bind( sigc::mem_fun(*this, &LayersPanel::_takeAction), id)) );
276 } else {
277 menulist.push_back( Gtk::Menu_Helpers::MenuElem( label, sigc::bind( sigc::mem_fun(*this, &LayersPanel::_takeAction), id)) );
278 }
279 return menulist.back();
280 }
282 void LayersPanel::_fireAction( unsigned int code )
283 {
284 if ( _desktop ) {
285 Verb *verb = Verb::get( code );
286 if ( verb ) {
287 SPAction *action = verb->get_action(_desktop);
288 if ( action ) {
289 sp_action_perform( action, NULL );
290 // } else {
291 // g_message("no action");
292 }
293 // } else {
294 // g_message("no verb for %u", code);
295 }
296 // } else {
297 // g_message("no active desktop");
298 }
299 }
301 // SP_VERB_LAYER_NEXT,
302 // SP_VERB_LAYER_PREV,
303 void LayersPanel::_takeAction( int val )
304 {
305 if ( !_pending ) {
306 _pending = new InternalUIBounce();
307 _pending->_actionCode = val;
308 _pending->_target = _selectedLayer();
309 Glib::signal_timeout().connect( sigc::mem_fun(*this, &LayersPanel::_executeAction), 0 );
310 }
311 }
313 bool LayersPanel::_executeAction()
314 {
315 // Make sure selected layer hasn't changed since the action was triggered
316 if ( _pending
317 && !( (_desktop && _desktop->currentLayer())
318 && (_desktop->currentLayer() != _pending->_target)
319 ) ) {
320 int val = _pending->_actionCode;
321 // SPObject* target = _pending->_target;
323 switch ( val ) {
324 case BUTTON_NEW:
325 {
326 _fireAction( SP_VERB_LAYER_NEW );
327 }
328 break;
329 case BUTTON_RENAME:
330 {
331 _fireAction( SP_VERB_LAYER_RENAME );
332 }
333 break;
334 case BUTTON_TOP:
335 {
336 _fireAction( SP_VERB_LAYER_TO_TOP );
337 }
338 break;
339 case BUTTON_BOTTOM:
340 {
341 _fireAction( SP_VERB_LAYER_TO_BOTTOM );
342 }
343 break;
344 case BUTTON_UP:
345 {
346 _fireAction( SP_VERB_LAYER_RAISE );
347 }
348 break;
349 case BUTTON_DOWN:
350 {
351 _fireAction( SP_VERB_LAYER_LOWER );
352 }
353 break;
354 case BUTTON_DELETE:
355 {
356 _fireAction( SP_VERB_LAYER_DELETE );
357 }
358 break;
359 }
361 delete _pending;
362 _pending = 0;
363 }
365 return false;
366 }
368 class LayersPanel::ModelColumns : public Gtk::TreeModel::ColumnRecord
369 {
370 public:
372 ModelColumns()
373 {
374 add(_colObject);
375 add(_colVisible);
376 add(_colLocked);
377 add(_colLabel);
378 }
379 virtual ~ModelColumns() {}
381 Gtk::TreeModelColumn<SPObject*> _colObject;
382 Gtk::TreeModelColumn<Glib::ustring> _colLabel;
383 Gtk::TreeModelColumn<bool> _colVisible;
384 Gtk::TreeModelColumn<bool> _colLocked;
385 };
387 void LayersPanel::_updateLayer( SPObject *layer ) {
388 _store->foreach( sigc::bind<SPObject*>(sigc::mem_fun(*this, &LayersPanel::_checkForUpdated), layer) );
389 }
391 bool LayersPanel::_checkForUpdated(const Gtk::TreePath &path, const Gtk::TreeIter& iter, SPObject* layer)
392 {
393 bool stopGoing = false;
394 Gtk::TreeModel::Row row = *iter;
395 Glib::ustring tmp = row[_model->_colLabel];
396 if ( layer == row[_model->_colObject] )
397 {
398 row[_model->_colLabel] = layer->label() ? layer->label() : SP_OBJECT_ID(layer);
399 row[_model->_colVisible] = SP_IS_ITEM(layer) ? !SP_ITEM(layer)->isHidden() : false;
400 row[_model->_colLocked] = SP_IS_ITEM(layer) ? SP_ITEM(layer)->isLocked() : false;
402 stopGoing = true;
403 }
405 return stopGoing;
406 }
408 void LayersPanel::_selectLayer( SPObject *layer ) {
409 if ( !layer || (_desktop && _desktop->doc() && (layer == _desktop->doc()->root)) ) {
410 if ( _tree.get_selection()->count_selected_rows() != 0 ) {
411 _tree.get_selection()->unselect_all();
412 }
413 } else {
414 _store->foreach( sigc::bind<SPObject*>(sigc::mem_fun(*this, &LayersPanel::_checkForSelected), layer) );
415 }
417 _checkTreeSelection();
418 }
420 bool LayersPanel::_checkForSelected(const Gtk::TreePath &path, const Gtk::TreeIter& iter, SPObject* layer)
421 {
422 bool stopGoing = false;
424 Gtk::TreeModel::Row row = *iter;
425 if ( layer == row[_model->_colObject] )
426 {
427 _tree.expand_to_path( path );
429 Glib::RefPtr<Gtk::TreeSelection> select = _tree.get_selection();
431 select->select(iter);
433 stopGoing = true;
434 }
436 return stopGoing;
437 }
439 void LayersPanel::_layersChanged()
440 {
441 // g_message("_layersChanged()");
442 SPDocument* document = _desktop->doc();
443 SPObject* root = document->root;
444 if ( root ) {
445 if ( _mgr && _mgr->includes( root ) ) {
446 SPObject* target = _desktop->currentLayer();
447 _store->clear();
449 #if DUMP_LAYERS
450 g_message("root:%p {%s} [%s]", root, root->id, root->label() );
451 #endif // DUMP_LAYERS
452 _addLayer( document, root, 0, target, 0 );
453 }
454 }
455 }
457 void LayersPanel::_addLayer( SPDocument* doc, SPObject* layer, Gtk::TreeModel::Row* parentRow, SPObject* target, int level )
458 {
459 if ( layer && (level < _maxNestDepth) ) {
460 unsigned int counter = _mgr->childCount(layer);
461 for ( unsigned int i = 0; i < counter; i++ ) {
462 SPObject *child = _mgr->nthChildOf(layer, i);
463 if ( child ) {
464 #if DUMP_LAYERS
465 g_message(" %3d layer:%p {%s} [%s]", level, child, child->id, child->label() );
466 #endif // DUMP_LAYERS
468 Gtk::TreeModel::iterator iter = parentRow ? _store->prepend(parentRow->children()) : _store->prepend();
469 Gtk::TreeModel::Row row = *iter;
470 row[_model->_colObject] = child;
471 row[_model->_colLabel] = child->label() ? child->label() : SP_OBJECT_ID(child);
472 row[_model->_colVisible] = SP_IS_ITEM(child) ? !SP_ITEM(child)->isHidden() : false;
473 row[_model->_colLocked] = SP_IS_ITEM(child) ? SP_ITEM(child)->isLocked() : false;
475 if ( target && child == target ) {
476 _tree.expand_to_path( _store->get_path(iter) );
478 Glib::RefPtr<Gtk::TreeSelection> select = _tree.get_selection();
479 select->select(iter);
481 _checkTreeSelection();
482 }
484 _addLayer( doc, child, &row, target, level + 1 );
485 }
486 }
487 }
488 }
490 SPObject* LayersPanel::_selectedLayer()
491 {
492 SPObject* obj = 0;
494 Gtk::TreeModel::iterator iter = _tree.get_selection()->get_selected();
495 if ( iter ) {
496 Gtk::TreeModel::Row row = *iter;
497 obj = row[_model->_colObject];
498 }
500 return obj;
501 }
503 void LayersPanel::_pushTreeSelectionToCurrent()
504 {
505 SPObject* inTree = _selectedLayer();
506 // TODO hunt down the possible API abuse in getting NULL
507 if ( _desktop->currentRoot() ) {
508 if ( inTree ) {
509 SPObject* curr = _desktop->currentLayer();
510 if ( curr != inTree ) {
511 _mgr->setCurrentLayer( inTree );
512 }
513 } else {
514 _mgr->setCurrentLayer( _desktop->doc()->root );
515 }
516 }
517 }
519 void LayersPanel::_checkTreeSelection()
520 {
521 bool sensitive = false;
522 bool sensitiveNonTop = false;
523 bool sensitiveNonBottom = false;
524 if ( _tree.get_selection()->count_selected_rows() > 0 ) {
525 sensitive = true;
527 SPObject* inTree = _selectedLayer();
528 if ( inTree ) {
530 sensitiveNonTop = (Inkscape::next_layer(inTree->parent, inTree) != 0);
531 sensitiveNonBottom = (Inkscape::previous_layer(inTree->parent, inTree) != 0);
533 if ( inTree->repr ) {
534 SPCSSAttr *css = sp_repr_css_attr(inTree->repr, "style");
535 if ( css ) {
536 _opacity.set_value( sp_repr_css_double_property( css, "opacity", 1.0 ) );
537 }
538 }
539 }
540 }
543 for ( std::vector<Gtk::Widget*>::iterator it = _watching.begin(); it != _watching.end(); ++it ) {
544 (*it)->set_sensitive( sensitive );
545 }
546 for ( std::vector<Gtk::Widget*>::iterator it = _watchingNonTop.begin(); it != _watchingNonTop.end(); ++it ) {
547 (*it)->set_sensitive( sensitiveNonTop );
548 }
549 for ( std::vector<Gtk::Widget*>::iterator it = _watchingNonBottom.begin(); it != _watchingNonBottom.end(); ++it ) {
550 (*it)->set_sensitive( sensitiveNonBottom );
551 }
552 }
554 void LayersPanel::_preToggle( GdkEvent const *event )
555 {
556 if ( _toggleEvent ) {
557 gdk_event_free(_toggleEvent);
558 _toggleEvent = 0;
559 }
561 if ( event && (event->type == GDK_BUTTON_PRESS) ) {
562 // Make a copy so we can keep it around.
563 _toggleEvent = gdk_event_copy(const_cast<GdkEvent*>(event));
564 }
565 }
567 void LayersPanel::_toggled( Glib::ustring const& str, int targetCol )
568 {
569 Gtk::TreeModel::Children::iterator iter = _tree.get_model()->get_iter(str);
570 Gtk::TreeModel::Row row = *iter;
572 Glib::ustring tmp = row[_model->_colLabel];
574 SPObject* obj = row[_model->_colObject];
575 SPItem* item = ( obj && SP_IS_ITEM(obj) ) ? SP_ITEM(obj) : 0;
576 if ( item ) {
577 switch ( targetCol ) {
578 case COL_VISIBLE:
579 {
580 bool newValue = !row[_model->_colVisible];
581 row[_model->_colVisible] = newValue;
582 item->setHidden( !newValue );
583 item->updateRepr();
584 sp_document_done( _desktop->doc() );
585 }
586 break;
588 case COL_LOCKED:
589 {
590 bool newValue = !row[_model->_colLocked];
591 row[_model->_colLocked] = newValue;
592 item->setLocked( newValue );
593 item->updateRepr();
594 sp_document_done( _desktop->doc() );
595 }
596 break;
597 }
598 }
599 }
601 void LayersPanel::_handleButtonEvent(GdkEventButton* evt)
602 {
603 // TODO - fix to a better is-popup function
604 if ( (evt->type == GDK_BUTTON_PRESS) && (evt->button == 3) ) {
607 {
608 Gtk::TreeModel::Path path;
609 Gtk::TreeViewColumn* col = 0;
610 int x = static_cast<int>(evt->x);
611 int y = static_cast<int>(evt->y);
612 int x2 = 0;
613 int y2 = 0;
614 if ( _tree.get_path_at_pos( x, y,
615 path, col,
616 x2, y2 ) ) {
617 _checkTreeSelection();
618 _popupMenu.popup(evt->button, evt->time);
619 }
620 }
622 }
623 }
625 void LayersPanel::_handleRowChange( Gtk::TreeModel::Path const& path, Gtk::TreeModel::iterator const& iter )
626 {
627 Gtk::TreeModel::Row row = *iter;
628 if ( row ) {
629 SPObject* obj = row[_model->_colObject];
630 if ( obj ) {
631 gchar const* oldLabel = obj->label();
632 Glib::ustring tmp = row[_model->_colLabel];
633 if ( oldLabel && oldLabel[0] && !tmp.empty() && (tmp != oldLabel) ) {
634 _mgr->renameLayer( obj, tmp.c_str() );
635 row[_model->_colLabel] = obj->label();
636 }
637 }
638 }
639 }
641 bool LayersPanel::_rowSelectFunction( Glib::RefPtr<Gtk::TreeModel> const & model, Gtk::TreeModel::Path const & path, bool currentlySelected )
642 {
643 bool val = true;
644 if ( !currentlySelected && _toggleEvent )
645 {
646 GdkEvent* event = gtk_get_current_event();
647 if ( event ) {
648 // (keep these checks separate, so we know when to call gdk_event_free()
649 if ( event->type == GDK_BUTTON_PRESS ) {
650 GdkEventButton const* target = reinterpret_cast<GdkEventButton const*>(_toggleEvent);
651 GdkEventButton const* evtb = reinterpret_cast<GdkEventButton const*>(event);
653 if ( (evtb->window == target->window)
654 && (evtb->send_event == target->send_event)
655 && (evtb->time == target->time)
656 && (evtb->state == target->state)
657 )
658 {
659 // Ooooh! It's a magic one
660 val = false;
661 }
662 }
663 gdk_event_free(event);
664 }
665 }
666 return val;
667 }
670 void LayersPanel::_opacityChanged()
671 {
672 SPObject* layer = _selectedLayer();
674 if ( _desktop && layer && !_opacityConnection.blocked() ) {
675 _opacityConnection.block();
678 Gtk::Adjustment* adj = _opacity.get_adjustment();
679 SPCSSAttr *css = sp_repr_css_attr_new();
681 Inkscape::CSSOStringStream os;
682 os << CLAMP( adj->get_value(), 0.0, 1.0 );
683 sp_repr_css_set_property( css, "opacity", os.str().c_str() );
685 sp_desktop_apply_css_recursive( layer, css, true );
686 layer->updateRepr();
688 sp_repr_css_attr_unref( css );
690 sp_document_maybe_done( _desktop->doc(), "layers:opacity" );
692 _opacityConnection.unblock();
693 }
694 }
698 /**
699 * Constructor
700 */
701 LayersPanel::LayersPanel() :
702 Inkscape::UI::Widget::Panel( Glib::ustring(), "dialogs.layers" ),
703 _maxNestDepth(20),
704 _mgr(0),
705 _desktop(0),
706 _model(0),
707 _pending(0),
708 _toggleEvent(0)
709 {
710 _maxNestDepth = prefs_get_int_attribute_limited("dialogs.layers", "maxDepth", 20, 1, 1000);
712 ModelColumns *zoop = new ModelColumns();
713 _model = zoop;
715 _store = Gtk::TreeStore::create( *zoop );
717 _tree.set_model( _store );
718 _tree.set_headers_visible(false);
720 ImageToggler* eyeRenderer = manage( new ImageToggler("visible", "hidden") );
721 int visibleColNum = _tree.append_column("vis", *eyeRenderer) - 1;
722 eyeRenderer->signal_pre_toggle().connect( sigc::mem_fun(*this, &LayersPanel::_preToggle) );
723 eyeRenderer->signal_toggled().connect( sigc::bind( sigc::mem_fun(*this, &LayersPanel::_toggled), (int)COL_VISIBLE) );
724 eyeRenderer->property_activatable() = true;
725 Gtk::TreeViewColumn* col = _tree.get_column(visibleColNum);
726 if ( col ) {
727 col->add_attribute( eyeRenderer->property_active(), _model->_colVisible );
728 }
730 ImageToggler * renderer = manage( new ImageToggler("width_height_lock", "lock_unlocked") );
731 int lockedColNum = _tree.append_column("lock", *renderer) - 1;
732 renderer->signal_pre_toggle().connect( sigc::mem_fun(*this, &LayersPanel::_preToggle) );
733 renderer->signal_toggled().connect( sigc::bind( sigc::mem_fun(*this, &LayersPanel::_toggled), (int)COL_LOCKED) );
734 renderer->property_activatable() = true;
735 col = _tree.get_column(lockedColNum);
736 if ( col ) {
737 col->add_attribute( renderer->property_active(), _model->_colLocked );
738 }
740 int nameColNum = _tree.append_column_editable("Name", _model->_colLabel) - 1;
742 _tree.set_expander_column( *_tree.get_column(nameColNum) );
745 _tree.get_selection()->signal_changed().connect( sigc::mem_fun(*this, &LayersPanel::_pushTreeSelectionToCurrent) );
746 _tree.get_selection()->set_select_function( sigc::mem_fun(*this, &LayersPanel::_rowSelectFunction) );
748 _tree.get_model()->signal_row_changed().connect( sigc::mem_fun(*this, &LayersPanel::_handleRowChange) );
749 _tree.signal_button_press_event().connect_notify( sigc::mem_fun(*this, &LayersPanel::_handleButtonEvent) );
751 _scroller.add( _tree );
752 _scroller.set_policy( Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC );
755 _opacityBox.pack_start( *manage( new Gtk::Label(_("Opacity:"))), Gtk::PACK_SHRINK );
757 _opacity.set_draw_value(false);
758 _opacity.set_value(1.0);
759 _opacity.set_range(0.0, 1.0);
760 _opacity.set_increments(0.01, 0.1);
761 _opacityBox.pack_start( _opacity, Gtk::PACK_EXPAND_WIDGET );
763 Gtk::SpinButton* spinBtn = manage( new Gtk::SpinButton(*_opacity.get_adjustment(), 0.0, 3) );
765 _opacityBox.pack_end( *spinBtn, Gtk::PACK_SHRINK );
766 _watching.push_back( &_opacityBox );
768 _getContents()->pack_start( _scroller, Gtk::PACK_EXPAND_WIDGET );
770 _getContents()->pack_end(_opacityBox, Gtk::PACK_SHRINK);
771 _getContents()->pack_end(_buttonsRow, Gtk::PACK_SHRINK);
773 _opacityConnection = _opacity.get_adjustment()->signal_value_changed().connect( sigc::mem_fun(*this, &LayersPanel::_opacityChanged) );
775 SPDesktop* targetDesktop = SP_ACTIVE_DESKTOP;
777 _buttonsRow.set_child_min_width( 16 );
779 Gtk::Button* btn = manage( new Gtk::Button() );
780 _styleButton( *btn, targetDesktop, SP_VERB_LAYER_NEW, GTK_STOCK_ADD, _("New") );
781 btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &LayersPanel::_takeAction), (int)BUTTON_NEW) );
782 _buttonsRow.add( *btn );
784 btn = manage( new Gtk::Button() );
785 _styleButton( *btn, targetDesktop, SP_VERB_LAYER_TO_TOP, GTK_STOCK_GOTO_TOP, _("Top") );
786 btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &LayersPanel::_takeAction), (int)BUTTON_TOP) );
787 _watchingNonTop.push_back( btn );
788 _buttonsRow.add( *btn );
790 btn = manage( new Gtk::Button() );
791 _styleButton( *btn, targetDesktop, SP_VERB_LAYER_RAISE, GTK_STOCK_GO_UP, _("Up") );
792 btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &LayersPanel::_takeAction), (int)BUTTON_UP) );
793 _watchingNonTop.push_back( btn );
794 _buttonsRow.add( *btn );
796 btn = manage( new Gtk::Button() );
797 _styleButton( *btn, targetDesktop, SP_VERB_LAYER_LOWER, GTK_STOCK_GO_DOWN, _("Dn") );
798 btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &LayersPanel::_takeAction), (int)BUTTON_DOWN) );
799 _watchingNonBottom.push_back( btn );
800 _buttonsRow.add( *btn );
802 btn = manage( new Gtk::Button() );
803 _styleButton( *btn, targetDesktop, SP_VERB_LAYER_TO_BOTTOM, GTK_STOCK_GOTO_BOTTOM, _("Bot") );
804 btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &LayersPanel::_takeAction), (int)BUTTON_BOTTOM) );
805 _watchingNonBottom.push_back( btn );
806 _buttonsRow.add( *btn );
808 // btn = manage( new Gtk::Button("Dup") );
809 // btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &LayersPanel::_takeAction), (int)BUTTON_DUPLICATE) );
810 // _buttonsRow.add( *btn );
812 btn = manage( new Gtk::Button() );
813 _styleButton( *btn, targetDesktop, SP_VERB_LAYER_DELETE, GTK_STOCK_REMOVE, _("X") );
814 btn->signal_clicked().connect( sigc::bind( sigc::mem_fun(*this, &LayersPanel::_takeAction), (int)BUTTON_DELETE) );
815 _watching.push_back( btn );
816 _buttonsRow.add( *btn );
821 // -------------------------------------------------------
822 {
823 _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_RENAME, 0, "Rename", (int)BUTTON_RENAME ) );
824 _watching.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_NEW, 0, "New", (int)BUTTON_NEW ) );
826 _popupMenu.items().push_back( Gtk::Menu_Helpers::SeparatorElem() );
828 _watchingNonTop.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_RAISE, GTK_STOCK_GO_UP, "Up", (int)BUTTON_UP ) );
829 _watchingNonBottom.push_back( &_addPopupItem( targetDesktop, SP_VERB_LAYER_LOWER, GTK_STOCK_GO_DOWN, "Down", (int)BUTTON_DOWN ) );
831 _popupMenu.show_all_children();
832 }
833 // -------------------------------------------------------
837 for ( std::vector<Gtk::Widget*>::iterator it = _watching.begin(); it != _watching.end(); ++it ) {
838 (*it)->set_sensitive( false );
839 }
840 for ( std::vector<Gtk::Widget*>::iterator it = _watchingNonTop.begin(); it != _watchingNonTop.end(); ++it ) {
841 (*it)->set_sensitive( false );
842 }
843 for ( std::vector<Gtk::Widget*>::iterator it = _watchingNonBottom.begin(); it != _watchingNonBottom.end(); ++it ) {
844 (*it)->set_sensitive( false );
845 }
847 g_signal_connect( G_OBJECT(INKSCAPE), "activate_desktop", G_CALLBACK( layers_panel_activated ), this );
850 setDesktop( targetDesktop );
854 show_all_children();
856 restorePanelPrefs();
857 }
859 LayersPanel::~LayersPanel()
860 {
861 if ( _model )
862 {
863 delete _model;
864 }
866 if ( _toggleEvent )
867 {
868 gdk_event_free( _toggleEvent );
869 _toggleEvent = 0;
870 }
871 }
874 void LayersPanel::setDesktop( SPDesktop* desktop )
875 {
876 if ( desktop != _desktop ) {
877 _layerChangedConnection.disconnect();
878 _layerUpdatedConnection.disconnect();
879 _changedConnection.disconnect();
880 if ( _mgr ) {
881 _mgr = 0;
882 }
883 if ( _desktop ) {
884 _desktop = 0;
885 }
887 _desktop = SP_ACTIVE_DESKTOP;
888 if ( _desktop ) {
889 setLabel( _desktop->doc()->name );
891 _mgr = _desktop->layer_manager;
892 if ( _mgr ) {
893 _layerChangedConnection = _mgr->connectCurrentLayerChanged( sigc::mem_fun(*this, &LayersPanel::_selectLayer) );
894 _layerUpdatedConnection = _mgr->connectLayerDetailsChanged( sigc::mem_fun(*this, &LayersPanel::_updateLayer) );
895 _changedConnection = _mgr->connectChanged( sigc::mem_fun(*this, &LayersPanel::_layersChanged) );
896 }
898 _layersChanged();
899 }
900 }
901 /*
902 GSList const *layers=sp_document_get_resource_list( _desktop->doc(), "layer" );
903 g_message( "layers list starts at %p", layers );
904 for ( GSList const *iter=layers ; iter ; iter = iter->next ) {
905 SPObject *layer=static_cast<SPObject *>(iter->data);
906 g_message(" {%s} [%s]", layer->id, layer->label() );
907 }
908 */
909 }
913 } //namespace Dialogs
914 } //namespace UI
915 } //namespace Inkscape
918 /*
919 Local Variables:
920 mode:c++
921 c-file-style:"stroustrup"
922 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
923 indent-tabs-mode:nil
924 fill-column:99
925 End:
926 */
927 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :