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
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"
45 #include <synfig/savecanvas.h>
47 #include "autorecover.h"
48 #include <sigc++/retype_return.h>
49 #include <sigc++/retype.h>
50 //#include <sigc++/hide.h>
51 #include <synfig/valuenode_composite.h>
52 #include <synfig/valuenode_duplicate.h>
53 #include "widget_waypointmodel.h"
54 #include <gtkmm/actiongroup.h>
55 #include "iconcontroller.h"
58 #include <ETL/stringf>
66 using namespace synfig;
67 using namespace studio;
70 /* === M A C R O S ========================================================= */
72 /* === G L O B A L S ======================================================= */
74 int studio::Instance::instance_count_=0;
76 /* === P R O C E D U R E S ================================================= */
78 /* === M E T H O D S ======================================================= */
80 Instance::Instance(synfig::Canvas::Handle canvas):
81 synfigapp::Instance (canvas),
82 // canvas_tree_store_ (Gtk::TreeStore::create(CanvasTreeModel())),
83 // canvas_tree_store_ (Gtk::TreeStore::create()),
84 history_tree_store_ (HistoryTreeStore::create(this)),
88 CanvasTreeModel model;
89 canvas_tree_store_=Gtk::TreeStore::create(model);
91 id_=instance_count_++;
93 // Connect up all the signals
94 signal_filename_changed().connect(sigc::mem_fun(*this,&studio::Instance::update_all_titles));
95 signal_unsaved_status_changed().connect(sigc::hide(sigc::mem_fun(*this,&studio::Instance::update_all_titles)));
96 signal_undo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_undo_status));
97 signal_redo_status().connect(sigc::mem_fun(*this,&studio::Instance::set_redo_status));
99 signal_saved().connect(
102 studio::AutoRecover::auto_backup
107 canvas_tree_store_=Gtk::TreeStore::create(canvas_tree_model);
109 refresh_canvas_tree();
112 Instance::~Instance()
117 Instance::get_visible_canvases()const
120 CanvasViewList::const_iterator iter;
121 for(iter=canvas_view_list_.begin();iter!=canvas_view_list_.end();++iter)
122 if((*iter)->is_visible())
128 Instance::create(synfig::Canvas::Handle canvas)
130 // Construct a new instance
131 handle<Instance> instance(new Instance(canvas));
133 // Add the new instance to the application's instance list
134 App::instance_list.push_back(instance);
136 // Set up the instance with the default UI manager
137 instance->synfigapp::Instance::set_ui_interface(App::get_ui_interface());
139 // Signal the new instance
140 App::signal_instance_created()(instance);
142 // And then make sure that is has been selected
143 App::set_selected_instance(instance);
145 // Create the initial window for the root canvas
146 instance->focus(canvas);
152 Instance::find_canvas_view(etl::handle<synfig::Canvas> canvas)
157 while(canvas->is_inline())
158 canvas=canvas->parent();
160 CanvasViewList::iterator iter;
162 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
163 if((*iter)->get_canvas()==canvas)
166 return CanvasView::create(this,canvas);
170 Instance::focus(etl::handle<synfig::Canvas> canvas)
172 handle<CanvasView> canvas_view=find_canvas_view(canvas);
174 canvas_view->present();
178 Instance::set_undo_status(bool x)
181 App::toolbox->update_undo_redo();
182 signal_undo_redo_status_changed()();
186 Instance::set_redo_status(bool x)
189 App::toolbox->update_undo_redo();
190 signal_undo_redo_status_changed()();
194 studio::Instance::save_as(const synfig::String &file_name)
196 if(synfigapp::Instance::save_as(file_name))
198 // after changing the filename, update the render settings with the new filename
199 list<handle<CanvasView> >::iterator iter;
200 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
201 (*iter)->render_settings.set_entry_filename();
202 App::add_recent_file(file_name);
209 studio::Instance::open()
211 App::dialog_open(get_file_name());
215 studio::Instance::save()
217 // if we don't have a real filename yet then we need to ask where to save it
218 if (!has_real_filename())
220 if (dialog_save_as())
223 return STATUS_CANCEL;
226 if (synfigapp::Instance::save())
229 App::dialog_error_blocking("Save - Error","Unable to save to '" + get_file_name() + "'");
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(DEFAULT_FILENAME_PREFIX) != 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 App::dialog_error_blocking("SaveAs - Error",
260 "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\"."
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 App::dialog_error_blocking("SaveAs - Error","Unable to check whether '" + filename + "' exists.");
318 // if the file exists and the user doesn't want to overwrite it, keep prompting for a filename
319 if ((stat_return == 0) &&
320 !App::dialog_yes_no("File exists",
323 "' already exists.\n\n"
324 "Do you want to replace it with the file you are saving?"))
328 if(save_as(filename))
330 synfig::set_file_version(ReleaseVersion(RELEASE_VERSION_END-1));
334 App::dialog_error_blocking("SaveAs - Error","Unable to save to '" + filename + "'");
341 Instance::update_all_titles()
343 list<handle<CanvasView> >::iterator iter;
344 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
345 (*iter)->update_title();
351 // This will increase the reference count so we don't get DELETED
352 // until we are ready
353 handle<Instance> me(this);
355 // Make sure we aren't selected as the current instance
356 if(studio::App::get_selected_instance()==this)
357 studio::App::set_selected_instance(0);
359 // Turn-off/clean-up auto recovery
360 studio::App::auto_recover->clear_backup(get_canvas());
362 // Remove us from the active instance list
363 std::list<etl::handle<studio::Instance> >::iterator iter;
364 for(iter=studio::App::instance_list.begin();iter!=studio::App::instance_list.end();iter++)
367 assert(iter!=studio::App::instance_list.end());
368 if(iter!=studio::App::instance_list.end())
369 studio::App::instance_list.erase(iter);
371 // Send out a signal that we are being deleted
372 studio::App::signal_instance_deleted()(this);
374 // Hide all of the canvas views
375 for(std::list<etl::handle<CanvasView> >::iterator iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
378 // Consume pending events before deleting the canvas views
379 while(studio::App::events_pending())studio::App::iteration(false);
381 // Delete all of the canvas views
382 canvas_view_list().clear();
384 // If there is another open instance to select,
385 // go ahead and do so. If not, never mind.
386 if(studio::App::instance_list.empty())
388 studio::App::set_selected_canvas_view(0);
389 studio::App::set_selected_instance(0);
392 studio::App::instance_list.front()->canvas_view_list().front()->present();
396 Instance::insert_canvas(Gtk::TreeRow row, synfig::Canvas::Handle canvas)
398 CanvasTreeModel canvas_tree_model;
401 row[canvas_tree_model.icon] = Gtk::Button().render_icon(Gtk::StockID("synfig-canvas"),Gtk::ICON_SIZE_SMALL_TOOLBAR);
402 row[canvas_tree_model.id] = canvas->get_id();
403 row[canvas_tree_model.name] = canvas->get_name();
404 if(canvas->is_root())
405 row[canvas_tree_model.label] = basename(canvas->get_file_name());
407 if(!canvas->get_id().empty())
408 row[canvas_tree_model.label] = canvas->get_id();
410 if(!canvas->get_name().empty())
411 row[canvas_tree_model.label] = canvas->get_name();
413 row[canvas_tree_model.label] = _("[Unnamed]");
415 row[canvas_tree_model.canvas] = canvas;
416 row[canvas_tree_model.is_canvas] = true;
417 row[canvas_tree_model.is_value_node] = false;
420 synfig::Canvas::Children::iterator iter;
421 synfig::Canvas::Children &children(canvas->children());
423 for(iter=children.begin();iter!=children.end();iter++)
424 insert_canvas(*(canvas_tree_store()->append(row.children())),*iter);
428 if(!canvas->value_node_list().empty())
430 Gtk::TreeRow valuenode_row = *(canvas_tree_store()->append(row.children()));
432 valuenode_row[canvas_tree_model.label] = "<defs>";
433 valuenode_row[canvas_tree_model.canvas] = canvas;
434 valuenode_row[canvas_tree_model.is_canvas] = false;
435 valuenode_row[canvas_tree_model.is_value_node] = false;
437 synfig::ValueNodeList::iterator iter;
438 synfig::ValueNodeList &value_node_list(canvas->value_node_list());
440 for(iter=value_node_list.begin();iter!=value_node_list.end();iter++)
441 insert_value_node(*(canvas_tree_store()->append(valuenode_row.children())),canvas,*iter);
448 Instance::insert_value_node(Gtk::TreeRow row,Canvas::Handle canvas,etl::handle<synfig::ValueNode> value_node)
450 CanvasTreeModel canvas_tree_model;
454 row[canvas_tree_model.id] = value_node->get_id();
455 row[canvas_tree_model.name] = value_node->get_id();
456 row[canvas_tree_model.label] = value_node->get_id();
457 row[canvas_tree_model.canvas] = canvas;
458 row[canvas_tree_model.value_node] = value_node;
459 row[canvas_tree_model.is_canvas] = false;
460 row[canvas_tree_model.is_value_node] = true;
461 row[canvas_tree_model.icon] = Gtk::Button().render_icon(valuenode_icon(value_node),Gtk::ICON_SIZE_SMALL_TOOLBAR);
466 Instance::refresh_canvas_tree()
468 canvas_tree_store()->clear();
469 Gtk::TreeRow row = *(canvas_tree_store()->prepend());
470 insert_canvas(row,get_canvas());
474 Instance::dialog_cvs_commit()
476 calc_repository_info();
479 App::dialog_error_blocking(_("Error"),_("You must first add this composition to the repository"));
486 if(synfigapp::Instance::get_action_count())
488 if(!App::dialog_yes_no(_("CVS Commit"), _("This will save any changes you have made. Are you sure?")))
495 App::dialog_error_blocking(_("Error"),_("The local copy of the file hasn't been changed since the last update.\nNothing to commit!"));
499 if(!App::dialog_entry(_("CVS Commit"),_("Enter a log message describing the changes you have made"), message))
502 OneMoment one_moment;
507 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to COMMIT"));
513 Instance::dialog_cvs_add()
515 calc_repository_info();
518 App::dialog_error_blocking(_("Error"),_("This composition has already been added to the repository"));
525 //if(!App::dialog_entry(_("CVS Add"),_("Enter a log message describing the file"), message))
527 OneMoment one_moment;
532 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to ADD"));
538 Instance::dialog_cvs_update()
540 calc_repository_info();
543 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to update from!"));
548 App::dialog_error_blocking(_("Info"),_("This file is up-to-date"));
554 String filename(get_file_name());
555 if(synfigapp::Instance::get_action_count())
557 if(!App::dialog_yes_no(_("CVS Update"), _("This will save any changes you have made. Are you sure?")))
561 OneMoment one_moment;
562 time_t oldtime=get_original_timestamp();
564 calc_repository_info();
565 // If something has been updated...
566 if(oldtime!=get_original_timestamp())
573 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
575 //update_all_titles();
579 Instance::dialog_cvs_revert()
581 calc_repository_info();
584 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to revert to!"));
589 String filename(get_file_name());
590 if(!App::dialog_yes_no(_("CVS Revert"),
591 _("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?")
595 OneMoment one_moment;
597 // Remove the old file
598 if(remove(get_file_name().c_str())!=0)
600 App::dialog_error_blocking(_("Error"),_("Unable to remove previous version"));
609 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
611 //update_all_titles();
615 Instance::_revert(Instance *instance)
617 OneMoment one_moment;
619 String filename(instance->get_file_name());
621 Canvas::Handle canvas(instance->get_canvas());
625 if(canvas->count()!=1)
628 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."));
639 // Schedule a revert to occur in a few moments
640 Glib::signal_timeout().connect(
643 sigc::ptr_fun(&Instance::_revert),
653 Instance::safe_revert()
655 if(synfigapp::Instance::get_action_count())
656 if(!App::dialog_yes_no(_("Revert to saved"), _("You will lose any changes you have made since your last save.\nAre you sure?")))
663 Instance::safe_close()
665 handle<CanvasView> canvas_view = find_canvas_view(get_canvas());
666 handle<synfigapp::UIInterface> uim=canvas_view->get_ui_interface();
668 // if the animation is currently playing, closing the window will cause a crash,
670 if (canvas_view->is_playing())
672 canvas_view->present();
673 App::dialog_error_blocking("Close Error", "The animation is currently playing so the window cannot be closed.");
676 if(get_action_count())
679 string str=strprintf(_("Would you like to save your changes to %s?"),basename(get_file_name()).c_str() );
680 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
681 if(answer==synfigapp::UIInterface::RESPONSE_YES)
683 enum Status status = save();
684 if (status == STATUS_OK) break;
685 else if (status == STATUS_CANCEL) return false;
687 if(answer==synfigapp::UIInterface::RESPONSE_NO)
689 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
695 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());
696 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
698 if(answer==synfigapp::UIInterface::RESPONSE_YES)
700 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
710 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
712 synfigapp::Action::CandidateList candidate_list;
713 synfigapp::Action::CandidateList::iterator iter;
715 candidate_list=compile_candidate_list(param_list,category);
717 candidate_list.sort();
719 // if(candidate_list.empty())
720 // synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
722 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
724 Gtk::StockID stock_id(get_action_stock_id(*iter));
726 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
728 action_group->add(Gtk::Action::create(
729 "action-"+iter->name,
731 iter->local_name,iter->local_name
736 *const_cast<studio::Instance*>(this),
737 &studio::Instance::process_action
744 ui_info+=strprintf("<menuitem action='action-%s' />",iter->name.c_str());
750 Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList ¶m_list,synfigapp::Action::Category category)const
752 synfigapp::Action::CandidateList candidate_list;
753 synfigapp::Action::CandidateList::iterator iter;
755 candidate_list=compile_candidate_list(param_list,category);
757 candidate_list.sort();
759 if(candidate_list.empty())
760 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
762 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
764 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
766 Gtk::Image* image(manage(new Gtk::Image()));
767 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
770 if(iter->task=="raise")
771 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
772 else if(iter->task=="lower")
773 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
774 else if(iter->task=="move_top")
775 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
776 else if(iter->task=="move_bottom")
777 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
778 else if(iter->task=="remove")
779 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
780 else if(iter->task=="set_on")
781 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
782 else if(iter->task=="set_off")
783 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
784 else if(iter->task=="duplicate")
785 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
786 else if(iter->task=="remove")
787 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
790 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
791 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
792 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
795 menu->items().push_back(
796 Gtk::Menu_Helpers::ImageMenuElem(
802 *const_cast<studio::Instance*>(this),
803 &studio::Instance::process_action
816 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
818 synfigapp::Action::CandidateList candidate_list;
819 synfigapp::Action::CandidateList candidate_list2;
821 synfigapp::Action::CandidateList::iterator iter;
823 candidate_list=compile_candidate_list(param_list,category);
824 candidate_list2=compile_candidate_list(param_list2,category);
826 candidate_list.sort();
828 if(candidate_list.empty())
829 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
830 if(candidate_list2.empty())
831 synfig::warning("%s:%d Action CandidateList2 is empty!", __FILE__, __LINE__);
833 // Separate out the candidate lists so that there are no conflicts
834 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
836 synfigapp::Action::CandidateList::iterator iter2(candidate_list2.find(iter->name));
837 if(iter2!=candidate_list2.end())
838 candidate_list2.erase(iter2);
841 for(iter=candidate_list2.begin();iter!=candidate_list2.end();++iter)
843 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
845 Gtk::Image* image(manage(new Gtk::Image()));
846 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
848 /* if(iter->task=="raise")
849 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
850 else if(iter->task=="lower")
851 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
852 else if(iter->task=="move_top")
853 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
854 else if(iter->task=="move_bottom")
855 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
856 else if(iter->task=="remove")
857 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
858 else if(iter->task=="set_on")
859 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
860 else if(iter->task=="set_off")
861 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
862 else if(iter->task=="duplicate")
863 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
864 else if(iter->task=="remove")
865 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
868 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
869 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
870 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
873 menu->items().push_back(
874 Gtk::Menu_Helpers::ImageMenuElem(
880 *const_cast<studio::Instance*>(this),
881 &studio::Instance::process_action
892 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
894 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
896 Gtk::Image* image(manage(new Gtk::Image()));
897 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
898 /* if(iter->task=="raise")
899 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
900 else if(iter->task=="lower")
901 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
902 else if(iter->task=="move_top")
903 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
904 else if(iter->task=="move_bottom")
905 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
906 else if(iter->task=="remove")
907 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
908 else if(iter->task=="set_on")
909 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
910 else if(iter->task=="set_off")
911 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
912 else if(iter->task=="duplicate")
913 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
914 else if(iter->task=="remove")
915 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
918 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
919 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
920 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
923 menu->items().push_back(
924 Gtk::Menu_Helpers::ImageMenuElem(
930 *const_cast<studio::Instance*>(this),
931 &studio::Instance::process_action
944 Instance::process_action(synfig::String name, synfigapp::Action::ParamList param_list)
946 assert(synfigapp::Action::book().count(name));
948 synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second);
950 synfigapp::Action::Handle action(entry.factory());
954 synfig::error("Bad Action");
958 action->set_param_list(param_list);
960 synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab());
961 synfigapp::Action::ParamVocab::const_iterator iter;
963 for(iter=param_vocab.begin();iter!=param_vocab.end();++iter)
965 if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion()))
968 // If the parameter is optionally user-supplied,
969 // and has not been already provided in the param_list,
970 // then we should go ahead and see if we can
971 // provide that data.
972 if(iter->get_user_supplied() && param_list.count(iter->get_name())==0)
974 switch(iter->get_type())
976 case synfigapp::Action::Param::TYPE_STRING:
979 if(!studio::App::dialog_entry(entry.local_name, iter->get_local_name()+":"+iter->get_desc(),str))
981 action->set_param(iter->get_name(),str);
985 synfig::error("Unsupported user-supplied action parameter");
992 if(!action->is_ready())
994 synfig::error("Action not ready");
998 perform_action(action);
1002 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location)
1004 Gtk::Menu& parammenu(*menu);
1006 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1008 if(!canvas_interface)
1011 synfigapp::Action::ParamList param_list,param_list2;
1012 param_list=canvas_interface->generate_param_list(value_desc);
1013 param_list.add("origin",location);
1015 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1017 param_list2=canvas_interface->generate_param_list(
1018 synfigapp::ValueDesc(
1019 ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
1023 param_list2.add("origin",location);
1026 // Populate the convert menu by looping through
1027 // the ValueNode book and find the ones that are
1030 // show the 'Convert' sub-menu if this valuedesc is anything other than either:
1031 // the 'Index' parameter of a Duplicate layer
1033 // a Duplicate ValueNode whose parent is not a (layer or ValueNode)
1034 if (!((value_desc.parent_is_layer_param() &&
1035 value_desc.get_layer()->get_name() == "duplicate" &&
1036 value_desc.get_param_name() == "index") ||
1037 (value_desc.is_value_node() &&
1038 ValueNode_Duplicate::Handle::cast_dynamic(value_desc.get_value_node()) &&
1039 !(value_desc.parent_is_layer_param() ||
1040 value_desc.parent_is_value_node()))))
1042 Gtk::Menu *convert_menu=manage(new Gtk::Menu());
1043 LinkableValueNode::Book::const_iterator iter;
1044 for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
1046 if(iter->second.check_type(value_desc.get_value_type()))
1048 convert_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->second.local_name,
1052 sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
1062 parammenu.items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::Stock::CONVERT,*convert_menu));
1065 if(param_list2.empty())
1066 add_actions_to_menu(¶mmenu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1068 add_actions_to_menu(¶mmenu, param_list2,param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1070 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1072 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
1075 if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
1077 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
1081 // try to find a waypoint at the current time - if we
1082 // can't, we don't want the menu entry - an exception is thrown
1083 WaypointList::iterator iter(value_node->find(canvas->get_time()));
1084 std::set<synfig::Waypoint, std::less<UniqueID> > waypoint_set;
1085 waypoint_set.insert(*iter);
1087 parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
1091 sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked_canvasview),
1107 edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1109 etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1112 "Edit Multiple Waypoints", // Title
1114 true // use_separator
1117 Widget_WaypointModel widget_waypoint_model;
1118 widget_waypoint_model.show();
1120 dialog.get_vbox()->pack_start(widget_waypoint_model);
1122 dialog.add_button(Gtk::StockID("gtk-apply"),1);
1123 dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1126 if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1128 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1130 std::list<synfigapp::ValueDesc>::iterator iter;
1131 for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1133 synfigapp::ValueDesc value_desc(*iter);
1135 if(!value_desc.is_valid())
1138 ValueNode_Animated::Handle value_node;
1140 // If this value isn't a ValueNode_Animated, but
1141 // it is somewhat constant, then go ahead and convert
1142 // it to a ValueNode_Animated.
1143 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1146 if(value_desc.is_value_node())
1147 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1149 value=value_desc.get_value();
1151 value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1153 synfigapp::Action::Handle action;
1155 if(!value_desc.is_value_node())
1157 action=synfigapp::Action::create("value_desc_connect");
1158 action->set_param("dest",value_desc);
1159 action->set_param("src",ValueNode::Handle(value_node));
1163 action=synfigapp::Action::create("value_node_replace");
1164 action->set_param("dest",value_desc.get_value_node());
1165 action->set_param("src",ValueNode::Handle(value_node));
1168 action->set_param("canvas",canvas_view->get_canvas());
1169 action->set_param("canvas_interface",canvas_interface);
1171 if(!canvas_interface->get_instance()->perform_action(action))
1173 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1180 if(value_desc.is_value_node())
1181 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1186 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set_smart"));
1190 canvas_view->get_ui_interface()->error(_("Unable to find waypoint_set_smart action"));
1195 action->set_param("canvas",canvas_view->get_canvas());
1196 action->set_param("canvas_interface",canvas_interface);
1197 action->set_param("value_node",ValueNode::Handle(value_node));
1198 action->set_param("time",canvas_interface->get_time());
1199 action->set_param("model",widget_waypoint_model.get_waypoint_model());
1201 if(!canvas_interface->get_instance()->perform_action(action))
1203 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1210 //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1219 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1221 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1223 synfigapp::Action::ParamList param_list;
1224 param_list=canvas_interface->generate_param_list(value_desc_list);
1226 add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1228 // Add the edit waypoints option if that might be useful
1229 if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1231 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1235 &edit_several_waypoints
1239 find_canvas_view(canvas)