Fix 1674142: We need to process pending events whether using close() or safe_close...
[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         while(studio::App::events_pending())studio::App::iteration(false);
326
327         // If there is another open instance to select,
328         // go ahead and do so. If not, never mind.
329         if(studio::App::instance_list.empty())
330         {
331                 studio::App::set_selected_canvas_view(0);
332                 studio::App::set_selected_instance(0);
333         }
334         else
335         {
336                 studio::App::set_selected_canvas_view(studio::App::instance_list.front()->canvas_view_list().front());
337                 //studio::App::set_selected_instance(studio::App::instance_list.front());
338         }
339 }
340
341         
342 void
343 Instance::insert_canvas(Gtk::TreeRow row,Canvas::Handle canvas)
344 {
345         CanvasTreeModel canvas_tree_model;
346         assert(canvas);
347
348         row[canvas_tree_model.icon] = Gtk::Button().render_icon(Gtk::StockID("synfig-canvas"),Gtk::ICON_SIZE_SMALL_TOOLBAR);    
349         row[canvas_tree_model.id] = canvas->get_id();
350         row[canvas_tree_model.name] = canvas->get_name();
351         if(canvas->is_root())
352                 row[canvas_tree_model.label] = basename(canvas->get_file_name());
353         else
354         if(!canvas->get_id().empty())
355                 row[canvas_tree_model.label] = canvas->get_id();
356         else
357         if(!canvas->get_name().empty())
358                 row[canvas_tree_model.label] = canvas->get_name();              
359         else
360                 row[canvas_tree_model.label] = _("[Unnamed]");          
361                 
362         row[canvas_tree_model.canvas] = canvas;
363         row[canvas_tree_model.is_canvas] = true;
364         row[canvas_tree_model.is_value_node] = false;
365
366         {
367                 synfig::Canvas::Children::iterator iter;
368                 synfig::Canvas::Children &children(canvas->children());
369         
370                 for(iter=children.begin();iter!=children.end();iter++)
371                         insert_canvas(*(canvas_tree_store()->append(row.children())),*iter);
372         }
373
374         /*
375         if(!canvas->value_node_list().empty())
376         {
377                 Gtk::TreeRow valuenode_row = *(canvas_tree_store()->append(row.children()));
378
379                 valuenode_row[canvas_tree_model.label] = "<defs>";
380                 valuenode_row[canvas_tree_model.canvas] = canvas;
381                 valuenode_row[canvas_tree_model.is_canvas] = false;
382                 valuenode_row[canvas_tree_model.is_value_node] = false;
383
384                 synfig::ValueNodeList::iterator iter;
385                 synfig::ValueNodeList &value_node_list(canvas->value_node_list());
386
387                 for(iter=value_node_list.begin();iter!=value_node_list.end();iter++)
388                         insert_value_node(*(canvas_tree_store()->append(valuenode_row.children())),canvas,*iter);
389         }
390         */
391 }
392
393
394 /*
395 void
396 Instance::insert_value_node(Gtk::TreeRow row,Canvas::Handle canvas,etl::handle<synfig::ValueNode> value_node)
397 {
398         CanvasTreeModel canvas_tree_model;
399         assert(value_node);
400         assert(canvas);
401
402         row[canvas_tree_model.id] = value_node->get_id();
403         row[canvas_tree_model.name] = value_node->get_id();
404         row[canvas_tree_model.label] = value_node->get_id();
405         row[canvas_tree_model.canvas] = canvas;
406         row[canvas_tree_model.value_node] = value_node;
407         row[canvas_tree_model.is_canvas] = false;
408         row[canvas_tree_model.is_value_node] = true;
409         row[canvas_tree_model.icon] = Gtk::Button().render_icon(valuenode_icon(value_node),Gtk::ICON_SIZE_SMALL_TOOLBAR);
410 }
411 */
412
413 void
414 Instance::refresh_canvas_tree()
415 {
416         canvas_tree_store()->clear();
417         Gtk::TreeRow row = *(canvas_tree_store()->prepend());
418         insert_canvas(row,get_canvas());
419 }
420
421 void
422 Instance::dialog_cvs_commit()
423 {
424         calc_repository_info();
425         if(!in_repository())
426         {
427                 App::dialog_error_blocking(_("Error"),_("You must first add this composition to the repository"));
428                 return;
429         }
430         try
431         {
432                 string message;
433
434                 if(synfigapp::Instance::get_action_count())
435                 {
436                         if(!App::dialog_yes_no(_("CVS Commit"), _("This will save any changes you have made. Are you sure?")))
437                                 return;
438                         save();
439                 }
440
441                 if(!is_modified())
442                 {
443                         App::dialog_error_blocking(_("Error"),_("The local copy of the file hasn't been changed since the last update.\nNothing to commit!"));
444                         return;
445                 }
446
447                 if(!App::dialog_entry(_("CVS Commit"),_("Enter a log message describing the changes you have made"), message))
448                         return;
449                 
450                 OneMoment one_moment;
451                 cvs_commit(message);
452         }
453         catch(...)
454         {
455                 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to COMMIT"));
456         }
457         update_all_titles();
458 }
459
460 void
461 Instance::dialog_cvs_add()
462 {
463         calc_repository_info();
464         if(in_repository())
465         {
466                 App::dialog_error_blocking(_("Error"),_("This composition has already been added to the repository"));
467                 return;
468         }
469         try
470         {
471                 string message;
472                 
473                 //if(!App::dialog_entry(_("CVS Add"),_("Enter a log message describing the file"), message))
474                 //      return;
475                 OneMoment one_moment;
476                 cvs_add();
477         }
478         catch(...)
479         {
480                 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to ADD"));
481         }
482         update_all_titles();
483 }
484
485 void
486 Instance::dialog_cvs_update()
487 {
488         calc_repository_info();
489         if(!in_repository())
490         {
491                 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to update from!"));
492                 return;
493         }
494         if(!is_updated())
495         {
496                 App::dialog_error_blocking(_("Info"),_("This file is up-to-date"));
497                 return;
498         }
499         
500         try
501         {
502                 String filename(get_file_name());
503                 if(synfigapp::Instance::get_action_count())
504                 {
505                         if(!App::dialog_yes_no(_("CVS Update"), _("This will save any changes you have made. Are you sure?")))
506                                 return;
507                         save();
508                 }
509                 OneMoment one_moment;
510                 time_t oldtime=get_original_timestamp();
511                 cvs_update();
512                 calc_repository_info();         
513                 // If something has been updated...
514                 if(oldtime!=get_original_timestamp())
515                 {
516                         revert();
517                 }
518         }
519         catch(...)
520         {
521                 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to UPDATE"));
522         }
523         //update_all_titles();
524 }
525
526 void
527 Instance::dialog_cvs_revert()
528 {
529         calc_repository_info();
530         if(!in_repository())
531         {
532                 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to revert to!"));
533                 return;
534         }
535         try
536         {
537                 String filename(get_file_name());
538                 if(!App::dialog_yes_no(_("CVS Revert"),
539                         _("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?")
540                 ))
541                         return;
542
543                 OneMoment one_moment;
544
545                 // Remove the old file
546                 if(remove(get_file_name().c_str())!=0)
547                 {
548                         App::dialog_error_blocking(_("Error"),_("Unable to remove previous version"));
549                         return;
550                 }
551
552                 cvs_update();
553                 revert();
554         }
555         catch(...)
556         {
557                 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to UPDATE"));
558         }
559         //update_all_titles();
560 }
561
562 void
563 Instance::_revert(Instance *instance)
564 {
565         OneMoment one_moment;
566
567         String filename(instance->get_file_name());
568
569         Canvas::Handle canvas(instance->get_canvas());
570         
571         instance->close();
572
573         if(canvas->count()!=1)
574         {
575                 one_moment.hide();
576                 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."));
577                 one_moment.show();
578         }
579         canvas=0;
580         
581         App::open(filename);
582 }
583
584 void
585 Instance::revert()
586 {
587         // Schedule a revert to occur in a few moments
588         Glib::signal_timeout().connect(
589                 sigc::bind_return(
590                         sigc::bind(
591                                 sigc::ptr_fun(&Instance::_revert),
592                                 this
593                         ),
594                         false
595                 )
596                 ,500
597         );
598 }
599
600 bool
601 Instance::safe_revert()
602 {
603         if(synfigapp::Instance::get_action_count())
604                 if(!App::dialog_yes_no(_("Revert to saved"), _("You will loose any changes you have made since your last save.\nAre you sure?")))
605                         return false;
606         revert();
607         return true;
608 }
609
610 bool
611 Instance::safe_close()
612 {
613         handle<synfigapp::UIInterface> uim;
614         uim=find_canvas_view(get_canvas())->get_ui_interface();
615
616         if(get_action_count())
617         {
618                 string str=strprintf(_("Would you like to save your changes to %s?"),basename(get_file_name()).c_str() );
619                 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
620                 if(answer==synfigapp::UIInterface::RESPONSE_YES)
621                         save();
622                 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
623                         return false;
624         }
625
626         if(is_modified())
627         {
628                 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());
629                 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
630
631                 if(answer==synfigapp::UIInterface::RESPONSE_YES)
632                         dialog_cvs_commit();
633                 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
634                         return false;
635         }
636         
637         close();
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 }