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, 2008 Chris Moore
10 ** Copyright (c) 2008 Carlos López
11 ** Copyright (c) 2009 Nikita Kitaev
13 ** This package is free software; you can redistribute it and/or
14 ** modify it under the terms of the GNU General Public License as
15 ** published by the Free Software Foundation; either version 2 of
16 ** the License, or (at your option) any later version.
18 ** This package is distributed in the hope that it will be useful,
19 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 ** General Public License for more details.
24 /* ========================================================================= */
26 /* === H E A D E R S ======================================================= */
37 #include <gtkmm/stock.h>
38 #include <gtkmm/image.h>
40 #include <gtkmm/button.h>
41 #include "canvasview.h"
43 #include <sigc++/signal.h>
44 #include <sigc++/adaptors/hide.h>
46 #include "onemoment.h"
47 #include <synfig/savecanvas.h>
49 #include "autorecover.h"
50 #include <sigc++/retype_return.h>
51 #include <sigc++/retype.h>
52 //#include <sigc++/hide.h>
53 #include <synfig/valuenode_composite.h>
54 #include <synfig/valuenode_duplicate.h>
55 #include "widget_waypointmodel.h"
56 #include <gtkmm/actiongroup.h>
57 #include "iconcontroller.h"
61 #include <ETL/stringf>
69 using namespace synfig;
70 using namespace studio;
73 /* === M A C R O S ========================================================= */
75 /* === G L O B A L S ======================================================= */
77 int studio::Instance::instance_count_=0;
79 /* === P R O C E D U R E S ================================================= */
81 /* === M E T H O D S ======================================================= */
83 Instance::Instance(synfig::Canvas::Handle canvas):
84 synfigapp::Instance (canvas),
85 // canvas_tree_store_ (Gtk::TreeStore::create(CanvasTreeModel())),
86 // canvas_tree_store_ (Gtk::TreeStore::create()),
87 history_tree_store_ (HistoryTreeStore::create(this)),
91 CanvasTreeModel model;
92 canvas_tree_store_=Gtk::TreeStore::create(model);
94 id_=instance_count_++;
96 // Connect up all the signals
97 signal_filename_changed().connect(sigc::mem_fun(*this,&studio::Instance::update_all_titles));
98 signal_unsaved_status_changed().connect(sigc::hide(sigc::mem_fun(*this,&studio::Instance::update_all_titles)));
99 signal_undo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_undo_status));
100 signal_redo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_redo_status));
102 signal_saved().connect(
105 studio::AutoRecover::auto_backup
110 canvas_tree_store_=Gtk::TreeStore::create(canvas_tree_model);
112 refresh_canvas_tree();
115 Instance::~Instance()
120 Instance::get_visible_canvases()const
123 CanvasViewList::const_iterator iter;
124 for(iter=canvas_view_list_.begin();iter!=canvas_view_list_.end();++iter)
125 if((*iter)->is_visible())
131 Instance::create(synfig::Canvas::Handle canvas)
133 // Construct a new instance
134 handle<Instance> instance(new Instance(canvas));
136 // Add the new instance to the application's instance list
137 App::instance_list.push_back(instance);
139 // Set up the instance with the default UI manager
140 instance->synfigapp::Instance::set_ui_interface(App::get_ui_interface());
142 // Signal the new instance
143 App::signal_instance_created()(instance);
145 // And then make sure that is has been selected
146 App::set_selected_instance(instance);
148 // Create the initial window for the root canvas
149 instance->focus(canvas);
155 Instance::find_canvas_view(etl::handle<synfig::Canvas> canvas)
160 while(canvas->is_inline())
161 canvas=canvas->parent();
163 CanvasViewList::iterator iter;
165 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
166 if((*iter)->get_canvas()==canvas)
169 return CanvasView::create(this,canvas);
173 Instance::focus(etl::handle<synfig::Canvas> canvas)
175 handle<CanvasView> canvas_view=find_canvas_view(canvas);
177 canvas_view->present();
181 Instance::set_undo_status(bool x)
184 App::toolbox->update_undo_redo();
185 signal_undo_redo_status_changed()();
189 Instance::set_redo_status(bool x)
192 App::toolbox->update_undo_redo();
193 signal_undo_redo_status_changed()();
197 studio::Instance::save_as(const synfig::String &file_name)
199 if(synfigapp::Instance::save_as(file_name))
201 // after changing the filename, update the render settings with the new filename
202 list<handle<CanvasView> >::iterator iter;
203 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
204 (*iter)->render_settings.set_entry_filename();
205 App::add_recent_file(etl::handle<Instance>(this));
212 studio::Instance::open()
214 App::dialog_open(get_file_name());
218 studio::Instance::save()
220 // if we don't have a real filename yet then we need to ask where to save it
221 if (!has_real_filename())
223 if (dialog_save_as())
226 return STATUS_CANCEL;
229 if (synfigapp::Instance::save())
231 App::add_recent_file(etl::handle<Instance>(this));
234 string msg(strprintf(_("Unable to save to '%s'"), get_file_name().c_str()));
235 App::dialog_error_blocking(_("Save - Error"), msg.c_str());
239 // the filename will be set to "Synfig Animation 1" or some such when first created
240 // and will be changed to an absolute path once it has been saved
241 // so if it still begins with "Synfig Animation " then we don't have a real filename yet
243 studio::Instance::has_real_filename()
245 return get_file_name().find(App::custom_filename_prefix.c_str()) != 0;
249 studio::Instance::dialog_save_as()
251 string filename = get_file_name();
252 Canvas::Handle canvas(get_canvas());
255 OneMoment one_moment;
256 std::set<Node*>::iterator iter;
257 for(iter=canvas->parent_set.begin();iter!=canvas->parent_set.end();++iter)
259 synfig::Node* node(*iter);
260 for(;!node->parent_set.empty();node=*node->parent_set.begin())
262 Layer::Handle parent_layer(dynamic_cast<Layer*>(node));
263 if(parent_layer && parent_layer->get_canvas()->get_root()!=get_canvas())
265 string msg(strprintf(_("There is currently a bug when using \"SaveAs\"\n"
266 "on a composition that is being referenced by other\n"
267 "files that are currently open. Close these\n"
268 "other files first before trying to use \"SaveAs\".")));
269 App::dialog_error_blocking(_("SaveAs - Error"), msg.c_str());
279 if (has_real_filename())
280 filename = absolute_path(filename);
282 // show the canvas' name if it has one, else its ID
283 while (App::dialog_save_file((_("Choose a Filename to Save As") +
285 (canvas->get_name().empty() ? canvas->get_id() : canvas->get_name()) +
287 filename, ANIMATION_DIR_PREFERENCE))
289 // If the filename still has wildcards, then we should
290 // continue looking for the file we want
291 string base_filename = basename(filename);
292 if (find(base_filename.begin(),base_filename.end(),'*')!=base_filename.end())
295 if (filename_extension(filename) == "")
300 String ext(filename_extension(filename));
301 if(ext!=".sif" && ext!=".sifz" && !App::dialog_yes_no(_("Unknown extension"),
302 _("You have given the file name an extension\nwhich I do not recognize. Are you sure this is what you want?")))
312 int stat_return = stat(filename.c_str(), &s);
314 // if stat() fails with something other than 'file doesn't exist', there's been a real
315 // error of some kind. let's give up now and ask for a new path.
316 if (stat_return == -1 && errno != ENOENT)
318 perror(filename.c_str());
319 string msg(strprintf(_("Unable to check whether '%s' exists."), filename.c_str()));
320 App::dialog_error_blocking(_("SaveAs - Error"),msg.c_str());
324 // if the file exists and the user doesn't want to overwrite it, keep prompting for a filename
325 string msg(strprintf(_("A file named '%s' already exists.\n\n"
326 "Do you want to replace it with the file you are saving?"), filename.c_str()));
327 if ((stat_return == 0) &&
328 !App::dialog_yes_no(_("File exists"),msg.c_str()))
332 if(save_as(filename))
334 synfig::set_file_version(ReleaseVersion(RELEASE_VERSION_END-1));
337 string msg(strprintf(_("Unable to save to '%s'"), filename.c_str()));
338 App::dialog_error_blocking(_("SaveAs - Error"),msg.c_str());
345 Instance::update_all_titles()
347 list<handle<CanvasView> >::iterator iter;
348 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
349 (*iter)->update_title();
355 // This will increase the reference count so we don't get DELETED
356 // until we are ready
357 handle<Instance> me(this);
359 // Make sure we aren't selected as the current instance
360 if(studio::App::get_selected_instance()==this)
361 studio::App::set_selected_instance(0);
363 // Turn-off/clean-up auto recovery
364 studio::App::auto_recover->clear_backup(get_canvas());
366 // Remove us from the active instance list
367 std::list<etl::handle<studio::Instance> >::iterator iter;
368 for(iter=studio::App::instance_list.begin();iter!=studio::App::instance_list.end();iter++)
371 assert(iter!=studio::App::instance_list.end());
372 if(iter!=studio::App::instance_list.end())
373 studio::App::instance_list.erase(iter);
375 // Send out a signal that we are being deleted
376 studio::App::signal_instance_deleted()(this);
378 // Hide all of the canvas views
379 for(std::list<etl::handle<CanvasView> >::iterator iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
382 // Consume pending events before deleting the canvas views
383 while(studio::App::events_pending())studio::App::iteration(false);
385 // Delete all of the canvas views
386 canvas_view_list().clear();
388 // If there is another open instance to select,
389 // go ahead and do so. If not, never mind.
390 if(studio::App::instance_list.empty())
392 studio::App::set_selected_canvas_view(0);
393 studio::App::set_selected_instance(0);
396 studio::App::instance_list.front()->canvas_view_list().front()->present();
400 Instance::insert_canvas(Gtk::TreeRow row, synfig::Canvas::Handle canvas)
402 CanvasTreeModel canvas_tree_model;
405 row[canvas_tree_model.icon] = Gtk::Button().render_icon(Gtk::StockID("synfig-canvas"),Gtk::ICON_SIZE_SMALL_TOOLBAR);
406 row[canvas_tree_model.id] = canvas->get_id();
407 row[canvas_tree_model.name] = canvas->get_name();
408 if(canvas->is_root())
409 row[canvas_tree_model.label] = basename(canvas->get_file_name());
411 if(!canvas->get_id().empty())
412 row[canvas_tree_model.label] = canvas->get_id();
414 if(!canvas->get_name().empty())
415 row[canvas_tree_model.label] = canvas->get_name();
417 row[canvas_tree_model.label] = _("[Unnamed]");
419 row[canvas_tree_model.canvas] = canvas;
420 row[canvas_tree_model.is_canvas] = true;
421 row[canvas_tree_model.is_value_node] = false;
424 synfig::Canvas::Children::iterator iter;
425 synfig::Canvas::Children &children(canvas->children());
427 for(iter=children.begin();iter!=children.end();iter++)
428 insert_canvas(*(canvas_tree_store()->append(row.children())),*iter);
432 if(!canvas->value_node_list().empty())
434 Gtk::TreeRow valuenode_row = *(canvas_tree_store()->append(row.children()));
436 valuenode_row[canvas_tree_model.label] = "<defs>";
437 valuenode_row[canvas_tree_model.canvas] = canvas;
438 valuenode_row[canvas_tree_model.is_canvas] = false;
439 valuenode_row[canvas_tree_model.is_value_node] = false;
441 synfig::ValueNodeList::iterator iter;
442 synfig::ValueNodeList &value_node_list(canvas->value_node_list());
444 for(iter=value_node_list.begin();iter!=value_node_list.end();iter++)
445 insert_value_node(*(canvas_tree_store()->append(valuenode_row.children())),canvas,*iter);
452 Instance::insert_value_node(Gtk::TreeRow row,Canvas::Handle canvas,etl::handle<synfig::ValueNode> value_node)
454 CanvasTreeModel canvas_tree_model;
458 row[canvas_tree_model.id] = value_node->get_id();
459 row[canvas_tree_model.name] = value_node->get_id();
460 row[canvas_tree_model.label] = value_node->get_id();
461 row[canvas_tree_model.canvas] = canvas;
462 row[canvas_tree_model.value_node] = value_node;
463 row[canvas_tree_model.is_canvas] = false;
464 row[canvas_tree_model.is_value_node] = true;
465 row[canvas_tree_model.icon] = Gtk::Button().render_icon(valuenode_icon(value_node),Gtk::ICON_SIZE_SMALL_TOOLBAR);
470 Instance::refresh_canvas_tree()
472 canvas_tree_store()->clear();
473 Gtk::TreeRow row = *(canvas_tree_store()->prepend());
474 insert_canvas(row,get_canvas());
478 Instance::dialog_cvs_commit()
480 calc_repository_info();
483 App::dialog_error_blocking(_("Error"),_("You must first add this composition to the repository"));
490 if(synfigapp::Instance::get_action_count())
492 if(!App::dialog_yes_no(_("CVS Commit"), _("This will save any changes you have made. Are you sure?")))
499 App::dialog_error_blocking(_("Error"),_("The local copy of the file hasn't been changed since the last update.\nNothing to commit!"));
503 if(!App::dialog_entry(_("CVS Commit"),_("Enter a log message describing the changes you have made"), message))
506 OneMoment one_moment;
511 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to COMMIT"));
517 Instance::dialog_cvs_add()
519 calc_repository_info();
522 App::dialog_error_blocking(_("Error"),_("This composition has already been added to the repository"));
529 //if(!App::dialog_entry(_("CVS Add"),_("Enter a log message describing the file"), message))
531 OneMoment one_moment;
536 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to ADD"));
542 Instance::dialog_cvs_update()
544 calc_repository_info();
547 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to update from!"));
552 App::dialog_error_blocking(_("Info"),_("This file is up-to-date"));
558 String filename(get_file_name());
559 if(synfigapp::Instance::get_action_count())
561 if(!App::dialog_yes_no(_("CVS Update"), _("This will save any changes you have made. Are you sure?")))
565 OneMoment one_moment;
566 time_t oldtime=get_original_timestamp();
568 calc_repository_info();
569 // If something has been updated...
570 if(oldtime!=get_original_timestamp())
577 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
579 //update_all_titles();
583 Instance::dialog_cvs_revert()
585 calc_repository_info();
588 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to revert to!"));
593 String filename(get_file_name());
594 if(!App::dialog_yes_no(_("CVS Revert"),
595 _("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?")
599 OneMoment one_moment;
601 // Remove the old file
602 if(remove(get_file_name().c_str())!=0)
604 App::dialog_error_blocking(_("Error"),_("Unable to remove previous version"));
613 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
615 //update_all_titles();
619 Instance::_revert(Instance *instance)
621 OneMoment one_moment;
623 String filename(instance->get_file_name());
625 Canvas::Handle canvas(instance->get_canvas());
629 if(canvas->count()!=1)
632 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."));
643 // Schedule a revert to occur in a few moments
644 Glib::signal_timeout().connect(
647 sigc::ptr_fun(&Instance::_revert),
657 Instance::safe_revert()
659 if(synfigapp::Instance::get_action_count())
660 if(!App::dialog_yes_no(_("Revert to saved"), _("You will lose any changes you have made since your last save.\nAre you sure?")))
667 Instance::safe_close()
669 handle<CanvasView> canvas_view = find_canvas_view(get_canvas());
670 handle<synfigapp::UIInterface> uim=canvas_view->get_ui_interface();
672 // if the animation is currently playing, closing the window will cause a crash,
674 if (canvas_view->is_playing())
676 canvas_view->present();
677 App::dialog_error_blocking("Close Error", "The animation is currently playing so the window cannot be closed.");
680 if(get_action_count())
683 string str=strprintf(_("Would you like to save your changes to %s?"),basename(get_file_name()).c_str() );
684 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
685 if(answer==synfigapp::UIInterface::RESPONSE_YES)
687 enum Status status = save();
688 if (status == STATUS_OK) break;
689 else if (status == STATUS_CANCEL) return false;
691 if(answer==synfigapp::UIInterface::RESPONSE_NO)
693 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
699 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());
700 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
702 if(answer==synfigapp::UIInterface::RESPONSE_YES)
704 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
714 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
716 synfigapp::Action::CandidateList candidate_list;
717 synfigapp::Action::CandidateList::iterator iter;
719 candidate_list=compile_candidate_list(param_list,category);
721 candidate_list.sort();
723 // if(candidate_list.empty())
724 // synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
726 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
728 Gtk::StockID stock_id(get_action_stock_id(*iter));
730 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
732 action_group->add(Gtk::Action::create(
733 "action-"+iter->name,
735 iter->local_name,iter->local_name
740 *const_cast<studio::Instance*>(this),
741 &studio::Instance::process_action
748 ui_info+=strprintf("<menuitem action='action-%s' />",iter->name.c_str());
754 Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList ¶m_list,synfigapp::Action::Category category)const
756 synfigapp::Action::CandidateList candidate_list;
757 synfigapp::Action::CandidateList::iterator iter;
759 candidate_list=compile_candidate_list(param_list,category);
761 candidate_list.sort();
763 if(candidate_list.empty())
764 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
766 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
768 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
770 Gtk::Image* image(manage(new Gtk::Image()));
771 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
774 if(iter->task=="raise")
775 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
776 else if(iter->task=="lower")
777 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
778 else if(iter->task=="move_top")
779 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
780 else if(iter->task=="move_bottom")
781 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
782 else if(iter->task=="remove")
783 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
784 else if(iter->task=="set_on")
785 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
786 else if(iter->task=="set_off")
787 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
788 else if(iter->task=="duplicate")
789 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
790 else if(iter->task=="remove")
791 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
794 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
795 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
796 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
799 menu->items().push_back(
800 Gtk::Menu_Helpers::ImageMenuElem(
806 *const_cast<studio::Instance*>(this),
807 &studio::Instance::process_action
820 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
822 synfigapp::Action::CandidateList candidate_list;
823 synfigapp::Action::CandidateList candidate_list2;
825 synfigapp::Action::CandidateList::iterator iter;
827 candidate_list=compile_candidate_list(param_list,category);
828 candidate_list2=compile_candidate_list(param_list2,category);
830 candidate_list.sort();
832 if(candidate_list.empty())
833 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
834 if(candidate_list2.empty())
835 synfig::warning("%s:%d Action CandidateList2 is empty!", __FILE__, __LINE__);
837 // Separate out the candidate lists so that there are no conflicts
838 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
840 synfigapp::Action::CandidateList::iterator iter2(candidate_list2.find(iter->name));
841 if(iter2!=candidate_list2.end())
842 candidate_list2.erase(iter2);
845 for(iter=candidate_list2.begin();iter!=candidate_list2.end();++iter)
847 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
849 Gtk::Image* image(manage(new Gtk::Image()));
850 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
852 /* if(iter->task=="raise")
853 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
854 else if(iter->task=="lower")
855 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
856 else if(iter->task=="move_top")
857 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
858 else if(iter->task=="move_bottom")
859 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
860 else if(iter->task=="remove")
861 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
862 else if(iter->task=="set_on")
863 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
864 else if(iter->task=="set_off")
865 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
866 else if(iter->task=="duplicate")
867 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
868 else if(iter->task=="remove")
869 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
872 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
873 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
874 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
877 menu->items().push_back(
878 Gtk::Menu_Helpers::ImageMenuElem(
884 *const_cast<studio::Instance*>(this),
885 &studio::Instance::process_action
896 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
898 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
900 Gtk::Image* image(manage(new Gtk::Image()));
901 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
902 /* if(iter->task=="raise")
903 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
904 else if(iter->task=="lower")
905 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
906 else if(iter->task=="move_top")
907 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
908 else if(iter->task=="move_bottom")
909 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
910 else if(iter->task=="remove")
911 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
912 else if(iter->task=="set_on")
913 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
914 else if(iter->task=="set_off")
915 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
916 else if(iter->task=="duplicate")
917 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
918 else if(iter->task=="remove")
919 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
922 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
923 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
924 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
927 menu->items().push_back(
928 Gtk::Menu_Helpers::ImageMenuElem(
934 *const_cast<studio::Instance*>(this),
935 &studio::Instance::process_action
948 Instance::process_action(synfig::String name, synfigapp::Action::ParamList param_list)
951 // synfig::info("%s:%d process_action: '%s'", __FILE__, __LINE__, name.c_str());
953 assert(synfigapp::Action::book().count(name));
955 synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second);
957 synfigapp::Action::Handle action(entry.factory());
961 synfig::error("Bad Action");
965 action->set_param_list(param_list);
967 synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab());
968 synfigapp::Action::ParamVocab::const_iterator iter;
970 for(iter=param_vocab.begin();iter!=param_vocab.end();++iter)
972 if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion()))
975 // If the parameter is optionally user-supplied,
976 // and has not been already provided in the param_list,
977 // then we should go ahead and see if we can
978 // provide that data.
979 if(iter->get_user_supplied() && param_list.count(iter->get_name())==0)
981 switch(iter->get_type())
983 case synfigapp::Action::Param::TYPE_STRING:
986 if(!studio::App::dialog_entry(entry.local_name, iter->get_local_name()+": "+iter->get_desc(),str))
988 action->set_param(iter->get_name(),str);
992 synfig::error("Unsupported user-supplied action parameter");
999 if(!action->is_ready())
1001 synfig::error("Action not ready");
1005 perform_action(action);
1009 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location, bool bezier)
1011 Gtk::Menu& parammenu(*menu);
1013 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1015 if(!canvas_interface)
1018 synfigapp::Action::ParamList param_list,param_list2;
1019 param_list=canvas_interface->generate_param_list(value_desc);
1020 param_list.add("origin",location);
1022 #ifdef BLINEPOINT_MENU_IS_VERTEX_MENU
1023 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1025 param_list2=canvas_interface->generate_param_list(
1026 synfigapp::ValueDesc(
1027 ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
1028 ,ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
1029 ->get_link_index_from_name("point")
1032 param_list2.add("origin",location);
1034 #endif // BLINEPOINT_MENU_IS_VERTEX_MENU
1036 // Populate the convert menu by looping through
1037 // the ValueNode book and find the ones that are
1040 // show the 'Convert' sub-menu if this valuedesc is anything other than either:
1041 // the 'Index' parameter of a Duplicate layer
1043 // a Duplicate ValueNode whose parent is not a (layer or ValueNode)
1044 if (!((value_desc.parent_is_layer_param() &&
1045 value_desc.get_layer()->get_name() == "duplicate" &&
1046 value_desc.get_param_name() == "index") ||
1047 (value_desc.is_value_node() &&
1048 ValueNode_Duplicate::Handle::cast_dynamic(value_desc.get_value_node()) &&
1049 !(value_desc.parent_is_layer_param() ||
1050 value_desc.parent_is_value_node()))))
1052 Gtk::Menu *convert_menu=manage(new Gtk::Menu());
1053 LinkableValueNode::Book::const_iterator iter;
1054 for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
1056 if(iter->second.check_type(value_desc.get_value_type()))
1058 convert_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->second.local_name,
1062 sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
1072 parammenu.items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::Stock::CONVERT,*convert_menu));
1075 synfigapp::Action::Category categories = synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE;
1078 categories = categories|synfigapp::Action::CATEGORY_BEZIER;
1080 const DuckList selected_ducks(find_canvas_view(canvas)->get_work_area()->get_selected_ducks());
1081 for(DuckList::const_iterator iter=selected_ducks.begin();iter!=selected_ducks.end();++iter)
1083 synfigapp::ValueDesc value_desc((*iter)->get_value_desc());
1084 if(value_desc.is_valid())
1085 param_list.add("selected_value_desc",value_desc);
1089 if(param_list2.empty())
1090 add_actions_to_menu(¶mmenu, param_list,categories);
1092 add_actions_to_menu(¶mmenu, param_list2,param_list,categories);
1094 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1096 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
1099 if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
1101 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
1105 // try to find a waypoint at the current time - if we
1106 // can't, we don't want the menu entry - an exception is thrown
1107 WaypointList::iterator iter(value_node->find(canvas->get_time()));
1108 std::set<synfig::Waypoint, std::less<UniqueID> > waypoint_set;
1109 waypoint_set.insert(*iter);
1111 parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
1115 sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked_canvasview),
1131 edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1133 etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1136 "Edit Multiple Waypoints", // Title
1138 true // use_separator
1141 Widget_WaypointModel widget_waypoint_model;
1142 widget_waypoint_model.show();
1144 dialog.get_vbox()->pack_start(widget_waypoint_model);
1146 dialog.add_button(Gtk::StockID("gtk-apply"),1);
1147 dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1150 if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1152 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1154 std::list<synfigapp::ValueDesc>::iterator iter;
1155 for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1157 synfigapp::ValueDesc value_desc(*iter);
1159 if(!value_desc.is_valid())
1162 ValueNode_Animated::Handle value_node;
1164 // If this value isn't a ValueNode_Animated, but
1165 // it is somewhat constant, then go ahead and convert
1166 // it to a ValueNode_Animated.
1167 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1170 if(value_desc.is_value_node())
1171 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1173 value=value_desc.get_value();
1175 value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1177 synfigapp::Action::Handle action;
1179 if(!value_desc.is_value_node())
1181 action=synfigapp::Action::create("ValueDescConnect");
1182 action->set_param("dest",value_desc);
1183 action->set_param("src",ValueNode::Handle(value_node));
1187 action=synfigapp::Action::create("ValueNodeReplace");
1188 action->set_param("dest",value_desc.get_value_node());
1189 action->set_param("src",ValueNode::Handle(value_node));
1192 action->set_param("canvas",canvas_view->get_canvas());
1193 action->set_param("canvas_interface",canvas_interface);
1195 if(!canvas_interface->get_instance()->perform_action(action))
1197 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1204 if(value_desc.is_value_node())
1205 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1210 synfigapp::Action::Handle action(synfigapp::Action::create("WaypointSetSmart"));
1214 canvas_view->get_ui_interface()->error(_("Unable to find WaypointSetSmart action"));
1219 action->set_param("canvas",canvas_view->get_canvas());
1220 action->set_param("canvas_interface",canvas_interface);
1221 action->set_param("value_node",ValueNode::Handle(value_node));
1222 action->set_param("time",canvas_interface->get_time());
1223 action->set_param("model",widget_waypoint_model.get_waypoint_model());
1225 if(!canvas_interface->get_instance()->perform_action(action))
1227 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1234 //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1243 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1245 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1247 synfigapp::Action::ParamList param_list;
1248 param_list=canvas_interface->generate_param_list(value_desc_list);
1250 add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1252 // Add the edit waypoints option if that might be useful
1253 if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1255 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1259 &edit_several_waypoints
1263 find_canvas_view(canvas)