Add actions (add to group, ,remove from group, export, un-export, set layer descripti...
[synfig.git] / synfig-studio / src / gtkmm / layeractionmanager.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file layeractionmanager.cpp
3 **      \brief Template File
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **      Copyright (c) 2007, 2008 Chris Moore
10 **
11 **      This package is free software; you can redistribute it and/or
12 **      modify it under the terms of the GNU General Public License as
13 **      published by the Free Software Foundation; either version 2 of
14 **      the License, or (at your option) any later version.
15 **
16 **      This package is distributed in the hope that it will be useful,
17 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
18 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 **      General Public License for more details.
20 **      \endlegal
21 */
22 /* ========================================================================= */
23
24 /* === H E A D E R S ======================================================= */
25
26 #ifdef USING_PCH
27 #       include "pch.h"
28 #else
29 #ifdef HAVE_CONFIG_H
30 #       include <config.h>
31 #endif
32
33 #include "layeractionmanager.h"
34 #include "layertree.h"
35 #include <synfig/context.h>
36 #include <synfig/layer_pastecanvas.h>
37 #include <synfigapp/action_param.h>
38 #include "instance.h"
39 #include <synfigapp/selectionmanager.h>
40
41 #include "general.h"
42
43 #endif
44
45 /* === U S I N G =========================================================== */
46
47 using namespace std;
48 using namespace etl;
49 using namespace synfig;
50 using namespace studio;
51
52 static const guint no_prev_popup((guint)-1);
53
54 /* === M A C R O S ========================================================= */
55
56 //#define ONE_ACTION_GROUP 1
57
58 /* === G L O B A L S ======================================================= */
59
60 /* === P R O C E D U R E S ================================================= */
61
62 /* === M E T H O D S ======================================================= */
63
64 LayerActionManager::LayerActionManager():
65         action_group_(Gtk::ActionGroup::create("action_group_layer_action_manager")),
66         popup_id_(no_prev_popup),
67         action_group_copy_paste(Gtk::ActionGroup::create("action_group_copy_paste")),
68         queued(false)
69 {
70         action_cut_=Gtk::Action::create(
71                 "cut",
72                 Gtk::StockID("gtk-cut")
73         );
74         action_cut_->signal_activate().connect(
75                 sigc::mem_fun(
76                         *this,
77                         &LayerActionManager::cut
78                 )
79         );
80         action_copy_=Gtk::Action::create(
81                 "copy",
82                 Gtk::StockID("gtk-copy")
83         );
84         action_copy_->signal_activate().connect(
85                 sigc::mem_fun(
86                         *this,
87                         &LayerActionManager::copy
88                 )
89         );
90         action_paste_=Gtk::Action::create(
91                 "paste",
92                 Gtk::StockID("gtk-paste")
93         );
94         action_paste_->signal_activate().connect(
95                 sigc::mem_fun(
96                         *this,
97                         &LayerActionManager::paste
98                 )
99         );
100
101
102         action_amount_inc_=Gtk::Action::create(
103                 "amount-inc",
104                 Gtk::StockID("gtk-add"),
105                 _("Increase Amount"),_("Increase Amount")
106         );
107         action_amount_inc_->signal_activate().connect(
108                 sigc::mem_fun(
109                         *this,
110                         &LayerActionManager::amount_inc
111                 )
112         );
113
114         action_amount_dec_=Gtk::Action::create(
115                 "amount-dec",
116                 Gtk::StockID("gtk-remove"),
117                 _("Decrease Amount"),_("Decrease Amount")
118         );
119         action_amount_dec_->signal_activate().connect(
120                 sigc::mem_fun(
121                         *this,
122                         &LayerActionManager::amount_dec
123                 )
124         );
125
126         action_amount_=Gtk::Action::create(
127                 "amount",
128                 Gtk::StockID("gtk-index"),
129                 _("Amount"),_("Amount")
130         );
131
132         action_select_all_child_layers_=Gtk::Action::create(
133                 "select-all-child-layers",
134                 Gtk::StockID("synfig-select_all_child_layers"),
135                 _("Select All Child Layers"),_("Select All Child Layers")
136         );
137         action_select_all_child_layers_->set_sensitive(false);
138 }
139
140 LayerActionManager::~LayerActionManager()
141 {
142 }
143
144 void
145 LayerActionManager::set_ui_manager(const Glib::RefPtr<Gtk::UIManager> &x)
146 {
147         clear();
148
149 #ifdef ONE_ACTION_GROUP
150         if(ui_manager_) get_ui_manager()->remove_action_group(action_group_);
151         ui_manager_=x;
152         if(ui_manager_) get_ui_manager()->insert_action_group(action_group_);
153 #else
154         ui_manager_=x;
155 #endif
156 }
157
158 void
159 LayerActionManager::set_layer_tree(LayerTree* x)
160 {
161         selection_changed_connection.disconnect();
162         layer_tree_=x;
163         if(layer_tree_)
164         {
165                 selection_changed_connection=layer_tree_->get_selection()->signal_changed().connect(
166                         sigc::mem_fun(*this,&LayerActionManager::queue_refresh)
167                 );
168         }
169 }
170
171 void
172 LayerActionManager::set_canvas_interface(const etl::handle<synfigapp::CanvasInterface> &x)
173 {
174         canvas_interface_=x;
175 }
176
177 void
178 LayerActionManager::clear()
179 {
180         if(ui_manager_)
181         {
182                 // Clear out old stuff
183                 if(popup_id_!=no_prev_popup)
184                 {
185                         get_ui_manager()->remove_ui(popup_id_);
186                         if(action_group_)get_ui_manager()->ensure_update();
187                         popup_id_=no_prev_popup;
188                         if(action_group_)while(!action_group_->get_actions().empty())action_group_->remove(*action_group_->get_actions().begin());
189 #ifdef ONE_ACTION_GROUP
190 #else
191                         if(action_group_)get_ui_manager()->remove_action_group(action_group_);
192                         action_group_=Gtk::ActionGroup::create("action_group_layer_action_manager");
193 #endif
194                 }
195         }
196
197         while(!update_connection_list.empty())
198         {
199                 update_connection_list.front().disconnect();
200                 update_connection_list.pop_front();
201         }
202 }
203
204 void
205 LayerActionManager::queue_refresh()
206 {
207         if(queued)
208                 return;
209
210         //queue_refresh_connection.disconnect();
211         queue_refresh_connection=Glib::signal_idle().connect(
212                 sigc::bind_return(
213                         sigc::mem_fun(*this,&LayerActionManager::refresh),
214                         false
215                 )
216         );
217
218         queued=true;
219 }
220
221 void
222 LayerActionManager::refresh()
223 {
224         if(queued)
225         {
226                 queued=false;
227                 //queue_refresh_connection.disconnect();
228         }
229
230
231         clear();
232
233         // Make sure we are ready
234         if(!ui_manager_ || !layer_tree_ || !canvas_interface_)
235         {
236                 synfig::error("LayerActionManager::refresh(): Not ready!");
237                 return;
238         }
239
240         String ui_info;
241
242         action_paste_->set_sensitive(!clipboard_.empty());
243         action_group_->add(action_paste_);
244
245         if(layer_tree_->get_selection()->count_selected_rows()!=0)
246         {
247                 bool multiple_selected(layer_tree_->get_selection()->count_selected_rows()>1);
248                 Layer::Handle layer(layer_tree_->get_selected_layer());
249
250                 {
251                         bool canvas_set(false);
252                         synfigapp::Action::ParamList param_list;
253                         param_list.add("time",get_canvas_interface()->get_time());
254                         param_list.add("canvas_interface",get_canvas_interface());
255                         {
256                                 synfigapp::SelectionManager::LayerList layer_list(layer_tree_->get_selected_layers());
257                                 synfigapp::SelectionManager::LayerList::iterator iter;
258                                 action_copy_->set_sensitive(!layer_list.empty());
259                                 action_cut_->set_sensitive(!layer_list.empty());
260                                 action_group_->add(action_copy_);
261                                 action_group_->add(action_cut_);
262
263                                 action_amount_inc_->set_sensitive(!layer_list.empty());
264                                 action_amount_dec_->set_sensitive(!layer_list.empty());
265                                 action_amount_->set_sensitive(!layer_list.empty());
266                                 action_group_->add(action_amount_inc_);
267                                 action_group_->add(action_amount_dec_);
268                                 action_group_->add(action_amount_);
269
270                                 for(iter=layer_list.begin();iter!=layer_list.end();++iter)
271                                 {
272                                         update_connection_list.push_back(
273                                                 (*iter)->signal_changed().connect(
274                                                         sigc::mem_fun(*this, &LayerActionManager::queue_refresh)
275                                                 )
276                                         );
277
278                                         if(!canvas_set)
279                                         {
280                                                 param_list.add("canvas",Canvas::Handle((*iter)->get_canvas()));
281                                                 canvas_set=true;
282                                                 update_connection_list.push_back(
283                                                         (*iter)->get_canvas()->signal_changed().connect(
284                                                                 sigc::mem_fun(*this, &LayerActionManager::queue_refresh)
285                                                         )
286                                                 );
287                                         }
288                                         param_list.add("layer",Layer::Handle(*iter));
289                                 }
290                         }
291
292                         if(!multiple_selected && layer->get_name()=="PasteCanvas")
293                         {
294                                 if (select_all_child_layers_connection)
295                                         select_all_child_layers_connection.disconnect();
296
297                                 select_all_child_layers_connection = action_select_all_child_layers_->signal_activate().connect(
298                                         sigc::bind(sigc::mem_fun(*layer_tree_,
299                                                                                          &studio::LayerTree::select_all_children_layers),
300                                                            Layer::LooseHandle(layer)));
301
302                                 action_select_all_child_layers_->set_sensitive(true);
303
304                                 ui_info+="<menuitem action='select-all-child-layers'/>";
305                         }
306                         else
307                                 action_select_all_child_layers_->set_sensitive(false);
308
309                         handle<studio::Instance>::cast_static(get_canvas_interface()->get_instance())->
310                                 add_actions_to_group(action_group_, ui_info,   param_list, synfigapp::Action::CATEGORY_LAYER);
311                 }
312         }
313
314         ui_info=("<ui>"
315                            "<popup action='menu-main'>"
316                              "<menu action='menu-layer'>" +
317                                    ui_info +
318                                    "<separator/>"
319                                "<menuitem action='cut' />"
320                                    "<menuitem action='copy' />"
321                                    "<menuitem action='paste' />"
322                                    "<separator/>"
323                              "</menu>"
324                            "</popup>" +
325                          "</ui>");
326         popup_id_=get_ui_manager()->add_ui_from_string(ui_info);
327 #ifdef ONE_ACTION_GROUP
328 #else
329         get_ui_manager()->insert_action_group(action_group_);
330 #endif
331 }
332
333 void
334 LayerActionManager::cut()
335 {
336         copy();
337         if(action_group_->get_action("action-LayerRemove"))
338                 action_group_->get_action("action-LayerRemove")->activate();
339 }
340
341 void
342 LayerActionManager::copy()
343 {
344         synfigapp::SelectionManager::LayerList layer_list(layer_tree_->get_selected_layers());
345         clipboard_.clear();
346         synfig::GUID guid;
347
348         while(!layer_list.empty())
349         {
350                 clipboard_.push_back(layer_list.front()->clone(guid));
351                 layer_list.pop_front();
352         }
353
354         action_paste_->set_sensitive(!clipboard_.empty());
355
356         //queue_refresh();
357 }
358
359 void
360 LayerActionManager::paste()
361 {
362         synfig::GUID guid;
363
364         // Create the action group
365         synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Paste"));
366
367         Canvas::Handle canvas(get_canvas_interface()->get_canvas());
368         int depth(0);
369
370         // we are temporarily using the layer to hold something
371         Layer::Handle layer(layer_tree_->get_selected_layer());
372         if(layer)
373         {
374                 depth=layer->get_depth();
375                 canvas=layer->get_canvas();
376         }
377
378         synfigapp::SelectionManager::LayerList layer_selection;
379
380         for(std::list<synfig::Layer::Handle>::iterator iter=clipboard_.begin();iter!=clipboard_.end();++iter)
381         {
382                 layer=(*iter)->clone(guid);
383                 layer_selection.push_back(layer);
384                 synfigapp::Action::Handle       action(synfigapp::Action::create("LayerAdd"));
385
386                 assert(action);
387                 if(!action)
388                         return;
389
390                 action->set_param("canvas",canvas);
391                 action->set_param("canvas_interface",etl::loose_handle<synfigapp::CanvasInterface>(get_canvas_interface()));
392                 action->set_param("new",layer);
393
394                 if(!action->is_ready())
395                 {
396                         return;
397                 }
398
399                 if(!get_instance()->perform_action(action))
400                 {
401                         return;
402                 }
403
404                 etl::handle<Layer_PasteCanvas> paste = etl::handle<Layer_PasteCanvas>::cast_dynamic(layer);
405                 if (paste) paste->update_renddesc();
406
407                 // synfig::info("DEPTH=%d",depth);
408
409                 // Action to move the layer (if necessary)
410                 if(depth>0)
411                 {
412                         synfigapp::Action::Handle       action(synfigapp::Action::create("LayerMove"));
413
414                         assert(action);
415                         if(!action)
416                                 return;
417
418                         action->set_param("canvas",canvas);
419                         action->set_param("canvas_interface",etl::loose_handle<synfigapp::CanvasInterface>(get_canvas_interface()));
420                         action->set_param("layer",layer);
421                         action->set_param("new_index",depth);
422
423                         if(!action->is_ready())
424                         {
425                                 //get_ui_interface()->error(_("Move Action Not Ready"));
426                                 //return 0;
427                                 return;
428                         }
429
430                         if(!get_instance()->perform_action(action))
431                         {
432                                 //get_ui_interface()->error(_("Move Action Not Ready"));
433                                 //return 0;
434                                 return;
435                         }
436                 }
437                 depth++;
438
439                 // automatically export the Index parameter of Duplicate layers when pasting
440                 int index = 1;
441                 export_dup_nodes(layer, canvas, index);
442         }
443         get_canvas_interface()->get_selection_manager()->clear_selected_layers();
444         get_canvas_interface()->get_selection_manager()->set_selected_layers(layer_selection);
445 }
446
447 void
448 LayerActionManager::export_dup_nodes(synfig::Layer::Handle layer, Canvas::Handle canvas, int &index)
449 {
450         // automatically export the Index parameter of Duplicate layers when pasting
451         if (layer->get_name() == "duplicate")
452                 while (true)
453                 {
454                         String name = strprintf(_("Index %d"), index++);
455                         try
456                         {
457                                 canvas->find_value_node(name);
458                         }
459                         catch (Exception::IDNotFound x)
460                         {
461                                 get_canvas_interface()->add_value_node(layer->dynamic_param_list().find("index")->second, name);
462                                 break;
463                         }
464                 }
465         else
466         {
467                 Layer::ParamList param_list(layer->get_param_list());
468                 for (Layer::ParamList::const_iterator iter(param_list.begin())
469                                  ; iter != param_list.end()
470                                  ; iter++)
471                         if (layer->dynamic_param_list().count(iter->first)==0 && iter->second.get_type()==ValueBase::TYPE_CANVAS)
472                         {
473                                 Canvas::Handle subcanvas(iter->second.get(Canvas::Handle()));
474                                 if (subcanvas && subcanvas->is_inline())
475                                         for (Context iter = subcanvas->get_context(); iter != subcanvas->end(); iter++)
476                                                 export_dup_nodes(*iter, canvas, index);
477                         }
478
479                 for (Layer::DynamicParamList::const_iterator iter(layer->dynamic_param_list().begin())
480                                  ; iter != layer->dynamic_param_list().end()
481                                  ; iter++)
482                         if (iter->second->get_type()==ValueBase::TYPE_CANVAS)
483                         {
484                                 Canvas::Handle canvas((*iter->second)(0).get(Canvas::Handle()));
485                                 if (canvas->is_inline())
486                                         //! \todo do we need to implement this?  and if so, shouldn't we check all canvases, not just the one at t=0s?
487                                         warning("%s:%d not yet implemented - do we need to export duplicate valuenodes in dynamic canvas parameters?", __FILE__, __LINE__);
488                         }
489         }
490 }
491
492 void
493 LayerActionManager::amount_inc()
494 {
495         float adjust(0.1);
496
497         // Create the action group
498         synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Increase Amount"));
499         synfigapp::SelectionManager::LayerList layer_list(layer_tree_->get_selected_layers());
500
501         for (; !layer_list.empty(); layer_list.pop_front())
502         {
503                 ValueBase value(layer_list.front()->get_param("amount"));
504                 if(value.same_type_as(Real()))
505                         get_canvas_interface()->change_value(synfigapp::ValueDesc(layer_list.front(),"amount"),value.get(Real())+adjust);
506         }
507 }
508
509 void
510 LayerActionManager::amount_dec()
511 {
512         float adjust(-0.1);
513
514         // Create the action group
515         synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Decrease Amount"));
516         synfigapp::SelectionManager::LayerList layer_list(layer_tree_->get_selected_layers());
517
518         for (; !layer_list.empty(); layer_list.pop_front())
519         {
520                 ValueBase value(layer_list.front()->get_param("amount"));
521                 if(value.same_type_as(Real()))
522                         get_canvas_interface()->change_value(synfigapp::ValueDesc(layer_list.front(),"amount"),value.get(Real())+adjust);
523         }
524 }