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 App::add_recent_file(file_name);
199 studio::Instance::save()
201 // the filename will be set to "Synfig Animation 1" or some such when first created
202 // and will be changed to an absolute path once it has been saved
203 // so if it still begins with "Synfig Animation " then we need to ask where to save it
204 if(get_file_name().find(DEFAULT_FILENAME_PREFIX)==0)
210 return synfigapp::Instance::save();
214 studio::Instance::dialog_save_as()
216 string filename=basename(get_file_name());
217 Canvas::Handle canvas(get_canvas());
220 OneMoment one_moment;
221 std::set<Node*>::iterator iter;
222 for(iter=canvas->parent_set.begin();iter!=canvas->parent_set.end();++iter)
224 synfig::Node* node(*iter);
225 for(;!node->parent_set.empty();node=*node->parent_set.begin())
227 Layer::Handle parent_layer(dynamic_cast<Layer*>(node));
228 if(parent_layer && parent_layer->get_canvas()->get_root()!=get_canvas())
230 App::dialog_error_blocking("SaveAs - Error",
231 "There is currently a bug when using \"SaveAs\"\n"
232 "on a composition that is being referenced by other\n"
233 "files that are currently open. Close these\n"
234 "other files first before trying to use \"SaveAs\"."
245 // show the canvas' name if it has one, else its ID
246 while(App::dialog_save_file(_("Choose a Filename to Save As") +
248 (canvas->get_name().empty()
250 : canvas->get_name()) +
253 // If the filename still has wildcards, then we should
254 // continue looking for the file we want
255 if(find(filename.begin(),filename.end(),'*')!=filename.end())
258 std::string base = basename(filename);
259 if(find(base.begin(),base.end(),'.')==base.end())
264 String ext(String(filename.begin()+filename.find_last_of('.')+1,filename.end()));
265 if(ext!="sif" && ext!="sifz" && !App::dialog_yes_no(_("Unknown extension"),
266 _("You have given the file name an extension\nwhich I do not recognise. Are you sure this is what you want?")))
278 // if stat() succeeds, or it fails with something other than 'file doesn't exist', the file exists
279 // if the file exists and the user doesn't want to overwrite it, keep prompting for a filename
280 if ((stat(filename.c_str(), &s) != -1 || errno != ENOENT) &&
281 !App::dialog_yes_no("File exists",
284 "' already exists.\n\n"
285 "Do you want to replace it with the file you are saving?"))
289 if(save_as(filename))
292 App::dialog_error_blocking("SaveAs - Error","Unable to save file");
297 Instance::update_all_titles()
299 list<handle<CanvasView> >::iterator iter;
300 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
301 (*iter)->update_title();
307 // This will increase the reference count so we don't get DELETED
308 // until we are ready
309 handle<Instance> me(this);
311 // Make sure we aren't selected as the current instance
312 if(studio::App::get_selected_instance()==this)
313 studio::App::set_selected_instance(0);
315 // Turn-off/clean-up auto recovery
316 studio::App::auto_recover->clear_backup(get_canvas());
318 // Remove us from the active instance list
319 std::list<etl::handle<studio::Instance> >::iterator iter;
320 for(iter=studio::App::instance_list.begin();iter!=studio::App::instance_list.end();iter++)
323 assert(iter!=studio::App::instance_list.end());
324 if(iter!=studio::App::instance_list.end())
325 studio::App::instance_list.erase(iter);
327 // Send out a signal that we are being deleted
328 studio::App::signal_instance_deleted()(this);
330 // Hide all of the canvas views
331 for(std::list<etl::handle<CanvasView> >::iterator iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
334 // Consume pending events before deleting the canvas views
335 while(studio::App::events_pending())studio::App::iteration(false);
337 // Delete all of the canvas views
338 canvas_view_list().clear();
340 // If there is another open instance to select,
341 // go ahead and do so. If not, never mind.
342 if(studio::App::instance_list.empty())
344 studio::App::set_selected_canvas_view(0);
345 studio::App::set_selected_instance(0);
349 studio::App::set_selected_canvas_view(studio::App::instance_list.front()->canvas_view_list().front());
350 //studio::App::set_selected_instance(studio::App::instance_list.front());
356 Instance::insert_canvas(Gtk::TreeRow row,Canvas::Handle canvas)
358 CanvasTreeModel canvas_tree_model;
361 row[canvas_tree_model.icon] = Gtk::Button().render_icon(Gtk::StockID("synfig-canvas"),Gtk::ICON_SIZE_SMALL_TOOLBAR);
362 row[canvas_tree_model.id] = canvas->get_id();
363 row[canvas_tree_model.name] = canvas->get_name();
364 if(canvas->is_root())
365 row[canvas_tree_model.label] = basename(canvas->get_file_name());
367 if(!canvas->get_id().empty())
368 row[canvas_tree_model.label] = canvas->get_id();
370 if(!canvas->get_name().empty())
371 row[canvas_tree_model.label] = canvas->get_name();
373 row[canvas_tree_model.label] = _("[Unnamed]");
375 row[canvas_tree_model.canvas] = canvas;
376 row[canvas_tree_model.is_canvas] = true;
377 row[canvas_tree_model.is_value_node] = false;
380 synfig::Canvas::Children::iterator iter;
381 synfig::Canvas::Children &children(canvas->children());
383 for(iter=children.begin();iter!=children.end();iter++)
384 insert_canvas(*(canvas_tree_store()->append(row.children())),*iter);
388 if(!canvas->value_node_list().empty())
390 Gtk::TreeRow valuenode_row = *(canvas_tree_store()->append(row.children()));
392 valuenode_row[canvas_tree_model.label] = "<defs>";
393 valuenode_row[canvas_tree_model.canvas] = canvas;
394 valuenode_row[canvas_tree_model.is_canvas] = false;
395 valuenode_row[canvas_tree_model.is_value_node] = false;
397 synfig::ValueNodeList::iterator iter;
398 synfig::ValueNodeList &value_node_list(canvas->value_node_list());
400 for(iter=value_node_list.begin();iter!=value_node_list.end();iter++)
401 insert_value_node(*(canvas_tree_store()->append(valuenode_row.children())),canvas,*iter);
409 Instance::insert_value_node(Gtk::TreeRow row,Canvas::Handle canvas,etl::handle<synfig::ValueNode> value_node)
411 CanvasTreeModel canvas_tree_model;
415 row[canvas_tree_model.id] = value_node->get_id();
416 row[canvas_tree_model.name] = value_node->get_id();
417 row[canvas_tree_model.label] = value_node->get_id();
418 row[canvas_tree_model.canvas] = canvas;
419 row[canvas_tree_model.value_node] = value_node;
420 row[canvas_tree_model.is_canvas] = false;
421 row[canvas_tree_model.is_value_node] = true;
422 row[canvas_tree_model.icon] = Gtk::Button().render_icon(valuenode_icon(value_node),Gtk::ICON_SIZE_SMALL_TOOLBAR);
427 Instance::refresh_canvas_tree()
429 canvas_tree_store()->clear();
430 Gtk::TreeRow row = *(canvas_tree_store()->prepend());
431 insert_canvas(row,get_canvas());
435 Instance::dialog_cvs_commit()
437 calc_repository_info();
440 App::dialog_error_blocking(_("Error"),_("You must first add this composition to the repository"));
447 if(synfigapp::Instance::get_action_count())
449 if(!App::dialog_yes_no(_("CVS Commit"), _("This will save any changes you have made. Are you sure?")))
456 App::dialog_error_blocking(_("Error"),_("The local copy of the file hasn't been changed since the last update.\nNothing to commit!"));
460 if(!App::dialog_entry(_("CVS Commit"),_("Enter a log message describing the changes you have made"), message))
463 OneMoment one_moment;
468 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to COMMIT"));
474 Instance::dialog_cvs_add()
476 calc_repository_info();
479 App::dialog_error_blocking(_("Error"),_("This composition has already been added to the repository"));
486 //if(!App::dialog_entry(_("CVS Add"),_("Enter a log message describing the file"), message))
488 OneMoment one_moment;
493 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to ADD"));
499 Instance::dialog_cvs_update()
501 calc_repository_info();
504 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to update from!"));
509 App::dialog_error_blocking(_("Info"),_("This file is up-to-date"));
515 String filename(get_file_name());
516 if(synfigapp::Instance::get_action_count())
518 if(!App::dialog_yes_no(_("CVS Update"), _("This will save any changes you have made. Are you sure?")))
522 OneMoment one_moment;
523 time_t oldtime=get_original_timestamp();
525 calc_repository_info();
526 // If something has been updated...
527 if(oldtime!=get_original_timestamp())
534 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to UPDATE"));
536 //update_all_titles();
540 Instance::dialog_cvs_revert()
542 calc_repository_info();
545 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to revert to!"));
550 String filename(get_file_name());
551 if(!App::dialog_yes_no(_("CVS Revert"),
552 _("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?")
556 OneMoment one_moment;
558 // Remove the old file
559 if(remove(get_file_name().c_str())!=0)
561 App::dialog_error_blocking(_("Error"),_("Unable to remove previous version"));
570 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to UPDATE"));
572 //update_all_titles();
576 Instance::_revert(Instance *instance)
578 OneMoment one_moment;
580 String filename(instance->get_file_name());
582 Canvas::Handle canvas(instance->get_canvas());
586 if(canvas->count()!=1)
589 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."));
600 // Schedule a revert to occur in a few moments
601 Glib::signal_timeout().connect(
604 sigc::ptr_fun(&Instance::_revert),
614 Instance::safe_revert()
616 if(synfigapp::Instance::get_action_count())
617 if(!App::dialog_yes_no(_("Revert to saved"), _("You will lose any changes you have made since your last save.\nAre you sure?")))
624 Instance::safe_close()
626 handle<synfigapp::UIInterface> uim;
627 uim=find_canvas_view(get_canvas())->get_ui_interface();
629 if(get_action_count())
631 string str=strprintf(_("Would you like to save your changes to %s?"),basename(get_file_name()).c_str() );
632 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
633 if(answer==synfigapp::UIInterface::RESPONSE_YES)
635 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
641 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());
642 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
644 if(answer==synfigapp::UIInterface::RESPONSE_YES)
646 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
657 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
659 synfigapp::Action::CandidateList candidate_list;
660 synfigapp::Action::CandidateList::iterator iter;
662 candidate_list=compile_candidate_list(param_list,category);
664 candidate_list.sort();
666 if(candidate_list.empty())
667 synfig::warning("Action CandidateList is empty!");
669 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
671 Gtk::StockID stock_id(get_action_stock_id(*iter));
673 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
675 action_group->add(Gtk::Action::create(
676 "action-"+iter->name,
678 iter->local_name,iter->local_name
683 *const_cast<studio::Instance*>(this),
684 &studio::Instance::process_action
691 ui_info+=strprintf("<menuitem action='action-%s' />",iter->name.c_str());
697 Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList ¶m_list,synfigapp::Action::Category category)const
699 synfigapp::Action::CandidateList candidate_list;
700 synfigapp::Action::CandidateList::iterator iter;
702 candidate_list=compile_candidate_list(param_list,category);
704 candidate_list.sort();
706 if(candidate_list.empty())
707 synfig::warning("Action CandidateList is empty!");
709 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
711 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
713 Gtk::Image* image(manage(new Gtk::Image()));
714 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
717 if(iter->task=="raise")
718 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
719 else if(iter->task=="lower")
720 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
721 else if(iter->task=="move_top")
722 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
723 else if(iter->task=="move_bottom")
724 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
725 else if(iter->task=="remove")
726 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
727 else if(iter->task=="set_on")
728 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
729 else if(iter->task=="set_off")
730 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
731 else if(iter->task=="duplicate")
732 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
733 else if(iter->task=="remove")
734 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
737 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
738 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
739 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
742 menu->items().push_back(
743 Gtk::Menu_Helpers::ImageMenuElem(
749 *const_cast<studio::Instance*>(this),
750 &studio::Instance::process_action
763 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
765 synfigapp::Action::CandidateList candidate_list;
766 synfigapp::Action::CandidateList candidate_list2;
768 synfigapp::Action::CandidateList::iterator iter;
770 candidate_list=compile_candidate_list(param_list,category);
771 candidate_list2=compile_candidate_list(param_list2,category);
773 candidate_list.sort();
775 if(candidate_list.empty())
776 synfig::warning("Action CandidateList is empty!");
777 if(candidate_list2.empty())
778 synfig::warning("Action CandidateList2 is empty!");
780 // Seperate out the candidate lists so that there are no conflicts
781 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
783 synfigapp::Action::CandidateList::iterator iter2(candidate_list2.find(iter->name));
784 if(iter2!=candidate_list2.end())
785 candidate_list2.erase(iter2);
788 for(iter=candidate_list2.begin();iter!=candidate_list2.end();++iter)
790 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
792 Gtk::Image* image(manage(new Gtk::Image()));
793 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
795 /* if(iter->task=="raise")
796 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
797 else if(iter->task=="lower")
798 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
799 else if(iter->task=="move_top")
800 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
801 else if(iter->task=="move_bottom")
802 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
803 else if(iter->task=="remove")
804 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
805 else if(iter->task=="set_on")
806 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
807 else if(iter->task=="set_off")
808 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
809 else if(iter->task=="duplicate")
810 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
811 else if(iter->task=="remove")
812 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
815 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
816 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
817 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
820 menu->items().push_back(
821 Gtk::Menu_Helpers::ImageMenuElem(
827 *const_cast<studio::Instance*>(this),
828 &studio::Instance::process_action
839 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
841 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
843 Gtk::Image* image(manage(new Gtk::Image()));
844 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
845 /* if(iter->task=="raise")
846 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
847 else if(iter->task=="lower")
848 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
849 else if(iter->task=="move_top")
850 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
851 else if(iter->task=="move_bottom")
852 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
853 else if(iter->task=="remove")
854 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
855 else if(iter->task=="set_on")
856 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
857 else if(iter->task=="set_off")
858 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
859 else if(iter->task=="duplicate")
860 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
861 else if(iter->task=="remove")
862 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
865 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
866 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
867 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
870 menu->items().push_back(
871 Gtk::Menu_Helpers::ImageMenuElem(
877 *const_cast<studio::Instance*>(this),
878 &studio::Instance::process_action
891 Instance::process_action(String name, synfigapp::Action::ParamList param_list)
893 assert(synfigapp::Action::book().count(name));
895 synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second);
897 synfigapp::Action::Handle action(entry.factory());
901 synfig::error("Bad Action");
905 action->set_param_list(param_list);
907 synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab());
908 synfigapp::Action::ParamVocab::const_iterator iter;
910 for(iter=param_vocab.begin();iter!=param_vocab.end();++iter)
912 if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion()))
915 // If the parameter is optionally user-supplied,
916 // and has not been already provided in the param_list,
917 // then we should go ahead and see if we can
918 // provide that data.
919 if(iter->get_user_supplied() && param_list.count(iter->get_name())==0)
921 switch(iter->get_type())
923 case synfigapp::Action::Param::TYPE_STRING:
926 if(!studio::App::dialog_entry(entry.local_name, iter->get_local_name()+":"+iter->get_desc(),str))
928 action->set_param(iter->get_name(),str);
932 synfig::error("Unsupported user-supplied action parameter");
939 if(!action->is_ready())
941 synfig::error("Action not ready");
945 perform_action(action);
949 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location)
951 Gtk::Menu& parammenu(*menu);
953 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
955 if(!canvas_interface)
958 synfigapp::Action::ParamList param_list,param_list2;
959 param_list=canvas_interface->generate_param_list(value_desc);
960 param_list.add("origin",location);
962 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
964 param_list2=canvas_interface->generate_param_list(
965 synfigapp::ValueDesc(
966 ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
970 param_list2.add("origin",location);
974 // Populate the convert menu by looping through
975 // the ValueNode book and find the ones that are
978 Gtk::Menu *convert_menu=manage(new Gtk::Menu());
979 LinkableValueNode::Book::const_iterator iter;
980 for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
982 if(iter->second.check_type(value_desc.get_value_type()))
984 convert_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->second.local_name,
988 sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
998 parammenu.items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::Stock::CONVERT,*convert_menu));
1001 if(param_list2.empty())
1002 add_actions_to_menu(¶mmenu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1004 add_actions_to_menu(¶mmenu, param_list2,param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1006 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1008 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
1011 if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
1013 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
1017 WaypointList::iterator iter(value_node->find(canvas->get_time()));
1018 parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
1022 sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked),
1038 edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1040 etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1043 "Edit Multiple Waypoints", // Title
1045 true // use_separator
1048 Widget_WaypointModel widget_waypoint_model;
1049 widget_waypoint_model.show();
1051 dialog.get_vbox()->pack_start(widget_waypoint_model);
1054 dialog.add_button(Gtk::StockID("gtk-apply"),1);
1055 dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1059 if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1062 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1064 std::list<synfigapp::ValueDesc>::iterator iter;
1065 for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1067 synfigapp::ValueDesc value_desc(*iter);
1069 if(!value_desc.is_valid())
1072 ValueNode_Animated::Handle value_node;
1074 // If this value isn't a ValueNode_Animated, but
1075 // it is somewhat constant, then go ahead and convert
1076 // it to a ValueNode_Animated.
1077 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1080 if(value_desc.is_value_node())
1081 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1083 value=value_desc.get_value();
1085 value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1087 synfigapp::Action::Handle action;
1089 if(!value_desc.is_value_node())
1091 action=synfigapp::Action::create("value_desc_connect");
1092 action->set_param("dest",value_desc);
1093 action->set_param("src",ValueNode::Handle(value_node));
1097 action=synfigapp::Action::create("value_node_replace");
1098 action->set_param("dest",value_desc.get_value_node());
1099 action->set_param("src",ValueNode::Handle(value_node));
1102 action->set_param("canvas",canvas_view->get_canvas());
1103 action->set_param("canvas_interface",canvas_interface);
1106 if(!canvas_interface->get_instance()->perform_action(action))
1108 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1115 if(value_desc.is_value_node())
1116 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1123 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set_smart"));
1127 canvas_view->get_ui_interface()->error(_("Unable to find waypoint_set_smart action"));
1133 action->set_param("canvas",canvas_view->get_canvas());
1134 action->set_param("canvas_interface",canvas_interface);
1135 action->set_param("value_node",ValueNode::Handle(value_node));
1136 action->set_param("time",canvas_interface->get_time());
1137 action->set_param("model",widget_waypoint_model.get_waypoint_model());
1139 if(!canvas_interface->get_instance()->perform_action(action))
1141 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1148 //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1157 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1159 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1161 synfigapp::Action::ParamList param_list;
1162 param_list=canvas_interface->generate_param_list(value_desc_list);
1164 add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1166 // Add the edit waypoints option if that might be useful
1167 if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1169 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1173 &edit_several_waypoints
1177 find_canvas_view(canvas)