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