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 "widgets/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(canvas_tree_model)),
86 history_tree_store_ (HistoryTreeStore::create(this)),
90 id_=instance_count_++;
92 // Connect up all the signals
93 signal_filename_changed().connect(sigc::mem_fun(*this,&studio::Instance::update_all_titles));
94 signal_unsaved_status_changed().connect(sigc::hide(sigc::mem_fun(*this,&studio::Instance::update_all_titles)));
95 signal_undo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_undo_status));
96 signal_redo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_redo_status));
98 signal_saved().connect(
101 studio::AutoRecover::auto_backup
106 refresh_canvas_tree();
109 Instance::~Instance()
114 Instance::get_visible_canvases()const
117 CanvasViewList::const_iterator iter;
118 for(iter=canvas_view_list_.begin();iter!=canvas_view_list_.end();++iter)
119 if((*iter)->is_visible())
125 Instance::create(synfig::Canvas::Handle canvas)
127 // Construct a new instance
128 handle<Instance> instance(new Instance(canvas));
130 // Add the new instance to the application's instance list
131 App::instance_list.push_back(instance);
133 // Set up the instance with the default UI manager
134 instance->synfigapp::Instance::set_ui_interface(App::get_ui_interface());
136 // Signal the new instance
137 App::signal_instance_created()(instance);
139 // And then make sure that is has been selected
140 App::set_selected_instance(instance);
142 // Create the initial window for the root canvas
143 instance->focus(canvas);
149 Instance::find_canvas_view(etl::handle<synfig::Canvas> canvas)
154 while(canvas->is_inline())
155 canvas=canvas->parent();
157 CanvasViewList::iterator iter;
159 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
160 if((*iter)->get_canvas()==canvas)
163 return CanvasView::create(this,canvas);
167 Instance::focus(etl::handle<synfig::Canvas> canvas)
169 handle<CanvasView> canvas_view=find_canvas_view(canvas);
171 canvas_view->present();
175 Instance::set_undo_status(bool x)
178 App::toolbox->update_undo_redo();
179 signal_undo_redo_status_changed()();
183 Instance::set_redo_status(bool x)
186 App::toolbox->update_undo_redo();
187 signal_undo_redo_status_changed()();
191 studio::Instance::save_as(const synfig::String &file_name)
193 if(synfigapp::Instance::save_as(file_name))
195 // after changing the filename, update the render settings with the new filename
196 list<handle<CanvasView> >::iterator iter;
197 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
198 (*iter)->render_settings.set_entry_filename();
199 App::add_recent_file(etl::handle<Instance>(this));
206 studio::Instance::open()
208 App::dialog_open(get_file_name());
212 studio::Instance::save()
214 // if we don't have a real filename yet then we need to ask where to save it
215 if (!has_real_filename())
217 if (dialog_save_as())
220 return STATUS_CANCEL;
223 if (synfigapp::Instance::save())
225 App::add_recent_file(etl::handle<Instance>(this));
228 string msg(strprintf(_("Unable to save to '%s'"), get_file_name().c_str()));
229 App::dialog_error_blocking(_("Save - Error"), msg.c_str());
233 // the filename will be set to "Synfig Animation 1" or some such when first created
234 // and will be changed to an absolute path once it has been saved
235 // so if it still begins with "Synfig Animation " then we don't have a real filename yet
237 studio::Instance::has_real_filename()
239 return get_file_name().find(App::custom_filename_prefix.c_str()) != 0;
243 studio::Instance::dialog_save_as()
245 string filename = get_file_name();
246 Canvas::Handle canvas(get_canvas());
249 OneMoment one_moment;
250 std::set<Node*>::iterator iter;
251 for(iter=canvas->parent_set.begin();iter!=canvas->parent_set.end();++iter)
253 synfig::Node* node(*iter);
254 for(;!node->parent_set.empty();node=*node->parent_set.begin())
256 Layer::Handle parent_layer(dynamic_cast<Layer*>(node));
257 if(parent_layer && parent_layer->get_canvas()->get_root()!=get_canvas())
259 //! \todo Fix big involving "Save As" with referenced compositions
260 string msg(strprintf(_("There is currently a bug when using \"SaveAs\"\n"
261 "on a composition that is being referenced by other\n"
262 "files that are currently open. Close these\n"
263 "other files first before trying to use \"SaveAs\".")));
264 App::dialog_error_blocking(_("SaveAs - Error"), msg.c_str());
274 if (has_real_filename())
275 filename = absolute_path(filename);
277 // show the canvas' name if it has one, else its ID
278 while (App::dialog_save_file((_("Choose a Filename to Save As") +
280 (canvas->get_name().empty() ? canvas->get_id() : canvas->get_name()) +
282 filename, ANIMATION_DIR_PREFERENCE))
284 // If the filename still has wildcards, then we should
285 // continue looking for the file we want
286 string base_filename = basename(filename);
287 if (find(base_filename.begin(),base_filename.end(),'*')!=base_filename.end())
290 if (filename_extension(filename) == "")
295 String ext(filename_extension(filename));
296 if(ext!=".sif" && ext!=".sifz" && !App::dialog_yes_no(_("Unknown extension"),
297 _("You have given the file name an extension\nwhich I do not recognize. Are you sure this is what you want?")))
307 int stat_return = stat(filename.c_str(), &s);
309 // if stat() fails with something other than 'file doesn't exist', there's been a real
310 // error of some kind. let's give up now and ask for a new path.
311 if (stat_return == -1 && errno != ENOENT)
313 perror(filename.c_str());
314 string msg(strprintf(_("Unable to check whether '%s' exists."), filename.c_str()));
315 App::dialog_error_blocking(_("SaveAs - Error"),msg.c_str());
319 // if the file exists and the user doesn't want to overwrite it, keep prompting for a filename
320 string msg(strprintf(_("A file named '%s' already exists.\n\n"
321 "Do you want to replace it with the file you are saving?"), filename.c_str()));
322 if ((stat_return == 0) &&
323 !App::dialog_yes_no(_("File exists"),msg.c_str()))
327 if(save_as(filename))
329 synfig::set_file_version(ReleaseVersion(RELEASE_VERSION_END-1));
332 string msg(strprintf(_("Unable to save to '%s'"), filename.c_str()));
333 App::dialog_error_blocking(_("SaveAs - Error"),msg.c_str());
340 Instance::update_all_titles()
342 list<handle<CanvasView> >::iterator iter;
343 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
344 (*iter)->update_title();
350 // This will increase the reference count so we don't get DELETED
351 // until we are ready
352 handle<Instance> me(this);
354 // Make sure we aren't selected as the current instance
355 if(studio::App::get_selected_instance()==this)
356 studio::App::set_selected_instance(0);
358 // Turn-off/clean-up auto recovery
359 studio::App::auto_recover->clear_backup(get_canvas());
361 // Remove us from the active instance list
362 std::list<etl::handle<studio::Instance> >::iterator iter;
363 for(iter=studio::App::instance_list.begin();iter!=studio::App::instance_list.end();iter++)
366 assert(iter!=studio::App::instance_list.end());
367 if(iter!=studio::App::instance_list.end())
368 studio::App::instance_list.erase(iter);
370 // Send out a signal that we are being deleted
371 studio::App::signal_instance_deleted()(this);
373 // Hide all of the canvas views
374 for(std::list<etl::handle<CanvasView> >::iterator iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
377 // Consume pending events before deleting the canvas views
378 while(studio::App::events_pending())studio::App::iteration(false);
380 // Delete all of the canvas views
381 canvas_view_list().clear();
383 // If there is another open instance to select,
384 // go ahead and do so. If not, never mind.
385 if(studio::App::instance_list.empty())
387 studio::App::set_selected_canvas_view(0);
388 studio::App::set_selected_instance(0);
391 studio::App::instance_list.front()->canvas_view_list().front()->present();
395 Instance::insert_canvas(Gtk::TreeRow row, synfig::Canvas::Handle canvas)
397 CanvasTreeModel canvas_tree_model;
400 row[canvas_tree_model.icon] = Gtk::Button().render_icon(Gtk::StockID("synfig-canvas"),Gtk::ICON_SIZE_SMALL_TOOLBAR);
401 row[canvas_tree_model.id] = canvas->get_id();
402 row[canvas_tree_model.name] = canvas->get_name();
403 if(canvas->is_root())
404 row[canvas_tree_model.label] = basename(canvas->get_file_name());
406 if(!canvas->get_id().empty())
407 row[canvas_tree_model.label] = canvas->get_id();
409 if(!canvas->get_name().empty())
410 row[canvas_tree_model.label] = canvas->get_name();
412 row[canvas_tree_model.label] = _("[Unnamed]");
414 row[canvas_tree_model.canvas] = canvas;
415 row[canvas_tree_model.is_canvas] = true;
416 row[canvas_tree_model.is_value_node] = false;
419 synfig::Canvas::Children::iterator iter;
420 synfig::Canvas::Children &children(canvas->children());
422 for(iter=children.begin();iter!=children.end();iter++)
423 insert_canvas(*(canvas_tree_store()->append(row.children())),*iter);
428 Instance::refresh_canvas_tree()
430 canvas_tree_store()->clear();
431 Gtk::TreeRow row = *(canvas_tree_store()->prepend());
432 insert_canvas(row,get_canvas());
436 Instance::dialog_cvs_commit()
438 calc_repository_info();
441 App::dialog_error_blocking(_("Error"),_("You must first add this composition to the repository"));
448 if(synfigapp::Instance::get_action_count())
450 if(!App::dialog_yes_no(_("CVS Commit"), _("This will save any changes you have made. Are you sure?")))
457 App::dialog_error_blocking(_("Error"),_("The local copy of the file hasn't been changed since the last update.\nNothing to commit!"));
461 if(!App::dialog_entry(_("CVS Commit"),_("Enter a log message describing the changes you have made"), message))
464 OneMoment one_moment;
469 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to COMMIT"));
475 Instance::dialog_cvs_add()
477 calc_repository_info();
480 App::dialog_error_blocking(_("Error"),_("This composition has already been added to the repository"));
487 //if(!App::dialog_entry(_("CVS Add"),_("Enter a log message describing the file"), message))
489 OneMoment one_moment;
494 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to ADD"));
500 Instance::dialog_cvs_update()
502 calc_repository_info();
505 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to update from!"));
510 App::dialog_error_blocking(_("Info"),_("This file is up-to-date"));
516 String filename(get_file_name());
517 if(synfigapp::Instance::get_action_count())
519 if(!App::dialog_yes_no(_("CVS Update"), _("This will save any changes you have made. Are you sure?")))
523 OneMoment one_moment;
524 time_t oldtime=get_original_timestamp();
526 calc_repository_info();
527 // If something has been updated...
528 if(oldtime!=get_original_timestamp())
535 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
537 //update_all_titles();
541 Instance::dialog_cvs_revert()
543 calc_repository_info();
546 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to revert to!"));
551 String filename(get_file_name());
552 if(!App::dialog_yes_no(_("CVS Revert"),
553 _("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?")
557 OneMoment one_moment;
559 // Remove the old file
560 if(remove(get_file_name().c_str())!=0)
562 App::dialog_error_blocking(_("Error"),_("Unable to remove previous version"));
571 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
573 //update_all_titles();
577 Instance::_revert(Instance *instance)
579 OneMoment one_moment;
581 String filename(instance->get_file_name());
583 Canvas::Handle canvas(instance->get_canvas());
587 if(canvas->count()!=1)
590 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."));
601 // Schedule a revert to occur in a few moments
602 Glib::signal_timeout().connect(
605 sigc::ptr_fun(&Instance::_revert),
615 Instance::safe_revert()
617 if(synfigapp::Instance::get_action_count())
618 if(!App::dialog_yes_no(_("Revert to saved"), _("You will lose any changes you have made since your last save.\nAre you sure?")))
625 Instance::safe_close()
627 handle<CanvasView> canvas_view = find_canvas_view(get_canvas());
628 handle<synfigapp::UIInterface> uim=canvas_view->get_ui_interface();
630 // if the animation is currently playing, closing the window will cause a crash,
632 if (canvas_view->is_playing())
634 canvas_view->present();
635 App::dialog_error_blocking("Close Error", "The animation is currently playing so the window cannot be closed.");
638 if(get_action_count())
641 string str=strprintf(_("Would you like to save your changes to %s?"),basename(get_file_name()).c_str() );
642 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
643 if(answer==synfigapp::UIInterface::RESPONSE_YES)
645 enum Status status = save();
646 if (status == STATUS_OK) break;
647 else if (status == STATUS_CANCEL) return false;
649 if(answer==synfigapp::UIInterface::RESPONSE_NO)
651 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
657 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());
658 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
660 if(answer==synfigapp::UIInterface::RESPONSE_YES)
662 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
672 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
674 synfigapp::Action::CandidateList candidate_list;
675 synfigapp::Action::CandidateList::iterator iter;
677 candidate_list=compile_candidate_list(param_list,category);
679 candidate_list.sort();
681 // if(candidate_list.empty())
682 // synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
684 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
686 Gtk::StockID stock_id(get_action_stock_id(*iter));
688 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
690 action_group->add(Gtk::Action::create(
691 "action-"+iter->name,
693 iter->local_name,iter->local_name
698 *const_cast<studio::Instance*>(this),
699 &studio::Instance::process_action
706 ui_info+=strprintf("<menuitem action='action-%s' />",iter->name.c_str());
712 Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList ¶m_list,synfigapp::Action::Category category)const
714 synfigapp::Action::CandidateList candidate_list;
715 synfigapp::Action::CandidateList::iterator iter;
717 candidate_list=compile_candidate_list(param_list,category);
719 candidate_list.sort();
721 if(candidate_list.empty())
722 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
724 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
726 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
728 Gtk::Image* image(manage(new Gtk::Image()));
729 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
731 menu->items().push_back(
732 Gtk::Menu_Helpers::ImageMenuElem(
738 *const_cast<studio::Instance*>(this),
739 &studio::Instance::process_action
752 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
754 synfigapp::Action::CandidateList candidate_list;
755 synfigapp::Action::CandidateList candidate_list2;
757 synfigapp::Action::CandidateList::iterator iter;
759 candidate_list=compile_candidate_list(param_list,category);
760 candidate_list2=compile_candidate_list(param_list2,category);
762 candidate_list.sort();
764 if(candidate_list.empty())
765 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
766 if(candidate_list2.empty())
767 synfig::warning("%s:%d Action CandidateList2 is empty!", __FILE__, __LINE__);
769 // Separate out the candidate lists so that there are no conflicts
770 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
772 synfigapp::Action::CandidateList::iterator iter2(candidate_list2.find(iter->name));
773 if(iter2!=candidate_list2.end())
774 candidate_list2.erase(iter2);
777 for(iter=candidate_list2.begin();iter!=candidate_list2.end();++iter)
779 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
781 Gtk::Image* image(manage(new Gtk::Image()));
782 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
784 menu->items().push_back(
785 Gtk::Menu_Helpers::ImageMenuElem(
791 *const_cast<studio::Instance*>(this),
792 &studio::Instance::process_action
803 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
805 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
807 Gtk::Image* image(manage(new Gtk::Image()));
808 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
810 menu->items().push_back(
811 Gtk::Menu_Helpers::ImageMenuElem(
817 *const_cast<studio::Instance*>(this),
818 &studio::Instance::process_action
831 Instance::process_action(synfig::String name, synfigapp::Action::ParamList param_list)
834 // synfig::info("%s:%d process_action: '%s'", __FILE__, __LINE__, name.c_str());
836 assert(synfigapp::Action::book().count(name));
838 synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second);
840 synfigapp::Action::Handle action(entry.factory());
844 synfig::error("Bad Action");
848 action->set_param_list(param_list);
850 synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab());
851 synfigapp::Action::ParamVocab::const_iterator iter;
853 for(iter=param_vocab.begin();iter!=param_vocab.end();++iter)
855 if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion()))
858 // If the parameter is optionally user-supplied,
859 // and has not been already provided in the param_list,
860 // then we should go ahead and see if we can
861 // provide that data.
862 if(iter->get_user_supplied() && param_list.count(iter->get_name())==0)
864 switch(iter->get_type())
866 case synfigapp::Action::Param::TYPE_STRING:
869 if(!studio::App::dialog_entry(entry.local_name, iter->get_local_name()+": "+iter->get_desc(),str))
871 action->set_param(iter->get_name(),str);
875 synfig::error("Unsupported user-supplied action parameter");
882 if(!action->is_ready())
884 synfig::error("Action not ready");
888 perform_action(action);
892 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location, bool bezier)
894 Gtk::Menu& parammenu(*menu);
896 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
898 if(!canvas_interface)
901 synfigapp::Action::ParamList param_list,param_list2;
902 param_list=canvas_interface->generate_param_list(value_desc);
903 param_list.add("origin",location);
905 #ifdef BLINEPOINT_MENU_IS_VERTEX_MENU
906 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
908 param_list2=canvas_interface->generate_param_list(
909 synfigapp::ValueDesc(
910 ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
911 ,ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
912 ->get_link_index_from_name("point")
915 param_list2.add("origin",location);
917 #endif // BLINEPOINT_MENU_IS_VERTEX_MENU
919 // Populate the convert menu by looping through
920 // the ValueNode book and find the ones that are
923 // show the 'Convert' sub-menu if this valuedesc is anything other than either:
924 // the 'Index' parameter of a Duplicate layer
926 // a Duplicate ValueNode whose parent is not a (layer or ValueNode)
927 if (!((value_desc.parent_is_layer_param() &&
928 value_desc.get_layer()->get_name() == "duplicate" &&
929 value_desc.get_param_name() == "index") ||
930 (value_desc.is_value_node() &&
931 ValueNode_Duplicate::Handle::cast_dynamic(value_desc.get_value_node()) &&
932 !(value_desc.parent_is_layer_param() ||
933 value_desc.parent_is_value_node()))))
935 Gtk::Menu *convert_menu=manage(new Gtk::Menu());
936 LinkableValueNode::Book::const_iterator iter;
937 for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
939 if(iter->second.check_type(value_desc.get_value_type()))
941 convert_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->second.local_name,
945 sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
955 parammenu.items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::Stock::CONVERT,*convert_menu));
958 synfigapp::Action::Category categories = synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE;
961 categories = categories|synfigapp::Action::CATEGORY_BEZIER;
963 const DuckList selected_ducks(find_canvas_view(canvas)->get_work_area()->get_selected_ducks());
964 for(DuckList::const_iterator iter=selected_ducks.begin();iter!=selected_ducks.end();++iter)
966 synfigapp::ValueDesc value_desc((*iter)->get_value_desc());
967 if(value_desc.is_valid())
968 param_list.add("selected_value_desc",value_desc);
972 if(param_list2.empty())
973 add_actions_to_menu(¶mmenu, param_list,categories);
975 add_actions_to_menu(¶mmenu, param_list2,param_list,categories);
977 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
979 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
982 if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
984 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
988 // try to find a waypoint at the current time - if we
989 // can't, we don't want the menu entry - an exception is thrown
990 WaypointList::iterator iter(value_node->find(canvas->get_time()));
991 std::set<synfig::Waypoint, std::less<UniqueID> > waypoint_set;
992 waypoint_set.insert(*iter);
994 parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
998 sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked_canvasview),
1014 edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1016 etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1019 "Edit Multiple Waypoints", // Title
1021 true // use_separator
1024 Widget_WaypointModel widget_waypoint_model;
1025 widget_waypoint_model.show();
1027 dialog.get_vbox()->pack_start(widget_waypoint_model);
1029 dialog.add_button(Gtk::StockID("gtk-apply"),1);
1030 dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1033 if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1035 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1037 std::list<synfigapp::ValueDesc>::iterator iter;
1038 for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1040 synfigapp::ValueDesc value_desc(*iter);
1042 if(!value_desc.is_valid())
1045 ValueNode_Animated::Handle value_node;
1047 // If this value isn't a ValueNode_Animated, but
1048 // it is somewhat constant, then go ahead and convert
1049 // it to a ValueNode_Animated.
1050 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1053 if(value_desc.is_value_node())
1054 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1056 value=value_desc.get_value();
1058 value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1060 synfigapp::Action::Handle action;
1062 if(!value_desc.is_value_node())
1064 action=synfigapp::Action::create("ValueDescConnect");
1065 action->set_param("dest",value_desc);
1066 action->set_param("src",ValueNode::Handle(value_node));
1070 action=synfigapp::Action::create("ValueNodeReplace");
1071 action->set_param("dest",value_desc.get_value_node());
1072 action->set_param("src",ValueNode::Handle(value_node));
1075 action->set_param("canvas",canvas_view->get_canvas());
1076 action->set_param("canvas_interface",canvas_interface);
1078 if(!canvas_interface->get_instance()->perform_action(action))
1080 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1087 if(value_desc.is_value_node())
1088 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1093 synfigapp::Action::Handle action(synfigapp::Action::create("WaypointSetSmart"));
1097 canvas_view->get_ui_interface()->error(_("Unable to find WaypointSetSmart action"));
1102 action->set_param("canvas",canvas_view->get_canvas());
1103 action->set_param("canvas_interface",canvas_interface);
1104 action->set_param("value_node",ValueNode::Handle(value_node));
1105 action->set_param("time",canvas_interface->get_time());
1106 action->set_param("model",widget_waypoint_model.get_waypoint_model());
1108 if(!canvas_interface->get_instance()->perform_action(action))
1110 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1117 //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1126 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1128 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1130 synfigapp::Action::ParamList param_list;
1131 param_list=canvas_interface->generate_param_list(value_desc_list);
1133 add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1135 // Add the edit waypoints option if that might be useful
1136 if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1138 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1142 &edit_several_waypoints
1146 find_canvas_view(canvas)