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