1 /* === S Y N F I G ========================================================= */
2 /*! \file gtkmm/instance.cpp
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
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.
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.
21 /* ========================================================================= */
23 /* === H E A D E R S ======================================================= */
34 #include <gtkmm/stock.h>
35 #include <gtkmm/image.h>
37 #include <gtkmm/button.h>
38 #include "canvasview.h"
40 #include <sigc++/signal.h>
41 #include <sigc++/adaptors/hide.h>
43 #include "onemoment.h"
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"
60 using namespace synfig;
61 using namespace studio;
64 /* === M A C R O S ========================================================= */
66 /* === G L O B A L S ======================================================= */
68 int studio::Instance::instance_count_=0;
70 /* === P R O C E D U R E S ================================================= */
72 /* === M E T H O D S ======================================================= */
74 Instance::Instance(Canvas::Handle canvas):
75 synfigapp::Instance (canvas),
76 // canvas_tree_store_ (Gtk::TreeStore::create(CanvasTreeModel())),
77 // canvas_tree_store_ (Gtk::TreeStore::create()),
78 history_tree_store_ (HistoryTreeStore::create(this)),
82 CanvasTreeModel model;
83 canvas_tree_store_=Gtk::TreeStore::create(model);
85 id_=instance_count_++;
87 // Connect up all the signals
88 signal_filename_changed().connect(sigc::mem_fun(*this,&studio::Instance::update_all_titles));
89 signal_unsaved_status_changed().connect(sigc::hide(sigc::mem_fun(*this,&studio::Instance::update_all_titles)));
90 signal_undo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_undo_status));
91 signal_redo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_redo_status));
93 signal_saved().connect(
96 studio::AutoRecover::auto_backup
101 canvas_tree_store_=Gtk::TreeStore::create(canvas_tree_model);
103 refresh_canvas_tree();
106 Instance::~Instance()
111 Instance::get_visible_canvases()const
114 CanvasViewList::const_iterator iter;
115 for(iter=canvas_view_list_.begin();iter!=canvas_view_list_.end();++iter)
116 if((*iter)->is_visible())
122 Instance::create(Canvas::Handle canvas)
124 // Construct a new instance
125 handle<Instance> instance(new Instance(canvas));
127 // Add the new instance to the application's instance list
128 App::instance_list.push_back(instance);
130 // Set up the instance with the default UI manager
131 instance->synfigapp::Instance::set_ui_interface(App::get_ui_interface());
133 // Signal the new instance
134 App::signal_instance_created()(instance);
136 // And then make sure that is has been selected
137 App::set_selected_instance(instance);
139 // Create the initial window for the root canvas
140 instance->focus(canvas);
146 Instance::find_canvas_view(Canvas::Handle canvas)
151 while(canvas->is_inline())
152 canvas=canvas->parent();
154 CanvasViewList::iterator iter;
156 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
157 if((*iter)->get_canvas()==canvas)
160 return CanvasView::create(this,canvas);
164 Instance::focus(Canvas::Handle canvas)
166 handle<CanvasView> canvas_view=find_canvas_view(canvas);
168 canvas_view->present();
172 Instance::set_undo_status(bool x)
175 App::toolbox->update_undo_redo();
176 signal_undo_redo_status_changed()();
180 Instance::set_redo_status(bool x)
183 App::toolbox->update_undo_redo();
184 signal_undo_redo_status_changed()();
188 studio::Instance::save_as(const synfig::String &file_name)
190 if(synfigapp::Instance::save_as(file_name))
192 // after changing the filename, update the render settings with the new filename
193 list<handle<CanvasView> >::iterator iter;
194 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
195 (*iter)->render_settings.set_entry_filename();
196 App::add_recent_file(file_name);
203 studio::Instance::save()
205 // the filename will be set to "Synfig Animation 1" or some such when first created
206 // and will be changed to an absolute path once it has been saved
207 // so if it still begins with "Synfig Animation " then we need to ask where to save it
208 if(get_file_name().find(DEFAULT_FILENAME_PREFIX)==0)
209 return dialog_save_as();
211 return synfigapp::Instance::save();
215 studio::Instance::dialog_save_as()
217 string filename=basename(get_file_name());
218 Canvas::Handle canvas(get_canvas());
221 OneMoment one_moment;
222 std::set<Node*>::iterator iter;
223 for(iter=canvas->parent_set.begin();iter!=canvas->parent_set.end();++iter)
225 synfig::Node* node(*iter);
226 for(;!node->parent_set.empty();node=*node->parent_set.begin())
228 Layer::Handle parent_layer(dynamic_cast<Layer*>(node));
229 if(parent_layer && parent_layer->get_canvas()->get_root()!=get_canvas())
231 App::dialog_error_blocking("SaveAs - Error",
232 "There is currently a bug when using \"SaveAs\"\n"
233 "on a composition that is being referenced by other\n"
234 "files that are currently open. Close these\n"
235 "other files first before trying to use \"SaveAs\"."
246 // show the canvas' name if it has one, else its ID
247 while(App::dialog_save_file(_("Choose a Filename to Save As") +
249 (canvas->get_name().empty()
251 : canvas->get_name()) +
254 // If the filename still has wildcards, then we should
255 // continue looking for the file we want
256 if(find(filename.begin(),filename.end(),'*')!=filename.end())
259 std::string base = basename(filename);
260 if(find(base.begin(),base.end(),'.')==base.end())
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?")))
279 // if stat() succeeds, or it fails with something other than 'file doesn't exist', the file exists
280 // if the file exists and the user doesn't want to overwrite it, keep prompting for a filename
281 if ((stat(filename.c_str(), &s) != -1 || errno != ENOENT) &&
282 !App::dialog_yes_no("File exists",
285 "' already exists.\n\n"
286 "Do you want to replace it with the file you are saving?"))
290 if(save_as(filename))
293 App::dialog_error_blocking("SaveAs - Error","Unable to save file");
300 Instance::update_all_titles()
302 list<handle<CanvasView> >::iterator iter;
303 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
304 (*iter)->update_title();
310 // This will increase the reference count so we don't get DELETED
311 // until we are ready
312 handle<Instance> me(this);
314 // Make sure we aren't selected as the current instance
315 if(studio::App::get_selected_instance()==this)
316 studio::App::set_selected_instance(0);
318 // Turn-off/clean-up auto recovery
319 studio::App::auto_recover->clear_backup(get_canvas());
321 // Remove us from the active instance list
322 std::list<etl::handle<studio::Instance> >::iterator iter;
323 for(iter=studio::App::instance_list.begin();iter!=studio::App::instance_list.end();iter++)
326 assert(iter!=studio::App::instance_list.end());
327 if(iter!=studio::App::instance_list.end())
328 studio::App::instance_list.erase(iter);
330 // Send out a signal that we are being deleted
331 studio::App::signal_instance_deleted()(this);
333 // Hide all of the canvas views
334 for(std::list<etl::handle<CanvasView> >::iterator iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
337 // Consume pending events before deleting the canvas views
338 while(studio::App::events_pending())studio::App::iteration(false);
340 // Delete all of the canvas views
341 canvas_view_list().clear();
343 // If there is another open instance to select,
344 // go ahead and do so. If not, never mind.
345 if(studio::App::instance_list.empty())
347 studio::App::set_selected_canvas_view(0);
348 studio::App::set_selected_instance(0);
352 studio::App::set_selected_canvas_view(studio::App::instance_list.front()->canvas_view_list().front());
353 //studio::App::set_selected_instance(studio::App::instance_list.front());
359 Instance::insert_canvas(Gtk::TreeRow row,Canvas::Handle canvas)
361 CanvasTreeModel canvas_tree_model;
364 row[canvas_tree_model.icon] = Gtk::Button().render_icon(Gtk::StockID("synfig-canvas"),Gtk::ICON_SIZE_SMALL_TOOLBAR);
365 row[canvas_tree_model.id] = canvas->get_id();
366 row[canvas_tree_model.name] = canvas->get_name();
367 if(canvas->is_root())
368 row[canvas_tree_model.label] = basename(canvas->get_file_name());
370 if(!canvas->get_id().empty())
371 row[canvas_tree_model.label] = canvas->get_id();
373 if(!canvas->get_name().empty())
374 row[canvas_tree_model.label] = canvas->get_name();
376 row[canvas_tree_model.label] = _("[Unnamed]");
378 row[canvas_tree_model.canvas] = canvas;
379 row[canvas_tree_model.is_canvas] = true;
380 row[canvas_tree_model.is_value_node] = false;
383 synfig::Canvas::Children::iterator iter;
384 synfig::Canvas::Children &children(canvas->children());
386 for(iter=children.begin();iter!=children.end();iter++)
387 insert_canvas(*(canvas_tree_store()->append(row.children())),*iter);
391 if(!canvas->value_node_list().empty())
393 Gtk::TreeRow valuenode_row = *(canvas_tree_store()->append(row.children()));
395 valuenode_row[canvas_tree_model.label] = "<defs>";
396 valuenode_row[canvas_tree_model.canvas] = canvas;
397 valuenode_row[canvas_tree_model.is_canvas] = false;
398 valuenode_row[canvas_tree_model.is_value_node] = false;
400 synfig::ValueNodeList::iterator iter;
401 synfig::ValueNodeList &value_node_list(canvas->value_node_list());
403 for(iter=value_node_list.begin();iter!=value_node_list.end();iter++)
404 insert_value_node(*(canvas_tree_store()->append(valuenode_row.children())),canvas,*iter);
412 Instance::insert_value_node(Gtk::TreeRow row,Canvas::Handle canvas,etl::handle<synfig::ValueNode> value_node)
414 CanvasTreeModel canvas_tree_model;
418 row[canvas_tree_model.id] = value_node->get_id();
419 row[canvas_tree_model.name] = value_node->get_id();
420 row[canvas_tree_model.label] = value_node->get_id();
421 row[canvas_tree_model.canvas] = canvas;
422 row[canvas_tree_model.value_node] = value_node;
423 row[canvas_tree_model.is_canvas] = false;
424 row[canvas_tree_model.is_value_node] = true;
425 row[canvas_tree_model.icon] = Gtk::Button().render_icon(valuenode_icon(value_node),Gtk::ICON_SIZE_SMALL_TOOLBAR);
430 Instance::refresh_canvas_tree()
432 canvas_tree_store()->clear();
433 Gtk::TreeRow row = *(canvas_tree_store()->prepend());
434 insert_canvas(row,get_canvas());
438 Instance::dialog_cvs_commit()
440 calc_repository_info();
443 App::dialog_error_blocking(_("Error"),_("You must first add this composition to the repository"));
450 if(synfigapp::Instance::get_action_count())
452 if(!App::dialog_yes_no(_("CVS Commit"), _("This will save any changes you have made. Are you sure?")))
459 App::dialog_error_blocking(_("Error"),_("The local copy of the file hasn't been changed since the last update.\nNothing to commit!"));
463 if(!App::dialog_entry(_("CVS Commit"),_("Enter a log message describing the changes you have made"), message))
466 OneMoment one_moment;
471 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to COMMIT"));
477 Instance::dialog_cvs_add()
479 calc_repository_info();
482 App::dialog_error_blocking(_("Error"),_("This composition has already been added to the repository"));
489 //if(!App::dialog_entry(_("CVS Add"),_("Enter a log message describing the file"), message))
491 OneMoment one_moment;
496 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to ADD"));
502 Instance::dialog_cvs_update()
504 calc_repository_info();
507 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to update from!"));
512 App::dialog_error_blocking(_("Info"),_("This file is up-to-date"));
518 String filename(get_file_name());
519 if(synfigapp::Instance::get_action_count())
521 if(!App::dialog_yes_no(_("CVS Update"), _("This will save any changes you have made. Are you sure?")))
525 OneMoment one_moment;
526 time_t oldtime=get_original_timestamp();
528 calc_repository_info();
529 // If something has been updated...
530 if(oldtime!=get_original_timestamp())
537 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to UPDATE"));
539 //update_all_titles();
543 Instance::dialog_cvs_revert()
545 calc_repository_info();
548 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to revert to!"));
553 String filename(get_file_name());
554 if(!App::dialog_yes_no(_("CVS Revert"),
555 _("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?")
559 OneMoment one_moment;
561 // Remove the old file
562 if(remove(get_file_name().c_str())!=0)
564 App::dialog_error_blocking(_("Error"),_("Unable to remove previous version"));
573 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to UPDATE"));
575 //update_all_titles();
579 Instance::_revert(Instance *instance)
581 OneMoment one_moment;
583 String filename(instance->get_file_name());
585 Canvas::Handle canvas(instance->get_canvas());
589 if(canvas->count()!=1)
592 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."));
603 // Schedule a revert to occur in a few moments
604 Glib::signal_timeout().connect(
607 sigc::ptr_fun(&Instance::_revert),
617 Instance::safe_revert()
619 if(synfigapp::Instance::get_action_count())
620 if(!App::dialog_yes_no(_("Revert to saved"), _("You will lose any changes you have made since your last save.\nAre you sure?")))
627 Instance::safe_close()
629 handle<synfigapp::UIInterface> uim;
630 uim=find_canvas_view(get_canvas())->get_ui_interface();
632 if(get_action_count())
635 string str=strprintf(_("Would you like to save your changes to %s?"),basename(get_file_name()).c_str() );
636 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
637 if(answer==synfigapp::UIInterface::RESPONSE_YES)
639 if(answer==synfigapp::UIInterface::RESPONSE_NO)
641 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
647 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());
648 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
650 if(answer==synfigapp::UIInterface::RESPONSE_YES)
652 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
663 Instance::add_actions_to_group(const Glib::RefPtr<Gtk::ActionGroup>& action_group, synfig::String& ui_info, const synfigapp::Action::ParamList ¶m_list, synfigapp::Action::Category category)const
665 synfigapp::Action::CandidateList candidate_list;
666 synfigapp::Action::CandidateList::iterator iter;
668 candidate_list=compile_candidate_list(param_list,category);
670 candidate_list.sort();
672 if(candidate_list.empty())
673 synfig::warning("Action CandidateList is empty!");
675 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
677 Gtk::StockID stock_id(get_action_stock_id(*iter));
679 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
681 action_group->add(Gtk::Action::create(
682 "action-"+iter->name,
684 iter->local_name,iter->local_name
689 *const_cast<studio::Instance*>(this),
690 &studio::Instance::process_action
697 ui_info+=strprintf("<menuitem action='action-%s' />",iter->name.c_str());
703 Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList ¶m_list,synfigapp::Action::Category category)const
705 synfigapp::Action::CandidateList candidate_list;
706 synfigapp::Action::CandidateList::iterator iter;
708 candidate_list=compile_candidate_list(param_list,category);
710 candidate_list.sort();
712 if(candidate_list.empty())
713 synfig::warning("Action CandidateList is empty!");
715 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
717 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
719 Gtk::Image* image(manage(new Gtk::Image()));
720 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
723 if(iter->task=="raise")
724 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
725 else if(iter->task=="lower")
726 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
727 else if(iter->task=="move_top")
728 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
729 else if(iter->task=="move_bottom")
730 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
731 else if(iter->task=="remove")
732 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
733 else if(iter->task=="set_on")
734 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
735 else if(iter->task=="set_off")
736 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
737 else if(iter->task=="duplicate")
738 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
739 else if(iter->task=="remove")
740 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
743 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
744 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
745 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
748 menu->items().push_back(
749 Gtk::Menu_Helpers::ImageMenuElem(
755 *const_cast<studio::Instance*>(this),
756 &studio::Instance::process_action
769 Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList ¶m_list,const synfigapp::Action::ParamList ¶m_list2,synfigapp::Action::Category category)const
771 synfigapp::Action::CandidateList candidate_list;
772 synfigapp::Action::CandidateList candidate_list2;
774 synfigapp::Action::CandidateList::iterator iter;
776 candidate_list=compile_candidate_list(param_list,category);
777 candidate_list2=compile_candidate_list(param_list2,category);
779 candidate_list.sort();
781 if(candidate_list.empty())
782 synfig::warning("Action CandidateList is empty!");
783 if(candidate_list2.empty())
784 synfig::warning("Action CandidateList2 is empty!");
786 // Seperate out the candidate lists so that there are no conflicts
787 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
789 synfigapp::Action::CandidateList::iterator iter2(candidate_list2.find(iter->name));
790 if(iter2!=candidate_list2.end())
791 candidate_list2.erase(iter2);
794 for(iter=candidate_list2.begin();iter!=candidate_list2.end();++iter)
796 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
798 Gtk::Image* image(manage(new Gtk::Image()));
799 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
801 /* if(iter->task=="raise")
802 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
803 else if(iter->task=="lower")
804 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
805 else if(iter->task=="move_top")
806 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
807 else if(iter->task=="move_bottom")
808 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
809 else if(iter->task=="remove")
810 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
811 else if(iter->task=="set_on")
812 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
813 else if(iter->task=="set_off")
814 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
815 else if(iter->task=="duplicate")
816 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
817 else if(iter->task=="remove")
818 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
821 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
822 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
823 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
826 menu->items().push_back(
827 Gtk::Menu_Helpers::ImageMenuElem(
833 *const_cast<studio::Instance*>(this),
834 &studio::Instance::process_action
845 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
847 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
849 Gtk::Image* image(manage(new Gtk::Image()));
850 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
851 /* if(iter->task=="raise")
852 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
853 else if(iter->task=="lower")
854 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
855 else if(iter->task=="move_top")
856 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
857 else if(iter->task=="move_bottom")
858 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
859 else if(iter->task=="remove")
860 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
861 else if(iter->task=="set_on")
862 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
863 else if(iter->task=="set_off")
864 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
865 else if(iter->task=="duplicate")
866 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
867 else if(iter->task=="remove")
868 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
871 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
872 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
873 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
876 menu->items().push_back(
877 Gtk::Menu_Helpers::ImageMenuElem(
883 *const_cast<studio::Instance*>(this),
884 &studio::Instance::process_action
897 Instance::process_action(String name, synfigapp::Action::ParamList param_list)
899 assert(synfigapp::Action::book().count(name));
901 synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second);
903 synfigapp::Action::Handle action(entry.factory());
907 synfig::error("Bad Action");
911 action->set_param_list(param_list);
913 synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab());
914 synfigapp::Action::ParamVocab::const_iterator iter;
916 for(iter=param_vocab.begin();iter!=param_vocab.end();++iter)
918 if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion()))
921 // If the parameter is optionally user-supplied,
922 // and has not been already provided in the param_list,
923 // then we should go ahead and see if we can
924 // provide that data.
925 if(iter->get_user_supplied() && param_list.count(iter->get_name())==0)
927 switch(iter->get_type())
929 case synfigapp::Action::Param::TYPE_STRING:
932 if(!studio::App::dialog_entry(entry.local_name, iter->get_local_name()+":"+iter->get_desc(),str))
934 action->set_param(iter->get_name(),str);
938 synfig::error("Unsupported user-supplied action parameter");
945 if(!action->is_ready())
947 synfig::error("Action not ready");
951 perform_action(action);
955 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location)
957 Gtk::Menu& parammenu(*menu);
959 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
961 if(!canvas_interface)
964 synfigapp::Action::ParamList param_list,param_list2;
965 param_list=canvas_interface->generate_param_list(value_desc);
966 param_list.add("origin",location);
968 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
970 param_list2=canvas_interface->generate_param_list(
971 synfigapp::ValueDesc(
972 ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
976 param_list2.add("origin",location);
980 // Populate the convert menu by looping through
981 // the ValueNode book and find the ones that are
984 Gtk::Menu *convert_menu=manage(new Gtk::Menu());
985 LinkableValueNode::Book::const_iterator iter;
986 for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
988 if(iter->second.check_type(value_desc.get_value_type()))
990 convert_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->second.local_name,
994 sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
1004 parammenu.items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::Stock::CONVERT,*convert_menu));
1007 if(param_list2.empty())
1008 add_actions_to_menu(¶mmenu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1010 add_actions_to_menu(¶mmenu, param_list2,param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1012 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1014 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
1017 if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
1019 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
1023 WaypointList::iterator iter(value_node->find(canvas->get_time()));
1024 parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
1028 sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked),
1044 edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1046 etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1049 "Edit Multiple Waypoints", // Title
1051 true // use_separator
1054 Widget_WaypointModel widget_waypoint_model;
1055 widget_waypoint_model.show();
1057 dialog.get_vbox()->pack_start(widget_waypoint_model);
1060 dialog.add_button(Gtk::StockID("gtk-apply"),1);
1061 dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1065 if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1068 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1070 std::list<synfigapp::ValueDesc>::iterator iter;
1071 for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1073 synfigapp::ValueDesc value_desc(*iter);
1075 if(!value_desc.is_valid())
1078 ValueNode_Animated::Handle value_node;
1080 // If this value isn't a ValueNode_Animated, but
1081 // it is somewhat constant, then go ahead and convert
1082 // it to a ValueNode_Animated.
1083 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1086 if(value_desc.is_value_node())
1087 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1089 value=value_desc.get_value();
1091 value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1093 synfigapp::Action::Handle action;
1095 if(!value_desc.is_value_node())
1097 action=synfigapp::Action::create("value_desc_connect");
1098 action->set_param("dest",value_desc);
1099 action->set_param("src",ValueNode::Handle(value_node));
1103 action=synfigapp::Action::create("value_node_replace");
1104 action->set_param("dest",value_desc.get_value_node());
1105 action->set_param("src",ValueNode::Handle(value_node));
1108 action->set_param("canvas",canvas_view->get_canvas());
1109 action->set_param("canvas_interface",canvas_interface);
1112 if(!canvas_interface->get_instance()->perform_action(action))
1114 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1121 if(value_desc.is_value_node())
1122 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1129 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set_smart"));
1133 canvas_view->get_ui_interface()->error(_("Unable to find waypoint_set_smart action"));
1139 action->set_param("canvas",canvas_view->get_canvas());
1140 action->set_param("canvas_interface",canvas_interface);
1141 action->set_param("value_node",ValueNode::Handle(value_node));
1142 action->set_param("time",canvas_interface->get_time());
1143 action->set_param("model",widget_waypoint_model.get_waypoint_model());
1145 if(!canvas_interface->get_instance()->perform_action(action))
1147 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1154 //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1163 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1165 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1167 synfigapp::Action::ParamList param_list;
1168 param_list=canvas_interface->generate_param_list(value_desc_list);
1170 add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1172 // Add the edit waypoints option if that might be useful
1173 if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1175 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1179 &edit_several_waypoints
1183 find_canvas_view(canvas)