my log
[synfig.git] / synfig-studio / trunk / src / gtkmm / instance.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file instance.cpp
3 **      \brief writeme
4 **
5 **      $Id: instance.cpp,v 1.2 2005/01/13 18:37:30 darco Exp $
6 **
7 **      \legal
8 **      Copyright (c) 2002 Robert B. Quattlebaum Jr.
9 **
10 **      This software and associated documentation
11 **      are CONFIDENTIAL and PROPRIETARY property of
12 **      the above-mentioned copyright holder.
13 **
14 **      You may not copy, print, publish, or in any
15 **      other way distribute this software without
16 **      a prior written agreement with
17 **      the copyright holder.
18 **      \endlegal
19 */
20 /* ========================================================================= */
21
22 /* === H E A D E R S ======================================================= */
23
24 #ifdef USING_PCH
25 #       include "pch.h"
26 #else
27 #ifdef HAVE_CONFIG_H
28 #       include <config.h>
29 #endif
30
31 #include "instance.h"
32 #include <cassert>
33 #include <gtkmm/stock.h>
34 #include <gtkmm/image.h>
35 #include <iostream>
36 #include <gtkmm/button.h>
37 #include "canvasview.h"
38 #include "app.h"
39 #include <sigc++/signal.h>
40 #include <sigc++/adaptors/hide.h>
41 #include "toolbox.h"
42 #include "onemoment.h"
43
44 #include "autorecover.h"
45 #include <sigc++/retype_return.h>
46 #include <sigc++/retype.h>
47 //#include <sigc++/hide.h>
48 #include <synfig/valuenode_composite.h>
49 #include "widget_waypointmodel.h"
50 #include <gtkmm/actiongroup.h>
51 #include "iconcontroler.h"
52
53 #endif
54
55 using namespace std;
56 using namespace etl;
57 using namespace synfig;
58 using namespace studio;
59 using namespace SigC;
60
61 /* === M A C R O S ========================================================= */
62
63 /* === G L O B A L S ======================================================= */
64
65 int studio::Instance::instance_count_=0;
66
67 /* === P R O C E D U R E S ================================================= */
68
69 /* === M E T H O D S ======================================================= */
70
71 Instance::Instance(Canvas::Handle canvas):
72         synfigapp::Instance             (canvas),
73 //      canvas_tree_store_              (Gtk::TreeStore::create(CanvasTreeModel())),
74 //      canvas_tree_store_              (Gtk::TreeStore::create()),
75         history_tree_store_             (HistoryTreeStore::create(this)),
76         undo_status_(false),
77         redo_status_(false)
78 {
79         CanvasTreeModel model;
80         canvas_tree_store_=Gtk::TreeStore::create(model);
81
82         id_=instance_count_++;
83         
84         // Connect up all the signals
85         signal_filename_changed().connect(sigc::mem_fun(*this,&studio::Instance::update_all_titles));
86         signal_unsaved_status_changed().connect(sigc::hide(sigc::mem_fun(*this,&studio::Instance::update_all_titles)));
87         signal_undo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_undo_status));
88         signal_redo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_redo_status));
89
90         signal_saved().connect(
91                 sigc::hide_return(
92                         sigc::ptr_fun(
93                                 studio::AutoRecover::auto_backup
94                         )
95                 )
96         );
97         
98         canvas_tree_store_=Gtk::TreeStore::create(canvas_tree_model);
99         
100         refresh_canvas_tree();
101 }
102
103 Instance::~Instance()
104 {
105 }
106
107 int
108 Instance::get_visible_canvases()const
109 {
110         int count(0);
111         CanvasViewList::const_iterator iter;
112         for(iter=canvas_view_list_.begin();iter!=canvas_view_list_.end();++iter)
113                 if((*iter)->is_visible())
114                         count++;
115         return count;
116 }
117
118 handle<Instance>
119 Instance::create(Canvas::Handle canvas)
120 {
121         // Construct a new instance
122         handle<Instance> instance(new Instance(canvas));
123
124         // Add the new instance to the application's instance list
125         App::instance_list.push_back(instance);
126         
127         // Set up the instance with the default UI manager
128         instance->synfigapp::Instance::set_ui_interface(App::get_ui_interface());
129         
130         // Signal the new instance
131         App::signal_instance_created()(instance);
132         
133         // And then make sure that is has been selected
134         App::set_selected_instance(instance);
135         
136         // Create the initial window for the root canvas
137         instance->focus(canvas);
138         
139         return instance;        
140 }
141
142 handle<CanvasView>
143 Instance::find_canvas_view(Canvas::Handle canvas)
144 {
145         if(!canvas)
146                 return 0;
147
148         while(canvas->is_inline())
149                 canvas=canvas->parent();
150
151         CanvasViewList::iterator iter;
152         
153         for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
154                 if((*iter)->get_canvas()==canvas)
155                         return *iter;
156         
157         return CanvasView::create(this,canvas);
158 }
159
160 void
161 Instance::focus(Canvas::Handle canvas)
162 {
163         handle<CanvasView> canvas_view=find_canvas_view(canvas);
164         assert(canvas_view);
165         canvas_view->present();
166 }
167
168 void
169 Instance::set_undo_status(bool x)
170 {
171         undo_status_=x;
172         App::toolbox->update_undo_redo();
173         signal_undo_redo_status_changed()();
174 }
175
176 void
177 Instance::set_redo_status(bool x)
178 {
179         redo_status_=x;
180         App::toolbox->update_undo_redo();
181         signal_undo_redo_status_changed()();
182 }
183
184 bool
185 studio::Instance::save_as(const synfig::String &file_name)const
186 {
187         if(synfigapp::Instance::save_as(file_name))
188         {
189                 App::add_recent_file(file_name);
190                 return true;
191         }
192         return false;
193 }
194
195 bool
196 studio::Instance::save_as(const synfig::String &file_name)
197 {
198         if(synfigapp::Instance::save_as(file_name))
199         {
200                 App::add_recent_file(file_name);
201                 return true;
202         }
203         return false;
204 }
205
206 bool
207 studio::Instance::save()
208 {
209         if(basename(get_file_name()).find("untitled")==0)
210         {
211                 dialog_save_as();
212                 return true;
213         }
214
215         return synfigapp::Instance::save();
216         
217 }
218
219 void
220 studio::Instance::dialog_save_as()
221 {
222         string filename="*.sif";
223         
224         Canvas::Handle canvas(get_canvas());
225         
226         {
227                 OneMoment one_moment;
228                 std::set<Node*>::iterator iter;
229                 for(iter=canvas->parent_set.begin();iter!=canvas->parent_set.end();++iter)
230                 {
231                         synfig::Node* node(*iter);
232                         for(;!node->parent_set.empty();node=*node->parent_set.begin())
233                         {
234                                 Layer::Handle parent_layer(dynamic_cast<Layer*>(node));
235                                 if(parent_layer && parent_layer->get_canvas()->get_root()!=get_canvas())
236                                 {
237                                         App::dialog_error_blocking("SaveAs - Error",
238                                                 "There is currently a bug when using \"SaveAs\"\n"
239                                                 "on a composition that is being referenced by other\n"
240                                                 "files that are currently open. Close these\n"
241                                                 "other files first before trying to use \"SaveAs\"."
242                                         );
243                                         
244                                         return;
245                                 }
246                                 if(parent_layer)
247                                         break;
248                         }
249                 }
250         }
251         
252         while(App::dialog_saveas_file("SaveAs", filename))
253         {
254                 // If the filename still has wildcards, then we should
255                 // continue looking for the file we want
256                 if(find(filename.begin(),filename.end(),'*')!=filename.end())
257                         continue;
258
259                 if(find(filename.begin(),filename.end(),'.')==filename.end())
260                         filename+=".sif";
261
262                 try
263                 {
264                         String ext(String(filename.begin()+filename.find_last_of('.')+1,filename.end()));
265                         if(ext!="sif" && ext!="sifz" && !App::dialog_yes_no(_("Unknown extension"),
266                                 _("You have given the file name an extension\nwhich I do not recognise. Are you sure this is what you want?")))
267                         {
268                                 continue;
269                         }
270                 }
271                 catch(...)
272                 {
273                         continue;
274                 }
275                         
276                 if(save_as(filename))
277                         break;
278                 
279                 App::dialog_error_blocking("SaveAs - Error","Unable to save file");
280         }
281 }
282         
283 void
284 Instance::update_all_titles()
285 {
286         list<handle<CanvasView> >::iterator iter;
287         for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
288                 (*iter)->update_title();
289 }
290
291 void
292 Instance::close()
293 {
294         // This will increase the reference count so we don't get DELETED
295         // until we are ready
296         handle<Instance> me(this);
297
298         // Make sure we aren't selected as the current instance
299         if(studio::App::get_selected_instance()==this)
300                 studio::App::set_selected_instance(0);
301
302         // Turn-off/clean-up auto recovery
303         studio::App::auto_recover->clear_backup(get_canvas());
304
305         // Remove us from the active instance list
306         std::list<etl::handle<studio::Instance> >::iterator iter;
307         for(iter=studio::App::instance_list.begin();iter!=studio::App::instance_list.end();iter++)
308                 if(*iter==this)
309                         break;
310         assert(iter!=studio::App::instance_list.end());
311         if(iter!=studio::App::instance_list.end())
312                 studio::App::instance_list.erase(iter);
313
314         // Send out a signal that we are being deleted
315         studio::App::signal_instance_deleted()(this);
316
317         // Hide all of the canvas views
318         for(std::list<etl::handle<CanvasView> >::iterator iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
319                 (*iter)->hide();
320
321         // Delete all of the canvas views
322         canvas_view_list().clear();
323
324         // If there is another open instance to select,
325         // go ahead and do so. If not, never mind.
326         if(studio::App::instance_list.empty())
327         {
328                 studio::App::set_selected_canvas_view(0);
329                 studio::App::set_selected_instance(0);
330         }
331         else
332         {
333                 studio::App::set_selected_canvas_view(studio::App::instance_list.front()->canvas_view_list().front());
334                 //studio::App::set_selected_instance(studio::App::instance_list.front());
335         }
336 }
337
338         
339 void
340 Instance::insert_canvas(Gtk::TreeRow row,Canvas::Handle canvas)
341 {
342         CanvasTreeModel canvas_tree_model;
343         assert(canvas);
344
345         row[canvas_tree_model.icon] = Gtk::Button().render_icon(Gtk::StockID("synfig-canvas"),Gtk::ICON_SIZE_SMALL_TOOLBAR);    
346         row[canvas_tree_model.id] = canvas->get_id();
347         row[canvas_tree_model.name] = canvas->get_name();
348         if(canvas->is_root())
349                 row[canvas_tree_model.label] = basename(canvas->get_file_name());
350         else
351         if(!canvas->get_id().empty())
352                 row[canvas_tree_model.label] = canvas->get_id();
353         else
354         if(!canvas->get_name().empty())
355                 row[canvas_tree_model.label] = canvas->get_name();              
356         else
357                 row[canvas_tree_model.label] = _("[Unnamed]");          
358                 
359         row[canvas_tree_model.canvas] = canvas;
360         row[canvas_tree_model.is_canvas] = true;
361         row[canvas_tree_model.is_value_node] = false;
362
363         {
364                 synfig::Canvas::Children::iterator iter;
365                 synfig::Canvas::Children &children(canvas->children());
366         
367                 for(iter=children.begin();iter!=children.end();iter++)
368                         insert_canvas(*(canvas_tree_store()->append(row.children())),*iter);
369         }
370
371         /*
372         if(!canvas->value_node_list().empty())
373         {
374                 Gtk::TreeRow valuenode_row = *(canvas_tree_store()->append(row.children()));
375
376                 valuenode_row[canvas_tree_model.label] = "<defs>";
377                 valuenode_row[canvas_tree_model.canvas] = canvas;
378                 valuenode_row[canvas_tree_model.is_canvas] = false;
379                 valuenode_row[canvas_tree_model.is_value_node] = false;
380
381                 synfig::ValueNodeList::iterator iter;
382                 synfig::ValueNodeList &value_node_list(canvas->value_node_list());
383
384                 for(iter=value_node_list.begin();iter!=value_node_list.end();iter++)
385                         insert_value_node(*(canvas_tree_store()->append(valuenode_row.children())),canvas,*iter);
386         }
387         */
388 }
389
390
391 /*
392 void
393 Instance::insert_value_node(Gtk::TreeRow row,Canvas::Handle canvas,etl::handle<synfig::ValueNode> value_node)
394 {
395         CanvasTreeModel canvas_tree_model;
396         assert(value_node);
397         assert(canvas);
398
399         row[canvas_tree_model.id] = value_node->get_id();
400         row[canvas_tree_model.name] = value_node->get_id();
401         row[canvas_tree_model.label] = value_node->get_id();
402         row[canvas_tree_model.canvas] = canvas;
403         row[canvas_tree_model.value_node] = value_node;
404         row[canvas_tree_model.is_canvas] = false;
405         row[canvas_tree_model.is_value_node] = true;
406         row[canvas_tree_model.icon] = Gtk::Button().render_icon(valuenode_icon(value_node),Gtk::ICON_SIZE_SMALL_TOOLBAR);
407 }
408 */
409
410 void
411 Instance::refresh_canvas_tree()
412 {
413         canvas_tree_store()->clear();
414         Gtk::TreeRow row = *(canvas_tree_store()->prepend());
415         insert_canvas(row,get_canvas());
416 }
417
418 void
419 Instance::dialog_cvs_commit()
420 {
421         calc_repository_info();
422         if(!in_repository())
423         {
424                 App::dialog_error_blocking(_("Error"),_("You must first add this composition to the repository"));
425                 return;
426         }
427         try
428         {
429                 string message;
430
431                 if(synfigapp::Instance::get_action_count())
432                 {
433                         if(!App::dialog_yes_no(_("CVS Commit"), _("This will save any changes you have made. Are you sure?")))
434                                 return;
435                         save();
436                 }
437
438                 if(!is_modified())
439                 {
440                         App::dialog_error_blocking(_("Error"),_("The local copy of the file hasn't been changed since the last update.\nNothing to commit!"));
441                         return;
442                 }
443
444                 if(!App::dialog_entry(_("CVS Commit"),_("Enter a log message describing the changes you have made"), message))
445                         return;
446                 
447                 OneMoment one_moment;
448                 cvs_commit(message);
449         }
450         catch(...)
451         {
452                 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to COMMIT"));
453         }
454         update_all_titles();
455 }
456
457 void
458 Instance::dialog_cvs_add()
459 {
460         calc_repository_info();
461         if(in_repository())
462         {
463                 App::dialog_error_blocking(_("Error"),_("This composition has already been added to the repository"));
464                 return;
465         }
466         try
467         {
468                 string message;
469                 
470                 //if(!App::dialog_entry(_("CVS Add"),_("Enter a log message describing the file"), message))
471                 //      return;
472                 OneMoment one_moment;
473                 cvs_add();
474         }
475         catch(...)
476         {
477                 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to ADD"));
478         }
479         update_all_titles();
480 }
481
482 void
483 Instance::dialog_cvs_update()
484 {
485         calc_repository_info();
486         if(!in_repository())
487         {
488                 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to update from!"));
489                 return;
490         }
491         if(!is_updated())
492         {
493                 App::dialog_error_blocking(_("Info"),_("This file is up-to-date"));
494                 return;
495         }
496         
497         try
498         {
499                 String filename(get_file_name());
500                 if(synfigapp::Instance::get_action_count())
501                 {
502                         if(!App::dialog_yes_no(_("CVS Update"), _("This will save any changes you have made. Are you sure?")))
503                                 return;
504                         save();
505                 }
506                 OneMoment one_moment;
507                 time_t oldtime=get_original_timestamp();
508                 cvs_update();
509                 calc_repository_info();         
510                 // If something has been updated...
511                 if(oldtime!=get_original_timestamp())
512                 {
513                         revert();
514                 }
515         }
516         catch(...)
517         {
518                 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to UPDATE"));
519         }
520         //update_all_titles();
521 }
522
523 void
524 Instance::dialog_cvs_revert()
525 {
526         calc_repository_info();
527         if(!in_repository())
528         {
529                 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to revert to!"));
530                 return;
531         }
532         try
533         {
534                 String filename(get_file_name());
535                 if(!App::dialog_yes_no(_("CVS Revert"),
536                         _("This will abandon all changes you have made\nsince the last time you performed a commit\noperation. This cannot be undone! Are you sure\nyou want to do this?")
537                 ))
538                         return;
539
540                 OneMoment one_moment;
541
542                 // Remove the old file
543                 if(remove(get_file_name().c_str())!=0)
544                 {
545                         App::dialog_error_blocking(_("Error"),_("Unable to remove previous version"));
546                         return;
547                 }
548
549                 cvs_update();
550                 revert();
551         }
552         catch(...)
553         {
554                 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to UPDATE"));
555         }
556         //update_all_titles();
557 }
558
559 void
560 Instance::_revert(Instance *instance)
561 {
562         OneMoment one_moment;
563
564         String filename(instance->get_file_name());
565
566         Canvas::Handle canvas(instance->get_canvas());
567         
568         instance->close();
569
570         if(canvas->count()!=1)
571         {
572                 one_moment.hide();
573                 App::dialog_error_blocking(_("Error: Revert Failed"),_("The revert operation has failed. This can be due to it being\nreferenced by another composition that is already open, or\nbecause of an internal error in SYNFIG Studio. Try closing any\ncompositions that might reference this composition and try\nagain, or restart SYNFIG studio."));
574                 one_moment.show();
575         }
576         canvas=0;
577         
578         App::open(filename);
579 }
580
581 void
582 Instance::revert()
583 {
584         // Schedule a revert to occur in a few moments
585         Glib::signal_timeout().connect(
586                 sigc::bind_return(
587                         sigc::bind(
588                                 sigc::ptr_fun(&Instance::_revert),
589                                 this
590                         ),
591                         false
592                 )
593                 ,500
594         );
595 }
596
597 bool
598 Instance::safe_revert()
599 {
600         if(synfigapp::Instance::get_action_count())
601                 if(!App::dialog_yes_no(_("Revert to saved"), _("You will loose any changes you have made since your last save.\nAre you sure?")))
602                         return false;
603         revert();
604         return true;
605 }
606
607 bool
608 Instance::safe_close()
609 {
610         handle<synfigapp::UIInterface> uim;
611         uim=find_canvas_view(get_canvas())->get_ui_interface();
612
613         if(get_action_count())
614         {
615                 string str=strprintf(_("Would you like to save your changes to %s?"),basename(get_file_name()).c_str() );
616                 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
617                 if(answer==synfigapp::UIInterface::RESPONSE_YES)
618                         save();
619                 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
620                         return false;
621         }
622
623         if(is_modified())
624         {
625                 string str=strprintf(_("%s has changes not yet on the CVS repository.\nWould you like to commit these changes?"),basename(get_file_name()).c_str());
626                 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
627
628                 if(answer==synfigapp::UIInterface::RESPONSE_YES)
629                         dialog_cvs_commit();
630                 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
631                         return false;
632         }
633         
634         close();
635
636         while(studio::App::events_pending())studio::App::iteration(false);
637
638         return true;
639 }
640
641
642 void
643 Instance::add_actions_to_group(const Glib::RefPtr<Gtk::ActionGroup>& action_group, synfig::String& ui_info,   const synfigapp::Action::ParamList &param_list, synfigapp::Action::Category category)const
644 {
645         synfigapp::Action::CanidateList canidate_list;
646         synfigapp::Action::CanidateList::iterator iter;
647         
648         canidate_list=compile_canidate_list(param_list,category);
649         
650         canidate_list.sort();
651
652         if(canidate_list.empty())
653                 synfig::warning("Action CanidateList is empty!");
654         
655         for(iter=canidate_list.begin();iter!=canidate_list.end();++iter)
656         {
657                 Gtk::StockID stock_id(get_action_stock_id(*iter));
658                 
659                 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
660                 {
661                         action_group->add(Gtk::Action::create(
662                                 "action-"+iter->name,
663                                 stock_id,
664                                 iter->local_name,iter->local_name
665                         ),
666                                 sigc::bind(
667                                         sigc::bind(
668                                                 sigc::mem_fun(
669                                                         *const_cast<studio::Instance*>(this),
670                                                         &studio::Instance::process_action
671                                                 ),
672                                                 param_list
673                                         ),
674                                         iter->name
675                                 )
676                         );
677                         ui_info+=strprintf("<menuitem action='action-%s' />",iter->name.c_str());
678                 }
679         }
680 }
681
682 void
683 Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList &param_list,synfigapp::Action::Category category)const
684 {
685         synfigapp::Action::CanidateList canidate_list;
686         synfigapp::Action::CanidateList::iterator iter;
687         
688         canidate_list=compile_canidate_list(param_list,category);
689         
690         canidate_list.sort();
691
692         if(canidate_list.empty())
693                 synfig::warning("Action CanidateList is empty!");
694         
695         for(iter=canidate_list.begin();iter!=canidate_list.end();++iter)
696         {
697                 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
698                 {
699                         Gtk::Image* image(manage(new Gtk::Image()));
700                         Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
701                         
702                         /*
703                         if(iter->task=="raise")
704                                 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
705                         else if(iter->task=="lower")
706                                 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
707                         else if(iter->task=="move_top")
708                                 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
709                         else if(iter->task=="move_bottom")
710                                 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
711                         else if(iter->task=="remove")
712                                 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
713                         else if(iter->task=="set_on")
714                                 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
715                         else if(iter->task=="set_off")
716                                 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
717                         else if(iter->task=="duplicate")
718                                 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
719                         else if(iter->task=="remove")
720                                 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
721                         else
722                         {
723                                 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
724                                 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
725                                 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
726                         }
727                         */
728                         menu->items().push_back(
729                                 Gtk::Menu_Helpers::ImageMenuElem(
730                                         iter->local_name,
731                                         *image,
732                                         sigc::bind(
733                                                 sigc::bind(
734                                                         sigc::mem_fun(
735                                                                 *const_cast<studio::Instance*>(this),
736                                                                 &studio::Instance::process_action
737                                                         ),
738                                                         param_list
739                                                 ),
740                                                 iter->name
741                                         )
742                                 )
743                         );
744                 }
745         }
746 }
747
748 void
749 Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList &param_list,const synfigapp::Action::ParamList &param_list2,synfigapp::Action::Category category)const
750 {
751         synfigapp::Action::CanidateList canidate_list;
752         synfigapp::Action::CanidateList canidate_list2;
753         
754         synfigapp::Action::CanidateList::iterator iter;
755         
756         canidate_list=compile_canidate_list(param_list,category);
757         canidate_list2=compile_canidate_list(param_list2,category);
758         
759         canidate_list.sort();
760
761         if(canidate_list.empty())
762                 synfig::warning("Action CanidateList is empty!");
763         if(canidate_list2.empty())
764                 synfig::warning("Action CanidateList2 is empty!");
765
766         // Seperate out the canidate lists so that there are no conflicts
767         for(iter=canidate_list.begin();iter!=canidate_list.end();++iter)
768         {
769                 synfigapp::Action::CanidateList::iterator iter2(canidate_list2.find(iter->name));
770                 if(iter2!=canidate_list2.end())
771                         canidate_list2.erase(iter2);
772         }
773                 
774         for(iter=canidate_list2.begin();iter!=canidate_list2.end();++iter)
775         {
776                 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
777                 {
778                         Gtk::Image* image(manage(new Gtk::Image()));
779                         Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
780
781 /*                      if(iter->task=="raise")
782                                 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
783                         else if(iter->task=="lower")
784                                 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
785                         else if(iter->task=="move_top")
786                                 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
787                         else if(iter->task=="move_bottom")
788                                 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
789                         else if(iter->task=="remove")
790                                 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
791                         else if(iter->task=="set_on")
792                                 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
793                         else if(iter->task=="set_off")
794                                 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
795                         else if(iter->task=="duplicate")
796                                 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
797                         else if(iter->task=="remove")
798                                 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
799                         else
800                         {
801                                 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
802                                 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
803                                 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
804                         }
805 */
806                         menu->items().push_back(
807                                 Gtk::Menu_Helpers::ImageMenuElem(
808                                         iter->local_name,
809                                         *image,
810                                         sigc::bind(
811                                                 sigc::bind(
812                                                         sigc::mem_fun(
813                                                                 *const_cast<studio::Instance*>(this),
814                                                                 &studio::Instance::process_action
815                                                         ),
816                                                         param_list2
817                                                 ),
818                                                 iter->name
819                                         )
820                                 )
821                         );
822                 }
823         }
824
825         for(iter=canidate_list.begin();iter!=canidate_list.end();++iter)
826         {
827                 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
828                 {
829                         Gtk::Image* image(manage(new Gtk::Image()));
830                         Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
831 /*                      if(iter->task=="raise")
832                                 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
833                         else if(iter->task=="lower")
834                                 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
835                         else if(iter->task=="move_top")
836                                 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
837                         else if(iter->task=="move_bottom")
838                                 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
839                         else if(iter->task=="remove")
840                                 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
841                         else if(iter->task=="set_on")
842                                 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
843                         else if(iter->task=="set_off")
844                                 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
845                         else if(iter->task=="duplicate")
846                                 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
847                         else if(iter->task=="remove")
848                                 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
849                         else
850                         {
851                                 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
852                                 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
853                                 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
854                         }
855 */
856                         menu->items().push_back(
857                                 Gtk::Menu_Helpers::ImageMenuElem(
858                                         iter->local_name,
859                                         *image,
860                                         sigc::bind(
861                                                 sigc::bind(
862                                                         sigc::mem_fun(
863                                                                 *const_cast<studio::Instance*>(this),
864                                                                 &studio::Instance::process_action
865                                                         ),
866                                                         param_list
867                                                 ),
868                                                 iter->name
869                                         )
870                                 )
871                         );
872                 }
873         }
874 }
875
876 void
877 Instance::process_action(String name, synfigapp::Action::ParamList param_list)
878 {
879         assert(synfigapp::Action::book().count(name));
880
881         synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second);
882         
883         synfigapp::Action::Handle action(entry.factory());
884
885         if(!action)
886         {
887                 synfig::error("Bad Action");
888                 return;
889         }
890         
891         action->set_param_list(param_list);
892
893         synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab());
894         synfigapp::Action::ParamVocab::const_iterator iter;
895         
896         for(iter=param_vocab.begin();iter!=param_vocab.end();++iter)
897         {
898                 if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion()))
899                         continue;
900
901                 // If the parameter is optionally user-supplied,
902                 // and has not been already provided in the param_list,
903                 // then we should go ahead and see if we can
904                 // provide that data.
905                 if(iter->get_user_supplied() && param_list.count(iter->get_name())==0)
906                 {
907                         switch(iter->get_type())
908                         {
909                         case synfigapp::Action::Param::TYPE_STRING:
910                         {
911                                 String str;
912                                 if(!studio::App::dialog_entry(entry.local_name, iter->get_local_name()+":"+iter->get_desc(),str))
913                                         return;
914                                 action->set_param(iter->get_name(),str);
915                                 break;
916                         }
917                         default:
918                                 synfig::error("Unsupported user-supplied action parameter");
919                                 return;
920                                 break;
921                         }
922                 }
923         }
924                 
925         if(!action->is_ready())
926         {
927                 synfig::error("Action not ready");
928                 return;
929         }
930
931         perform_action(action);
932 }
933
934 void
935 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location)
936 {
937         Gtk::Menu& parammenu(*menu);
938         
939         etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
940         
941         if(!canvas_interface)
942                 return;
943         
944         synfigapp::Action::ParamList param_list,param_list2;
945         param_list=canvas_interface->generate_param_list(value_desc);
946         param_list.add("origin",location);
947
948         if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
949         {
950                 param_list2=canvas_interface->generate_param_list(
951                         synfigapp::ValueDesc(
952                                 ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
953                                 ,0
954                         )
955                 );
956                 param_list2.add("origin",location);
957         }
958
959         
960         // Populate the convert menu by looping through
961         // the ValueNode book and find the ones that are
962         // relevant.
963         {
964                 Gtk::Menu *convert_menu=manage(new Gtk::Menu());
965                 LinkableValueNode::Book::const_iterator iter;
966                 for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
967                 {
968                         if(iter->second.check_type(value_desc.get_value_type()))
969                         {
970                                 convert_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->second.local_name,
971                                         sigc::hide_return(
972                                                 sigc::bind(
973                                                         sigc::bind(
974                                                                 sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
975                                                                 iter->first
976                                                         ),
977                                                         value_desc
978                                                 )
979                                         )
980                                 ));
981                         }
982                 }
983
984                 parammenu.items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::Stock::CONVERT,*convert_menu));                       
985         }
986
987         if(param_list2.empty())
988                 add_actions_to_menu(&parammenu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
989         else
990                 add_actions_to_menu(&parammenu, param_list2,param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
991
992         if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
993         {
994                 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
995         }
996
997         if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
998         {
999                 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
1000                 
1001                 try
1002                 {
1003                         WaypointList::iterator iter(value_node->find(canvas->get_time()));
1004                         parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
1005                                 sigc::bind(
1006                                         sigc::bind(
1007                                                 sigc::bind(
1008                                                         sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked),
1009                                                         -1
1010                                                 ),
1011                                                 *iter
1012                                         ),
1013                                         value_desc
1014                                 )
1015                         ));
1016                 }
1017                 catch(...)
1018                 {
1019                 }
1020         }
1021 }
1022
1023 void
1024 edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1025 {
1026         etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1027
1028         Gtk::Dialog dialog(
1029                 "Edit Multiple Waypoints",              // Title
1030                 true,           // Modal
1031                 true            // use_separator
1032         );
1033
1034         Widget_WaypointModel widget_waypoint_model;
1035         widget_waypoint_model.show();
1036
1037         dialog.get_vbox()->pack_start(widget_waypoint_model);
1038         
1039         
1040         dialog.add_button(Gtk::StockID("gtk-apply"),1);
1041         dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1042         dialog.show();
1043         
1044         DEBUGPOINT();
1045         if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1046                 return;
1047         DEBUGPOINT();
1048         synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1049
1050         std::list<synfigapp::ValueDesc>::iterator iter;
1051         for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1052         {
1053                 synfigapp::ValueDesc value_desc(*iter);
1054                 
1055                 if(!value_desc.is_valid())
1056                         continue;
1057
1058                 ValueNode_Animated::Handle value_node;
1059                 
1060                 // If this value isn't a ValueNode_Animated, but
1061                 // it is somewhat constant, then go ahead and convert
1062                 // it to a ValueNode_Animated.
1063                 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1064                 {
1065                         ValueBase value;
1066                         if(value_desc.is_value_node())
1067                                 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1068                         else
1069                                 value=value_desc.get_value();
1070                         
1071                         value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1072                         
1073                         synfigapp::Action::Handle action;
1074                         
1075                         if(!value_desc.is_value_node())
1076                         {
1077                                 action=synfigapp::Action::create("value_desc_connect");
1078                                 action->set_param("dest",value_desc);
1079                                 action->set_param("src",ValueNode::Handle(value_node));
1080                         }
1081                         else
1082                         {
1083                                 action=synfigapp::Action::create("value_node_replace");
1084                                 action->set_param("dest",value_desc.get_value_node());
1085                                 action->set_param("src",ValueNode::Handle(value_node));
1086                         }
1087                         
1088                         action->set_param("canvas",canvas_view->get_canvas());
1089                         action->set_param("canvas_interface",canvas_interface);
1090                         
1091         
1092                         if(!canvas_interface->get_instance()->perform_action(action))
1093                         {
1094                                 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1095                                 group.cancel();
1096                                 return;
1097                         }
1098                 }
1099                 else
1100                 {
1101                         if(value_desc.is_value_node())
1102                                 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1103                 }
1104                 
1105                 
1106                 if(value_node)
1107                 {
1108                         
1109                         synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set_smart"));
1110
1111                         if(!action)
1112                         {
1113                                 canvas_view->get_ui_interface()->error(_("Unable to find waypoint_set_smart action"));
1114                                 group.cancel();
1115                                 return;
1116                         }
1117                                 
1118
1119                         action->set_param("canvas",canvas_view->get_canvas());
1120                         action->set_param("canvas_interface",canvas_interface);                 
1121                         action->set_param("value_node",ValueNode::Handle(value_node));                  
1122                         action->set_param("time",canvas_interface->get_time());                                         
1123                         action->set_param("model",widget_waypoint_model.get_waypoint_model());
1124                 
1125                         if(!canvas_interface->get_instance()->perform_action(action))
1126                         {
1127                                 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1128                                 group.cancel();
1129                                 return;
1130                         }
1131                 }
1132                 else
1133                 {
1134                         //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1135                         //group.cancel();
1136                         //return;
1137                 }
1138                         
1139         }
1140 }
1141
1142 void
1143 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1144 {
1145         etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1146
1147         synfigapp::Action::ParamList param_list;
1148         param_list=canvas_interface->generate_param_list(value_desc_list);
1149
1150         add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1151
1152         // Add the edit waypoints option if that might be useful
1153         if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1154         {
1155                 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1156                         sigc::bind(
1157                                 sigc::bind(
1158                                         sigc::ptr_fun(
1159                                                 &edit_several_waypoints
1160                                         ),
1161                                         value_desc_list
1162                                 ),
1163                                 find_canvas_view(canvas)
1164                         )
1165                 ));
1166         }
1167 }