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::save()
211 // the filename will be set to "Synfig Animation 1" or some such when first created
212 // and will be changed to an absolute path once it has been saved
213 // so if it still begins with "Synfig Animation " then we need to ask where to save it
214 if(get_file_name().find(DEFAULT_FILENAME_PREFIX)==0)
216 if (dialog_save_as())
219 return STATUS_CANCEL;
222 if (synfigapp::Instance::save())
225 App::dialog_error_blocking("Save - Error","Unable to save to '" + get_file_name() + "'");
230 studio::Instance::dialog_save_as()
232 string filename = get_file_name();
233 Canvas::Handle canvas(get_canvas());
236 OneMoment one_moment;
237 std::set<Node*>::iterator iter;
238 for(iter=canvas->parent_set.begin();iter!=canvas->parent_set.end();++iter)
240 synfig::Node* node(*iter);
241 for(;!node->parent_set.empty();node=*node->parent_set.begin())
243 Layer::Handle parent_layer(dynamic_cast<Layer*>(node));
244 if(parent_layer && parent_layer->get_canvas()->get_root()!=get_canvas())
246 App::dialog_error_blocking("SaveAs - Error",
247 "There is currently a bug when using \"SaveAs\"\n"
248 "on a composition that is being referenced by other\n"
249 "files that are currently open. Close these\n"
250 "other files first before trying to use \"SaveAs\"."
261 if (get_file_name().find(DEFAULT_FILENAME_PREFIX) != 0)
262 filename = absolute_path(filename);
264 // show the canvas' name if it has one, else its ID
265 while (App::dialog_save_file((_("Choose a Filename to Save As") +
267 (canvas->get_name().empty() ? canvas->get_id() : canvas->get_name()) +
269 filename, ANIMATION_DIR_PREFERENCE))
271 // If the filename still has wildcards, then we should
272 // continue looking for the file we want
273 string base_filename = basename(filename);
274 if (find(base_filename.begin(),base_filename.end(),'*')!=base_filename.end())
277 if (filename_extension(filename) == "")
282 String ext(filename_extension(filename));
283 if(ext!=".sif" && ext!=".sifz" && !App::dialog_yes_no(_("Unknown extension"),
284 _("You have given the file name an extension\nwhich I do not recognize. Are you sure this is what you want?")))
294 int stat_return = stat(filename.c_str(), &s);
296 // if stat() fails with something other than 'file doesn't exist', there's been a real
297 // error of some kind. let's give up now and ask for a new path.
298 if (stat_return == -1 && errno != ENOENT)
300 perror(filename.c_str());
301 App::dialog_error_blocking("SaveAs - Error","Unable to check whether '" + filename + "' exists.");
305 // if the file exists and the user doesn't want to overwrite it, keep prompting for a filename
306 if ((stat_return == 0) &&
307 !App::dialog_yes_no("File exists",
310 "' already exists.\n\n"
311 "Do you want to replace it with the file you are saving?"))
315 if(save_as(filename))
317 synfig::set_file_version(ReleaseVersion(RELEASE_VERSION_END-1));
321 App::dialog_error_blocking("SaveAs - Error","Unable to save to '" + filename + "'");
328 Instance::update_all_titles()
330 list<handle<CanvasView> >::iterator iter;
331 for(iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
332 (*iter)->update_title();
338 // This will increase the reference count so we don't get DELETED
339 // until we are ready
340 handle<Instance> me(this);
342 // Make sure we aren't selected as the current instance
343 if(studio::App::get_selected_instance()==this)
344 studio::App::set_selected_instance(0);
346 // Turn-off/clean-up auto recovery
347 studio::App::auto_recover->clear_backup(get_canvas());
349 // Remove us from the active instance list
350 std::list<etl::handle<studio::Instance> >::iterator iter;
351 for(iter=studio::App::instance_list.begin();iter!=studio::App::instance_list.end();iter++)
354 assert(iter!=studio::App::instance_list.end());
355 if(iter!=studio::App::instance_list.end())
356 studio::App::instance_list.erase(iter);
358 // Send out a signal that we are being deleted
359 studio::App::signal_instance_deleted()(this);
361 // Hide all of the canvas views
362 for(std::list<etl::handle<CanvasView> >::iterator iter=canvas_view_list().begin();iter!=canvas_view_list().end();iter++)
365 // Consume pending events before deleting the canvas views
366 while(studio::App::events_pending())studio::App::iteration(false);
368 // Delete all of the canvas views
369 canvas_view_list().clear();
371 // If there is another open instance to select,
372 // go ahead and do so. If not, never mind.
373 if(studio::App::instance_list.empty())
375 studio::App::set_selected_canvas_view(0);
376 studio::App::set_selected_instance(0);
379 studio::App::instance_list.front()->canvas_view_list().front()->present();
384 Instance::insert_canvas(Gtk::TreeRow row, synfig::Canvas::Handle canvas)
386 CanvasTreeModel canvas_tree_model;
389 row[canvas_tree_model.icon] = Gtk::Button().render_icon(Gtk::StockID("synfig-canvas"),Gtk::ICON_SIZE_SMALL_TOOLBAR);
390 row[canvas_tree_model.id] = canvas->get_id();
391 row[canvas_tree_model.name] = canvas->get_name();
392 if(canvas->is_root())
393 row[canvas_tree_model.label] = basename(canvas->get_file_name());
395 if(!canvas->get_id().empty())
396 row[canvas_tree_model.label] = canvas->get_id();
398 if(!canvas->get_name().empty())
399 row[canvas_tree_model.label] = canvas->get_name();
401 row[canvas_tree_model.label] = _("[Unnamed]");
403 row[canvas_tree_model.canvas] = canvas;
404 row[canvas_tree_model.is_canvas] = true;
405 row[canvas_tree_model.is_value_node] = false;
408 synfig::Canvas::Children::iterator iter;
409 synfig::Canvas::Children &children(canvas->children());
411 for(iter=children.begin();iter!=children.end();iter++)
412 insert_canvas(*(canvas_tree_store()->append(row.children())),*iter);
416 if(!canvas->value_node_list().empty())
418 Gtk::TreeRow valuenode_row = *(canvas_tree_store()->append(row.children()));
420 valuenode_row[canvas_tree_model.label] = "<defs>";
421 valuenode_row[canvas_tree_model.canvas] = canvas;
422 valuenode_row[canvas_tree_model.is_canvas] = false;
423 valuenode_row[canvas_tree_model.is_value_node] = false;
425 synfig::ValueNodeList::iterator iter;
426 synfig::ValueNodeList &value_node_list(canvas->value_node_list());
428 for(iter=value_node_list.begin();iter!=value_node_list.end();iter++)
429 insert_value_node(*(canvas_tree_store()->append(valuenode_row.children())),canvas,*iter);
437 Instance::insert_value_node(Gtk::TreeRow row,Canvas::Handle canvas,etl::handle<synfig::ValueNode> value_node)
439 CanvasTreeModel canvas_tree_model;
443 row[canvas_tree_model.id] = value_node->get_id();
444 row[canvas_tree_model.name] = value_node->get_id();
445 row[canvas_tree_model.label] = value_node->get_id();
446 row[canvas_tree_model.canvas] = canvas;
447 row[canvas_tree_model.value_node] = value_node;
448 row[canvas_tree_model.is_canvas] = false;
449 row[canvas_tree_model.is_value_node] = true;
450 row[canvas_tree_model.icon] = Gtk::Button().render_icon(valuenode_icon(value_node),Gtk::ICON_SIZE_SMALL_TOOLBAR);
455 Instance::refresh_canvas_tree()
457 canvas_tree_store()->clear();
458 Gtk::TreeRow row = *(canvas_tree_store()->prepend());
459 insert_canvas(row,get_canvas());
463 Instance::dialog_cvs_commit()
465 calc_repository_info();
468 App::dialog_error_blocking(_("Error"),_("You must first add this composition to the repository"));
475 if(synfigapp::Instance::get_action_count())
477 if(!App::dialog_yes_no(_("CVS Commit"), _("This will save any changes you have made. Are you sure?")))
484 App::dialog_error_blocking(_("Error"),_("The local copy of the file hasn't been changed since the last update.\nNothing to commit!"));
488 if(!App::dialog_entry(_("CVS Commit"),_("Enter a log message describing the changes you have made"), message))
491 OneMoment one_moment;
496 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to COMMIT"));
502 Instance::dialog_cvs_add()
504 calc_repository_info();
507 App::dialog_error_blocking(_("Error"),_("This composition has already been added to the repository"));
514 //if(!App::dialog_entry(_("CVS Add"),_("Enter a log message describing the file"), message))
516 OneMoment one_moment;
521 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to ADD"));
527 Instance::dialog_cvs_update()
529 calc_repository_info();
532 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to update from!"));
537 App::dialog_error_blocking(_("Info"),_("This file is up-to-date"));
543 String filename(get_file_name());
544 if(synfigapp::Instance::get_action_count())
546 if(!App::dialog_yes_no(_("CVS Update"), _("This will save any changes you have made. Are you sure?")))
550 OneMoment one_moment;
551 time_t oldtime=get_original_timestamp();
553 calc_repository_info();
554 // If something has been updated...
555 if(oldtime!=get_original_timestamp())
562 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
564 //update_all_titles();
568 Instance::dialog_cvs_revert()
570 calc_repository_info();
573 App::dialog_error_blocking(_("Error"),_("This file is not under version control, so there is nothing to revert to!"));
578 String filename(get_file_name());
579 if(!App::dialog_yes_no(_("CVS Revert"),
580 _("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?")
584 OneMoment one_moment;
586 // Remove the old file
587 if(remove(get_file_name().c_str())!=0)
589 App::dialog_error_blocking(_("Error"),_("Unable to remove previous version"));
598 App::dialog_error_blocking(_("Error"),_("An error has occurred when trying to UPDATE"));
600 //update_all_titles();
604 Instance::_revert(Instance *instance)
606 OneMoment one_moment;
608 String filename(instance->get_file_name());
610 Canvas::Handle canvas(instance->get_canvas());
614 if(canvas->count()!=1)
617 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."));
628 // Schedule a revert to occur in a few moments
629 Glib::signal_timeout().connect(
632 sigc::ptr_fun(&Instance::_revert),
642 Instance::safe_revert()
644 if(synfigapp::Instance::get_action_count())
645 if(!App::dialog_yes_no(_("Revert to saved"), _("You will lose any changes you have made since your last save.\nAre you sure?")))
652 Instance::safe_close()
654 handle<CanvasView> canvas_view = find_canvas_view(get_canvas());
655 handle<synfigapp::UIInterface> uim=canvas_view->get_ui_interface();
657 // if the animation is currently playing, closing the window will cause a crash,
659 if (canvas_view->is_playing())
661 canvas_view->present();
662 App::dialog_error_blocking("Close Error", "The animation is currently playing so the window cannot be closed.");
665 if(get_action_count())
668 string str=strprintf(_("Would you like to save your changes to %s?"),basename(get_file_name()).c_str() );
669 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
670 if(answer==synfigapp::UIInterface::RESPONSE_YES)
672 enum Status status = save();
673 if (status == STATUS_OK) break;
674 else if (status == STATUS_CANCEL) return false;
676 if(answer==synfigapp::UIInterface::RESPONSE_NO)
678 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
684 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());
685 int answer=uim->yes_no_cancel(get_canvas()->get_name(),str,synfigapp::UIInterface::RESPONSE_YES);
687 if(answer==synfigapp::UIInterface::RESPONSE_YES)
689 if(answer==synfigapp::UIInterface::RESPONSE_CANCEL)
700 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
702 synfigapp::Action::CandidateList candidate_list;
703 synfigapp::Action::CandidateList::iterator iter;
705 candidate_list=compile_candidate_list(param_list,category);
707 candidate_list.sort();
709 // if(candidate_list.empty())
710 // synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
712 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
714 Gtk::StockID stock_id(get_action_stock_id(*iter));
716 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
718 action_group->add(Gtk::Action::create(
719 "action-"+iter->name,
721 iter->local_name,iter->local_name
726 *const_cast<studio::Instance*>(this),
727 &studio::Instance::process_action
734 ui_info+=strprintf("<menuitem action='action-%s' />",iter->name.c_str());
740 Instance::add_actions_to_menu(Gtk::Menu *menu, const synfigapp::Action::ParamList ¶m_list,synfigapp::Action::Category category)const
742 synfigapp::Action::CandidateList candidate_list;
743 synfigapp::Action::CandidateList::iterator iter;
745 candidate_list=compile_candidate_list(param_list,category);
747 candidate_list.sort();
749 if(candidate_list.empty())
750 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
752 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
754 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
756 Gtk::Image* image(manage(new Gtk::Image()));
757 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
760 if(iter->task=="raise")
761 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
762 else if(iter->task=="lower")
763 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
764 else if(iter->task=="move_top")
765 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
766 else if(iter->task=="move_bottom")
767 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
768 else if(iter->task=="remove")
769 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
770 else if(iter->task=="set_on")
771 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
772 else if(iter->task=="set_off")
773 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
774 else if(iter->task=="duplicate")
775 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
776 else if(iter->task=="remove")
777 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
780 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
781 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
782 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
785 menu->items().push_back(
786 Gtk::Menu_Helpers::ImageMenuElem(
792 *const_cast<studio::Instance*>(this),
793 &studio::Instance::process_action
806 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
808 synfigapp::Action::CandidateList candidate_list;
809 synfigapp::Action::CandidateList candidate_list2;
811 synfigapp::Action::CandidateList::iterator iter;
813 candidate_list=compile_candidate_list(param_list,category);
814 candidate_list2=compile_candidate_list(param_list2,category);
816 candidate_list.sort();
818 if(candidate_list.empty())
819 synfig::warning("%s:%d Action CandidateList is empty!", __FILE__, __LINE__);
820 if(candidate_list2.empty())
821 synfig::warning("%s:%d Action CandidateList2 is empty!", __FILE__, __LINE__);
823 // Separate out the candidate lists so that there are no conflicts
824 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
826 synfigapp::Action::CandidateList::iterator iter2(candidate_list2.find(iter->name));
827 if(iter2!=candidate_list2.end())
828 candidate_list2.erase(iter2);
831 for(iter=candidate_list2.begin();iter!=candidate_list2.end();++iter)
833 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
835 Gtk::Image* image(manage(new Gtk::Image()));
836 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
838 /* if(iter->task=="raise")
839 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
840 else if(iter->task=="lower")
841 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
842 else if(iter->task=="move_top")
843 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
844 else if(iter->task=="move_bottom")
845 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
846 else if(iter->task=="remove")
847 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
848 else if(iter->task=="set_on")
849 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
850 else if(iter->task=="set_off")
851 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
852 else if(iter->task=="duplicate")
853 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
854 else if(iter->task=="remove")
855 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
858 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
859 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
860 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
863 menu->items().push_back(
864 Gtk::Menu_Helpers::ImageMenuElem(
870 *const_cast<studio::Instance*>(this),
871 &studio::Instance::process_action
882 for(iter=candidate_list.begin();iter!=candidate_list.end();++iter)
884 if(!(iter->category&synfigapp::Action::CATEGORY_HIDDEN))
886 Gtk::Image* image(manage(new Gtk::Image()));
887 Gtk::Stock::lookup(get_action_stock_id(*iter),Gtk::ICON_SIZE_MENU,*image);
888 /* if(iter->task=="raise")
889 Gtk::Stock::lookup(Gtk::Stock::GO_UP,Gtk::ICON_SIZE_MENU,*image);
890 else if(iter->task=="lower")
891 Gtk::Stock::lookup(Gtk::Stock::GO_DOWN,Gtk::ICON_SIZE_MENU,*image);
892 else if(iter->task=="move_top")
893 Gtk::Stock::lookup(Gtk::Stock::GOTO_TOP,Gtk::ICON_SIZE_MENU,*image);
894 else if(iter->task=="move_bottom")
895 Gtk::Stock::lookup(Gtk::Stock::GOTO_BOTTOM,Gtk::ICON_SIZE_MENU,*image);
896 else if(iter->task=="remove")
897 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
898 else if(iter->task=="set_on")
899 Gtk::Stock::lookup(Gtk::Stock::YES,Gtk::ICON_SIZE_MENU,*image);
900 else if(iter->task=="set_off")
901 Gtk::Stock::lookup(Gtk::Stock::NO,Gtk::ICON_SIZE_MENU,*image);
902 else if(iter->task=="duplicate")
903 Gtk::Stock::lookup(Gtk::Stock::COPY,Gtk::ICON_SIZE_MENU,*image);
904 else if(iter->task=="remove")
905 Gtk::Stock::lookup(Gtk::Stock::DELETE,Gtk::ICON_SIZE_MENU,*image);
908 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->name),Gtk::ICON_SIZE_MENU,*image) ||
909 Gtk::Stock::lookup(Gtk::StockID("gtk-"+iter->task),Gtk::ICON_SIZE_MENU,*image) ||
910 Gtk::Stock::lookup(Gtk::StockID("synfig-"+iter->task),Gtk::ICON_SIZE_MENU,*image);
913 menu->items().push_back(
914 Gtk::Menu_Helpers::ImageMenuElem(
920 *const_cast<studio::Instance*>(this),
921 &studio::Instance::process_action
934 Instance::process_action(synfig::String name, synfigapp::Action::ParamList param_list)
936 assert(synfigapp::Action::book().count(name));
938 synfigapp::Action::BookEntry entry(synfigapp::Action::book().find(name)->second);
940 synfigapp::Action::Handle action(entry.factory());
944 synfig::error("Bad Action");
948 action->set_param_list(param_list);
950 synfigapp::Action::ParamVocab param_vocab(entry.get_param_vocab());
951 synfigapp::Action::ParamVocab::const_iterator iter;
953 for(iter=param_vocab.begin();iter!=param_vocab.end();++iter)
955 if(!iter->get_mutual_exclusion().empty() && param_list.count(iter->get_mutual_exclusion()))
958 // If the parameter is optionally user-supplied,
959 // and has not been already provided in the param_list,
960 // then we should go ahead and see if we can
961 // provide that data.
962 if(iter->get_user_supplied() && param_list.count(iter->get_name())==0)
964 switch(iter->get_type())
966 case synfigapp::Action::Param::TYPE_STRING:
969 if(!studio::App::dialog_entry(entry.local_name, iter->get_local_name()+":"+iter->get_desc(),str))
971 action->set_param(iter->get_name(),str);
975 synfig::error("Unsupported user-supplied action parameter");
982 if(!action->is_ready())
984 synfig::error("Action not ready");
988 perform_action(action);
992 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas, synfigapp::ValueDesc value_desc, float location)
994 Gtk::Menu& parammenu(*menu);
996 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
998 if(!canvas_interface)
1001 synfigapp::Action::ParamList param_list,param_list2;
1002 param_list=canvas_interface->generate_param_list(value_desc);
1003 param_list.add("origin",location);
1005 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1007 param_list2=canvas_interface->generate_param_list(
1008 synfigapp::ValueDesc(
1009 ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node())
1013 param_list2.add("origin",location);
1017 // Populate the convert menu by looping through
1018 // the ValueNode book and find the ones that are
1021 // show the 'Convert' sub-menu if this valuedesc is anything other than either:
1022 // the 'Index' parameter of a Duplicate layer
1024 // a Duplicate ValueNode whose parent is not a (layer or ValueNode)
1025 if (!((value_desc.parent_is_layer_param() &&
1026 value_desc.get_layer()->get_name() == "duplicate" &&
1027 value_desc.get_param_name() == "index") ||
1028 (value_desc.is_value_node() &&
1029 ValueNode_Duplicate::Handle::cast_dynamic(value_desc.get_value_node()) &&
1030 !(value_desc.parent_is_layer_param() ||
1031 value_desc.parent_is_value_node()))))
1033 Gtk::Menu *convert_menu=manage(new Gtk::Menu());
1034 LinkableValueNode::Book::const_iterator iter;
1035 for(iter=LinkableValueNode::book().begin();iter!=LinkableValueNode::book().end();++iter)
1037 if(iter->second.check_type(value_desc.get_value_type()))
1039 convert_menu->items().push_back(Gtk::Menu_Helpers::MenuElem(iter->second.local_name,
1043 sigc::mem_fun(*canvas_interface.get(),&synfigapp::CanvasInterface::convert),
1053 parammenu.items().push_back(Gtk::Menu_Helpers::StockMenuElem(Gtk::Stock::CONVERT,*convert_menu));
1056 if(param_list2.empty())
1057 add_actions_to_menu(¶mmenu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1059 add_actions_to_menu(¶mmenu, param_list2,param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1061 if(value_desc.get_value_type()==ValueBase::TYPE_BLINEPOINT && value_desc.is_value_node() && ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()))
1063 value_desc=synfigapp::ValueDesc(ValueNode_Composite::Handle::cast_dynamic(value_desc.get_value_node()),0);
1066 if(value_desc.is_value_node() && ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()))
1068 ValueNode_Animated::Handle value_node(ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node()));
1072 // try to find a waypoint at the current time - if we
1073 // can't, we don't want the menu entry - an exception is thrown
1074 WaypointList::iterator iter(value_node->find(canvas->get_time()));
1075 std::set<synfig::Waypoint, std::less<UniqueID> > waypoint_set;
1076 waypoint_set.insert(*iter);
1078 parammenu.items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoint"),
1083 sigc::mem_fun(*find_canvas_view(canvas),&studio::CanvasView::on_waypoint_clicked_canvasview),
1084 synfig::Waypoint::SIDE_UNSPECIFIED
1101 edit_several_waypoints(etl::handle<CanvasView> canvas_view, std::list<synfigapp::ValueDesc> value_desc_list)
1103 etl::handle<synfigapp::CanvasInterface> canvas_interface(canvas_view->canvas_interface());
1106 "Edit Multiple Waypoints", // Title
1108 true // use_separator
1111 Widget_WaypointModel widget_waypoint_model;
1112 widget_waypoint_model.show();
1114 dialog.get_vbox()->pack_start(widget_waypoint_model);
1117 dialog.add_button(Gtk::StockID("gtk-apply"),1);
1118 dialog.add_button(Gtk::StockID("gtk-cancel"),0);
1121 if(dialog.run()==0 || widget_waypoint_model.get_waypoint_model().is_trivial())
1123 synfigapp::Action::PassiveGrouper group(canvas_interface->get_instance().get(),_("Set Waypoints"));
1125 std::list<synfigapp::ValueDesc>::iterator iter;
1126 for(iter=value_desc_list.begin();iter!=value_desc_list.end();++iter)
1128 synfigapp::ValueDesc value_desc(*iter);
1130 if(!value_desc.is_valid())
1133 ValueNode_Animated::Handle value_node;
1135 // If this value isn't a ValueNode_Animated, but
1136 // it is somewhat constant, then go ahead and convert
1137 // it to a ValueNode_Animated.
1138 if(!value_desc.is_value_node() || ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node()))
1141 if(value_desc.is_value_node())
1142 value=ValueNode_Const::Handle::cast_dynamic(value_desc.get_value_node())->get_value();
1144 value=value_desc.get_value();
1146 value_node=ValueNode_Animated::create(value,canvas_interface->get_time());
1148 synfigapp::Action::Handle action;
1150 if(!value_desc.is_value_node())
1152 action=synfigapp::Action::create("value_desc_connect");
1153 action->set_param("dest",value_desc);
1154 action->set_param("src",ValueNode::Handle(value_node));
1158 action=synfigapp::Action::create("value_node_replace");
1159 action->set_param("dest",value_desc.get_value_node());
1160 action->set_param("src",ValueNode::Handle(value_node));
1163 action->set_param("canvas",canvas_view->get_canvas());
1164 action->set_param("canvas_interface",canvas_interface);
1167 if(!canvas_interface->get_instance()->perform_action(action))
1169 canvas_view->get_ui_interface()->error(_("Unable to convert to animated waypoint"));
1176 if(value_desc.is_value_node())
1177 value_node=ValueNode_Animated::Handle::cast_dynamic(value_desc.get_value_node());
1184 synfigapp::Action::Handle action(synfigapp::Action::create("waypoint_set_smart"));
1188 canvas_view->get_ui_interface()->error(_("Unable to find waypoint_set_smart action"));
1194 action->set_param("canvas",canvas_view->get_canvas());
1195 action->set_param("canvas_interface",canvas_interface);
1196 action->set_param("value_node",ValueNode::Handle(value_node));
1197 action->set_param("time",canvas_interface->get_time());
1198 action->set_param("model",widget_waypoint_model.get_waypoint_model());
1200 if(!canvas_interface->get_instance()->perform_action(action))
1202 canvas_view->get_ui_interface()->error(_("Unable to set a specific waypoint"));
1209 //get_canvas_view()->get_ui_interface()->error(_("Unable to animate a specific valuedesc"));
1218 Instance::make_param_menu(Gtk::Menu *menu,synfig::Canvas::Handle canvas,const std::list<synfigapp::ValueDesc>& value_desc_list)
1220 etl::handle<synfigapp::CanvasInterface> canvas_interface(find_canvas_interface(canvas));
1222 synfigapp::Action::ParamList param_list;
1223 param_list=canvas_interface->generate_param_list(value_desc_list);
1225 add_actions_to_menu(menu, param_list,synfigapp::Action::CATEGORY_VALUEDESC|synfigapp::Action::CATEGORY_VALUENODE);
1227 // Add the edit waypoints option if that might be useful
1228 if(canvas->rend_desc().get_time_end()-Time::epsilon()>canvas->rend_desc().get_time_start())
1230 menu->items().push_back(Gtk::Menu_Helpers::MenuElem(_("Edit Waypoints"),
1234 &edit_several_waypoints
1238 find_canvas_view(canvas)