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 if (dialog_save_as())
212 return STATUS_CANCEL;
214 if (synfigapp::Instance::save())
217 App::dialog_error_blocking("Save - Error","Unable to save to '" + get_file_name() + "'");
222 studio::Instance::dialog_save_as()
224 string filename=basename(get_file_name());
225 Canvas::Handle canvas(get_canvas());
228 OneMoment one_moment;
229 std::set<Node*>::iterator iter;
230 for(iter=canvas->parent_set.begin();iter!=canvas->parent_set.end();++iter)
232 synfig::Node* node(*iter);
233 for(;!node->parent_set.empty();node=*node->parent_set.begin())
235 Layer::Handle parent_layer(dynamic_cast<Layer*>(node));
236 if(parent_layer && parent_layer->get_canvas()->get_root()!=get_canvas())
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\"."
253 // show the canvas' name if it has one, else its ID
254 while(App::dialog_save_file(_("Choose a Filename to Save As") +
256 (canvas->get_name().empty()
258 : canvas->get_name()) +
261 // If the filename still has wildcards, then we should
262 // continue looking for the file we want
263 if(find(filename.begin(),filename.end(),'*')!=filename.end())
266 std::string base = basename(filename);
267 if(find(base.begin(),base.end(),'.')==base.end())
272 String ext(String(filename.begin()+filename.find_last_of('.')+1,filename.end()));
273 if(ext!="sif" && ext!="sifz" && !App::dialog_yes_no(_("Unknown extension"),
274 _("You have given the file name an extension\nwhich I do not recognise. Are you sure this is what you want?")))
286 int stat_return = stat(filename.c_str(), &s);
288 // if stat() fails with something other than 'file doesn't exist', there's been a real
289 // error of some kind. let's give up now and ask for a new path.
290 if (stat_return == -1 && errno != ENOENT)
292 perror(filename.c_str());
293 App::dialog_error_blocking("SaveAs - Error","Unable to check whether '" + filename + "' exists.");
297 // if the file exists and the user doesn't want to overwrite it, keep prompting for a filename
298 if ((stat_return == 0) &&
299 !App::dialog_yes_no("File exists",
302 "' already exists.\n\n"
303 "Do you want to replace it with the file you are saving?"))
307 if(save_as(filename))
310 App::dialog_error_blocking("SaveAs - Error","Unable to save to '" + filename + "'");
317 Instance::update_all_titles()
319 list<handle<CanvasView> >::iterator iter;
320 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
321 (*iter)->update_title();
327 // This will increase the reference count so we don't get DELETED
328 // until we are ready
329 handle<Instance> me(this);
331 // Make sure we aren't selected as the current instance
332 if(studio::App::get_selected_instance()==this)
333 studio::App::set_selected_instance(0);
335 // Turn-off/clean-up auto recovery
336 studio::App::auto_recover->clear_backup(get_canvas());
338 // Remove us from the active instance list
339 std::list<etl::handle<studio::Instance> >::iterator iter;
340 for(iter=studio::App::instance_list.begin();iter!=studio::App::instance_list.end();iter++)
343 assert(iter!=studio::App::instance_list.end());
344 if(iter!=studio::App::instance_list.end())
345 studio::App::instance_list.erase(iter);
347 // Send out a signal that we are being deleted
348 studio::App::signal_instance_deleted()(this);
350 // Hide all of the canvas views
351 for(std::list<etl::handle<CanvasView> >::iterator iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
354 // Consume pending events before deleting the canvas views
355 while(studio::App::events_pending())studio::App::iteration(false);
357 // Delete all of the canvas views
358 canvas_view_list().clear();
360 // If there is another open instance to select,
361 // go ahead and do so. If not, never mind.
362 if(studio::App::instance_list.empty())
364 studio::App::set_selected_canvas_view(0);
365 studio::App::set_selected_instance(0);
369 studio::App::set_selected_canvas_view(studio::App::instance_list.front()->canvas_view_list().front());
370 //studio::App::set_selected_instance(studio::App::instance_list.front());
376 Instance::insert_canvas(Gtk::TreeRow row,Canvas::Handle canvas)
378 CanvasTreeModel canvas_tree_model;
381 row[canvas_tree_model.icon] = Gtk::Button().render_icon(Gtk::StockID("synfig-canvas"),Gtk::ICON_SIZE_SMALL_TOOLBAR);
382 row[canvas_tree_model.id] = canvas->get_id();
383 row[canvas_tree_model.name] = canvas->get_name();
384 if(canvas->is_root())
385 row[canvas_tree_model.label] = basename(canvas->get_file_name());
387 if(!canvas->get_id().empty())
388 row[canvas_tree_model.label] = canvas->get_id();
390 if(!canvas->get_name().empty())
391 row[canvas_tree_model.label] = canvas->get_name();
393 row[canvas_tree_model.label] = _("[Unnamed]");
395 row[canvas_tree_model.canvas] = canvas;
396 row[canvas_tree_model.is_canvas] = true;
397 row[canvas_tree_model.is_value_node] = false;
400 synfig::Canvas::Children::iterator iter;
401 synfig::Canvas::Children &children(canvas->children());
403 for(iter=children.begin();iter!=children.end();iter++)
404 insert_canvas(*(canvas_tree_store()->append(row.children())),*iter);
408 if(!canvas->value_node_list().empty())
410 Gtk::TreeRow valuenode_row = *(canvas_tree_store()->append(row.children()));
412 valuenode_row[canvas_tree_model.label] = "<defs>";
413 valuenode_row[canvas_tree_model.canvas] = canvas;
414 valuenode_row[canvas_tree_model.is_canvas] = false;
415 valuenode_row[canvas_tree_model.is_value_node] = false;
417 synfig::ValueNodeList::iterator iter;
418 synfig::ValueNodeList &value_node_list(canvas->value_node_list());
420 for(iter=value_node_list.begin();iter!=value_node_list.end();iter++)
421 insert_value_node(*(canvas_tree_store()->append(valuenode_row.children())),canvas,*iter);
429 Instance::insert_value_node(Gtk::TreeRow row,Canvas::Handle canvas,etl::handle<synfig::ValueNode> value_node)
431 CanvasTreeModel canvas_tree_model;
435 row[canvas_tree_model.id] = value_node->get_id();
436 row[canvas_tree_model.name] = value_node->get_id();
437 row[canvas_tree_model.label] = value_node->get_id();
438 row[canvas_tree_model.canvas] = canvas;
439 row[canvas_tree_model.value_node] = value_node;
440 row[canvas_tree_model.is_canvas] = false;
441 row[canvas_tree_model.is_value_node] = true;
442 row[canvas_tree_model.icon] = Gtk::Button().render_icon(valuenode_icon(value_node),Gtk::ICON_SIZE_SMALL_TOOLBAR);
447 Instance::refresh_canvas_tree()
449 canvas_tree_store()->clear();
450 Gtk::TreeRow row = *(canvas_tree_store()->prepend());
451 insert_canvas(row,get_canvas());
455 Instance::dialog_cvs_commit()
457 calc_repository_info();
460 App::dialog_error_blocking(_("Error"),_("You must first add this composition to the repository"));
467 if(synfigapp::Instance::get_action_count())
469 if(!App::dialog_yes_no(_("CVS Commit"), _("This will save any changes you have made. Are you sure?")))
476 App::dialog_error_blocking(_("Error"),_("The local copy of the file hasn't been changed since the last update.\nNothing to commit!"));
480 if(!App::dialog_entry(_("CVS Commit"),_("Enter a log message describing the changes you have made"), message))
483 OneMoment one_moment;
488 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to COMMIT"));
494 Instance::dialog_cvs_add()
496 calc_repository_info();
499 App::dialog_error_blocking(_("Error"),_("This composition has already been added to the repository"));
506 //if(!App::dialog_entry(_("CVS Add"),_("Enter a log message describing the file"), message))
508 OneMoment one_moment;
513 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to ADD"));
519 Instance::dialog_cvs_update()
521 calc_repository_info();
524 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to update from!"));
529 App::dialog_error_blocking(_("Info"),_("This file is up-to-date"));
535 String filename(get_file_name());
536 if(synfigapp::Instance::get_action_count())
538 if(!App::dialog_yes_no(_("CVS Update"), _("This will save any changes you have made. Are you sure?")))
542 OneMoment one_moment;
543 time_t oldtime=get_original_timestamp();
545 calc_repository_info();
546 // If something has been updated...
547 if(oldtime!=get_original_timestamp())
554 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to UPDATE"));
556 //update_all_titles();
560 Instance::dialog_cvs_revert()
562 calc_repository_info();
565 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to revert to!"));
570 String filename(get_file_name());
571 if(!App::dialog_yes_no(_("CVS Revert"),
572 _("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?")
576 OneMoment one_moment;
578 // Remove the old file
579 if(remove(get_file_name().c_str())!=0)
581 App::dialog_error_blocking(_("Error"),_("Unable to remove previous version"));
590 App::dialog_error_blocking(_("Error"),_("An error has occured when trying to UPDATE"));
592 //update_all_titles();
596 Instance::_revert(Instance *instance)
598 OneMoment one_moment;
600 String filename(instance->get_file_name());
602 Canvas::Handle canvas(instance->get_canvas());
606 if(canvas->count()!=1)
609 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."));
620 // Schedule a revert to occur in a few moments
621 Glib::signal_timeout().connect(
624 sigc::ptr_fun(&Instance::_revert),
634 Instance::safe_revert()
636 if(synfigapp::Instance::get_action_count())
637 if(!App::dialog_yes_no(_("Revert to saved"), _("You will lose any changes you have made since your last save.\nAre you sure?")))
644 Instance::safe_close()
646 handle<CanvasView> canvas_view = find_canvas_view(get_canvas());
647 handle<synfigapp::UIInterface> uim=canvas_view->get_ui_interface();
649 // if the animation is currently playing, closing the window will cause a crash,
651 if (canvas_view->is_playing())
653 canvas_view->present();
654 App::dialog_error_blocking("Close Error", "The animation is currently playing so the window cannot be closed.");
657 if(get_action_count())
660 string str=strprintf(_("Would you like to save your changes to %s?"),basename(get_file_name()).c_str() );
661 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
662 if(answer==synfigapp::UIInterface::RESPONSE_YES)
664 enum Status status = save();
665 if (status == STATUS_OK) break;
666 else if (status == STATUS_CANCEL) return false;
668 if(answer==synfigapp::UIInterface::RESPONSE_NO)
670 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
676 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());
677 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
679 if(answer==synfigapp::UIInterface::RESPONSE_YES)
681 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
692 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
694 synfigapp::Action::CandidateList candidate_list;
695 synfigapp::Action::CandidateList::iterator iter;
697 candidate_list=compile_candidate_list(param_list,category);
699 candidate_list.sort();
701 if(candidate_list.empty())
702 synfig::warning("Action CandidateList is empty!");
704 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
706 Gtk::StockID stock_id(get_action_stock_id(*iter));
708 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
710 action_group->add(Gtk::Action::create(
711 "action-"+iter->name,
713 iter->local_name,iter->local_name
718 *const_cast<studio::Instance*>(this),
719 &studio::Instance::process_action
726 ui_info+=strprintf("<menuitem action='action-%s' />",iter->name.c_str());
732 Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList ¶m_list,synfigapp::Action::Category category)const
734 synfigapp::Action::CandidateList candidate_list;
735 synfigapp::Action::CandidateList::iterator iter;
737 candidate_list=compile_candidate_list(param_list,category);
739 candidate_list.sort();
741 if(candidate_list.empty())
742 synfig::warning("Action CandidateList is empty!");
744 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
746 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
748 Gtk::Image* image(manage(new Gtk::Image()));
749 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
752 if(iter->task=="raise")
753 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
754 else if(iter->task=="lower")
755 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
756 else if(iter->task=="move_top")
757 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
758 else if(iter->task=="move_bottom")
759 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
760 else if(iter->task=="remove")
761 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
762 else if(iter->task=="set_on")
763 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
764 else if(iter->task=="set_off")
765 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
766 else if(iter->task=="duplicate")
767 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
768 else if(iter->task=="remove")
769 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
772 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
773 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
774 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
777 menu->items().push_back(
778 Gtk::Menu_Helpers::ImageMenuElem(
784 *const_cast<studio::Instance*>(this),
785 &studio::Instance::process_action
798 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
800 synfigapp::Action::CandidateList candidate_list;
801 synfigapp::Action::CandidateList candidate_list2;
803 synfigapp::Action::CandidateList::iterator iter;
805 candidate_list=compile_candidate_list(param_list,category);
806 candidate_list2=compile_candidate_list(param_list2,category);
808 candidate_list.sort();
810 if(candidate_list.empty())
811 synfig::warning("Action CandidateList is empty!");
812 if(candidate_list2.empty())
813 synfig::warning("Action CandidateList2 is empty!");
815 // Seperate out the candidate lists so that there are no conflicts
816 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
818 synfigapp::Action::CandidateList::iterator iter2(candidate_list2.find(iter->name));
819 if(iter2!=candidate_list2.end())
820 candidate_list2.erase(iter2);
823 for(iter=candidate_list2.begin();iter!=candidate_list2.end();++iter)
825 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
827 Gtk::Image* image(manage(new Gtk::Image()));
828 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
830 /* if(iter->task=="raise")
831 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
832 else if(iter->task=="lower")
833 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
834 else if(iter->task=="move_top")
835 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
836 else if(iter->task=="move_bottom")
837 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
838 else if(iter->task=="remove")
839 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
840 else if(iter->task=="set_on")
841 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
842 else if(iter->task=="set_off")
843 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
844 else if(iter->task=="duplicate")
845 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
846 else if(iter->task=="remove")
847 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
850 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
851 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
852 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
855 menu->items().push_back(
856 Gtk::Menu_Helpers::ImageMenuElem(
862 *const_cast<studio::Instance*>(this),
863 &studio::Instance::process_action
874 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
876 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
878 Gtk::Image* image(manage(new Gtk::Image()));
879 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
880 /* if(iter->task=="raise")
881 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
882 else if(iter->task=="lower")
883 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
884 else if(iter->task=="move_top")
885 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
886 else if(iter->task=="move_bottom")
887 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
888 else if(iter->task=="remove")
889 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
890 else if(iter->task=="set_on")
891 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
892 else if(iter->task=="set_off")
893 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
894 else if(iter->task=="duplicate")
895 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
896 else if(iter->task=="remove")
897 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
900 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
901 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
902 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
905 menu->items().push_back(
906 Gtk::Menu_Helpers::ImageMenuElem(
912 *const_cast<studio::Instance*>(this),
913 &studio::Instance::process_action
926 Instance::process_action(String name, synfigapp::Action::ParamList param_list)
928 assert(synfigapp::Action::book().count(name));
930 synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second);
932 synfigapp::Action::Handle action(entry.factory());
936 synfig::error("Bad Action");
940 action->set_param_list(param_list);
942 synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab());
943 synfigapp::Action::ParamVocab::const_iterator iter;
945 for(iter=param_vocab.begin();iter!=param_vocab.end();++iter)
947 if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion()))
950 // If the parameter is optionally user-supplied,
951 // and has not been already provided in the param_list,
952 // then we should go ahead and see if we can
953 // provide that data.
954 if(iter->get_user_supplied() && param_list.count(iter->get_name())==0)
956 switch(iter->get_type())
958 case synfigapp::Action::Param::TYPE_STRING:
961 if(!studio::App::dialog_entry(entry.local_name, iter->get_local_name()+":"+iter->get_desc(),str))
963 action->set_param(iter->get_name(),str);
967 synfig::error("Unsupported user-supplied action parameter");
974 if(!action->is_ready())
976 synfig::error("Action not ready");
980 perform_action(action);
984 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location)
986 Gtk::Menu& parammenu(*menu);
988 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
990 if(!canvas_interface)
993 synfigapp::Action::ParamList param_list,param_list2;
994 param_list=canvas_interface->generate_param_list(value_desc);
995 param_list.add("origin",location);
997 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
999 param_list2=canvas_interface->generate_param_list(
1000 synfigapp::ValueDesc(
1001 ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
1005 param_list2.add("origin",location);
1009 // Populate the convert menu by looping through
1010 // the ValueNode book and find the ones that are
1013 Gtk::Menu *convert_menu=manage(new Gtk::Menu());
1014 LinkableValueNode::Book::const_iterator iter;
1015 for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
1017 if(iter->second.check_type(value_desc.get_value_type()))
1019 convert_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->second.local_name,
1023 sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
1033 parammenu.items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::Stock::CONVERT,*convert_menu));
1036 if(param_list2.empty())
1037 add_actions_to_menu(¶mmenu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1039 add_actions_to_menu(¶mmenu, param_list2,param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1041 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1043 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
1046 if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
1048 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
1052 WaypointList::iterator iter(value_node->find(canvas->get_time()));
1053 parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
1057 sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked),
1073 edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1075 etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1078 "Edit Multiple Waypoints", // Title
1080 true // use_separator
1083 Widget_WaypointModel widget_waypoint_model;
1084 widget_waypoint_model.show();
1086 dialog.get_vbox()->pack_start(widget_waypoint_model);
1089 dialog.add_button(Gtk::StockID("gtk-apply"),1);
1090 dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1094 if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1097 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1099 std::list<synfigapp::ValueDesc>::iterator iter;
1100 for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1102 synfigapp::ValueDesc value_desc(*iter);
1104 if(!value_desc.is_valid())
1107 ValueNode_Animated::Handle value_node;
1109 // If this value isn't a ValueNode_Animated, but
1110 // it is somewhat constant, then go ahead and convert
1111 // it to a ValueNode_Animated.
1112 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1115 if(value_desc.is_value_node())
1116 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1118 value=value_desc.get_value();
1120 value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1122 synfigapp::Action::Handle action;
1124 if(!value_desc.is_value_node())
1126 action=synfigapp::Action::create("value_desc_connect");
1127 action->set_param("dest",value_desc);
1128 action->set_param("src",ValueNode::Handle(value_node));
1132 action=synfigapp::Action::create("value_node_replace");
1133 action->set_param("dest",value_desc.get_value_node());
1134 action->set_param("src",ValueNode::Handle(value_node));
1137 action->set_param("canvas",canvas_view->get_canvas());
1138 action->set_param("canvas_interface",canvas_interface);
1141 if(!canvas_interface->get_instance()->perform_action(action))
1143 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1150 if(value_desc.is_value_node())
1151 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1158 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set_smart"));
1162 canvas_view->get_ui_interface()->error(_("Unable to find waypoint_set_smart action"));
1168 action->set_param("canvas",canvas_view->get_canvas());
1169 action->set_param("canvas_interface",canvas_interface);
1170 action->set_param("value_node",ValueNode::Handle(value_node));
1171 action->set_param("time",canvas_interface->get_time());
1172 action->set_param("model",widget_waypoint_model.get_waypoint_model());
1174 if(!canvas_interface->get_instance()->perform_action(action))
1176 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1183 //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1192 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1194 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1196 synfigapp::Action::ParamList param_list;
1197 param_list=canvas_interface->generate_param_list(value_desc_list);
1199 add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1201 // Add the edit waypoints option if that might be useful
1202 if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1204 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1208 &edit_several_waypoints
1212 find_canvas_view(canvas)