1 /* === S Y N F I G ========================================================= */
2 /*! \file gtkmm/instance.cpp
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 ** Copyright (c) 2007 Chris Moore
11 ** This package is free software; you can redistribute it and/or
12 ** modify it under the terms of the GNU General Public License as
13 ** published by the Free Software Foundation; either version 2 of
14 ** the License, or (at your option) any later version.
16 ** This package is distributed in the hope that it will be useful,
17 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 ** General Public License for more details.
22 /* ========================================================================= */
24 /* === H E A D E R S ======================================================= */
35 #include <gtkmm/stock.h>
36 #include <gtkmm/image.h>
38 #include <gtkmm/button.h>
39 #include "canvasview.h"
41 #include <sigc++/signal.h>
42 #include <sigc++/adaptors/hide.h>
44 #include "onemoment.h"
46 #include "autorecover.h"
47 #include <sigc++/retype_return.h>
48 #include <sigc++/retype.h>
49 //#include <sigc++/hide.h>
50 #include <synfig/valuenode_composite.h>
51 #include <synfig/valuenode_duplicate.h>
52 #include "widget_waypointmodel.h"
53 #include <gtkmm/actiongroup.h>
54 #include "iconcontroller.h"
64 using namespace synfig;
65 using namespace studio;
68 /* === M A C R O S ========================================================= */
70 /* === G L O B A L S ======================================================= */
72 int studio::Instance::instance_count_=0;
74 /* === P R O C E D U R E S ================================================= */
76 /* === M E T H O D S ======================================================= */
78 Instance::Instance(synfig::Canvas::Handle canvas):
79 synfigapp::Instance (canvas),
80 // canvas_tree_store_ (Gtk::TreeStore::create(CanvasTreeModel())),
81 // canvas_tree_store_ (Gtk::TreeStore::create()),
82 history_tree_store_ (HistoryTreeStore::create(this)),
86 CanvasTreeModel model;
87 canvas_tree_store_=Gtk::TreeStore::create(model);
89 id_=instance_count_++;
91 // Connect up all the signals
92 signal_filename_changed().connect(sigc::mem_fun(*this,&studio::Instance::update_all_titles));
93 signal_unsaved_status_changed().connect(sigc::hide(sigc::mem_fun(*this,&studio::Instance::update_all_titles)));
94 signal_undo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_undo_status));
95 signal_redo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_redo_status));
97 signal_saved().connect(
100 studio::AutoRecover::auto_backup
105 canvas_tree_store_=Gtk::TreeStore::create(canvas_tree_model);
107 refresh_canvas_tree();
110 Instance::~Instance()
115 Instance::get_visible_canvases()const
118 CanvasViewList::const_iterator iter;
119 for(iter=canvas_view_list_.begin();iter!=canvas_view_list_.end();++iter)
120 if((*iter)->is_visible())
126 Instance::create(synfig::Canvas::Handle canvas)
128 // Construct a new instance
129 handle<Instance> instance(new Instance(canvas));
131 // Add the new instance to the application's instance list
132 App::instance_list.push_back(instance);
134 // Set up the instance with the default UI manager
135 instance->synfigapp::Instance::set_ui_interface(App::get_ui_interface());
137 // Signal the new instance
138 App::signal_instance_created()(instance);
140 // And then make sure that is has been selected
141 App::set_selected_instance(instance);
143 // Create the initial window for the root canvas
144 instance->focus(canvas);
150 Instance::find_canvas_view(etl::handle<synfig::Canvas> canvas)
155 while(canvas->is_inline())
156 canvas=canvas->parent();
158 CanvasViewList::iterator iter;
160 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
161 if((*iter)->get_canvas()==canvas)
164 return CanvasView::create(this,canvas);
168 Instance::focus(etl::handle<synfig::Canvas> canvas)
170 handle<CanvasView> canvas_view=find_canvas_view(canvas);
172 canvas_view->present();
176 Instance::set_undo_status(bool x)
179 App::toolbox->update_undo_redo();
180 signal_undo_redo_status_changed()();
184 Instance::set_redo_status(bool x)
187 App::toolbox->update_undo_redo();
188 signal_undo_redo_status_changed()();
192 studio::Instance::save_as(const synfig::String &file_name)
194 if(synfigapp::Instance::save_as(file_name))
196 // after changing the filename, update the render settings with the new filename
197 list<handle<CanvasView> >::iterator iter;
198 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
199 (*iter)->render_settings.set_entry_filename();
200 App::add_recent_file(file_name);
207 studio::Instance::save()
209 // the filename will be set to "Synfig Animation 1" or some such when first created
210 // and will be changed to an absolute path once it has been saved
211 // so if it still begins with "Synfig Animation " then we need to ask where to save it
212 if(get_file_name().find(DEFAULT_FILENAME_PREFIX)==0)
214 if (dialog_save_as())
217 return STATUS_CANCEL;
220 if (synfigapp::Instance::save())
223 App::dialog_error_blocking("Save - Error","Unable to save to '" + get_file_name() + "'");
228 studio::Instance::dialog_save_as()
230 string filename = get_file_name();
231 Canvas::Handle canvas(get_canvas());
234 OneMoment one_moment;
235 std::set<Node*>::iterator iter;
236 for(iter=canvas->parent_set.begin();iter!=canvas->parent_set.end();++iter)
238 synfig::Node* node(*iter);
239 for(;!node->parent_set.empty();node=*node->parent_set.begin())
241 Layer::Handle parent_layer(dynamic_cast<Layer*>(node));
242 if(parent_layer && parent_layer->get_canvas()->get_root()!=get_canvas())
244 App::dialog_error_blocking("SaveAs - Error",
245 "There is currently a bug when using \"SaveAs\"\n"
246 "on a composition that is being referenced by other\n"
247 "files that are currently open. Close these\n"
248 "other files first before trying to use \"SaveAs\"."
259 // show the canvas' name if it has one, else its ID
260 while (App::dialog_save_file((_("Choose a Filename to Save As") +
262 (canvas->get_name().empty() ? canvas->get_id() : canvas->get_name()) +
264 filename, ANIMATION_DIR_PREFERENCE))
266 // If the filename still has wildcards, then we should
267 // continue looking for the file we want
268 string base_filename = basename(filename);
269 if (find(base_filename.begin(),base_filename.end(),'*')!=base_filename.end())
272 if (filename_extension(filename) == "")
277 String ext(filename_extension(filename));
278 if(ext!=".sif" && ext!=".sifz" && !App::dialog_yes_no(_("Unknown extension"),
279 _("You have given the file name an extension\nwhich I do not recognize. Are you sure this is what you want?")))
289 int stat_return = stat(filename.c_str(), &s);
291 // if stat() fails with something other than 'file doesn't exist', there's been a real
292 // error of some kind. let's give up now and ask for a new path.
293 if (stat_return == -1 && errno != ENOENT)
295 perror(filename.c_str());
296 App::dialog_error_blocking("SaveAs - Error","Unable to check whether '" + filename + "' exists.");
300 // if the file exists and the user doesn't want to overwrite it, keep prompting for a filename
301 if ((stat_return == 0) &&
302 !App::dialog_yes_no("File exists",
305 "' already exists.\n\n"
306 "Do you want to replace it with the file you are saving?"))
310 if(save_as(filename))
313 App::dialog_error_blocking("SaveAs - Error","Unable to save to '" + filename + "'");
320 Instance::update_all_titles()
322 list<handle<CanvasView> >::iterator iter;
323 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
324 (*iter)->update_title();
330 // This will increase the reference count so we don't get DELETED
331 // until we are ready
332 handle<Instance> me(this);
334 // Make sure we aren't selected as the current instance
335 if(studio::App::get_selected_instance()==this)
336 studio::App::set_selected_instance(0);
338 // Turn-off/clean-up auto recovery
339 studio::App::auto_recover->clear_backup(get_canvas());
341 // Remove us from the active instance list
342 std::list<etl::handle<studio::Instance> >::iterator iter;
343 for(iter=studio::App::instance_list.begin();iter!=studio::App::instance_list.end();iter++)
346 assert(iter!=studio::App::instance_list.end());
347 if(iter!=studio::App::instance_list.end())
348 studio::App::instance_list.erase(iter);
350 // Send out a signal that we are being deleted
351 studio::App::signal_instance_deleted()(this);
353 // Hide all of the canvas views
354 for(std::list<etl::handle<CanvasView> >::iterator iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
357 // Consume pending events before deleting the canvas views
358 while(studio::App::events_pending())studio::App::iteration(false);
360 // Delete all of the canvas views
361 canvas_view_list().clear();
363 // If there is another open instance to select,
364 // go ahead and do so. If not, never mind.
365 if(studio::App::instance_list.empty())
367 studio::App::set_selected_canvas_view(0);
368 studio::App::set_selected_instance(0);
371 studio::App::instance_list.front()->canvas_view_list().front()->present();
376 Instance::insert_canvas(Gtk::TreeRow row, synfig::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 occurred 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 occurred 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 occurred 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 occurred 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("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
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("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
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("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
812 if(candidate_list2.empty())
813 synfig::warning("%s:%d Action CandidateList2 is empty!", __FILE__, __LINE__);
815 // Separate 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(synfig::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 // show the 'Convert' sub-menu if this valuedesc is anything other than either:
1014 // the 'Index' parameter of a Duplicate layer
1016 // a Duplicate ValueNode whose parent is not a (layer or ValueNode)
1017 if (!((value_desc.parent_is_layer_param() &&
1018 value_desc.get_layer()->get_name() == "duplicate" &&
1019 value_desc.get_param_name() == "index") ||
1020 (value_desc.is_value_node() &&
1021 ValueNode_Duplicate::Handle::cast_dynamic(value_desc.get_value_node()) &&
1022 !(value_desc.parent_is_layer_param() ||
1023 value_desc.parent_is_value_node()))))
1025 Gtk::Menu *convert_menu=manage(new Gtk::Menu());
1026 LinkableValueNode::Book::const_iterator iter;
1027 for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
1029 if(iter->second.check_type(value_desc.get_value_type()))
1031 convert_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->second.local_name,
1035 sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
1045 parammenu.items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::Stock::CONVERT,*convert_menu));
1048 if(param_list2.empty())
1049 add_actions_to_menu(¶mmenu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1051 add_actions_to_menu(¶mmenu, param_list2,param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1053 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1055 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
1058 if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
1060 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
1064 WaypointList::iterator iter(value_node->find(canvas->get_time()));
1065 parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
1069 sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked),
1085 edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1087 etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1090 "Edit Multiple Waypoints", // Title
1092 true // use_separator
1095 Widget_WaypointModel widget_waypoint_model;
1096 widget_waypoint_model.show();
1098 dialog.get_vbox()->pack_start(widget_waypoint_model);
1101 dialog.add_button(Gtk::StockID("gtk-apply"),1);
1102 dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1105 if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1107 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1109 std::list<synfigapp::ValueDesc>::iterator iter;
1110 for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1112 synfigapp::ValueDesc value_desc(*iter);
1114 if(!value_desc.is_valid())
1117 ValueNode_Animated::Handle value_node;
1119 // If this value isn't a ValueNode_Animated, but
1120 // it is somewhat constant, then go ahead and convert
1121 // it to a ValueNode_Animated.
1122 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1125 if(value_desc.is_value_node())
1126 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1128 value=value_desc.get_value();
1130 value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1132 synfigapp::Action::Handle action;
1134 if(!value_desc.is_value_node())
1136 action=synfigapp::Action::create("value_desc_connect");
1137 action->set_param("dest",value_desc);
1138 action->set_param("src",ValueNode::Handle(value_node));
1142 action=synfigapp::Action::create("value_node_replace");
1143 action->set_param("dest",value_desc.get_value_node());
1144 action->set_param("src",ValueNode::Handle(value_node));
1147 action->set_param("canvas",canvas_view->get_canvas());
1148 action->set_param("canvas_interface",canvas_interface);
1151 if(!canvas_interface->get_instance()->perform_action(action))
1153 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1160 if(value_desc.is_value_node())
1161 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1168 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set_smart"));
1172 canvas_view->get_ui_interface()->error(_("Unable to find waypoint_set_smart action"));
1178 action->set_param("canvas",canvas_view->get_canvas());
1179 action->set_param("canvas_interface",canvas_interface);
1180 action->set_param("value_node",ValueNode::Handle(value_node));
1181 action->set_param("time",canvas_interface->get_time());
1182 action->set_param("model",widget_waypoint_model.get_waypoint_model());
1184 if(!canvas_interface->get_instance()->perform_action(action))
1186 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1193 //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1202 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1204 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1206 synfigapp::Action::ParamList param_list;
1207 param_list=canvas_interface->generate_param_list(value_desc_list);
1209 add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1211 // Add the edit waypoints option if that might be useful
1212 if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1214 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1218 &edit_several_waypoints
1222 find_canvas_view(canvas)